Потоковое аудио с низкой задержкой на Android

Краткая версия: я разрабатываю приложение для синтезатора и использую Opensl с низкой задержкой. Я выполнял все аудио расчеты в функции обратного вызова Opensl (я знаю, что не должен, но я все равно это сделал). Теперь вычисления занимают около 75% процессорного времени на моем Nexus 4, поэтому следующим шагом будет выполнение всех вычислений в нескольких потоках.

Проблема, с которой я столкнулся, заключалась в том, что звук начал заикаться, поскольку поток обратного вызова, очевидно, работает с высоким приоритетом, а мой новый поток - нет. Если я использую больше/больше буферов, проблема исчезает, но и в реальном времени тоже. Установка более высокого приоритета для нового потока, похоже, не работает. Итак, возможно ли вообще сделать потоковое аудио с малой задержкой или мне нужно сделать все в обратном вызове, чтобы он работал?

У меня есть буфер из 256 выборок, и это около 5 мс, и это должно быть возрастом для планировщика потоков, чтобы запустить мой поток вычислений.


person Floaf    schedule 07.06.2013    source источник
comment
Почему вы говорите, что не должны выполнять все вычисления звука в обратном вызове? Я не знаком с Opensl, но в большинстве аудиобиблиотек это именно то, что вы ДЕЙСТВИТЕЛЬНО хотите делать. Вам следует избегать блокировки на произвольное время (например, ввода-вывода).   -  person Bjorn Roche    schedule 07.06.2013
comment
@Bjorn Roche: Если вам нужна низкая задержка, вам нужно выполнить синтез в обратном вызове. Если вы делаете это асинхронно, вам потребуется больше буферизации, что в конечном итоге удвоит системную задержку.   -  person marko    schedule 07.06.2013
comment
В каждом учебнике, который я читал, говорилось, что я должен выполнять очень мало задач в обратном вызове. И на самом деле удалось разбить Android (до такой степени, что появился белый текст с Google), и это воспроизводилось каждый раз, когда я слишком много обрабатывал в обратном вызове. И в openSL обратный вызов просто говорит, что ему нужно больше данных до того, как буфер закончится, поэтому у меня должно быть достаточно времени, чтобы разбудить другой поток, чтобы выполнить работу. Я имею в виду, что на четырехъядерном телефоне по крайней мере одно ядро ​​должно простаивать и быть готовым к работе с мунбером.   -  person Floaf    schedule 07.06.2013


Ответы (1)


Я думаю, что фундаментальная проблема заключается в производительности вашего синтезаторного движка. Приличное количество каналов с процессором Cortex-A8 или -A9 достижимо с одним ядром. На каком языке вы это реализовали? Если это Java, я рекомендую портировать его на C++.

Использование нескольких потоков для синтеза, безусловно, возможно, но влечет за собой новые проблемы, а именно то, что каждый поток должен синхронизироваться, прежде чем можно будет микшировать сгенерированный звук.

Если вы не возьмете на себя дополнительную задержку, связанную с асинхронным запуском потоков синтеза, вероятная установка заключается в том, что в обратном вызове рендеринга вы сигнализируете дополнительным потокам синтеза, а затем ждете их завершения, прежде чем микшировать звук из все вместе.

(очевидная оптимизация заключается в том, что обратный вызов рендеринга выполняет часть обработки сам, поскольку он уже выполняется на ЦП и в противном случае ничего не делал бы).

Вот в чем проблема. Если вы не можете быть уверены, что ваши потоки рендеринга синтезатора выполняются с приоритетом в реальном времени, вы потенциально можете получить попадание по планированию каждый раз, когда запускается обратный вызов рендеринга, и потенциально еще один, если вы заблокируете поток обратного вызова, ожидая, пока потоки рендеринга синтезатора догонят.

В прошлый раз, когда я смотрел аудио на Android, у Bionic не было средства установки приоритета потока в реальном времени (например, SCHED_FIFO). В любом случае, разрешено ли это вообще, зависит от политики операционной системы: в настольной системе Linux вам либо нужно быть пользователем root, либо настроить соответствующий ulimit (как root) — я не уверен, что здесь делает Android, но я очень подозреваю, что загруженным приложениям по умолчанию не дается это разрешение. Ни другое полезное разрешение, которое заключается в mlock() коде и его вероятном стеке, необходимом в физической памяти.

person marko    schedule 07.06.2013
comment
Да, он написан на C++ и да, один экземпляр движка может работать на одном ядре. Идея состоит (была ли?) в том, чтобы запустить несколько экземпляров синтезатора, где другие экземпляры будут запускаться секвенсором. Вот почему мне нужна дополнительная вычислительная мощность. Но, с другой стороны, я, вероятно, мог бы вычислить/рендерить их на несколько буферов вперед и просто иметь дело с основным экземпляром синтезатора в обратном вызове. - person Floaf; 07.06.2013
comment
И вы правы насчет приоритета потока, приложение может только понизить его приоритет, а не повысить его. Думаю, я немного расстроен, так как поток обратного вызова работает с высоким приоритетом. - person Floaf; 07.06.2013
comment
@Floaf: этот подход будет работать нормально - на самом деле, вы можете немного предварительно рассчитать. Вы можете подумать о том, можно ли распараллелить ваш синтезаторный движок, воспроизводя два голоса одновременно — таким образом вы, вероятно, получите гораздо лучшее использование NEON. В любом случае есть много дешевых выигрышей. Еще один совет: если у вас есть под рукой iPad или iPhone, инструменты профилирования Apple намного лучше, чем что-либо на Android — вы можете буквально профилировать до уровня инструкций и найти дорогие детали. вашего алгоритма - зная, что результаты будут близки для других устройств ARM. - person marko; 08.06.2013
comment
Я немного изучал NEON, так как, например, алгоритм реверберации потребляет около 12% мощности процессора, и это не нормально. И было бы интересно изучить NEON. Меня беспокоит только то, что я должен сделать то же самое для процессоров Intel в последних телефонах. Так много нужно узнать, так мало времени. - person Floaf; 08.06.2013
comment
@Floaf Я бы посоветовал вам максимально оптимизировать C/C++, чтобы избежать именно этой проблемы. Однако, если честно, телефонов с архитектурой x86 не так много, и вряд ли это изменится в ближайшее время. - person marko; 08.06.2013
comment
Я согласен с тем, что оптимизация C/C++ оптимальна. Если вы используете компилятор с автоматической векторизацией, вы можете полагаться на некоторые из ваших циклов обработки звука, которые легко векторизуются для регистрации NEON. - person omygaudio; 10.06.2013
comment
Проблема с многопоточностью заключается в том, что вы, очевидно, не можете иметь взаимозависимость аудиоданных между потоками. Это похоже на путешествие в один конец на Марс. Когда потоки объединяются, вам все равно нужно смешивать буферы. Ожидание объединения этих потоков может стоить вам денег. - person omygaudio; 10.06.2013