2014-06-29 53 views
1

我第一次在C++中使用OpenAL生成編碼聲音。 我想要做的是產生無限的竇波成雙緩衝方式。 而問題是,聲音是閃爍/滯後。我認爲這是在緩衝之間,我不知道爲什麼它是這樣的。我的OpenAL C++音頻流緩衝區gliching

我的代碼:

void _OpenALEngine::play() 
{ 
    if(!m_running && !m_threadRunning) 
    { 
     ALfloat sourcePos[] = {0,0,0}; 
     ALfloat sourceVel[] = {0,0,0}; 
     ALfloat sourceOri[] = {0,0,0,0,0,0}; 
     alGenSources(1, &FSourceID); 
     alSourcefv (FSourceID, AL_POSITION, sourcePos); 
     alSourcefv (FSourceID, AL_VELOCITY, sourceVel); 
     alSourcefv (FSourceID, AL_DIRECTION, sourceOri); 
     GetALError(); 

     ALuint FBufferID[2]; 
     alGenBuffers(2, &FBufferID[0]); 
     GetALError(); 

     // Gain 
     ALfloat listenerPos[] = {0,0,0}; 
     ALfloat listenerVel[] = {0,0,0}; 
     ALfloat listenerOri[] = {0,0,0,0,0,0}; 
     alListenerf(AL_GAIN, 1.0); 
     alListenerfv(AL_POSITION, listenerPos); 
     alListenerfv(AL_VELOCITY, listenerVel); 
     alListenerfv(AL_ORIENTATION, listenerOri); 
     GetALError(); 

     alSourceQueueBuffers(FSourceID, 2, &FBufferID[0]); 
     GetALError(); 

     alSourcePlay(FSourceID); 
     GetALError(); 

     m_running = true; 
     m_threadRunning = true; 
     Threading::Thread thread(Threading::ThreadStart(this, &_OpenALEngine::threadPlaying)); 
     thread.Start(); 
    } 
} 

Void _OpenALEngine::threadPlaying() 
{ 

    while(m_running) 
    { 
     // Check how much data is processed in OpenAL's internal queue. 
     ALint Processed; 
     alGetSourcei(FSourceID, AL_BUFFERS_PROCESSED, &Processed); 
     GetALError(); 

     // Add more buffers while we need them. 
     while (Processed--) 
     { 
      alSourceUnqueueBuffers(FSourceID, 1, &BufID); 

      runBuffer(); // <--- Generate the sinus wave and submit the Array to the submitBuffer method. 

      alSourceQueueBuffers(FSourceID, 1, &BufID); 

      ALint val; 
      alGetSourcei(FSourceID, AL_SOURCE_STATE, &val); 
      if(val != AL_PLAYING) 
      { 
       alSourcePlay(FSourceID); 
      } 
     } 

     // Don't kill the CPU. 
     Thread::Sleep(1); 
    } 

    m_threadRunning = false; 

    return Void(); 
} 

void _OpenALEngine::submitBuffer(byte* buffer, int length) 
{ 
    // Submit more data to OpenAL 
    alBufferData(BufID, AL_FORMAT_MONO8, buffer, length * sizeof(byte), 44100); 
} 

我生成在runBuffer()方法的正弦波。而正弦發生器是正確的,因爲當我將緩衝區陣列從4096增加到40960時,閃爍/滯後聲音的間隔更大。非常感謝你,如果有人知道這個問題,並會分享它:)

+0

讓我們知道如何爲解決 –

+0

沒有抱歉,我沒有得到它的工作。現在我使用Windows多媒體庫,而不是現在: http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3 但很快我會再試一次並解決它。 – Alexander

回答

0

我已經寫了OpenAL流媒體服務器,所以我知道你的痛苦 - 我的直覺是確認你已經產生了單獨的線程的I/O邏輯哪些可用您的流媒體音頻數據 - 與保持上述OpenAL代碼的線程分開?如果沒有,這會導致你的症狀。下面是一個簡單的推出每個邏輯塊到自己的線程:

std::thread t1(launch_producer_streaming_io, chosen_file, another_input_parm); 

std::this_thread::sleep_for (std::chrono::milliseconds(100)); 

std::thread t2(launch_consumer_openal, its_input_parm1, parm2); 

// ------------------------- 

