2
我正在使用Speex,OpenAL和linsndfile的應用程序。令人討厭的刻度與speex
問題是,當我使用libsndfile在文件(原始pcm無符號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
......我想通了。對於那些閱讀此內容的人,您需要像解碼器一樣將內存解碼器狀態和位(可能不是位)保存在內存中。我爲每個幀創建和銷燬解碼器的事實產生了這個「滴答聲」。現在一切正常。順便說一下...我不應該知道這一點,它不在文檔中。所以錯誤報告它。 –
下一次,請提供實際答案,並在系統允許時接受它。我無法將你的評論轉換爲答案,所以我真的別無選擇,只能用一個不太合適的密切理由來解決問題。 –