Android и звук: как делать правильно
В статье рассматривается архитектура и API для создания приложений, воспроизводящих музыку. Мы напишем простое приложение, которое будет проигрывать небольшой заранее заданный плейлист, но «по-взрослому» — с использованием официально рекомендуемых практик. Мы применим MediaSession и MediaController для организации единой точки доступа к медиаплееру, и MediaBrowserService для поддержки Android Auto. А также оговорим ряд шагов, которые обязательны, если мы не хотим вызвать ненависти пользователя.
В первом приближении задача выглядит просто: в activity создаем MediaPlayer, при нажатии кнопки Play начинаем воспроизведение, а Stop — останавливаем. Все прекрасно работает ровно до тех пор, пока пользователь не выйдет из activity. Очевидным решением будет перенос MediaPlayer в сервис. Однако теперь у нас встают вопросы организации доступа к плееру из UI. Нам придется реализовать binded-сервис, придумать для него API, который позволил бы управлять плеером и получать от него события. Но это только половина дела: никто, кроме нас, не знает API сервиса, соответственно, наша activity будет единственным средством управления. Пользователю придется зайти в приложение и нажать Pause, если он хочет позвонить. В идеале нам нужен унифицированный способ сообщить Android, что наше приложение является плеером, им можно управлять и что в настоящий момент мы играем такой-то трек из такого-то альбома. Чтобы система со своей стороны подсобила нам с UI. В Lollipop (API 21) был представлен такой механизм в виде классов MediaSession и MediaController. Немногим позже в support library появились их близнецы MediaSessionCompat и MediaControllerCompat.
Следует сразу отметить, что MediaSession не имеет отношения к воспроизведению звука, он только об управлении плеером и его метаданными.
MediaSession
Итак, мы создаем экземпляр MediaSession в сервисе, заполняем его сведениями о нашем плеере, его состоянии и отдаем MediaSession.Callback, в котором определены методы onPlay, onPause, onStop, onSkipToNext и прочие. В эти методы мы помещаем код управления MediaPlayer (в примере воспользуемся ExoPlayer). Наша цель, чтобы события и от аппаратных кнопок, и из окна блокировки, и с часов под Android Wear вызывали эти методы.
Полностью рабочий код доступен на GitHub (ветка master). В статьи приводятся только переработанные выдержки из него.
Для доступа извне к MediaSession требуется токен. Для этого научим сервис его отдавать
и пропишем в манифест
MediaController
Теперь реализуем activity с кнопками управления. Создаем экземпляр MediaController и передаем в конструктор полученный из сервиса токен.
MediaController предоставляет как методы управления плеером play, pause, stop, так и коллбэки onPlaybackStateChanged(PlaybackState state) и onMetadataChanged(MediaMetadata metadata). К одному MediaSession могут подключиться несколько MediaController, таким образом можно легко обеспечить консистентность состояний кнопок во всех окнах.
Наша activity работает, но ведь идея исходно была, чтобы из окна блокировки тоже можно было управлять. И тут мы приходим к важному моменту: в API 21 полностью переделали окно блокировки, теперь там отображаются уведомления и кнопки управления плеером надо делать через уведомления. К этому мы вернемся позже, давайте пока рассмотрим старое окно блокировки.
Как только мы вызываем mediaSession.setActive(true), система магическим образом присоединяется без всяких токенов к MediaSession и показывает кнопки управления на фоне картинки из метаданных.
Однако в силу исторических причин события о нажатии кнопок приходят не напрямую в MediaSession, а в виде бродкастов. Соответственно, нам надо еще подписаться на эти бродкасты и перебросить их в MediaSession.
MediaButtonReceiver
Для этого разработчики Android любезно предлагают нам воспользоваться готовым ресивером MediaButtonReceiver.
Добавим его в манифест
MediaButtonReceiver при получении события ищет в приложении сервис, который также принимает «android.intent.action.MEDIA_BUTTON» и перенаправляет его туда. Поэтому добавим аналогичный интент-фильтр в сервис
Если подходящий сервис не найден или их несколько, будет выброшен IllegalStateException.
Теперь в сервис добавим
Метод handleIntent анализирует коды кнопок из intent и вызывает соответствующие коллбэки в mediaSession. Получилось немного плясок с бубном, но зато почти без написания кода.
На системах с API >= 21 система не использует бродкасты для отправки событий нажатия на кнопки и вместо этого напрямую обращается в MediaSession. Однако, если наш MediaSession неактивен (setActive(false)), его пробудят бродкастом. И для того, чтобы этот механизм работал, надо сообщить MediaSession, в какой ресивер отправлять бродкасты.
Добавим в onCreate сервиса
На системах с API Так это выглядит
Android 4.4
MIUI 8 (базируется на Android 6, то есть теоретически окно блокировки не должно отображать наш трек, но здесь уже сказывается кастомизация MIUI).
Уведомления
Однако, как ранее упоминалось, начиная с API 21 окно блокировки научилось отображать уведомления. И по этому радостному поводу, вышеописанный механизм был выпилен. Так что теперь давайте еще формировать уведомления. Это не только требование современных систем, но и просто удобно, поскольку пользователю не придется выключать и включать экран, чтобы просто нажать паузу. Заодно применим это уведомление для перевода сервиса в foreground-режим.
Нам не придется рисовать кастомное уведомление, поскольку Android предоставляет специальный стиль для плееров — Notification.MediaStyle.
Добавим в сервис два метода
И добавим вызов refreshNotificationAndForegroundStatus(int playbackState) во все коллбэки MediaSession.
Android 4.4
Android 7.1.1
Android Wear
Started service
В принципе у нас уже все работает, но есть засада: наша activity запускает сервис через binding. Соответственно, после того, как activity отцепится от сервиса, он будет уничтожен и музыка остановится. Поэтому нам надо в onPlay добавить
Никакой обработки в onStartCommand не надо, наша цель не дать системе убить сервис после onUnbind.
А в onStop добавить
В случае, если к сервису привязаны клиенты, stopSelf ничего не делает, только взводит флаг, что после onUnbind сервис можно уничтожить. Так что это вполне безопасно.
ACTION_AUDIO_BECOMING_NOISY
Продолжаем полировать сервис. Допустим пользователь слушает музыку в наушниках и выдергивает их. Если эту ситуацию специально не обработать, звук переключится на динамик телефона и его услышат все окружающие. Было бы хорошо в этом случае встать на паузу.
Для этого в Android есть специальный бродкаст AudioManager.ACTION_AUDIO_BECOMING_NOISY.
Добавим в onPlay
В onPause и onStop
И по факту события встаем на паузу
Android Auto
Начиная с API 21 появилась возможность интегрировать телефон с экраном в автомобиле. Для этого необходимо поставить приложение Android Auto и подключить телефон к совместимому автомобилю. На экран автомобиля будет выведены крупные контролы для управления навигацией, сообщениями и музыкой. Давайте предложим Android Auto наше приложение в качестве поставщика музыки.
Если у вас под рукой нет совместимого автомобиля, что, согласитесь, иногда бывает, можно просто запустить приложение и экран самого телефона будет работать в качестве автомобильного.
Исходный код выложен на GitHub (ветка MediaBrowserService).
Прежде всего надо указать в манифесте, что наше приложение совместимо с Android Auto.
Добавим в манифест
Здесь automotive_app_desc — это ссылка на файл automotive_app_desc.xml, который надо создать в папке xml
Преобразуем наш сервис в MediaBrowserService. Его задача, помимо всего ранее сделанного, отдавать токен в Android Auto и предоставлять плейлисты.
Поправим декларацию сервиса в манифесте
Во-первых, теперь наш сервис экспортируется, поскольку к нему будут подсоединяться снаружи.
И, во-вторых, добавлен интент-фильтр android.media.browse.MediaBrowserService.
Меняем родительский класс на MediaBrowserServiceCompat.
Поскольку теперь сервис должен отдавать разные IBinder в зависимости от интента, поправим onBind
Имплементируем два абстрактных метода, возвращающие плейлисты
И, наконец, имплементируем новый коллбэк MediaSession
Здесь mediaId — это тот, который мы отдали в setMediaId в onLoadChildren.
Плейлист
Трек
UPDATE от 27.10.2017: Пример на GitHub переведен на targetSdkVersion=26. Из релевантных теме статьи изменений необходимо отметить следующее:
- android.support.v7.app.NotificationCompat.MediaStyle теперь deprecated. Вместо него следует использовать android.support.v4.media.app.NotificationCompat.MediaStyle. Соответственно, больше нет необходимости использовать android.support.v7.app.NotificationCompat, теперь можно использовать android.support.v4.app.NotificationCompat
- Метод AudioManager.requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) теперь тоже deprecated. Вместо него надо использовать AudioManager.requestAudioFocus(AudioFocusRequest focusRequest). AudioFocusRequest — новый класс, добавленный с API 26, поэтому не забывайте проверять на API level.
Создание AudioFocusRequest выглядит следующим образом
Теперь запрашиваем фокус
Разумеется, все вышеописанные изменения вносить необязательно, старые методы работать не перестали.
Вот мы и добрались до конца. В целом тема эта довольно запутанная. Плюс отличия реализаций на разных API level и у разных производителей. Очень надеюсь, что я ничего не упустил. Но если у вас есть, что исправить и добавить, с удовольствием внесу изменения в статью.
Еще очень рекомендую к просмотру доклад Ian Lake. Доклад от 2015 года, но вполне актуален.
Источник
Как передавать звук с Android на компьютер с Windows 7 (DLNA, UPnP)?
Уже пару недель пытаюсь выяснить, как можно передавать звук с андроида на компьютер.
У меня под теликом установлен компьютер (Win7). К нему подключены колонки, саб. Я бы хотел звук с телефона Samsung Galaxy S II, Android 4.1.2 CyanogenMod, передавать на компьютер по Wifi.
Но мне надо, чтобы не просто определенный плеер играл файлы, которые у меня есть на телефоне или компьютере, а чтобы любой звук шел на компьютер. Например, я играю треки плеером Meridian, который берет музыку из Вконтакта. Или я слушаю радио на телефоне через TuneIn Radio. На телефоне этих MP3 нет.
Я попробовал поставить на телефоны программы Skifta и Bubble UPnP (работающие по DLNA и UPnP), настроил Windows Media Player на компьютере, чтобы разрешить ему принимать Stream. Skifta и Bubble UPnP сканируют телефон, проигрывают mp3 файлы с телефона, Windows Media Player передает звук на колонки, все работает. Но меня интересует вариант передачи любого звука на компьютер
Думаю, что это можно было бы сделать с помощью Bluetooth, но для начала хотелось бы узнать, можно ли сделать так по Wifi, раз он всё равно включен.
Нужно, наверное, что-то типа драйвера, который мог бы переключать Android с проигрывания звука на собственных спикерах на проигрывание по DLNA. На компьютере должен работать принимающий сервер, пусть даже для начала Windows Media Player.
Вопрос, собственно: есть ли такие драйверы или программы, которые позволяли бы Android выполнять переключение звука на лету?
Источник
Сравниваем 6 популярных музыкальных плееров под Android с поддержкой BitPerfect-доступа к USB ЦАПу. Кто победит?
Напоминаю, что в прошлом сравнении у нас схлестнулись 5 бесплатных музыкальных плееров под Android с поддержкой Hi-Res Audio. Кто пропустил, рекомендую почитать сначала первую часть. Теперь пойдет самая жара, так как сегодня в сравнении примут участие 6 музыкальных плееров, которые поддерживают BitPerfect доступ к USB ЦАПам. То есть через них можно напрямую выводит звук на тот самый «свисток», о которых так часто заходит речь в наших обзорах. Для примера можно вспомнить воистину народные ЦАПы: iBasso DC03, HiBy FC3, TempoTec Sonata HD II или E1DA 9038D. Это я привел по одному из разных ценовых категорий, еще больше вариантов можете посмотреть в соответствующей подборке на этом же ресурсе.
В сравнении примут участие (о чем вы меня неоднократно просили) PowerAmp, Neutron, USB Audio Player, NePlayer, HiBy Music и FiiO Music. К сожалению, плееры от Shanling умеют работать только со своими устройствами. Исключались также программы не поддерживающие разрешение 24 бита 96 кГц, которое по определению является нижней границей Hi-Res Audio и на котором у нас происходят все измерения. Из аппаратуры использовался аудиоинтерфейс Motu M4, а в качестве источника я взял Hi-Res аудиоплеер на Android Shanling M3X и к нему USB ЦАП iBasso DC03. Уровень громкости составляет 100%, все приложения скачаны из Play Market, величина дополнительной нагрузки — 32 ома. В процессе я делаю ровно 4 замера, чтобы исключить случайные отклонения. Итак, все формальности учтены, пора представлять участников.
Участники
О HiBy Music и FiiO Music мы уже говорили в первой части сравнений. Это крупные производители аудиообрудования с которыми уже давно принято считаться у всего аудиофилского сообщества. Кроме того, HiBy Music много лет был известен как поставщик программного обеспечения для большого числа аудиодевайсов из Китая и не только. Возможностей у этих музыкальных плееров просто масса: разного вида эквалайзеры, поддержка Hi-Res форматов, MQA, DSD. В общем, довольно серьезные программы.
PowerAmp, пожалуй что был первым нормальным плеером с поддержкой lossless форматов на Android. Он реально пионер в этой сфере, поэтому и количество скачиваний у него превосходит все возможные пределы. Звуковой движок менялся уже несколько раз, есть все необходимые настройки, эквалайзер и красивые эффекты визуализации. По умолчанию схема формирования сигнала у PowerAmp какая-то просто дичайшая, но ее можно настроить.
Следом за PowerAmp появился аудиофилский Neutron. Он также поддерживает USB ЦАПы, имеет целую кучу разнообразных настроек, есть даже тонкости для DSD сигнала. Интерфейс конечно тут на любителя, но то дело второе.
USB Audio Player изначально предназначался исключительно для воспроизведения музыки через USB ЦАПы, но по просьбе пользователей его функционал был расширен. Один из лучших в своем классе, не единичны случаи, когда производители аудиообрудования предустанавливали его на свои девайсы в качестве основного музыкального софта. Мой фаворит, думаю, что именно он превзойдет всех.
Следующий участник — NePlayer. Играет он только из плейлиста, причем к папкам доступа нет вообще. Родом, судя по всему, из Японии. Очень мало информации, однако USB ЦАПы он тоже поддерживает и умеет разделять музыку по качеству.
Тестирование
1. Начнем с неравномерности АЧХ в диапазоне 40 Гц — 15 кГц (Дб).
HiBy Music | -0.06 | -0.18 |
FiiO Music | +0.04 | -0.09 |
PowerAmp | -0.05 | -0.24 |
Neutron | -0.06 | -0.18 |
USB Audio Player | -0.06 | -0.17 |
NePlayer | -0.06 | -0.17 |
По цифрам видно, что все справились очень хорошо, но Neutron зачем-то обрезает частотный диапазон на 20 кГц. По этой причине он здесь оказался аутсайдером.
2. Следом идет уровень шума (Дб).
HiBy Music | -108.8 |
FiiO Music | -108.8 |
PowerAmp | -108.2 |
Neutron | -107.8 |
USB Audio Player | -108.8 |
NePlayer | -108.5 |
Тоже все в принципе молодцы, худший результат у Neutron.
3. Самый важный, на мой взгляд, параметр — динамический диапазон (Дб).
HiBy Music | 109.0 |
FiiO Music | 102.2 |
PowerAmp | 108.2 |
Neutron | 107.8 |
USB Audio Player | 109.0 |
NePlayer | 108.6 |
В этом случае сплоховал FiiO Music и чуть лучше результат у Neutron. Самый же топ показали HiBy Music и USB Audio Player.
4. Ну и последняя пара — уровень гармонических и интермодуляционых искажений в процентах.
HiBy Music | 0.00080 | 0.00165 |
FiiO Music | 0.00078 | 0.314 |
PowerAmp | 0.00082 | 0.00323 |
Neutron | 0.00080 | 0.010 |
USB Audio Player | 0.00078 | 0.00168 |
NePlayer | 0.00078 | 0.00166 |
Аутсайдеры опять FiiO Music и Neutron, все остальные почти ноздря в ноздрю.
Выводы
Какие можно подвести итоги? Если вы планируете использовать со смартфоном мобильный, ну или даже стационарный, USB ЦАП, то выбирать FiiO Music и Neutron не нужно, у них самые низкие показатели качества среди измеренных нами. На удивление неплохо себя показал PowerAmp на своем новом движке и неочень популярный NePlayer. От них, честно скажу, не ожидал. Ну а реальными лидерами оказались бесплатный HiBy Music и платный USB Audio Player. Разницы между ними в данном аспекте использования вообще никакой. Однозначно могу их рекомендовать и использовать по назначению. Тем более, что они к тому же еще и поддерживают стримминговые сервисы в режиме BitPerfect, а это вообще высший уровень качества.
На этом у меня все. Если понравилось, оставляйте свои комментарии, обсудим, а возможно и дополним сравнения новыми участниками. Предлагайте ваши варианты с коротким пояснением, почему именно его. Ну а выбрать себе хороший мобильный ЦАП можно, например, из моей подборки.
Источник