Раздражающий тик с speex

Я делаю приложение, в котором я использую Speex, OpenAL и linsndfile.

Проблема в том, что когда я записываю буфер сэмплов в файл (raw pcm unsigned 16), используя libsndfile, все в порядке. Но если я кодирую их, а затем декодирую их с помощью Spexx, я получаю "галочку" между каждым сэмплом. Сначала я расскажу о потере выборки или слишком большом буфере. Но я ничего не нашел. Сначала код был в архитектуре с потоками наддува и был разделен на несколько классов. Даже несмотря на этот минимальный код, моя проблема остается.

Вот полный минимальный код, который создает проблему. Спасибо за помощь.

#include <iostream>
#include <stdexcept>
#include <vector>
#include <cstring>
#include <algorithm>
#include <sndfile.h>
#include <AL/al.h>
#include <AL/alc.h>
#include <speex/speex.h>

typedef std::vector<ALshort> Samples;

std::string chooseDevice()
{
    std::vector<std::string> ret;
    const ALCchar* DeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);

    int i = 0;
    if (DeviceList)
        while (strlen(DeviceList) > 0) {
            std::string tmp = DeviceList;
            ret.push_back(tmp);
            DeviceList += strlen(DeviceList) + 1;
            std::cout << i++  <<  ": " <<  tmp << std::endl;
        }
    std::cout << "Choose a device : " << std::flush;
    if (!(std::cin >> i))
        throw std::runtime_error("NaN");
    return ret[i];
}

SNDFILE* openFile()
{
    SNDFILE* file;
    SF_INFO fileInfo;
    fileInfo.channels = 1;
    fileInfo.samplerate = 8000;
    fileInfo.format = SF_FORMAT_PCM_16 | SF_FORMAT_WAV;
    file = sf_open("capture.wav", SFM_WRITE, &fileInfo);
    if (!file)
        throw std::runtime_error("SNDFILE error");
    return file;
}

enum Mode {DIRECT = 1, ENCODE_AND_DECODE};

void writeToFile(SNDFILE* file, const Samples& samples, const Mode mode)
{
    static std::vector<ALshort> buffer;
    switch (mode) {
        case DIRECT: sf_write_short(file, &samples[0], samples.size()); break;      // Ecriture ici
        case ENCODE_AND_DECODE: {
                std::copy(samples.begin(), samples.end(), std::back_inserter(buffer));
                int frameSize;
                void* encoderState = speex_encoder_init(&speex_wb_mode);
                speex_encoder_ctl(encoderState, SPEEX_GET_FRAME_SIZE, &frameSize);

                // AL pourrait donner trop ou pas assez d'échantillons
                while (buffer.size() >= frameSize) {
                    // encodage
                    void* encoderState = speex_encoder_init(&speex_wb_mode);
                    SpeexBits bits;
                    speex_encoder_ctl(encoderState, SPEEX_GET_FRAME_SIZE, &frameSize);
                    buffer.reserve(2*frameSize);
                    std::vector<char> output;
                    output.resize(2*frameSize);
                    speex_bits_init(&bits);
                    speex_encode_int(encoderState, &buffer[0], &bits);
                    int bytes = speex_bits_write(&bits, &output[0], output.size());
                    speex_bits_destroy(&bits);
                    speex_encoder_destroy(encoderState);

                    // décodage
                    speex_bits_init(&bits);
                    encoderState = speex_decoder_init(&speex_wb_mode);
                    speex_encoder_ctl(encoderState, SPEEX_GET_FRAME_SIZE, &frameSize);
                    speex_bits_read_from(&bits, &output[0], bytes);
                    std::vector<short> samples(frameSize); 
                    speex_decode_int(encoderState, &bits, &samples[0]);
                    sf_write_short(file, &samples[0], frameSize);                         // Ecriture ici
                    speex_decoder_destroy(encoderState);
                    speex_bits_destroy(&bits);
                    buffer.erase(buffer.begin(), buffer.begin()+frameSize);
                    std::cout << "." << std::flush;
                }
            }
            break;
    }
}

void closeFile(SNDFILE* file)
{
    sf_close(file);
    std::cout << "enregistré dans capture.wav" << std::endl;
}

int main()
{
    ALCdevice* device;
    ALCdevice* captureDevice;
    ALCcontext* context;

    device = alcOpenDevice(0);
    if (!device)
        throw std::runtime_error("Unable to open the AL device");
    context = alcCreateContext(device, 0);
    if (!context)
        throw std::runtime_error("Unable to create AL context");
    if (!alcMakeContextCurrent(context))
        throw std::runtime_error("Unable to set the context");
    if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_FALSE)
        throw std::runtime_error("AL Capture extension not available");
    captureDevice = alcCaptureOpenDevice(chooseDevice().c_str(), 8000, AL_FORMAT_MONO16, 8000);
    if (!captureDevice)
        throw std::runtime_error("Unable to open the capture device");

    Samples samples;

    SNDFILE* file = openFile();
    std::cout << "Mode direct (1) ou encodage et décodage ? (2) : " << std::endl;
    int i;
    std::cin >> i;
    Mode mode = Mode(i);
    time_t start = time(0);
    alcCaptureStart(captureDevice);
    while (time(0) - start < 5) {
        ALCint availableSamples;
        alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, 1, &availableSamples);
        if (availableSamples > 0) {
            samples.resize(availableSamples);
            alcCaptureSamples(captureDevice, &samples[0], availableSamples);
            writeToFile(file, samples, mode); 
        }
    }
    alcCaptureStop(captureDevice);
    closeFile(file);
    alcCaptureCloseDevice(captureDevice);
    alcMakeContextCurrent(0);
    alcDestroyContext(context);
    alcCloseDevice(device);
}

-lspeex -lsndfile -lopenal

1 ответ

Ну, скажем так: если вам не нужно сохранять одно и то же состояние для всех декодируемых кадров, в первую очередь не будет объекта состояния.

Другие вопросы по тегам