Программирование звука в Android — SoundPool и MediaPlayer
Перед любым разработчиком, занимающимся созданием красивых интерактивных приложений, рано или поздно встает вопрос об использовании мультимедийных возможностей телефона. Для работы со звуком в Android программисты активно используют две библиотеки. Первая из них предоставляет класс SoundPool, вторая — MediaPlayer.
Класс SoundPool удобно использовать для проигрывания коротких аудиоклипов. С его помощью можно проигрывать несколько звуков одновременно, при этом размер звукового файла не должен превышать 1 Mb. Класс MediaPlayer лучше подходит для воспроизведения долгих аудио и видеороликов.
Android управление звуком
Android поддерживает различные аудиопотоки для разных целей. Регулятор расположенная на торце телефона качелька для регулирования громкости управляет только одним аудиопотоком. Для того, чтобы привязать эту кнопку к аудиопотоку необходимо указать его тип в приложении
Как проиграть звук через SoundPool
Напишем приложение, которое будет проигрывать звук при касании экрана. Создадим новый проект с именем CrazySong, пакет назовем
ru.mobilab.crazysong, в качестве главной Activity укажем CrazySongActivity.
Приведем main.xml к виду
Давайте рассмотрим последовательность действий, которую необходимо выполнить для того, чтобы воспроизвести звуковой файл. Прежде всего мы должны создать объект soundPool. Его конструктор имеет несколько параметров. Первый параметр задает максимальное число одновременно проигрываемых файлов. Второй параметр задает тип аудиопотока. В большинстве случаев здесь подойдет значение soundPool STREAM_MUSIC, хотя возможно использование и других аудиопотоков. Их назначение довольно очевидно. (STREAM_ALARM, STREAM_DTMF, STREAM_NOTIFICATION, STREAM_RING,STREAM_SYSTEM, STREAM_VOICE_CALL). Третий параметр задает sample-rate. В настоящее время он ни на что не влияет, поэтому здесь устанавливаем 0.
После того, как мы создали объект для SoundPool, с помощью setOnLoadCompleteListener добавим к нему OnLoadCompleteListener, который будет отслеживать завершение загрузки файлов. В качестве параметров метод onLoadComplete принимает объект SoundPool, номер загруженного сэмпла и статус завершения операции. В случае, если все прошло успешно, статус равен нулю.
Прежде чем проигрывать звуковые файлы их необходимо загрузить в SoundPool. Сделать это можно с помощью метода load. Метод имеет несколько реализаций, различающихся набором параметров и источником, поставляющим звуковые файлы. Можно загружать файлы из папки asset, можно из APK ресурсов, можно из файла, указав к нему путь, можно из FileDescriptor. Мы будем рассматривать ситуацию, когда звуковые сэмплы входят в состав APK пакета, то есть лежат в папке res/raw вашего проекта. Для загрузки нам понадобятся три параметра: ссылка на контекст из которого мы получаем доступ к ресурсам программы (в большинстве случаев подойдет this), ссылка на ресурс, полученная через объект R, и приоритет, который пока ни на что не влияет, и в документации рекомендуется устанавливать значение 1. Загруженные ресурсы можно выгрузить с помощью методов unload, если требуется удалить один сэмпл, и release — если нужно полностью очистить SoundPool.
Для проигрывания сэмпла используется метод play, имеющий следующие параметры:
- soundID переменная с номером сэмпла. Этот номер возвращается в результате выполнения метода load.
- leftVolume значение громкости левого канала (от 0.0 до 1.0)
- rightVolume значение громкости правого канала (от 0.0 до 1.0)
- priority приоритет потока (0 — самый низкий приоритет)
- loop сколько раз нужно повторить сэмпл (0 не повторять, -1 — зациклить)
- rate скорость воспроизведения (может изменяться от 0.5 до 2.0, 1 — нормальная скорость)
В результате выполнения метода play возвращается номер streamID (или 0 в случае ошибки), который можно использовать для управления воспроизведением. Например это значение используется при вызове методов pause и resume, stop, setVolume, setLoop. Если число максимально проигрываемых файлов превышено, то вызов очередного метода play приведет к завершению воспроизведения одного из проигрываемых в данный момент сэмплов.
Код нашего класса приведен ниже
package ru.mobilab.crazysong;
SoundPool и утечка памяти
На форумах встречаются сообщения о том, что использование SoundPool вызывает проблему утечки памяти. Какой-то внятной и проверенной информацию по этому поводу найти не удалось. Если вы не собираетесь больше проигрывать звуки, рекомендуется выполнить код
но похоже, что этот метод не решает проблему вAndroid 2.1. Если Вы располагаете информацией об этой проблеме и ее решении, просьба отписаться в комментариях.
Воспроизведение фоновой музыки в Android с помощью MediaPlayer
SoundPool отлично подходит для озвучивания различных игровых событий: выстрелов, взрывов, реплик. Однако для проигрывания фоновой музыки нужно использовать класс MediaPlayer. Метод create этого класса имеет два параметра: контекст и ссылку на ресурс или URI адрес музыкального файла. Для запуска/паузы/остановки воспроизведения служат методы start(), pause() и stop() соответственно. Все просто.
В случае, если Вы собираетесь подгружать файл из интернета, последовательность действий будет немного другая. Метод create использовать не нужно. Источник аудиоданных задается с помощью метода setDataSource, после которого нужно вызвать метод prepare(), блокирующий выполнение потока до тех пор, пока медиаплеер не будет готов к воспроизведению музыки. Метод prepareAsync() выполняет те же действия в асинхронном режиме, то есть не блокирует вызвавший его поток. В случае использования prepareAsync нужно использовать OnPreparedListener для определения момента, когда MediaPlayer готов к проигрыванию файла. Заметим, что при использовании метода create метод prepare вызывать не нужно, поскольку он вызывается внутри create.
Остался еще один актуальный вопрос: как определить, что воспроизведение файла закончилось? Для этой цели служит функция обратного вызова onCompletion. Чтобы привязать ее к нашему медиаплееру нужно выполнить следующий код:
Вот собственно и все. Воспроизведение звука не такая уж и сложная тема. Архив с проектом можетескачать здесь.
Источник
Полный список
MediaPlayer – класс, который позволит вам проигрывать аудио/видео файлы с возможностью сделать паузу и перемотать в нужную позицию. MediaPlayer умеет работать с различными источниками, это может быть: путь к файлу (на SD или в инете), адрес потока, Uri или файл из папки res/raw.
Напишем небольшое приложение аудио-плеер и используем в нем все эти возможности.
Project name: P1261_MediaPlayer
Build Target: Android 2.3.3
Application name: MediaPlayer
Package name: ru.startandroid.develop.p1261mediaplayer
Create Activity: MainActivity
Добавляем строки в strings.xml:
Так это выглядит на экране
Кнопки верхнего ряда запускают проигрывание треков из различных источников. Кнопки среднего ряда – это пауза, возобновление, стоп и чекбокс повторения трека. А в нижнем ряду кнопки перемотки назад/вперед и вывод в лог текущей информации.
Создайте папку res/raw и положите в нее какой-нить звуковой файл с именем explosion.mp3. Например, его можно скачать здесь — http://dl.dropboxusercontent.com/u/6197740/explosion.mp3.
Разбираемся. Сначала создаем константы-пути, которые будет использовать проигрыватель. Это файл в инете (DATA_HTTP), поток в инете (DATA_STREAM), файл на флэшке (DATA_SD) и Uri на мелодию из системы (DATA_URI). Для SD и Uri укажите ваши значения, чтобы такие файлы существовали. (По получению Uri в конце урока есть вспомогательный код)
В onCreate получаем AudioManager, находим на экране чекбокс и настраиваем так, чтобы он включал/выключал режим повтора для плеера.
onClickStart – метод для обработки нажатий на кнопки верхнего ряда. Сначала мы освобождаем ресурсы текущего проигрывателя. Затем в зависимости от нажатой кнопки стартуем проигрывание. Какие методы для этого используются?
setDataSource – задает источник данных для проигрывания
setAudioStreamType – задает аудио-поток, который будет использован для проигрывания. Их существует несколько: STREAM_MUSIC, STREAM_NOTIFICATION и п. Подробнее их можно посмотреть в доках по AudioManager. Предполагаю, что созданы они для того, чтобы можно было задавать разные уровни громкости, например, играм, звонкам и уведомлениям. Этот метод можно и пропустить, если вам не надо явно указывать какой-то поток. Насколько я понял, по умолчанию используется STREAM_MUSIC.
Далее используется метод prepare или prepareAsync (в паре с OnPreparedListener). Эти методы подготавливают плеер к проигрыванию. И, как понятно из названия, prepareAsync делает это асинхронно, и, когда все сделает, сообщит об этом слушателю из метода setOnPreparedListener. А метод prepare работает синхронно. Соотвественно, если хотим прослушать файл из инета, то используем prepareAsync, иначе наше приложение повесится, т.к. заблокируется основной поток, который обслуживает UI.
Ну и метод start запускает проигрывание.
В случае с raw-файлом мы используем метод create. В нем уже будет выполнен метод prepare и нам остается только выполнить start.
Далее мы для плеера включаем/выключаем повтор (setLooping) в зависимости от текущего значения чекбокса. И вешаем слушателя (setOnCompletionListener), который получит уведомление, когда проигрывание закончится.
В методе releaseMP мы выполняем метод release. Он освобождает используемые проигрывателем ресурсы, его рекомендуется вызывать когда вы закончили работу с плеером. Более того, хелп рекомендует вызывать этот метод и при onPause/onStop, если нет острой необходимости держать объект.
В методе onClick мы обрабатываем нажатия на кнопки управления проигрывателем. Какие здесь используются методы?
start – возобновляет проигрывание
seekTo – переход к определенной позиции трека (в милисекундах)
getCurrentPosition – получить текущую позицию (в милисекундах)
getDuration – общая продолжительность трека
isLooping – включен ли режим повтора
getStreamVolume – получить уровень громкости указанного потока
Далее идут методы
onPrepared – метод слушателя OnPreparedListener. Вызывается, когда плеер готов к проигрыванию.
onCompletion – метод слушателя OnCompletionListener. Вызывается, когда достигнут конец проигрываемого содержимого.
В методе onDestroy обязательно освобождаем ресурсы проигрывателя.
В манифесте добавляем права на интернет — android.permission.INTERNET.
Все сохраняем, запускаем приложение. Дизайн получившегося плеера, конечно, не ахти какой :), но нас сейчас интересует функционал.
Еще раз перечислю возможные действия. Нажимая верхние кнопки, мы запускаем проигрывание из различных источников. Кнопки среднего ряда позволят нам поставить паузу, возобновить/остановить проигрывание и включить режим повтора. Кнопки нижнего ряда перематывают назад/вперед на 3 сек (3000 мсек) и выводят инфу в лог.
Я включу проигрывание файла с SD и выведу инфу в лог (кнопка Info).
start SD
Playing true
Time 4702 / 170588
Looping false
Volume 10
Проигрывание идет, текущая позиция – 4-я секунда из 170, режим повтора выключен, громкость — 10.
Уменьшу громкость (кнопками устройства или эмулятора), включу режим повтора (чекбокс Loop), поставлю паузу (кнопка Pause) и снова выведу инфу в лог:
Playing false
Time 46237 / 170588
Looping true
Volume 6
Видим, что проигрывание остановилось, текущая позиция уже 46 секунд, режим повтора включен, а громкость уменьшилась до 6.
Теперь включу проигрывание потока (кнопка Stream). Смотрим лог:
08:49:13.799: D/myLogs(18805): start Stream
08:49:13.809: D/myLogs(18805): prepareAsync
08:49:27.589: D/myLogs(18805): onPrepared
Обратите внимание, сколько прошло времени с начала (prepareAsync) до завершения (onPrepared) подготовки проигрывателя – 14 секунд. Если бы мы использовали метод prepare, а не prepareAsync, то наше приложение было бы недоступно все это время.
Расскажу еще про несколько методов, которые я не использовал в примере, но о которых стоит знать.
Метод reset – сбрасывает плеер в начальное состояние, после него необходимо снова вызвать setDataSource и prepare. Похож на onRelease, но позволяет продолжить работу с этим же объектом. А вот после onRelease надо создавать новый объект MediaPlayer.
Метод setOnBufferingUpdateListener устанавливает слушателя буферизации проигрываемого потока. По идее слушатель будет получать процент буферизации, но у меня оно как-то странно работает — показывает или 0 или 100.
Метод setOnErrorListener устанавливает слушателя для получения ошибок. Особенно это полезно при методe prepareAsync. Если в ходе этого метода возникнут ошибки, то их можно поймать только так.
Метод setWakeMode позволяет ставить стандартную (PowerManager.WakeLock) блокировку на время проигрывания, указав тип блокировки. Не забудьте в манифесте добавить права на WAKE_LOCK.
По поводу видео. Метод setDisplay позволяет указать плееру, куда выводить изображение. Размещаете на экране компонент SurfaceView (вкладка Advanced), вызываете его метод getHolder и полученный объект передаете в setDisplay. Плеер выведет изображение на этот компонент.
Чтобы узнать размер проигрываемого изображения можно использовать методы getVideoHeight и getVideoWidth.
В хелпе класса MediaPlayer есть хорошая схема состояний плеера. Она кажется запутанной, но если посидеть и поразбираться, то вполне можно все понять. Схема полезная, советую вникнуть.
А здесь можно посмотреть какие форматы поддерживаются системой.
Также хелп любезно предоставляет нам код, который позволит просмотреть существующие медиа-файлы в системе:
На следующем уроке:
— работаем с SoundPool
Присоединяйтесь к нам в Telegram:
— в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
— в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование
— ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня
— новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме
Источник
Android: функция setVolume MediaPlayer
о params Установите, что сделать плеер без звука и полный звук
7 ответов
эта функция действительно замечательная. Благодаря этому вы можете создать шкалу громкости с любым количеством шагов!
предположим, вы хотите 50 шагов:
затем, чтобы установить setVolume на любое значение в этом диапазоне (0-49), вы делаете это:
приятно и легко! И не используйте AudioManager для установки громкости! Это вызовет множество побочных эффектов, таких как отключение беззвучного режима, что сделает ваших пользователей безумными!
после user100858 решение я просто отправляю свой точный код, который работает:
soundVolume-это громкость, которую вы хотите установить между 0 и MAX_VOLUME. Таким образом, между 0 и 100 в этом примере.
на Android MediaPlayer.setVolume , поиск в интернете, кажется, показывает 0.0f без звука, 1.0f для полного звука.
другие ответы не верны, или, по крайней мере, они не правильно настроены.
выполните следующий тест, используя их код (например, Tomasz или ssuukk):
1) Установите 100 как «максимальный объем» / количество шагов и отправьте том 50.
он возвращает: 0.150514997831991
2) Установите 1000 как «максимальный объем» / количество шагов и отправьте том 500.
что он возвращает? То же значение, 0.150514997831991, правильно?
Неа. Вместо этого: 0.100343331887994
другими словами, существующие ответы изменяют, как они масштабируют входной объем-процент (т. е. кривую преобразования) на основе того, сколько шагов громкости вы установили.
Я провел последние несколько часов, изучая эту проблему; достаточно, чтобы я не хотел вдаваться в подробности, объясняя проблему. Вместо этого я просто опубликую большой блок кода/комментариев в своей программе. (это в C#, для Xamarin Android, но функциональность должна быть такой же для Java)
вывод
документация разрежена, поэтому я не могу точно знать, есть ли у меня правильная система масштабирования/тип единицы, которую ожидает метод SetVolume.
предполагая, что он ожидает значение амплитуды, код выше может быть правильным кодом настройки громкости для него. (принимать пожеланную громкость, линейную, как входной сигнал, и выводить наружу / устанавливать значение амплитуды необходимо для разъема Метод SetVolume)
Я не уверен, что это правильно, и слишком устал, чтобы подтвердить. Если у кого есть еще мысли, не стесняйтесь добавлять их. (3 + часов достаточно, чтобы потратить на такой вопрос, как этот, за один день)
редактировать
после внимательного прослушивания и сравнения эффекта громкости-затухания:
- просто отправка желаемой громкости в метод SetVolume.
- Exponentiating (в основном) нужные-громкость до отправка его, чтобы сделать его амплитудным (или подобным) значением, которое, как говорит метод SetVolume, он ожидает.
Я нахожу, что Вариант 1, кажется, ближе к линейной громкости затухания! Иначе говоря. из фактического прослушивания и сравнения базового подхода с различными подходами преобразования, показанными здесь, кажется, что документация неверна, и метод SetVolume на самом деле просто ожидает значение громкости в линейном масштабе. (возможно, они обновили его, чтобы работать больше интуитивно в одной из последних версий API, но не обновили документы?)
Если это так, то конечно легко. Это то, что я собираюсь делать сейчас. (хотя я сохраню подход возведения в степень / фиксации масштаба как настройку программы, я полагаю, просто чтобы иметь повод сохранить некоторый результат всего этого времени!)
Источник