alBufferData() menyetel AL_INVALID_OPERATION saat menggunakan ID buffer yang diperoleh dari alSourceUnqueueBuffers()

Saya mencoba melakukan streaming data audio dari disk menggunakan mekanisme antrian buffer OpenAL. Saya memuat dan memasukkan 4 buffer ke dalam antrean, memulai pemutaran sumber, dan memeriksa secara berkala untuk menyegarkan antrean. Semuanya tampak berjalan baik sekali, hingga pertama kali saya mencoba memuat data ke dalam buffer daur ulang yang saya dapatkan dari alSourceUnqueueBuffers(). Dalam situasi ini, alBufferData() selalu menyetel AL_INVALID_OPERATION, yang menurut v1 resmi. 1 spesifikasi, sepertinya tidak dapat dilakukan.

Saya telah mencari secara ekstensif di Google dan StackOverflow, dan sepertinya tidak dapat menemukan alasan mengapa hal ini bisa terjadi. Hal terdekat yang saya temukan adalah seseorang dengan masalah yang mungkin terkait dalam postingan forum yang diarsipkan, tetapi detailnya sedikit dan tanggapannya nol. Ada juga pertanyaan SO ini dengan keadaan yang sedikit berbeda, tetapi satu-satunya saran jawaban tidak membantu.

Mungkin membantu: Saya tahu konteks dan perangkat saya dikonfigurasi dengan benar, karena memuat file wav kecil sepenuhnya ke dalam buffer tunggal dan memutarnya berfungsi dengan baik. Melalui eksperimen, saya juga menemukan bahwa mengantri 2 buffer, memulai pemutaran sumber, dan segera memuat dan mengantrekan dua buffer lainnya tidak menimbulkan kesalahan; hanya ketika saya telah menghapus antrean buffer yang diproses, saya mengalami masalah.

Kode yang relevan:

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;
}

Dan inilah tutup layar beranotasi dari apa yang saya lihat di debugger ketika saya menemukan kesalahan:

Tampilan konten debugger yang dianotasi

Saya telah mencoba sedikit penyesuaian dan variasi, dan hasilnya selalu sama. Saya telah membuang-buang waktu berhari-hari untuk mencoba memperbaikinya. Tolong bantu :)


person Ionoclast Brigham    schedule 14.04.2014    source sumber


Jawaban (1)


Kesalahan ini terjadi ketika Anda mencoba mengisi buffer dengan data, ketika buffer masih dalam antrian ke sumber.

Juga kode ini salah.

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.
}

Anda dapat mengisi buffer dengan data hanya jika tidak ada antrian dari sumber. Namun blok if pertama Anda mendapatkan tBufId yang dimasukkan ke dalam antrean sumber. Tulis ulang kode seperti itu

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.
}
person mrDIMAS    schedule 01.11.2015
comment
Saya sebenarnya sudah lama beralih ke SDL Mixer, ketika sepertinya tidak ada seorang pun yang tahu apa yang mungkin terjadi dengan kode AL saya. Jika ada yang bisa ikut serta untuk memverifikasi jawaban ini, saya akan dengan senang hati menerimanya. - person Ionoclast Brigham; 03.11.2015