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