t1.join(); 
t2.join(); 

其中launch_producer_streaming_io是一個方法被調用,其輸入PARMS其業務的輸入/輸出,不斷提供音頻數據... launch_consumer_openal是方法在它自己的線程中啓動,在那裏你實例化你的OpenAL類

0

類似的問題都在互聯網上,我不是100%肯定這是解決這個問題。但它可能是,如果不是,它至少可以幫助別人。大多數其他線程都在不同的論壇上,我沒有到處註冊,只是爲了分享我的知識......

下面的代碼是我在2天的實驗後得出的結果。我找到的大多數解決方案都不適合我... (這不完全是我的代碼,我剝離了一些特殊的部分,所以我很抱歉,如果有拼寫錯誤或類似的問題,以防止它被逐字複製)

我的實驗是在iPhone上進行的。我發現的一些事情可能是iOS特有的。

問題在於,在處理過的緩衝區標記爲何處並沒有保證,並且可用於排隊。試圖構建一個睡眠版本,直到一個緩衝區再次可用,我看到這可能比預期的晚得多(我使用非常小的緩衝區)。所以我意識到等待緩衝區可用的常見想法(對大多數框架有效,但不適用於openAL)是錯誤的。相反,您應該等到您應該排隊另一個緩衝區的時間。因此,你必須放棄雙緩衝的想法。當時間到了,你應該檢查一個緩衝區是否存在並且將其解除排隊。但如果沒有可用的,你需要創建一個第三...

等待緩衝區應該入隊的時間可以通過計算相對於系統時鐘的時間,這對我來說工作得很好,但我決定去對於那些依賴與openAL同步的時間源的版本。我想出的最好是等待隊列中留下的內容。在這裏,iOS似乎並不完全符合openAL規範,因爲AL_SAMPLE_OFFSET應該精確到一個樣本,但我從來沒有看到任何東西,只有2048的倍數。這大約是44100微秒的45微秒,這就是代碼中50000的來源(稍微多一點而不是最小單位的iOS手柄) 根據塊的大小,這可以很容易地變大。但是,與該代碼我有alSourcePlay()再次需要在最後〜小時的3倍(相比,自稱是該解決方案高達10%與其他實現分鐘)

uint64 enqueued(0); // keep track of samples in queue 
while (bKeepRunning) 
{ 
    // check if enough in buffer and wait 
    ALint off; 
    alGetSourcei(m_Source, AL_SAMPLE_OFFSET, &off); 
    uint32 left((enqueued-off)*1000000/SAMPLE_RATE); 
    if (left > 50000) // at least 50000 mic-secs in buffer 
    usleep(left - 50000); 

    // check for available buffer 
    ALuint buffer; 

    ALint processed; 
    alGetSourcei(m_Source, AL_BUFFERS_PROCESSED, &processed); 
    switch (processed) 
    { 
    case 0: // no buffer to unqueue->create new 
    alGenBuffers(1, &buffer); 
    break; 
    case 1: // on buffer to unqueue->use that 
    alSourceUnqueueBuffers(m_Source, 1, &buffer); 
    enqueued -= BLOCK_SIZE_SAMPLES; 
    break; 
    default: // multiple buffers to unqueue->take one,delete on 
    {  // could also delete more if processed>2 
      // but doesn't happen often 
      // therefore simple implementation(will del. in next loop) 
     ALuint bufs[2]; 
     alSourceUnqueueBuffers(m_Source, 2, bufs); 

     alDeleteBuffers(1, bufs); 
     buffer = bufs[1]; 
     enqueued -= 2*BLOCK_SIZE_SAMPLES; 
    } 
    break; 
    } 

    // fill block 
    alBufferData(buffer, AL_FORMAT_STEREO16, pData, 
       BLOCK_SIZE_SAMPLES*4, SAMPLE_RATE); 
    alSourceQueueBuffers(m_Source, 1, &buffer); 

    //check state 
    ALint state; 
    alGetSourcei(m_Source, AL_SOURCE_STATE, &state); 
    if (state != AL_PLAYING) 
    { 
    enqueued = BLOCK_SIZE_SAMPLES; 
    alSourcePlay(m_Source); 
    } 
    else 
    enqueued += BLOCK_SIZE_SAMPLES; 
}