Annoying tick with speex [closed]
Asked Answered
V

1

2

I am making an application in which I use Speex, OpenAL and linsndfile.

The problem is that when i write a sample buffer in the file (raw pcm unsigned 16) using libsndfile everything is right.But if i encode them, then decode them using Spexx, I get like a 'tick' between each sample. I first though about a sample loss or some buffer being too big. But i did'nt find anything. At first the code was in an architecture with boost threads and was splitted in multiple classes. Even whith this minimal code, my problem is still.

Here is the full minimal code which produce the problem. Thanks for you help.

#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

Vesica answered 8/9, 2011 at 12:21 Comment(2)
Whell ... I figured it out. For those who read this, you need to keep your decoder state and your bits (maybe not the bits) in memory like the encoder. The fact that I was creating and destroying a decoder for each frame produced this "tick". Now everything is right. And by the way ... i wasn't supposed to know that and it's not in the documentation. So bug report it is.Vesica
Next time, please provide an actual answer and accept it once the system allows you to. I can't convert your comment into an answer, so I really have no choice but to close this out with a close reason that doesn't really fit.Polard
T
0

Well, put it this way, if you didn't need to keep the same state for all the frames you decode, there would be no state object in the first place.

Tamica answered 16/9, 2011 at 19:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.