讨厌的速度蜱虫

Annoying tick with speex

本文关键字:速度 讨厌      更新时间:2023-10-16

我正在制作一个使用Speex, OpenAL和linsndfile的应用程序。

问题是,当我使用libsndfile在文件(原始pcm unsigned 16)中写入示例缓冲区时,一切都是正确的。但如果我对它们进行编码,然后使用Spexx进行解码,我就会在每个样本之间得到一个"刻度"。我首先想到的是样本丢失或缓冲区太大。但是我什么也没找到。起初,代码是在一个带有boost线程的体系结构中,并被拆分为多个类。即使有了这么少的代码,我的问题仍然是。

下面是产生这个问题的完整的最小代码。谢谢你的帮助。

#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

这么说,如果你不需要为你解码的所有帧保持相同的状态,那么首先就不会有状态对象了