Кодируем музыку в браузере

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

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

image

Запустить код и прослушать можно здесь:
https://jsbin.com/nosojaz/edit? html, output

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


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

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

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

т.е. просто возврат объекта содержащего пресет (инструмент, если в терминах 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

Запустить и прослушать пример можно тут: https://jsfiddle.net/sss1024/c53Lwete/2/

В JSFiddle, в отличии от JSBin, можно ограничиться чистым JS-кодом без HTML, а скрипты инструментов и плеера WebAudioFont указать в левой части редактора в разделе External Resources.

Предупреждение для начинающих гитаристов: если в музыкальном магазине проверяете гитару перед покупкой и машинально начинаете наигрывать «Дым над водой» — сразу взимается штраф 150 руб.

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

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

© Habrahabr.ru