2014-04-14 27 views
2

获得缓冲区我试图使用OpenAL的缓冲区队列机制从磁盘流音频数据。我加载和排队4个缓冲区,启动源播放,并定期检查以刷新队列。一切看起来都很精彩,直到我第一次尝试将数据加载到我从alSourceUnqueueBuffers()获得的循环缓冲区中。在这种情况下,alBufferData()总是设置AL_INVALID_OPERATION,根据the official v1.1 spec,它似乎不应该能够做到。alBufferData()设置AL_INVALID_OPERATION当使用缓冲区ID从alSourceUnqueueBuffers()

我已经在谷歌和StackOverflow上进行了大量搜索,似乎找不到任何理由解释为什么会发生这种情况。我发现最接近的东西是a possibly-related issue在一个存档的论坛帖子中,但细节很少,回应为空。还有this SO question,情况稍有不同,但唯一的答案建议并没有帮助。

可能有帮助:我知道我的上下文和设备配置正确,因为将小的wav文件完全加载到单个缓冲区并播放它们可以正常工作。通过实验,我还发现排队2个缓冲区,启动源播放,并立即加载和排队其他两个缓冲区不会导致错误;只有当我排除了经过处理的缓冲区时才会遇到麻烦。

相关的代码:

static constexpr int MAX_BUFFER_COUNT = 4; 

#define alCall(funcCall) {funcCall; SoundyOutport::CheckError(__FILE__, __LINE__, #funcCall) ? abort() : ((void)0); } 

bool SoundyOutport::CheckError(const string &pFile, int pLine, const string &pfunc) 
{ 
    ALenum tErrCode = alGetError(); 
    if(tErrCode != 0) 
    { 
     auto tMsg = alGetString(tErrCode); 
     Log::e(ro::TAG) << tMsg << " at " << pFile << "(" << pLine << "):\n" 
         << "\tAL call " << pfunc << " failed." << end; 
     return true; 
    } 
    return false; 
} 

void SoundyOutport::EnqueueBuffer(const float* pData, int pFrames) 
{ 
    static int called = 0; 
    ++called; 

    ALint tState; 
    alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState)); 
    if(tState == AL_STATIC) 
    { 
     Stop(); 
//  alCall(alSourcei(mSourceId, AL_BUFFER, NULL)); 
    } 

    ALuint tBufId = AL_NONE; 
    int tQueuedBuffers = QueuedUpBuffers(); 
    int tReady = ProcessedBuffers(); 
    if(tQueuedBuffers < MAX_BUFFER_COUNT) 
    { 
     tBufId = mBufferIds[tQueuedBuffers]; 
    } 
    else if(tReady > 0) 
    { 
     // the fifth time through, this code gets hit 
     alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId)); 

     // debug code: make sure these values go down by one 
     tQueuedBuffers = QueuedUpBuffers(); 
     tReady = ProcessedBuffers(); 
    } 
    else 
    { 
     return; // no update needed yet. 
    } 

    void* tConverted = convert(pData, pFrames); 

    // the fifth time through, we get AL_INVALID_OPERATION, and call abort() 
    alCall(alBufferData(tBufId, mFormat, tConverted, pFrames * mBitdepth/8, mSampleRate)); 

    alCall(alSourceQueueBuffers(mSourceId, 1, &mBufferId)); 
    if(mBitdepth == BITDEPTH_8) 
    { 
     delete (uint8_t*)tConverted; 
    } 
    else // if(mBitdepth == BITDEPTH_16) 
    { 
     delete (uint16_t*)tConverted; 
    } 
} 

void SoundyOutport::PlayBufferedStream() 
{ 
    if(!StreamingMode() || !QueuedUpBuffers()) 
    { 
     Log::w(ro::TAG) << "Attempted to play an unbuffered stream" << end; 
     return; 
    } 

    alCall(alSourcei(mSourceId, AL_LOOPING, AL_FALSE)); // never loop streams 
    alCall(alSourcePlay(mSourceId)); 
} 

int SoundyOutport::QueuedUpBuffers() 
{ 
    int tCount = 0; 
    alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tCount)); 
    return tCount; 
} 

int SoundyOutport::ProcessedBuffers() 
{ 
    int tCount = 0; 
    alCall(alGetSourcei(mSourceId, AL_BUFFERS_PROCESSED, &tCount)); 
    return tCount; 
} 

void SoundyOutport::Stop() 
{ 
    if(Playing()) 
    { 
     alCall(alSourceStop(mSourceId)); 
    } 

    int tBuffers; 
    alCall(alGetSourcei(mSourceId, AL_BUFFERS_QUEUED, &tBuffers)); 
    if(tBuffers) 
    { 
     ALuint tDummy[tBuffers]; 
     alCall(alSourceUnqueueBuffers(mSourceId, tBuffers, tDummy)); 
    } 
    alCall(alSourcei(mSourceId, AL_BUFFER, AL_NONE)); 
} 

bool SoundyOutport::Playing() 
{ 
    ALint tPlaying; 
    alCall(alGetSourcei(mSourceId, AL_SOURCE_STATE, &tPlaying)); 
    return tPlaying == AL_PLAYING; 
} 

bool SoundyOutport::StreamingMode() 
{ 
    ALint tState; 
    alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState)); 
    return tState == AL_STREAMING; 
} 

bool SoundyOutport::StaticMode() 
{ 
    ALint tState; 
    alCall(alGetSourcei(mSourceId, AL_SOURCE_TYPE, &tState)); 
    return tState == AL_STATIC; 
} 

而这里的什么我在调试器中看到的注释的屏幕盖时,我打的错误:

Annotated view of debugger contents

我已经尝试了一堆小小的调整和变化,结果总是一样的。我浪费了太多时间来解决这个问题。请帮助:)

回答

0

当您尝试使用数据填充缓冲区时,当缓冲区仍然排队等待源时,会发生此错误。

此代码是错误的。

if(tQueuedBuffers < MAX_BUFFER_COUNT) 
{ 
    tBufId = mBufferIds[tQueuedBuffers]; 
} 
else if(tReady > 0) 
{ 
    // the fifth time through, this code gets hit 
    alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId)); 

    // debug code: make sure these values go down by one 
    tQueuedBuffers = QueuedUpBuffers(); 
    tReady = ProcessedBuffers(); 
} 
else 
{ 
    return; // no update needed yet. 
} 

只有当数据从源中消失时,才可以使用数据填充缓冲区。但你的第一个如果块得到tBufId排队的源。像重写代码,以便

if(tReady > 0) 
{ 
    // the fifth time through, this code gets hit 
    alCall(alSourceUnqueueBuffers(mSourceId, 1, &tBufId)); 

    // debug code: make sure these values go down by one 
    tQueuedBuffers = QueuedUpBuffers(); 
    tReady = ProcessedBuffers(); 
} 
else 
{ 
    return; // no update needed yet. 
} 
+0

其实我切换到SDL搅拌机很久以前,当似乎没有人知道什么可能是与我的AL代码。如果任何人都可以参加验证这个答案,我会很乐意接受它。 –