Содержание

MP3 Player JS. Flash аудиоплеер

Заставлять пользователей слушать музыку на вашем сайте это плохая идея (подгружать фоновую мелодию).

А вот если им дать возможность формировать свои плейлисты и возможность управлять прослушиванием (регулировать громкость, переключать и «перематывать» мелодии, и т.д.), то это может стать фишкой вашего сайта.

Если вы уже копались в сети, то наверняка уже находили множество аудиоплееров. Некоторые из них простые, некоторые навороченные, но что объединяет большинство из них, это уже готовый дизайн - один стандартный шаблон или несколько из которых можно выбрать.

В одном из своих проектов я столкнулся с необходимостью разместить музыкальный проигрыватель дизайн, которого был разработан специально для этого проекта. Тут я уже не мог взять какое-то стандартное решение, которое максимально подходило бы под общий дизайн.

Поэтому я начал искать плеер у которого изначально нет отображения, или это отображение можно легко менять (т. е. не зашито во flash). И такие тоже встречаются.

Свой выбор я остановил на MP3 Player JS. В чем прелесть этого проигрывателя - разработчик предоставляет flash файл, который не имеет внешнего отображения на странице и небольшую документацию по API этого проигрывателя. Все.

На основе документации предлагается разработать свой управляющий js-объект. Ну а так как мы имеем дело с JavaScript, то мы легко сможем настроить и необходимый нам внешний вид проигрывателя.


О MP3 Player JS

Сайт разработчиков: flash-mp3-player.net
Полная документация по типу JS: MP3 Player JS documentation

На сайте разработчика представлено несколько типов плееров. Некоторые типы с уже имеющемся визуальным отображением, для которых вы можете прямо на сайте разработчика настроить цветовое решение и параметры объекта.

Но нас интересует тип JS - данный вариант изначально не имеет графического отображения.

Для работы с плеером нам потребуются следующие файлы:


API плеера

Прежде чем мы начнем создавать свой управляющий объект, давайте рассмотрим что же нам дает проигрыватель.

Если вы ожидает тонну документации по работе с плеером, то я вас хочу огорчить, а может и порадовать - вся документация по проигрывателю типа JS помещается на одном листе А4.

На мой взгляд это очень здорово, т.к. работа плеера теперь зависит только от нашего воображения.

Параметры flash

Параметров всего пять:

Эти параметры передаются при создании html-объекта. Однако их можно изменять в процессе работы - особенно это актуально для параметра enabled. К примеру, если вы остановите мелодию, то данные от flash все равно будут приходить, хотя ни чего и не играется. Поэтому есть резон передать значение false параметру endbled, а при запуске следующей мелодии снова вернуть ему значение true.

Методы управления flash

Эти методы предназначены для управления flash:

Свойства js-объекта

Это основные свойства, которые присущи js-объекту, и которые обновляет flash.

События js-объекта

У нашего объекта будет всего лишь два события, которые вызывает flash:


Создание управляющего скрипта

Здесь я расскажу как реализовать основные возможности проигрывателя. Мы рассмотрим:

Я буду рассматривать создание плагина для jQuery, но вы вольны разработать объект на чистом JavaScript.

Со следующих строчек начнется полет моей фантазии, поэтому не стоит воспринимать это решение как что-то должное. Это скорее одна реализация из бесконечного числа возможных.

Создание jQuery плагина

Итак что же будет делать наш плагин. Внутри указанного html-элемента (предполагается что это div), плагин передаст инициализирующие параметры нашему объекту и построит html представление плеера.

В качестве параметров предлагаю передавать следующее:

Итак у нас получится примерно следующее:

JavaScript:
 
//объект получающий события от flash
var _audioplayer_listener = new Object(); 
 
