Будем делать проигрыватель файлов .mid на Javascript и Web Audio API.

image

Конечный результат может выглядеть вот так.

image

Для чего


Делать свой плеер имеет смысл для интерактивных музыкальных приложений т.к. будет возможен полный контроль за воспроизведением (смена инструментов, редактирование нот в рилтайме, точное позиционирование и т.п.).

Для обычной фоновой музыки больше подходит обычный mp3.

Составные части


Файл с нотами надо как-то прочитать, загрузить используемые инструменты, синтезировать звук и отправить его в аудиовыход.

Всё просто.

Чтение файлов


Простой поиск по GitHub даёт нам кучу проектов для чтения MIDI-событий из файлов.

Выберем https://github.com/nfroidure/MIDIFile. Он выдаёт массив событий от смены программы, noteOn/noteOff и пр.

Для удобства придётся его немного модифицировать чтоб сразу получать ноты со временем и длительностью:

image

Загрузка музыкальных инструментов


Для звука используется библиотека WebAudioFont, загрузка инструментов в ней делается буквально парой строчек, примерно так:

for (var i = 0; i < song.tracks.length; i++) {
	var nn = findFirstIns(player, song.tracks[i].program);
	var info = player.loader.instrumentInfo(nn);
	player.loader.startLoad(audioContext, info.url, info.variable);
	}

Синтез звука


Можно отправить в очередь воспроизведения все ноты сразу. Но их тысячи даже в небольших музыкальных фрагментах и такой размер «повесит» аудио подсистему. Выход — отправлять небольшими кусками через обычную функцию setInterval:

setInterval(function () {
	if (audioContext.currentTime > nextStepTime - stepDuration) {
		sendNotes(song, songStart, currentSongTime, currentSongTime + stepDuration, audioContext, input, player);
		currentSongTime = currentSongTime + stepDuration;
		nextStepTime = nextStepTime + stepDuration;
	}
}, 22);

О параметрах функции отправки звуков в очередь воспроизведения можно прочитать на странице проекта WebAudioFont.

Интерактив


В любой момент можно определить в каком месте файла сейчас находится воспроизведения. В демо-плеере это выглядит так:

function showPosition(song, currentSongTime) {
	var o = document.getElementById('position');
	o.value = 100 * currentSongTime / song.duration;
	document.getElementById('tmr').innerHTML = '' + Math.round(100 * currentSongTime / song.duration) + '%';
}

Смена инструмента выглядит примерно так:

var nn=selectIns.value;
var info = player.loader.instrumentInfo(nn);					
player.loader.startLoad(audioContext, info.url, info.variable);
player.loader.waitLoad(function () {
	console.log('loaded');
	song.tracks[i].info = info;
});

— буквально несколько строчек и всё подгружается само.

Исходный код


Пример плеера доступен по ссылке

Весь код занимает чуть менее 300 строк.

Комментарии (3)


  1. inoyakaigor
    09.04.2018 13:55

    Я думал тут будет свой велосипед с бoльшим количеством кровавых подробностей: чтение и разбор MIDI-файла руками, синтез звука через Web Audio API и т.п. и т.д.


    1. musicriffstudio Автор
      09.04.2018 14:12

      в 21 веке живём. 300 строк и полноценный звук на выходе.

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


      1. domix32
        10.04.2018 11:29

        Это как раз самый смак.