Управление воспроизведением аудио
Управление аудио фокусом
Для избежания одновременного воспроизведения музыки разными приложениями Android использует понятие аудио фокус. Музыку может воспроизводить только то приложение, которое в данный момент времени владеет аудио фокусом, поэтому до начала воспроизведения музыки приложение должно запросить и получить аудио фокус. Более того оно должно знать, как отслеживать потерю аудио фокуса и соответствующим образом реагировать в случае потери.
Запрос на получение аудио фокуса выполняется вызовом метода requestAudioFocus(), который в случае успеха возвращает константу AUDIOFOCUS_REQUEST_GRANTED. При этом необходимо указывать какой поток используется и какого рода аудио фокус требуется: временный или постоянный. Временный фокус предполагает воспроизведение короткого аудио произведения, например, инструкций по навигации. Постоянный фокус предполагает длительное воспроизведение аудио контента, например, прослушивание музыки.
Запрос фокуса необходимо выполнять непосредственно перед началом воспроизведения, например, когда пользователь нажимает кнопку play. Следующий код демонстрирует запрос на постоянный аудио фокус:
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE); ... //запрос аудио фокуса для воспроизведения int result = am.requestAudioFocus(afChangeListener, //используя поток музыки AudioManager.STREAM_MUSIC, //запрос постоянного фокуса AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { am.registerMediaButtonEventReceiver(RemoteControlReceiver); // начало воспроизведения }
Как только заканчивается воспроизведение, необходимо вызвать метод abandonAudioFocus(), который сообщает системе, что аудио фокус больше не требуется и отменяет регистрацию соответствующего AudioManager.OnAudioFocusChangeListener В случае освобождения временного фокуса, любое приостановленное приложение может продолжить воспроизведение.
//освобождение аудио фокуса при завершении воспроизведения am.abandonAudioFocus(afChangeListener);
При запросе временного аудио фокуса существует дополнительная опция: возможность так называемого "ducking". В обычных условиях приложение, теряющее аудио фокус, заглушает воспроизведение, при запросе временного аудио фокуса с возможностью "ducking", работавшему до этого аудио приложению дается возможность лишь приглушить воспроизведение до возвращения фокуса к нему.
//запрос аудио фокуса для воспроизведения int result = am.requestAudioFocus(afChangeListener, //используя поток музыки AudioManager.STREAM_MUSIC, //запрос фокуса AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // начало воспроизведения }
"Ducking" особенно подходит для приложений, которые используют аудио поток периодически, например, для озвучивания рекомендаций водителю.
Приложение может потерять аудио фокус, как ему реагировать на потерю фокуса зависит от способа этой потери. Метод onAudioFocusChange() слушателя изменений аудио фокуса, зарегистрированного при запросе аудио фокуса, получает параметр, описывающий событие изменения фокуса. Потеря фокуса может быть постоянной или временной с возможностью "ducking" или без нее.
Временная потеря аудио фокуса заставляет приложение заглушить воспроизведение аудио потока, но сохраняет состояние воспроизведения. Необходимо следить за изменениями состояния аудио фокуса и быть готовым продолжить воспроизведение после возвращения фокуса.
Если потеря аудио фокуса является постоянной, приложение должно корректно завершиться, т.е. остановить воспроизведение, удалить слушателей событий от медиа кнопок и освободить аудио фокус. Для возобновления проигрывания аудио контента необходимо дождаться действия от пользователя, например, нажатия кнопки play.
Следующий фрагмент кода описывает приостановку воспроизведения в случае временной потери фокуса, возобновление воспроизведения при возвращении фокуса. В случае постоянной потери выполняется отмена регистрации приемника событий нажатия медиа кнопок и останавливается отслеживание событий изменения фокуса.
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT){ //приостановить воспроизведение } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { //продолжить воспроизведение } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { am.unregisterMediaButtonEventReceiver(RemoteControlReceiver); am.abandonAudioFocus(afChangeListener); // прекратить воспроизведение } } };
В случае временной потери аудио фокуса с возможностью "ducking" вместо паузы лучше использовать снижение уровня громкости.
"Ducking" это и есть процесс снижения уровня громкости воспроизведения аудио потока, чтобы позволить другому приложению воспроизвести свой аудио контент. Следующий фрагмент кода снижает громкость при временной потере фокуса, после возвращения фокуса восстанавливает прежний уровень громкости
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // снижение громкости } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // восстановление уровня громкости } } };
Взаимодействие с оборудованием для воспроизведения аудио
Пользователи имеют возможность выбора при прослушивании аудио на Android устройствах. Большинство устройств имеют встроенный динамик, выход для наушников, поддержку A2DP аудио и т.д.
Поведение приложения может зависить от устройства, на которое направлен вывод аудио контента. Можно запросить AudioManager определить какое устройство используется для вывода аудио, динамик мобильного устройства, проводные наушники или Bluetooth устройство.
if (isBluetoothA2dpOn()) { //адаптировать вывод для Bluetooth } else if (isSpeakerphoneOn()) { //адаптировать вывод для динамика устройства } else if (isWiredHeadsetOn()) { // адаптировать вывод для наушников } else { //никто его не слышит, аудио все еще играет? }
Когда наушники отсоединяются или Bluetooth устройство становится недоступным, аудио поток автоматически перенаправляется на встроенный динамик. Если громкость воспроизведения высокая, то неожиданный шум из динамиков может неприятно удивить, особенно окружающих.
К счастью система рассылает сообщение ACTION_AUDIO_BECOMING_NOISY, когда это происходит. Хорошей практикой является привычка регистрировать приемник широковещательных сообщений для получения этого сообщения, каждый раз как приложение воспроизводит аудио. В случае с воспроизведением музыки пользователи обычно ожидают приостановки воспроизведения, в случае с играми ожидается существенное понижение уровня громкости.
private class NoisyAudioStreamReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) { // приостановить воспроизведение } } } private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); private void startPlayback() { registerReceiver(myNoisyAudioStreamReceiver, intentFilter); } private void stopPlayback() { unregisterReceiver(myNoisyAudioStreamReceiver); }
Задание:
- Для тренировки постройте приложение для записи и воспроизведения медиа-контента, добавьте возможности управления громкостью и управления аудио фокусом.
- Постройте приложение с элементами распознавания речи, пример создания такого приложения: http://www.pandacoder.com/android_speech_recognition/.