//плагин
(function($) {
  $.fn.audioplayer = function(options) {
    //передаем данные по-умолчанию (плейлист, путь до flash, интервал обновления данных)
    options = $.extend({
      playlist: [{name: "Test", artist: "Tester", src:"test.mp3", duration:"00:00"}], 
      flash_url: "playermp3_js.swf",
      interval: 400
    }, options || {});
 
    //элемент родитель плеера
    var player = $(this);
 
    if(!player.data("audioplayer")) {
      var plugin = function() {
 
        //тут будет весь наш управляющий скрипт
 
        //параметры и функции, которые будут доступны из-вне
        return {
          flash_url:options.flash_url,
          interval:options.interval,
          listener:_audioplayer_listener,
          playlist:options.playlist,
          track:player_track,
          trackSet:function(track) {
            apTrackSetNum(track);
          },
          prev:function() {
            apPrev();
          },
          play:function() {
            apPlay();
          },
          pause:function() {
            apPause();
          },
          stop:function() {
            apStop();
          },
          next:function() {
            apNext();
          },
          playlistSet:function(playlist) {
            apPlaylistSet(playlist);
          }
	}      
      }
      player.data("audioplayer", plugin());
    }
    else {
      return player.data("audioplayer");
    }
  }
})(jQuery);

Обратите внимание на параметр playlist - он представляет собой массив объектов (мелодий), содержащих четыре поля:

Надо сразу оговориться, что длительность и ID3-теги (хотя это и не описано в документации) можно получить при загрузке мелодии во flesh, но есть одна маленькая деталь, что точное время определяется только после полной загрузки файла. А при получении русских названий из ID3-тегов они ломаются (используется какая-то хитрая кодировка) - тут можете конечно поколдовать и написать определитель кодировки, который вы будете вызывать через AJAX (на сколько мне известно в JavaScript не предусмотрено работа с кодировкой и все строки он хранит в utf-8).

Представьте, запускаете вы файл, а его продолжительность изменяется по мере его загрузки во flesh и вместо русских букв каракули - не очень хорошо, на мой взгляд. Именно для избежания этого мы и передаем данные из вне, а не получаем от flesh.

Инициализация плагина

Оформление наших скриптов в форме плагина, позволяет организовать очень простую инициализацию и управление нашим плеером:

JavaScript:
 
//создаем плеер внутри элемента с классом myaudioplayer
$('.myaudioplayer').audiopleyer({
  interval: 500,
  playlist: [{name:"song1", artist:"artist", src:"/mp3/song1.mp3", duration:"01:20"},
             {name:"song2", artist:"artist", src:"/mp3/song2.mp3", duration:"01:38"}]
});
 
//переопределяем плейлист
$('.myaudioplayer').audiopleyer().playlistSet([
  {name:"song10", artist:"artist", src:"/mp3/song10.mp3", duration:"02:20"},
  {name:"song20", artist:"artist", src:"/mp3/song20.mp3", duration:"02:38"},
  {name:"song30", artist:"artist", src:"/mp3/song30.mp3", duration:"02:19"}
]);

HTML отображение плеера

Мы создадим три основных html-блока:

Для создания html представления мы воспользуемся jQuery, не зря же мы пишем плагин для jQuery - давате использовать и его возможности.

Графическое отображение

JavaScript:
 
//обертка для нашего плеера, которая будет помещена в родительский элемент (смотри выше - var player = $(this))
var player_wrapper = $('<div class="audioplayer"/>');
 
//часть для управления
var player_control = $('<div class="control"/>');
 
//стандартные кнопки
var player_control_prev = $('<div class="prev" title="Prev"/>');
var player_control_play = $('<div class="play" title="Play"/>');
var player_control_pause = $('<div class="pause" title="Pause"/>');
var player_control_stop = $('<div class="stop" title="Stop"/>');
var player_control_next = $('<div class="next" title="Next"/>');
 
//звук
var player_control_volume = $('<div class="volume"/>');
var player_control_volume_slider = $('<div class="slider"/>');
player_control_volume.append(player_control_volume_slider);
 
//сделаем наш бегунок в виде slider предоставляемого jQueryUI
//зададим первоначальное значение уровня громкости 50%
player_control_volume_slider.slider({
  range: "min",
  value:50,
  slide:function(event, ui){
    //зададим обработчик перемещения слайдера, функция apSetVolume рассматривается далее
    apVolumeSet(ui.value);
  }
});
 
//прогресс бар (панель отображающая бегунок времени, текущее время и длительность мелодии) 
var player_control_progress = $('<div class="progress"/>');
var player_control_progress_position = $('<div class="position"/>');
var player_control_progress_track = $('<div class="track"/>');
var player_control_progress_track_slider = $('<div class="slider"/>');
player_control_progress_track.append(player_control_progress_track_slider);
var player_control_progress_duration = $('<div class="duration"/>');
player_control_progress.append(player_control_progress_position, player_control_progress_track, player_control_progress_duration);
 
