В прошлой статье показал, как можно работать с WAV и Ogg форматами в ndk с помощью OpenaAL. Вот только в той реализации файлы целиком в память грузились. Кое-что дописал для работы с WAV, теперь файл можно грузить кусками по мере необходимости.
Вообще, изначально пытался создать несколько буферов, а потом, когда заканчивает играть один, переключался на другой. При таком способе в момент переключения между буферами слышен щелчок, так что этот вариант пришлось сразу отбросить. Как оказалось, в OpenAL можно сразу очередь буферов настроить (надо приучить себя сначала документацию читать, перед тем, как свои костыли городить).
Небольшое отступление
Этот код по работе с OpenAL уже вышел за рамки туториала, поэтому решил как отдельный проект сделать. Может позже по фану и работу с другими форматами сделаю (с тем же mp3, к примеру). Сам проект можно посмотреть на github’е.
Для стриминга начальную инициализацию необходимо слегка поменять:
//создаём сорс и буферыalGenBuffers(BUFF_COUNT, buffers);alGenSources(1, &source);if(alGetError() != AL_NO_ERROR){ LOGI("Error generating :(\n"); return;}//создаём буферыfor(int i=0;i<BUFF_COUNT;++i) creatBuffer(i);if(alGetError() != AL_NO_ERROR) { LOGI("Error loading :(\n"); return ;}//ставим в очередь к источнику source буферыalSourceQueueBuffers(source, BUFF_COUNT, buffers);if(alGetError() != AL_NO_ERROR) { LOGI("Error alSourceQueueBuffers :(\n"); return;}
Создаём столько буферов, сколько нам необходимо.
void OALWav ::creatBuffer(int index){ //определям размер данных для чтения int size = Min(BUFFER_SIZE, header.dataSize -curPos); //читаем чанки unsigned char * data = readRiffs(size); if(!data)return; //создаём буферы createBufferFromWave(data,size, index);}
Ну и ещё метод по чтению куска файла:
Чтение куска WAV файла
unsigned char* OALWav::readRiffs(int size){if(curPos>=header.dataSize) {f.close();return 0;}if (!(// Заголовки должны быть валидны.// Проблема в том, что не всегда так.// Многие конвертеры недобросовестные пихают в эти заголовки свои логотипы =/memcmp("RIFF",header.riff,4) ||memcmp("WAVE",header.wave,4) ||memcmp("fmt ",header.fmt,4) ||memcmp("data",header.data,4))){if (buf){int r = f.read(buf,size,1);if(r){curPos +=r;return buf;}free(buf);}}f.close();return 0;}
Теперь остаётся только проиграть аудио файл:
//начинаем игратьalSourcePlay(source);//если ещё не весь файл прочиталиwhile(curPos<header.dataSize) {ALint val;ALuint buffer;//проверяем, какие буферы уже отыгралиalGetSourcei(source, AL_BUFFERS_PROCESSED, &val);if(val <= 0)continue;//для каждого проигранного буфераwhile(val--) {//размер данных для чтенияint size = Min(BUFFER_SIZE, header.dataSize -curPos);//читаем новые чанкиunsigned char * data = readRiffs(size);//дропаем из очереди отыгравший буферalSourceUnqueueBuffers(source, 1, &buffer);//создаём его по новой с новыми чанкамиalBufferData(buffer, format, data, size, header.samplesPerSec);//ставим в очередь к источнику source буферыalSourceQueueBuffers(source, 1, &buffer);if(alGetError() != AL_NO_ERROR) {LOGI("Error buffering :(\n");}}}
Надо бы тоже самое и для Ogg сделать, как время будет. Сам проект, как уже выше писал, можно посмотреть на github’е.