2011-09-08 113 views
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

+1

......我想通了。對於那些閱讀此內容的人,您需要像解碼器一樣將內存解碼器狀態和位(可能不是位)​​保存在內存中。我爲每個幀創建和銷燬解碼器的事實產生了這個「滴答聲」。現在一切正常。順便說一下...我不應該知道這一點,它不在文檔中。所以錯誤報告它。 –

+0

下一次,請提供實際答案,並在系統允許時接受它。我無法將你的評論轉換爲答案,所以我真的別無選擇,只能用一個不太合適的密切理由來解決問題。 –

回答

0

嗯,這樣說吧,如果你沒有需要保持相同的狀態爲你解碼幀,將有擺在首位無狀態對象。