В прошлой статье показал, как можно работать с 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’е.