Мультимедиа
Управление воспроизведением и фоновое аудио
Для того, чтобы рассмотреть различные виды воспроизведения аудио, обратимся к примеру "Средство управления воспроизведением msAudioCategory" (http://code.msdn.microsoft.com/windowsapps/Playback-Manager-e6526e67). Я не привожу здесь скриншот, так как пример лишь работает с аудио, и там особенно нечего показывать! ВВместо этого позвольте мне изложить особенности поведения различных его сценариев следующей таблице, а так же – некоторые категории, которые не представлены в примере, но могут быть использованы в ваших приложениях. В каждом сценарии вам сначала нужно выбрать аудиофайл с помощью средства выбора файлов:
Сценарий | msAudioCategory | Описание |
---|---|---|
1 | BackgroundCapableMedia | Воспроизводит выбранный аудиофайл, и когда приложение видимо, и в фоновом режиме, в том числе тогда, когда пользователь находится на Рабочем столе, на Начальном экране или на экране блокировки. Приложение не приостанавливается, находясь в фоновом режиме, в этом можно убедиться посредством Диспетчера задач. Это обычно исопользуется для воспроизведения локальных списков воспроизведения, локальных или потоковых медиафайлов, музыкальных видео и так далее. Использование этой категории требует объявления в манифесте и наличия обработчиков аппаратных кнопок управления воспроизведением. |
2 | Communications | Как и в случае BackgroundCapableMedia, воспроизведение звука продолжается и когда приложение находится в фоновом режиме. Используйте это для чатов, VoIP-приложений и так далее. |
3 | Other (по умолчанию для элементов audio) | Проигрывает выбранный аудиофайл, когда приложение находится на переднем плане, микшируется с фоновым аудио, когда приложение переходит в фоновый режим, воспроизведение приостанавливается. |
4 | ForegroundOnlyMedia | Проигрывает выбранный аудиофайл, когда приложение находится на переднем плане. Воспроизведение приостанавливается, когда приложение переходит в фоновый режим. Когда воспроизводится аудио из этой категории, фоновое аудио отключается. |
5 | Alert | Проигрывает выбранный аудиофайл, когда приложение находится на переднем плане, ослабляет фоновое аудио. Используется для оповещений, наподобие звонков, и для системных сообщений. |
n/a | GameMedia | Используется для окружающей музыки в играх. |
n/a | GameEffects | Используется для игровых звуковых эффектов, предназначенных для смешивания с воспроизводимыми звуками (все немузыкальные звуки). |
n/a | SoundEffects | Другие звуковые эффекты (за пределами игр), которые предназначены для смешивания с воспроизводимым звуком, такие, как короткие звонки, писки, стуки, бульканья, которые сопутствуют неким действиям, но не являются оповещениями. |
Когда речь идет об отдельном звуковом потоке, между некоторыми из этих категорий нет особой разницы. Действительно, как показано в таблице, звуки разных категорий по-разному воздействуют на другие звуковые потоки, проигрываемые одновременно с ними. Для этой цели в Windows SDK сделан необычный ход: предоставлен второй пример, идентичный вышеописанному, "Дополнительный пример для средства управления воспроизведением" (http://code.msdn.microsoft.com/windowsapps/Playback-Manager2-55c5b86d). Это позволяет вам запустить данные приложения одновременно (одно – в прикрепленном режиме, другое в заполняющем, или одно, либо оба, в фоновом режиме) и воспроизводить аудио различных категорий, наблюдая их взаимодействие.
Как комбинируются различные аудиопотоки, рассматривается в документе "Воспроизведение аудио в приложениях для Windows 8" (http://msdn.microsoft.com/library/windows/hardware/hh770517.aspx). Однко, самое важное, это чтобы вы присваивали каждому аудиопотоку наиболее соответствующую ему категорию. Эти категории помогают средству управления воспроизведением правильно микшировать звуковые потоки, основываясь на ожиданиях пользователя. Речь идет и о нескольких потоках в одном приложении, и о потоках аудио, поступающих от разных приложений (с ограничением количества приложений, которые могут одновременно воспроизводить аудио в фоновом режиме). Например, пользователи ожидают, что будильник, важная форма оповещения, временно приглушит другие аудиопотоки. Похожим образом, пользователи ожидают, что аудиопоток от приложения переднего плана имеет преимущество перед потоком той же категории, воспроизводимым в фоновом режиме. Как разработчик, поэтому, избегайте развлечений с категориями. Просто на значайте наиболее подходящие категории вашим аудиопотокам и позвольте средству управления воспроизведением делать свою работу с аудио, которое поступает из всех источников и создает последовательный опыт взаимодействия пользователя и системы в целом.
Установка категории для любого элемента audio, заключается в простом задании его атрибута msAudioCategory. Каждый сценарий в примере выполняет это одинаково, устанавливая категорию перед установкой атрибута src (здесь показан код из js/backgroundcapablemedia.js):
audtag = document.createElement('audio'); audtag.setAttribute("msAudioCategory", "BackgroundCapableMedia"); audtag.setAttribute("src", fileLocation);
Конечно, вы можете сделать то же самое в разметке. Вот примеры:
<audio id="audio1" src="song.mp3" msAudioCategory="BackgroundCapableMedia"></audio> <audio id="audio2" src="voip.mp3" msAudioCategory="Communications"></audio> <audio id="audio3" src="lecture.mp3" msAudioCategory="Other"></audio>
Однако, в случае с BackgroundCapableMedia and Communcations недостаточно лишь установить категорию: вам так же нужно задать расширение фоновой задачи в манифесте. Это просто сделать на закладке Объявления (Declarations) редактора манифеста:
Во-первых, выберите Фоновые задачи (Background tasks) из выпадающего списка Доступные объявления (Available declarations). Затем установите флаг Аудио (Audio) в группе Поддерживаемые типы задач (Supported task types) и укажите начальную страницу (Start page) в разделе параметров приложения (App settings). Начальная страница не особенно важна для фонового аудио (так приложение никогда не будет запущено извне для этой цели), но это нужно для соответствия правилам заполнения манифеста.
В XML-файле манифеста эти объявления выглядят следующим образом, взгляните на них:
<Application Id="App" StartPage="default.html"> <!-- ... --> <Extensions> <Extension Category="windows.backgroundTasks" StartPage="default.html"> <BackgroundTasks> <Task Type="audio" /> </BackgroundTasks> </Extension> </Extensions> </Application>
Кроме того, приложения, воспроизводящие фоновое аудио, должны доблять прослушиватели для событий Windows.Media.MediaControl (http://msdn.microsoft.com/library/windows/apps/windows.media.mediacontrol.aspx), как мы уже упоминали, так пользователь сможет управлять воспроизведением фонового аудио с помощью элементов управления воспроизведением мультимедиа (смотрите следующий раздел). Это так же необходимо, потому что это позволяет средству управления воспроизведением управлять аудиопотоками, когда пользователь переключается между приложениями. Если вы не предоставите эти прослушиватели, ваше аудио всегда будет приостановлено, звук будет выключен, когда приложение переходит в фоновый режим.
Как это сделать, показано в примере о средстве управления воспроизведением. Вот код (часть опущена) из файла js/communication.js:
mediaControl = Windows.Media.MediaControl; mediaControl.addEventListener("soundlevelchanged", soundLevelChanged, false); mediaControl.addEventListener("playpausetogglepressed", playpause, false); mediaControl.addEventListener("playpressed", play, false); mediaControl.addEventListener("stoppressed", stop, false); mediaControl.addEventListener("pausepressed", pause, false); // переменная audtag это глобальный аудиоэлемент для страницы function playpause() { if (!audtag.paused) { audtag.pause(); } else { audtag.play(); } } function play() { audtag.play(); } function stop() { // Здесь ничего не делается } function pause() { audtag.pause(); } function soundLevelChanged() { //Перехватываем уведомление SoundLevel и определяем его состояние. //Если звук выключен, приостанавливаем воспроизведение. var soundLevel = Windows.Media.MediaControl.soundLevel; //Здесь ничего не делается, параметры приведены для показа содержимого перечисления switch (soundLevel) { case Windows.Media.SoundLevel.muted: break; case Windows.Media.SoundLevel.low: break; case Windows.Media.SoundLevel.full: break; } appMuted(); } function appMuted() { if (audtag) { if (!audtag.paused) { audtag.pause(); } } }
С технической точки зрения, обработчик для события soundLevelChanged здесь не требуется, но другие четыре нужны. Подобная минимальная реализация – это часть упражнения к этой лекции AudioPlayback, код которого так же использует флаг MediaControl.isPlaying для настройки кнопки проигрывание / пауза в пользовательском интерфейсе проигрывателя аудио (смотрите в следующем разделе).
Вот некоторые дополнительные замечания, касающиеся фонового аудио:
- Причина, по которой мы различаем события paypressed, pausepressed и playpaysepressed заключается в поддержке разнообразного аппаратного обеспечения, при этом некоторые устройства имеют раздельные кнопки запуска воспроизведения и постановки на паузу, а у некоторых предусмотрена лишь одна кнопка и для того, и для другого.
- Если воспроизведение аудио приостановлено, приложение, воспроизводящее фоновое аудио, будет приостановлено, как и прочие, но если пользователь нажмет кнопку воспроизведения, приложение возобновит работу и продолжит проигрывание аудио.
- Использование функции фонового воспроизведения аудио тщательно оценивается при отправке приложения в Магазин Windows. Если вы попытаетесь проигрывать беззвучную запись для того, чтобы предотвратить приостановку приложения, приложение не пройдет сертификацию.
- Приложения фонового воспроизведения аудио должны осторожно использовать сетевое соединение для приема потоковых данных для поддержки состояния низкого потребления энергии, которое называется режим ожидания с подключением (connected standby). Подробности вы можете узнать в материале "Создание приложений фонового воспроизведения мультимедиа, экономящих энергию" (http://msdn.microsoft.com/library/windows/desktop/jj247568.aspx).
Посмотрим теперь на еще одну важную причину, по которой мы должны реализовывать события элементов управления мультимедиа: пользовательский интерфейс, который Windows отображает в ответ на нажатия аппаратных кнопок.
Пользовательский интерфейс управления воспроизведением
Как было упомянуто в предыдущем разделе, приложениям, которые воспроизводят фоновое аудио, нужно предоставлять обработчики событий объекта MediaControl. Благодаря этому пользовали могут управлять воспроизведением с помощью аппаратных кнопок (они встроены во многие устройства, в том числе – клавиатуры, пульты дистанционного управления) без необходимости переключаться на приложение. Это особенно важно, так как фоновое аудио продолжает воспроиизводиться не только тогда, когда пользователь переключается к другому приложению, но и тогда, когда пенеходит на Начальный экран, и на рабочий стол, или блокирует устройство.
Стандартный пользовательский интерфейс для управления воспроизведением, как показано на рис. 5.3., расположен в левом верхнем углу экрана, независимо от того, что в данный момент отображается на экране. Прикосновение к имени приложения приводит к переключению на приложение.
Рис. 5.3. Интерфейс управления воспроизведением аудио отображается поверх Начального экрана (слева) и рабочего стола (справа)
На изображениях вы можете видеть имя приложения, по умолчанию отображаемое в интерфейсе, взятое из свойства Отображаемое имя (Display Name), из манифеста. Хотя это и применимо, в идеале аудиоприложению следует передавать подробные метаданные записи в MediaControl, в частности, это свойства albumArt, trackName и artistName (длина двух последних не должна превышать 128 символов). Подобное сделано в примере "Конфигурация кнопок для мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-Buttons-ea57d8e2), который показывает, как получать сведения об альбоме для аудиозаписи, мы вернемся к этому позже.
С использованием этих метаданных, интерфейс управления воспроизведения выглядит так, как показано ниже. Прикосновение к изображению альбома, названию записи или имени исполнителя приводит к переходу в приложение.
Вы можете заметить в вышеприведенных изображенийх, что кнопки перехода на следующую и предыдущую запись заблокированы. Это потому, что приложение не имеет прослушивателей для событий nexttrackpressed и previoustrackpressed объекта MediaControl. Мы увидим, как пользоваться ими, в следующем разделе. Есть, так же, и другие события, такие, как channeldownpressed, channeluppressed, fastforwardpressed, rewindpressed, recordpressed, и stoppressed, хотя они и не представлены в интерфейсе.
Последовательное воспроизведение аудиозаписей
Приложения, которые воспроизводят аудиозаписи (такие, как музыку, аудиокниги или записи лекций) часто имеют некоторый список записей для последовательного воспроизведения, в особенности, когда приложение исполняется в фоновом режиме. В данном случае важно быстро начинать воспроизведение следующей записи, так как в противном случае Windows приостановит приложение через 10 секунд после завершения воспроизведения текущей записи. Для этой цели прослушивают событие ended элемента audio и устанавливают audio.src на следующую запись. В данном случае хорошая оптимизация заключается в использовании второго объекта Audio и установка его атрибута src после того, как первая запись начнет воспроизводиться. Таким образом, вторая запись будет предварительно загружена и готова к немедленому воспроизведению, таким образом, устраняя потенциальные задержки между записями при воспроизведении. Это реализовано в упражнении к этой лекции AudioPlayback, где я разделил одну песню на четыре сегмента для непрерывного воспроизведения. Я так же покажу здесь, как обрабатывать событии кнопок "Предыдущая запись" и "Следующая запись", вместе с установкой номера сегмента в качестве названия записи:
var mediaControl = Windows.Media.MediaControl; var playlist = ["media/segment1.mp3", "media/segment2.mp3", "media/segment3.mp3", "media/segment4.mp3"]; var curSong = 0; var audio1 = null; var preload = null; document.getElementById("btnSegments").addEventListener("click", playSegments); audio1 = document.getElementById("audioSegments"); preload = document.createElement("audio"); function playSegments() { //Всегда сбрасывайте прослушиватели событий объектов WinRT для предотвращения дублирования и утечек памяти mediaControl.removeEventListener("nexttrackpressed", nextHandler); mediaControl.removeEventListener("previoustrackpressed", prevHandler); curSong = 0; //Приостановим другую музыку document.getElementById("musicPlayback").pause(); //Set up media control listeners setMediaControl(audio1); //Покажем элемент (изначально скрытый) и начнем воспроизведение audio1.style.display = ""; audio1.volume = 0.5; //50%; playCurrent(); //Предварительная загрузка следующая запись и готовность к переключению var preload = document.createElement("audio"); preload.setAttribute("preload", "auto"); preload.src = playlist[1]; //Переключаемся на следующую запись, как только очередная окончится или будет нажат кнопка "Следующая запись" audio1.addEventListener("ended", nextHandler); mediaControl.addEventListener("nexttrackpressed", nextHandler); } } function nextHandler () { curSong++; //Активируем кнопку "Предыдущая запись", если у нас есть хотя бы одна предыдущая запись if (curSong > 0) { mediaControl.addEventListener("previoustrackpressed", prevHandler); } if (curSong < playlist.length) { //playlist[curSong] уже должен быть загружен playCurrent(); //Установка следующей заранее загруженной композиции var nextTrack = curSong + 1; if (nextTrack < playlist.length) { preload.src = playlist[nextTrack]; } else { preload.src = null; mediaControl.removeEventListener("nexttrackpressed", nextHandler); } } } function prevHandler() { //Если уже проигрываем последнюю запись, снова добавим обработчик кнопки "Следующая запись" if (curSong == playlist.length - 1) { mediaControl.addEventListener("nexttrackpressed", nextHandler); } curSong--; if (curSong == 0) { mediaControl.removeEventListener("previoustrackpressed", prevHandler); } playCurrent(); preload.src = playlist[curSong + 1]; //Это должно работать всегда } function playCurrent() { audio1.src = playlist[curSong]; audio1.play(); mediaControl.trackName = "Segment " + (curSong + 1); }
При последовательном проигрывании записей, как здесь, из приложения, написанного на JavaScript и HTML, вы можете отметить небольшие паузы между записями, особенно, если первая запись напрямую переходит во вторую. Это существующие ограничения платформы, где существуют уровни между HTML-элементом audio и низкоуровневым API XAudio2, которое, в конечном счете, выполняет реальную работу. В какой-то мере вы можете смягчить этот эффект, например, вы можете организовать плавный переход одной записи в другую, или наложить третью запись на конец первой и начало второй. Так же вы можете использовать отрицательное значение смещения по времени для начала воспроизведения второй записи немного раньше, чем будет окончена первая. Но если вам нужен по-настоящему бесшовный переход между записями, вам нужно обойти элемент audio и использовать API XAudio2 из WinRT-компонента для непосредственного воспроизведения музыки. О том, как это сделать, идет речь в сообщении блога для разработчиков приложений для Windows 8: "Создание собственных компонентов среды выполнения Windows для разработки эффективных приложений" (http://blogs.msdn.com/b/windowsappdev_ru/archive/2012/08/13/windows-metro.aspx).