Будем делать проигрыватель файлов .mid на Javascript и Web Audio API.
![image](https://habrastorage.org/webt/cn/sh/hi/cnshhis8gjge7iihn-iyyzxycqw.jpeg)
Конечный результат может выглядеть вот так.
![image](https://habrastorage.org/webt/je/9r/md/je9rmdmk8f3htjmovj4xxovlgbi.png)
Делать свой плеер имеет смысл для интерактивных музыкальных приложений т.к. будет возможен полный контроль за воспроизведением (смена инструментов, редактирование нот в рилтайме, точное позиционирование и т.п.).
Для обычной фоновой музыки больше подходит обычный mp3.
Файл с нотами надо как-то прочитать, загрузить используемые инструменты, синтезировать звук и отправить его в аудиовыход.
Всё просто.
Простой поиск по GitHub даёт нам кучу проектов для чтения MIDI-событий из файлов.
Выберем https://github.com/nfroidure/MIDIFile. Он выдаёт массив событий от смены программы, noteOn/noteOff и пр.
Для удобства придётся его немного модифицировать чтоб сразу получать ноты со временем и длительностью:
![image](https://habrastorage.org/webt/hs/rn/lk/hsrnlkaqgmxmmnauekwg118kjz0.png)
Для звука используется библиотека WebAudioFont, загрузка инструментов в ней делается буквально парой строчек, примерно так:
Можно отправить в очередь воспроизведения все ноты сразу. Но их тысячи даже в небольших музыкальных фрагментах и такой размер «повесит» аудио подсистему. Выход — отправлять небольшими кусками через обычную функцию setInterval:
О параметрах функции отправки звуков в очередь воспроизведения можно прочитать на странице проекта WebAudioFont.
В любой момент можно определить в каком месте файла сейчас находится воспроизведения. В демо-плеере это выглядит так:
Смена инструмента выглядит примерно так:
— буквально несколько строчек и всё подгружается само.
Пример плеера доступен по ссылке
Весь код занимает чуть менее 300 строк.
![image](https://habrastorage.org/webt/cn/sh/hi/cnshhis8gjge7iihn-iyyzxycqw.jpeg)
Конечный результат может выглядеть вот так.
![image](https://habrastorage.org/webt/je/9r/md/je9rmdmk8f3htjmovj4xxovlgbi.png)
Для чего
Делать свой плеер имеет смысл для интерактивных музыкальных приложений т.к. будет возможен полный контроль за воспроизведением (смена инструментов, редактирование нот в рилтайме, точное позиционирование и т.п.).
Для обычной фоновой музыки больше подходит обычный mp3.
Составные части
Файл с нотами надо как-то прочитать, загрузить используемые инструменты, синтезировать звук и отправить его в аудиовыход.
Всё просто.
Чтение файлов
Простой поиск по GitHub даёт нам кучу проектов для чтения MIDI-событий из файлов.
Выберем https://github.com/nfroidure/MIDIFile. Он выдаёт массив событий от смены программы, noteOn/noteOff и пр.
Для удобства придётся его немного модифицировать чтоб сразу получать ноты со временем и длительностью:
![image](https://habrastorage.org/webt/hs/rn/lk/hsrnlkaqgmxmmnauekwg118kjz0.png)
Загрузка музыкальных инструментов
Для звука используется библиотека 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 строк.
inoyakaigor
Я думал тут будет свой велосипед с бoльшим количеством кровавых подробностей: чтение и разбор MIDI-файла руками, синтез звука через Web Audio API и т.п. и т.д.
musicriffstudio Автор
в 21 веке живём. 300 строк и полноценный звук на выходе.
Статьи про разбор бинарного формата и выдачу бип-бип в спикер, возможно, кому-то и интересны. Но как-то малорезультативны.
domix32
Это как раз самый смак.