//сделаем бегунок времени в виде slider предоставляемого jQueryUI
player_control_progress_track_slider.slider({
  range: "min",
  slide:function(event, ui) {
    //зададим обработчик перемещения слайдера, функция apSetPosition рассматривается далее
    apPositionSet(ui.value);
  }
});
 
//кнопки shuffle и repeat
var player_control_shuffle = $('<div class="shuffle" title="Shuffle"/>');
var player_control_repeat = $('<div class="repeat" title="Repeat"/>');
 
//название и исполнитель
var player_control_title = $('<div class="title"/>');
var player_control_title_artist = $('<p class="artist"/>');
var player_control_title_name = $('<p class="name"/>');
player_control_title.append(var player_control_title_artist, var player_control_title_name);
 
//собираем все в player_control
player_control.append(player_control_prev, player_control_play, player_control_pause, player_control_stop, player_control_next, player_control_volume, player_control_progress, player_control_shuffle, player_control_repeat, player_control_title);

Объект содержащий сам flesh плеер

Теперь мы создадим html-объект и передадим ему наши настройки:

JavaScript:
 
var player_swf = $('<div class="swf"/>');
//передаем ссылку к flesh
var player_swf_object = $('<object id="swf_audioplayer" type="application/x-shockwave-flash" data="' + options.flash_url + '" width="1" height="1"/>');
 
//параметры объекта
var player_swf_object_param1 = $('<param name="movie" value="' + options.flash_url + '"/>');
var player_swf_object_param2 = $('<param name="AllowScriptAccess" value="always"/>');
 
//передаем управляющие параметры - интервал обновления данных и название объекта которуму передавать эти данные
var player_swf_object_param3 = $('<param name="FlashVars" value="listener=_audioplayer_listener&amp;interval=' + options.interval + '"/>');
 
//собираем объект
player_swf_object.append(player_swf_object_param1, player_swf_object_param2, player_swf_object_param3);
player_swf.append(player_swf_object);
 
//создаем привязку к flash объекту, по которой мы будем передавать управляющие параметры.
//тут мы должны получить сам объект, а не объект в обертке jQuery, поэтому используем классический метод JavaScript
var swf = document.getElementById("swf_audioplayer");

Плейлист

Как вы уже видели, плейлист передается в виде массива мелодий, представляющих собой объекты с полями:

В принципе если вы не хотите отображать плейлист, то можете этот блок не создавать вообще.

JavaScript:
 
var player_playlist = $('<div class="playlist"/>');
 
//формируем плейлист
for(var i = 0; i < options.playlist.length; i++) {
  var song = options.playlist[i];
  var player_playlist_song = $('<div class="song"/>');
  var player_playlist_song_name = $('<p class="name"/>').html(song.name);
  var player_playlist_song_artist = $('<p class="artist"/>').html(song.artist);
  var player_playlist_song_duartion = $('<p class="duartion"/>').html(song.duartion);
  var player_playlist_song_src = $('<p class="src"/>').html(song.src);
  player_playlist_song.click(function(){
    apTrackSet(this);
  });
  player_playlist_song.append(player_playlist_song_name, player_playlist_song_artist, player_playlist_song_duartion, player_playlist_song_src);
  player_playlist.append(player_playlist_song);
};

Формирование HTML структуры плеера

Основные блоки, отвечающие за внешнее представление, готовы и остается только собрать их воедино:

JavaScript:
 
player_wrapper.append(player_control, player_swf, player_playlist);
 
//помещаем полученный блок в элемент-родитель, предварительно очистив все его содержимое
player.html('').append(player_wrapper);

В результате для инициализации:

JavaScript:
 
$('.myaudioplayer').audiopleyer({
  interval: 500,
  playlist: [{name:"song1", artist:"artist", src:"/mp3/song1.mp3", duration:"01:20"},
             {name:"song2", artist:"artist", src:"/mp3/song2.mp3", duration:"01:38"}]
});

Мы должны получить следующий HTML код:

HTML:
 
