Регулярно появляются статьи о создании кода Javascript который издаёт звуки в браузере. В них объясняется чем частота ноты До отличается от частоты ноты Ля, примеры кода исправно издают «бип-бип».

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

image

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

Как это работает


Для воспроизведения используется технология WebAudioFont. Скрипты иснструментов берутся непосредственно из проекта на GitHub

Ноты с длительностью забиваются в массив, каждый элемент которого это функция вида:

function bass(pitch, duration) {
	return {
		preset : _tone_Rubber_32Bass000079_461_460_45127,
		pitch : pitch,
		duration : duration
	};
}

T.е. просто возврат объекта содержащего пресет (инструмент, если в терминах WebAudioFont), высоту и длительность ноты.

Для наглядности высоту будем задавать в заранее заданных константах (var C = 0; var Cs= 1; var D = 2; и т.д.), а длительность — в долях от полной ноты.

Естественно, для ударных высота с длительностью не имеют смысла и поэтому задаются одинаковыми значениями.

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

function beats(notes) {
	for (var n = 0; n < notes.length; n++) {
		var beat = notes[n];
		for (var i = 0; i < beat.length; i++) {
			if (beat[i]) {
				player.queueWaveTable(audioContext
				, audioContext.destination
				, beat[i].preset
				, startTime + n * beatLen
				, beat[i].pitch
				, beat[i].duration);
			}
		}
	}
}

Отправляем массив в плеер бесконечно с определённым интервалом:

setInterval(function () {
	if (audioContext.currentTime > startTime - 1 / 4 * N) {
		nextPiece();
		startTime = startTime + pieceLen;
	}
}, 20);

После каждой отправки увеличиваем переменную startTime, в которой хранится время начала текущего куска.

Предупреждение: в современных браузерах при переводе фокуса в другое окно функции setTimeout и setInterval принудительно замедляются и мелодия будет «заедать».

Ещё пример для JSFiddle


Всё примерно то же самое, но нот побольше и инструменты воспроизводятся каждый через собственный GainNode для корректировки уровня громкости:

image

Запустить и прослушать пример можно тут.

В JSFiddle, в отличии от JSBin, можно ограничиться чистым JS-кодом без HTML, а скрипты инструментов и плеера WebAudioFont указать в левой части редактора в разделе External Resources.
Предупреждение для начинающих гитаристов: если в музыкальном магазине проверяете гитару перед покупкой и машинально начинаете наигрывать «Дым над водой» — сразу взимается штраф 150 руб.

Примеры работаеют и в мобильных браузерах, но редактировать код на маленьких экранах телефонов не очень удобно.
Поделиться с друзьями
-->

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


  1. igor_suhorukov
    05.11.2016 14:11

    Респект!!! А есть трекеры в браузере как Scream Tracker, OpenMPT


    1. DeXPeriX
      06.11.2016 17:57

      А меня больше волнуют не трекеры, а то, насколько сложно будет сделать конвертер из какого-нибудь распространённого трекерного формата в вот такое вот веб-аудио.


      1. igor_suhorukov
        06.11.2016 18:46
        +1

        Зачем? Трекер файл достаточно сложный по формату, эффектам и т.п. Если можно просто встроить воспроизведение трекер файла в страницу?

        Сам спросил — сам нашел https://github.com/deskjet/chiptune2.js — исходники кросскомпилированного libopenmpt из C/C++ в javascript

        http://deskjet.github.io/chiptune2.js — онлайн демо
        Даже отлично играет
        Hybrid song
        Heaven Seven by Exceed
        UnreaL ][ / PM из демки second reality by future crew


      1. GreyCat
        07.11.2016 12:22

        Есть готовый парсер MIDI :)
        Даже много, на самом деле…


    1. POPSuL
      14.11.2016 02:02

      В статье про WebAudioFont проскакивала ссылка на вот этот ресурс http://mod.haxor.fi/Manwe/mod.song_of_peace
      Вроде-бы не плохо звучит. Только вот возможности загрузить свой трек не нашёл.


  1. vintage
    05.11.2016 18:47

    А почему бы просто не генерировать midi файл налету?


    1. musicriffstudio
      05.11.2016 19:30
      +1

      зачем?


      1. vintage
        05.11.2016 19:44

        Чтобы воспроизводить?


        1. musicriffstudio
          05.11.2016 20:00
          +1

          он и так воспроизводится.


          1. vintage
            05.11.2016 20:37

            Только загружает 130Кб в течении полторы секунды.


            1. vintage
              05.11.2016 20:52

              Мда, Audio API не поддерживает MIDI. 21 век :-(


              1. musicriffstudio
                05.11.2016 22:44
                +6

                Стандарт General MIDI появился очень давно. Он описывает формат обмена сообщениями между устройствами, см. https://ru.wikipedia.org/wiki/MIDI.

                MIDI не имеет абсолютно никакого отношения непосредственно к воспроизведению звука. Воспроизведением занимается синтезатор, который получает команды в формате MIDI-сообщений. Сообщения могут быть как указанием начать проигрывать определённую ноту, так и чем-то непривычным, типа «сменить банк» или «повернуть колёсико частоты».

                Файлы с расширением .mid являются списком MIDI-команд которые может воспроизвести почти любое музыкальное ПО.

                К сожалению, укоренилось мнение что звук дешёвых аудиокарт с саундбанками размером в жалкий мегабайт, который слышен при воспроизведении в виндовом плеере, это и есть нечто называемое «MIDI».

                Это ошибка, см. объяснение выше.

                Таким образом, заявление вида «Audio API не поддерживает MIDI» не имеет смысла.

                Для браузера поддержка GM описывается стандартом Web MIDI API, в нём говорится именно о передаче сообщений между музыкальными устройствами и браузером. Web Audio API отвечает за синтез звука, читать файлы с расширением .mid в его задачи не входит.

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

                Для большинства есть тэг <audio> который прекрасно подходит для задач воспроизведения файлов mp3, которые звучат одинаково вне зависимости от стоимости и настроек оборудования.

                Итого:

                описываемый в статье способ кодирования музыки не имеет никакого отношения к MIDI или файлам с расширением .mid, не нуждается в них и прекрасно работает сам по себе, при минимальном для таких вещей объёме кода.


                1. vintage
                  06.11.2016 08:24
                  -7

                  Боюсь тест Тьюринга вы бы не прошли.


                  1. YaakovTooth
                    06.11.2016 18:03
                    +1

                    Прямое оскорбление? Вы, чай, паблосом ошиблись, м?


                    1. vintage
                      06.11.2016 19:49

                      А вот вы бы прошли.