Раздражающий тик с 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 ответ
Ну, скажем так: если вам не нужно сохранять одно и то же состояние для всех декодируемых кадров, в первую очередь не будет объекта состояния.