<div class="myaudioplayer">
  <div class="audioplayer">
    <div class="control">
      <div class="prev" title="Prev"></div>
      <div class="play" title="Play"></div>
      <div class="pause" title="Pause"></div>
      <div class="stop" title="Stop"></div>
      <div class="next" title="Next"></div>
      <div class="volume">
        <div class="slider ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all">
          <div class="ui-slider-range ui-widget-header ui-slider-range-min" style="width:50%;"></div>
          <a class="ui-slider-handle ui-state-default ui-corner-all" href="#" style="left:50%;"></a>
        </div>
      </div>
      <div class="progress">
        <div class="position"></div>
        <div class="track">
        <div class="slider ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all">
          <div class="ui-slider-range ui-widget-header ui-slider-range-min" style="width:0%;"></div>
          <a class="ui-slider-handle ui-state-default ui-corner-all" href="#" style="left:0%;"></a>
        </div>
        </div>
        <div class="duration"></div>
      </div>
      <div class="shuffle" title="Shuffle"></div>
      <div class="repeat" title="Repeat"></div>
      <div class="title">
        <p class="artist"></p>
        <p class="name"></p>
      </div>
    </div>
    <div class="swf">
      <object id="swf_audioplayer" type="application/x-shockwave-flash" data="playermp3_js.swf" width="1" height="1">
        <param name="movie" value="playermp3_js.swf"/>
        <param name="AllowScriptAccess" value="always"/>
        <param name="FlashVars" value="listener=_audioplayer_listener&amp;interval=500"/>
      </object>
    </div>
    <div class="playlist">
      <div class="song">
        <p class="name">song1</p>
        <p class="artist">artist</p>
        <p class="duration">01:20</p>
        <p class="src">/mp3/song1.mp3</p>        
      </div>
      <div class="song">
        <p class="name">song2</p>
        <p class="artist">artist</p>
        <p class="duration">01:38</p>
        <p class="src">/mp3/song2.mp3</p>
      </div>
    </div>
  </div>
</div>

JS-объект (listener)

Cам объект мы уже определили, осталось только добавить обработчики двух событий, генерируемых flesh.

Событие onInit

При вызове этого события мы зададим первоначальные параметры нашего объекта, а так же добавим несколько полей, которые пригодятся нам в будущем:

Вы спросите, для чего нам isPaused если есть стандартный флаг isPlaying? Параметр isPlaying показывает играет или нет мелодия в конкретный момент. Но мелодия может не играть по двум причинам - ее поставили на паузу или ее остановили совсем, что эквивалентно тому что мелодия доиграла до конца. Именно для различения этих состояний нам и нужен флаг isPaused.

В качестве начальных параметров зададим уровень громкости 50%, загрузим в плеер первую мелодию

JavaScript:
 
_audioplayer_listener.onInit = function() {
  this.track = 0;
  this.isPaused = false;
  this.isRepeat = false;
  this.isShuffle = false;
  swf.SetVariable("method:setVolume", 50);
  if(options.playlist.length > 0) {
    swf.SetVariable("method:setUrl", options.playlist[0].src);
    player_control_duration.html(options.playlist[0].duration);
    player_control_artist.html(options.playlist[0].artist);
    player_control_name.html(options.playlist[0].name);    
  }
  else {
    this.url = false;
  }
}

Событие onUpdate

На это событие приходится основная нагрузка по обновлению графического представления нашего проигрывателя.

JavaScript:
 
_audioplayer_listener.onUpdate = function() {
  //параметр isPlaying приходит из flesh в виде строки, а для нас представляет интерес логический параметр
  this.isPlaying = (this.isPlaying == "true") ? true : false;
 
  //если у нас мелодия остановилась, но пауза не нажата, это значит что она доиграла до конца и следует запустить следубщую
  if(!this.isPlaying && !this.isPaused) {
    swf.SetVariable("enabled", "false");
    apNext();
  }
 
  //обновляем счетчик прошедшего времени
  var time = Math.round(this.position / 1000);
  var minutes = Math.flour(time / 60);
  var seconds = time - minutes * 60;
  player_control_progress_position.html(minutes + ":" + seconds);
 
  //передвигаем бегунок времени
  //я уже отмечал, что полное время показывается только после полной загрузки файла
  //поэтому, чтобы наш бегунок не скакал туда-сюда при загрузке файла, будем учитывать процент загрузки
  player_control_progress_track_slider.slider("value", Math.round((this.position / this.duration) * this.bytesPercent));
}

