alBufferData() устанавливает AL_INVALID_OPERATION при использовании идентификатора буфера, полученного из alSourceUnqueueBuffers()
Я пытаюсь транслировать аудиоданные с диска, используя механизм очереди буфера OpenAL. Я загружаю и ставлю в очередь 4 буфера, запускаю исходное воспроизведение и регулярно проверяю, чтобы обновить очередь. Все выглядит великолепно, вплоть до первой попытки загрузить данные в переработанный буфер, полученный из alSourceUnqueueBuffers(). В этой ситуации alBufferData() всегда устанавливает AL_INVALID_OPERATION, что, в соответствии с официальной спецификацией v1.1, не должно быть в состоянии сделать это.
Я много искал в Google и Stackru, и не могу найти причину, по которой это могло бы произойти. Самым близким, что я обнаружил, был кто-то, возможно, связанный с проблемой в заархивированном сообщении на форуме, но подробностей немного, а ответы нулевые. Был также этот вопрос с несколько иными обстоятельствами, но предложение единственного ответа не помогает.
Возможно, полезно: я знаю, что мой контекст и устройство настроены правильно, потому что загрузка небольших файлов 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;
}
И вот аннотированная крышка экрана того, что я вижу в моем отладчике, когда я сталкиваюсь с ошибкой:
Я перепробовал несколько небольших изменений и вариаций, и результат всегда один и тот же. Я потратил слишком много дней, пытаясь это исправить. Пожалуйста помоги:)
1 ответ
Эта ошибка возникает, когда вы пытаетесь заполнить буфер данными, когда буфер все еще находится в очереди к источнику.
Также этот код неверен.
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.
}
Вы можете заполнить буфер данными, только если он отменен из источника. Но ваш первый блок if получает 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.
}