Мой OpenAL C ++ аудио потоковый буфер блестит

Я впервые кодирую звук с помощью OpenAL в C ++.
Что я хочу сделать, так это создать бесконечную синусоидальную волну в виде двойной буферизации.
И проблема в том, что звук сверкающие / лаги. Я думаю, что это между буферизацией, и я не знаю, почему это так.

Мой код:

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, звук с блеском / лагами звучит с большим интервалом. Большое спасибо, если кто-то знает проблему и поделится ею 🙂

1

Решение

Я написал потоковые серверы OpenAL, так что я знаю вашу боль — мой инстинкт — подтвердить, что вы создали отдельные потоки для логики ввода / вывода, которая доступна для ваших потоковых аудиоданных — отдельно от потока для хранения вашего вышеуказанного кода 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 — это метод, вызываемый со своими входными параметрами, который обслуживает ввод / вывод для непрерывной подачи аудиоданных … launch_consumer_openal — это метод, запускаемый в своем собственном потоке, в котором вы создаете экземпляр своего класса OpenAL

0

Другие решения

Подобные проблемы есть во всем Интернете, и я не уверен на 100%, что это решение этой проблемы. Но это, вероятно, так, а если нет, то, по крайней мере, может помочь другим. Большинство других тем находятся на разных форумах, и я не регистрируюсь везде, чтобы поделиться своими знаниями …

Код ниже — это то, что я придумал после 2 дней экспериментов. Большинство решений, которые я нашел, не работало для меня …
(это не совсем мой код, я лишил его некоторых частей, специфичных для моего случая, поэтому я извиняюсь, если есть опечатки или аналогичные, которые мешают его копировать дословно)

Мои эксперименты были на iPhone. Некоторые из вещей, которые я обнаружил, могут быть специфичными для iOS.

Проблема в том, что нет гарантии, в какой момент обработанный буфер помечается как таковой и доступен для отмены очереди. Пытаясь создать версию, которая спит до тех пор, пока буфер не станет снова доступным, я увидел, что это может быть намного (я использую очень маленькие буферы) позже, чем ожидалось. Поэтому я понял, что распространенная идея ждать, пока буфер не станет доступным (который работает для большинства фреймворков, но не openAL), неверна. Вместо этого вам следует подождать, пока вы не поставите в очередь другой буфер.
При этом вы должны отказаться от идеи двойной буферизации. Когда придет время, вы должны проверить, существует ли буфер, и снять его. Но если ничего не доступно, вам нужно создать третий …

Ожидание, когда буфер должен быть поставлен в очередь, может быть выполнено путем вычисления времени относительно системных часов, что для меня довольно неплохо, но я решил выбрать версию, в которой я полагаюсь на источник времени, который определенно синхронизирован с openAL. Лучшее, что я придумал, это ожидание в зависимости от того, что осталось в очереди. Здесь iOS выглядит не полностью в соответствии со спецификацией openAL, потому что AL_SAMPLE_OFFSET должен быть точным для одного образца, но я никогда не видел ничего, кроме кратных 2048. Это примерно 45 микросекунд при 44100, отсюда 50000 в коде (немного больше чем самый маленький блок iOS обрабатывает)
В зависимости от размера блока это может легко быть больше. Но с этим кодом у меня было 3 раза, что alSourcePlay () снова был нужен за последние ~ час (по сравнению с 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;
}
0

По вопросам рекламы [email protected]