Управляющие функции

Давайте рассмотрим все функции, которые нам потребуются для управления проигрывателем.

apTrackSet()

Эта функция определяет номер выбранной песни в списке и передает его функции apPlaylistSetNum(). Как вы помните мы повесили обработчик onclick на каждую песню плейлиста, который передает елемент по которому произошел щелчок мыши.

JavaScript:
 
function apTrackSet(element) {
  //определяем номер песни в списке
  var song_src = $('.src', element).html();
  var song_number = 0;
  for(var i = 0; i < options.playlist.length; i++) {
    if(options.playlist[i].src == song_src) {
      song_number = i;
      break;
    }
  }
 
  //устанавливаем песню
  apTrackSetNum(song_number);
}

apTrackSetNum()

Эта функция будет останавливать проигрыватель и запускать выбранную мелодию из плейлиста по ее номеру. Согласно описанию нашего плагина, эта функция доступна из-вне.

JavaScript:
 
function apTrackSetNum(num) {
  if(num > 0 && num < options.playlist.length) {
    //получаем URL установленно песни
    var url = options.playlist[num].src;
 
    //обновляем данные графической части
    player_control_duration.html(options.playlist[num].duration);
    player_control_artist.html(options.playlist[num].artist);
    player_control_name.html(options.playlist[num].name);
 
    //обновляем поля объекта 
    _audioplayer_listener.track = num;
 
    //останавливаем проигрыватель
    swf.SetVariable("method:stop", "");
    swf.SetVariable("enabled", "false");
 
    //запускаем выбранную песню
    swf.SetVariable("method:setUrl", url);
    swf.SetVariable("method:play", "");
    swf.SetVariable("enabled", "true");
  }
}

apPlay()

Запускает воспроизведение мелодии.

JavaScript:
 
function apPlay() {
  if(_audioplayer_listener.url) {
    swf.SetVariable("method:play", "");
    swf.SetVariable("enabled", "true");
    _audioplayer_listener.isPaused = false;
  }
}

apPause()

Ставим мелодию на паузу.

JavaScript:
 
function apPause() {
  if(_audioplayer_listener.url) {
    swf.SetVariable("method:pause", "");
    _audioplayer_listener.isPaused = true;
  }
}

apStop()

Останавливаем проигрыватель.

JavaScript:
 
function apStop() {
  swf.SetVariable("method:stop", "");
  swf.SetVariable("enabled", "false");
  player_control_progress_track_slider.slider("value", 0);
}

apNext()

Запускаем следующую песню. Если установлен флаг isRepeat, то повторяем текущую еще раз.

В данном случаи если стоит флаг isShuffle, то мы выбираем песню случайным образом. Но в целом я бы посоветовал формировать второй список воспроизведения путем перемешивания основного списка воспроизведения и в зависимости от isShuffle брать песню из первого или из второго списка.

JavaScript:
 
function apNext() {
  if(!_audioplayer_listener.isRepeat) {
    if(_audioplayer_listener.isShuffle) {
      _audioplayer_listener.track = Math.floor(Math.random() * (options.playlist.length + 1))
    }
    else {
      _audioplayer_listener.track++;
      if(_audioplayer_listener.track >= options.playlist.length) {
        _audioplayer_listener.track = 0;
      }
    }
  }
 
  apTruckSetNum(_audioplayer_listener.track);
}

apPrev()

Запускаем предыдущую песню.

Все аналогично apNext().

JavaScript:
 
function apPrev() {
  if(!_audioplayer_listener.isRepeat) {
    if(_audioplayer_listener.isShuffle) {
      _audioplayer_listener.track = Math.floor(Math.random() * (options.playlist.length + 1))
    }
    else {
      _audioplayer_listener.track--;
      if(_audioplayer_listener.track < 0) {
        _audioplayer_listener.track = options.playlist.length - 1;
      }
    }
  }
 
  apTruckSetNum(_audioplayer_listener.track);
}

apVolumeSet()

Устанавливаем уровень громкости. Напоминаю, что запуск этой функции осуществляется обработчиком событий бегунка уровня звука (slider).

JavaScript:
 
function apVolumeSet(volume) {
  swf.SetVariable("method:setVolume", volume);
}

apPositionSet()

Устанавливаем место воспроизведения мелодии. Напоминаю, что запуск этой функции осуществляется обработчиком событий бегунка времени (slider).

Не забываем, что обработчик передает значение от 0 до 100.

JavaScript:
 
function apPositionSet(position) {
  //переводим проценты в миллисекунды
  position = Math.round(position * _audioplayer_listener.duration / 100);
  swf.SetVariable("method:setPosition", position);
}

apPlaylistSet()

Устанавливаем новый плейлист. По сути нам нужно проделать тоже самое, что мы делали при инициализации нашего плагина.

JavaScript:
 
function apPlaylistSet(playlist) {
  //очищаем текущий плейлист
  player_playlist.html('');
 
  //формируем новый
  for(var i = 0; i < playlist.length; i++) {
    var song = playlist[i];
    var player_playlist_song = $('<div class="song"/>');
    var player_playlist_song_name = $('<p class="name"/>').html(song.name);
    var player_playlist_song_artist = $('<p class="artist"/>').html(song.artist);
    var player_playlist_song_src = $('<p class="src"/>').html(song.src);
    var player_playlist_song_duration = $('<p class="duration"/>').html(song.duration);
    player_playlist_song.click(function(){
      apTrackSet(this);
    });
    player_playlist_song.append(player_playlist_song_name, player_playlist_song_artist, player_playlist_song_duartion, player_playlist_song_src);
    player_playlist.append(player_playlist_song);    
  }  
}

Обработчики нажатий на управляющие элементы плеера

Как вы понимает основной функционал уже описан и остается привязать выполнение тех или иных функций при нажатии на управляющие кнопки плеера.

Обработчики бегунков уже были описаны, а остальные описываются буквально парой строк, поэтому я приведу их единым блоком:

JavaScript:
 
//нажатие на кнопку play
player_control_play.click(function() {
  apPlay();
});
 
//нажатие на кнопку pause
player_control_pause.click(function() {
  apPause();
});
 
//нажатие на кнопку stop
player_control_stop.click(function() {
  apStop();
});
 
//нажатие на кнопку prev
player_control_prev.click(function() {
  apPrev();
});
 
//нажатие на кнопку next
player_control_next.click(function() {
  apNext();
});
 
//нажатие на кнопку shuffle
player_control_shuffle.click(function() {
  if(_audioplayer_listener.isShuffle) {
    _audioplayer_listener.isShuffle = false;
  }
  else {
    _audioplayer_listener.isShuffle = true;
  }
});
 
//нажатие на кнопку repeat
player_control_repeat.click(function() {
  if(_audioplayer_listener.isRepeat) {
    _audioplayer_listener.isRepeat = false;
  }
  else {
    _audioplayer_listener.isRepeat = true;
  }
});

Плеер готов к работе.


Резюме

Как показывает практика, чем больше ограничений разработчик накладывает на свое приложение - тем проще с ним работать. Однако не надо забывать, что вам придется работать в рамках этих ограничений и если вы захотите большого, то придется поменять приложение.

С другой стороны, чем меньше ограничений у приложения, тем более квалифицированного подхода оно требует.

Не совсем подходящая, но тем не менее аналогия из области разработки дизайна. Давно известно, что заказчику нужно показывать два, максимум три варианта макета. В этом случаи он быстро примет решение. Если же ему показать 10 вариантов, то принятие решения может затянутся на месяца.

Понятно, что в большинстве случаев подойдет стандартный дизайн аудио плеера с возможностью кастомизации цветового решения. Даже в данном случаи разработчик flesh предлагает целых 4 варианта стандартного дизайна с очень гибкой системой настройки цветового решения.

Но если вы столкнулись с необходимостью или просто хотите создать уникальный дизайн аудио плеера, то рекомендую обратить внимания на представленное решение. Оно полностью развязывает вам руки - единственные ограничения это мастерство дизайнера, создающего внешнее представление плеера и мастерство программиста, который описывает внутреннее представление.

Причем мастерство обоих ограничено их воображением и практическим опытом.