简体   繁体   English

如何使用 QByteArray 而不是 SNDFILE

[英]How to use QByteArray instead of SNDFILE

I want to use "fvad" library to detect audio silence part, but I have QByteArray instead of SNDFILE.我想使用“fvad”库来检测音频静音部分,但我有 QByteArray 而不是 SNDFILE。 How can I do that?我怎样才能做到这一点?

sf_read_double(infile, buf0, framelen)

[ https://github.com/dpirch/libfvad/blob/master/examples/fvadwav.c][1] [ https://github.com/dpirch/libfvad/blob/master/examples/fvadwav.c][1]

This is my code: /* * Copyright (c) 2016 Daniel Pirch * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree.这是我的代码:/* * 版权所有 (c) 2016 Daniel Pirch * * 此源代码的使用受 BSD 样式许可 * 管理,该许可可在源 * 树根目录的 LICENSE 文件中找到。 An additional intellectual property rights grant can be found * in the file PATENTS.可以在文件 PATENTS.* 中找到额外的知识产权授予。 All contributing project authors may * be found in the AUTHORS file in the root of the source tree.所有有贡献的项目作者都可以在源代码树的根目录的 AUTHORS 文件中找到。 */ */

 #define _POSIX_C_SOURCE 200809L

#include <fvad.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sndfile.h>

static bool process_sf(SNDFILE *infile, Fvad *vad,
    size_t framelen, SNDFILE *outfiles[2], FILE *listfile)
{
    bool success = false;
    double *buf0 = NULL;
    int16_t *buf1 = NULL;
    int vadres, prev = -1;
    long frames[2] = {0, 0};
    long segments[2] = {0, 0};

    if (framelen > SIZE_MAX / sizeof (double)
            || !(buf0 = malloc(framelen * sizeof *buf0))
            || !(buf1 = malloc(framelen * sizeof *buf1))) {
        fprintf(stderr, "failed to allocate buffers\n");
        goto end;
    }

    while (sf_read_double(infile, buf0, framelen) == (sf_count_t)framelen) {

        // Convert the read samples to int16
        for (size_t i = 0; i < framelen; i++)
            buf1[i] = buf0[i] * INT16_MAX;

        vadres = fvad_process(vad, buf1, framelen);
        if (vadres < 0) {
            fprintf(stderr, "VAD processing failed\n");
            goto end;
        }

        if (listfile) {
            fprintf(listfile, "%d\n", vadres);
        }

        vadres = !!vadres; // make sure it is 0 or 1

        if (outfiles[vadres]) {
            sf_write_double(outfiles[!!vadres], buf0, framelen);
        }

        frames[vadres]++;
        if (prev != vadres) segments[vadres]++;
        prev = vadres;
    }

    printf("voice detected in %ld of %ld frames (%.2f%%)\n",
        frames[1], frames[0] + frames[1],
        frames[0] + frames[1] ?
            100.0 * ((double)frames[1] / (frames[0] + frames[1])) : 0.0);
    printf("%ld voice segments, average length %.2f frames\n",
        segments[1], segments[1] ? (double)frames[1] / segments[1] : 0.0);
    printf("%ld non-voice segments, average length %.2f frames\n",
        segments[0], segments[0] ? (double)frames[0] / segments[0] : 0.0);

    success = true;

end:
    if (buf0) free(buf0);
    if (buf1) free(buf1);
    return success;
}




static bool parse_int(int *dest, const char *s, int min, int max)
{
    char *endp;
    long val;

    errno = 0;
    val = strtol(s, &endp, 10);
    if (!errno && !*endp && val >= min && val <= max) {
        *dest = val;
        return true;
    } else {
        return false;
    }
}


int main(int argc, char *argv[])
{
    int retval;
    const char *in_fname, *out_fname[2] = {NULL, NULL}, *list_fname = NULL;
    SNDFILE *in_sf = NULL, *out_sf[2] = {NULL, NULL};
    SF_INFO in_info = {0}, out_info[2];
    FILE *list_file = NULL;
    int mode, frame_ms = 10;
    Fvad *vad = NULL;

    /*
     * create fvad instance
     */
    vad = fvad_new();
    if (!vad) {
        fprintf(stderr, "out of memory\n");
        goto fail;
    }

    /*
     * parse arguments
     */
    for (int ch; (ch = getopt(argc, argv, "m:f:o:n:l:h")) != -1;) {
        switch (ch) {
        case 'm':
            if (!parse_int(&mode, optarg, 0, 3) || fvad_set_mode(vad, mode) < 0) {
                fprintf(stderr, "invalid mode '%s'\n", optarg);
                goto argfail;
            }
            break;
        case 'f':
            if (!parse_int(&frame_ms, optarg, 10, 30) || frame_ms % 10 != 0) {
                fprintf(stderr, "invalid frame length '%s'\n", optarg);
                goto argfail;
            }
            break;
        case 'o':
            out_fname[1] = optarg;
            break;
        case 'n':
            out_fname[0] = optarg;
            break;
        case 'l':
            list_fname = optarg;
            break;
        case 'h':
            printf(
                "Usage: %s [OPTION]... FILE\n"
                "Reads FILE in wav format and performs voice activity detection (VAD).\n"
                "Options:\n"
                "  -m MODE      set VAD operating mode (aggressiveness) (0-3, default 0)\n"
                "  -f DURATION  set frame length in ms (10, 20, 30; default 10)\n"
                "  -o FILE      write detected voice frames to FILE in wav format\n"
                "  -n FILE      write detected non-voice frames to FILE in wav format\n"
                "  -l FILE      write list of per-frame detection results to FILE\n"
                "  -h           display this help and exit\n",
                argv[0]);
            goto success;

        default: goto argfail;
        }
    }

    if (optind >= argc) {
        fprintf(stderr, "input file expected\n");
        goto argfail;
    }

    in_fname = argv[optind++];

    if (optind < argc) {
        fprintf(stderr, "unexpected argument '%s'; only one input file expected\n", argv[optind]);
        goto argfail;
    }

    /*
     * open and check input file
     */
    in_sf = sf_open(in_fname, SFM_READ, &in_info);
    if (!in_sf) {
        fprintf(stderr, "Cannot open input file '%s': %s\n", in_fname, sf_strerror(NULL));
        goto fail;
    }

    if (in_info.channels != 1) {
        fprintf(stderr, "only single-channel wav files supported; input file has %d channels\n", in_info.channels);
        goto fail;
    }

    if (fvad_set_sample_rate(vad, in_info.samplerate) < 0) {
        fprintf(stderr, "invalid sample rate: %d Hz\n", in_info.samplerate);
        goto fail;
    }

    /*
     * open required output files
     */
    for (int i = 0; i < 2; i++) {
        if (out_fname[i]) {
            out_info[i] = (SF_INFO){
                .samplerate = in_info.samplerate,
                .channels = 1,
                .format = SF_FORMAT_WAV | SF_FORMAT_PCM_16
            };
            out_sf[i] = sf_open(out_fname[i], SFM_WRITE, &out_info[i]);
            if (!out_sf[i]) {
                fprintf(stderr, "Cannot open output file '%s': %s\n", out_fname[i], sf_strerror(NULL));
                goto fail;
            }
        }
    }

    if (list_fname) {
        list_file = fopen(list_fname, "w");
        if (!list_file) {
            fprintf(stderr, "Cannot open output file '%s': %s\n", list_fname, strerror(errno));
            goto fail;
        }
    }

    /*
     * run main loop
     */
    if (!process_sf(in_sf, vad,
            (size_t)in_info.samplerate / 1000 * frame_ms, out_sf, list_file))
        goto fail;

    /*
     * cleanup
     */
success:
    retval = EXIT_SUCCESS;
    goto end;

argfail:
    fprintf(stderr, "Try '%s -h' for more information.\n", argv[0]);
fail:
    retval = EXIT_FAILURE;
    goto end;

end:
    if (in_sf) sf_close(in_sf);
    for (int i = 0; i < 2; i++)
        if (out_sf[i]) sf_close(out_sf[i]);
    if (list_file) fclose(list_file);
    if (vad) fvad_free(vad);

    return retval;
}

You should use sf_open_virtual instead of sf_open to create the SNDFILE instance.您应该使用sf_open_virtual而不是sf_open来创建SNDFILE实例。 So, as per the documentation, you should pass SF_VIRTUAL_IO* to sf_open_virtual .因此,根据文档,您应该将SF_VIRTUAL_IO*传递给sf_open_virtual

The definition for each member of that struct, have to be implemented to reflect your QBuffer.该结构的每个成员的定义必须实现以反映您的 QBuffer。 So, you need to create a helper class that accepts a reference to a QBuffer and implements methods with the same footprint as follows.因此,您需要创建一个辅助类,该类接受对 QBuffer 的引用,并实现具有相同足迹的方法,如下所示。

  typedef sf_count_t  (*sf_vio_get_filelen) (void *user_data) ;
  typedef sf_count_t  (*sf_vio_seek)        (sf_count_t offset, int whence, void *user_data) ;
  typedef sf_count_t  (*sf_vio_read)        (void *ptr, sf_count_t count, void *user_data) ;
  typedef sf_count_t  (*sf_vio_write)       (const void *ptr, sf_count_t count, void *user_data) ;
  typedef sf_count_t  (*sf_vio_tell)        (void *user_data) ;

Here is an implementation of functions you need to pass to the structure:这是您需要传递给结构的函数的实现:

static sf_count_t
qbuffer_get_filelen (void *user_data)
{   QBuffer *buff = (QBuffer *) user_data ;
    return buff->size();
}

static sf_count_t
qbuffer_seek (sf_count_t offset, int whence, void *user_data)
{
    QBuffer *buff = (QBuffer *) user_data ;
    switch (whence)
    {   case SEEK_SET :
        buff->seek(offset);
        break ;

    case SEEK_CUR :
         buff->seek(buff->pos()+offset);
        break ;

    case SEEK_END :
        buff->seek(buff->size()+offset);
        break ;
    default :
        break ;
    } ;
    return buff->pos();
}

static sf_count_t
qbuffer_read (void *ptr, sf_count_t count, void *user_data)
{
    QBuffer *buff = (QBuffer *) user_data ;
    return buff->read((char*)ptr,count);

}
static sf_count_t
qbuffer_write (const void *ptr, sf_count_t count, void *user_data)
{
    QBuffer *buff = (QBuffer *) user_data ;
    return buff->write((const char*)ptr,count);
}

static sf_count_t
qbuffer_tell (void *user_data)
{
    QBuffer *buff = (QBuffer *) user_data ;
    return buff->pos() ;
}

And you can create the structure like this:你可以创建这样的结构:

    SF_VIRTUAL_IO qbuffer_virtualio ;
    qbuffer_virtualio.get_filelen = qbuffer_get_filelen ;
    qbuffer_virtualio.seek = qbuffer_seek ;
    qbuffer_virtualio.read = qbuffer_read ;
    qbuffer_virtualio.write = qbuffer_write ;
    qbuffer_virtualio.tell = qbuffer_tell ;

Then when you are calling sf_open_virtual然后当你调用sf_open_virtual

    QBuffer buffer(&yourArray);
    buffer.open(QIODevice::ReadWrite);
    sf_open_virtual (&qbuffer_virtualio , mode, sfinfo, (void *)(&buffer));
    // doing whatever
    // you may want to close the buffer 
    buffer.close();

You may also put those interface function is a class with the name of QBuffer_SFVIRTUAL_Interface and reference them like:你也可以把那些接口函数放在一个名为QBuffer_SFVIRTUAL_Interface的类中,并像这样引用它们:

    qbuffer_virtualio.get_filelen = QBuffer_SFVIRTUAL_Interface::qbuffer_get_filelen ;
    qbuffer_virtualio.seek = QBuffer_SFVIRTUAL_Interface::qbuffer_seek ;
    qbuffer_virtualio.read = QBuffer_SFVIRTUAL_Interface::qbuffer_read ;
    qbuffer_virtualio.write = QBuffer_SFVIRTUAL_Interface::qbuffer_write ;
    qbuffer_virtualio.tell = QBuffer_SFVIRTUAL_Interface::qbuffer_tell ;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM