Привет, Хабраюзер!
Несколько месяцев назад у меня возникла идея создать плеер для Android с огромным количеством эффектов.
Единственным плеером на тот момент (сейчас о нем не знаю :) с хоть какой-то обработкой звука был PowerAMP, но количество звуковых эффектов в нем было, мягко говоря, скудным.
Я попробовал реализовать эту идею.
Ничего особенного из этого не вышло, но что произошло в этой теме я вам расскажу.
Итак, кому интересно, прошу под кат.
Ищу библиотеку.
Для начала я решил найти ту самую OpenSource библиотеку, которая умеет гибко обрабатывать звук с помощью различных эффектов.
В конце концов я случайно встретил СоХ .
На первый взгляд это была идеальная библиотека для реализации такого проекта, но оказалось, что не все так просто.
Первые проблемы
Познакомившись с SoX, я обнаружил следующие проблемы: 1. Библиотека отлично работает на всех основных ОС, НО не оптимизирована под ARM. 2. Описание API библиотеки, как и самого API, было достаточно скудным; почти все функции было проще реализовать через командную строку.3. SoX вместо использования одного FFmpeg для декодирования использует ряд библиотек (можно посмотреть на официальном сайте).
Следовательно, все эти библиотеки также придется скомпилировать для Android NDK. 4. SoX прекрасно воспроизводил и декодировал практически любой формат, но при применении эффектов звук можно было либо декодировать в файл или байтовый массив, либо воспроизводить через Alsa/CoreAudio. Третий вариант не был однозначным, так как в Android теоретически есть Alsa, но она не всегда работает и в целом это не рекомендуемый метод воспроизведения звука.
Поэтому единственный вариант — декодировать всё в массив байтов и передать в Java (AudioTrack).
Но даже это оказалось довольно сложно реализовать, так как.
Поясню на примере :) Вот часть кода из официального примера применения обработки звука с помощью эффектов:
В этом случае декодируется звук и применяется несколько эффектов, затем все выводится через ALSA. Как видите, чтобы создать effect_chain нужно сначала иметь 2 потока: для чтения и для вывода информации.assert(sox_init() == SOX_SUCCESS); assert(in = sox_open_read(argv[1], NULL, NULL, NULL)); /* Change "alsa" in this line to use an alternative audio device driver: */ assert(out= sox_open_write("default", &in->signal, NULL, "alsa", NULL, NULL)); chain = sox_create_effects_chain(&in->encoding, &out->encoding); e = sox_create_effect(sox_find_effect("input")); args[0] = (char *)in, assert(sox_effect_options(e, 1, args) == SOX_SUCCESS); assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS); e = sox_create_effect(sox_find_effect("trim")); args[0] = "10", assert(sox_effect_options(e, 1, args) == SOX_SUCCESS); assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS); if (in->signal.rate != out->signal.rate) { e = sox_create_effect(sox_find_effect("rate")); assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS); assert(sox_add_effect(chain, e, &in->signal, &out->signal) == SOX_SUCCESS); } if (in->signal.channels != out->signal.channels) { e = sox_create_effect(sox_find_effect("channels")); assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS); assert(sox_add_effect(chain, e, &in->signal, &out->signal) == SOX_SUCCESS); } e = sox_create_effect(sox_find_effect("output")); args[0] = (char *)out, assert(sox_effect_options(e, 1, args) == SOX_SUCCESS); assert(sox_add_effect(chain, e, &in->signal, &out->signal) == SOX_SUCCESS); sox_flow_effects(chain, NULL, NULL);
Вот официальный пример декодирования аудиофайла по частям в массив байтов:
assert(sox_init() == SOX_SUCCESS);
/* Open the input file (with default parameters) */
assert(in = sox_open_read(argv[1], NULL, NULL, NULL));
#if defined FIXED_BUFFER
assert(out = sox_open_mem_write(buffer, buffer_size, &in->signal, NULL, "sox", NULL));
#else
assert(out = sox_open_memstream_write(&buffer, &buffer_size, &in->signal, NULL, "sox", NULL));
#endif
while ((number_read = sox_read(in, samples, MAX_SAMPLES)))
assert(sox_write(out, samples, number_read) == number_read);
Как видите, в этом случае звук декодируется по частям, а программа получает буфер.
Этот пример полностью рабочий, НО в нем практически невозможно применить эффекты (что и является целью программы), так как для этого нужно было бы для каждой части аудио (размером 16484 байт) создать считываемый и записать поток в другой буфер и затем обработать эти потоки.
У меня это не получилось, и ни один пример не описывает такую возможность.
Тогда было бы логично весь звук с помощью эффектов декодировать в один буфер, НО этот вариант использует много оперативной памяти (когда я тестировал, при декодировании небольшого аудиофайла размером 10-11 Мб оно доходило до 100 МБ).
Но все же я решил попробовать.
Несмотря на эти проблемы, я решил заняться портированием SoX и всех связанных с ним библиотек.
Неделя была потрачена на знакомство с Android NDK. В принципе, портирование не было сложным процессом, похожим на тот, который я описывал в предыдущей статье.
Некоторые библиотеки уже скомпилированы (например, FFmpeg).
В итоге получил скомпилированный sox.so, который реально работал и работает сейчас :) Пришло время решать задачу 3. Все оказалось не так уж и сложно - я создал отдельный поток, который по мере заполнения буфера отправляет его в программу Java, и она воспроизводит ее.
Проблема с памятью практически решена (ведь она используется много, а приложение ее не тратит).
У меня появилось приложение, которое воспроизводит практически все аудиоформаты и добавляет к нему эффекты (теперь фленджер, можно добавить любой другой).
С одной стороны, уже можно продолжать разработку, НО у меня возникли сразу две новые проблемы: 1. Программа слишком сильно нагружает процессор (до 80% при декодировании).
Я не знаю, как оптимизировать код C для ARM, поэтому не вижу возможности решить эту проблему самостоятельно.
2. Программа нестабильна.
Примерно в 1-2 из 10 запусков ничего не воспроизводится (проверено на Galaxy Tab).
Я тоже не смог решить эту проблему.
Итак, чтобы работа не пропала, я решил опубликовать весь код на GitHub и написать статью здесь.
Ссылка на код. Если кто-то видит возможности решения этих проблем, напишите мне на почту или в личку.
Надеюсь, моя идея кого-то заинтересует :) Теги: #Android #SoX #audio #effects #flanger #stereo Enhancer #разработка для Android
-
Какой Широкополосный Доступ Лучше?
19 Oct, 24 -
Nokia Начала Закрывать Конкурентов
19 Oct, 24 -
Левитация Светящейся Лампочки
19 Oct, 24 -
Возвращение Блудного Медведя
19 Oct, 24