Пианино в 24 строки на Javascript: если играть, то музыку

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

    Я тоже решил принять участие в этой специальной спонтанной олимпиаде кодерского мастерства, и вспомнил фразу одной моей подруги-музыканта: «Если уж играть, то на пианино». И решил: да будет так. Вместо игры напишу пианино. И написал.

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

    Итак, начнем.

    Клавиатура классического фортепиано состоит из 88 клавиш, покрывающих диапазон от A0 (Ля суб-контр-октавы, частота звучания 27.5 Гц) до C8 (До пятой октавы, частота 4186 Гц). Каждая октава на клавиатуре состоит из двенадцати нот:
    До, До-диез, Ре, Ре-диез, Ми, Фа, Фа-диез, Соль, Соль-диез, Ля, Ля-диез/Си-бемоль, Си. Жирным выделены клавиши верхнего ряда, они на клавиатуре обычно бывают черного цвета.

    Собственно, вот так выглядит одна октава:

    image

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

    Nx = N1 × 2x-1, где:
    • N – название ноты;
    • x — номер октавы (от 0 до 8);
    • Nx, соответственно, частота звука, соответствующая ноте N октавы x;

    В формуле фигурирует N1 вместо N0 лишь потому, что часть нот суб-контр-октавы (N0) имеет частоту звучания ниже порога слышимости человеческим ухом (< 20 Hz).

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

    C: 32.703,
    С#: 34.648,
    D: 36.708,
    D#: 38.891,
    E: 41.203,
    F: 43.654,
    F#: 46.249,
    G: 48.999,
    G#: 51.913,
    A: 55,
    A#: 58.27,
    B: 61.735

    Основываясь на этом, пишем функцию, принимающую в качестве аргумента строку с именем клавиши в виде "A4" или "C5#", и возвращающую частоту её звучания:

    function play(key) {
    	var controctave = {
    			'C': 32.703,
    			'С#': 34.648,
    			'D': 36.708,
    			'D#': 38.891,
    			'E': 41.203,
    			'F': 43.654,
    			'F#': 46.249,
    			'G': 48.999,
    			'G#': 51.913,
    			'A': 55,
    			'A#': 58.27,
    			'B': 61.735,
    		},
    		note = key[0].toUpperCase(),
    		octave = parseInt(key[1]),
    		sharp = key[2] == '#' ? true : false;
    	if (sharp) {
    		return controctave[note + '#'] * Math.pow(2, octave-1);
    	} else {
    		return controctave[note] * Math.pow(2, octave-1);
    	}
    }
    

    Ах, да, мы же пишем не красиво, а коротко. Немного подсократим:

    function play(key) {
    	var controctave = { 'C': 32.703, 'С#': 34.648, 'D': 36.708, 'D#': 38.891, 'E': 41.203, 'F': 43.654, 'F#': 46.249, 'G': 48.999, 'G#': 51.913, 'A': 55, 'A#': 58.27, 'B': 61.735};
    	freq = key[2] == '#' ? controctave[key[0].toUpperCase() + '#'] * Math.pow(2, (key[1]|0) - 1) : controctave[key[0].toUpperCase()] * Math.pow(2, (key[1]|0) - 1);
    	return freq; }
    


    Уже использовано четыре строчки кода.

    Давайте нарисуем клавиатуру

    88 клавиш клавиатуры начинаются с ноты Ля (A0).
    Соответственно, цикл будет такой: в цикле рисуем по 12 клавиш, и каждую вторую, четвертую, седьмую, девятую и одиннадцатую делаем черной. Каждой клавише присвоим id, соответствующей ноте, которую она должна воспроизводить при нажатии.

    В общем, так:

    var width = 1000;
    var deck = document.createElement('div'), 
    	octave = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'], 
    	id = "", 
    	keynumber = 0,
    	whitekeys = 0,
    	keys = [];
    
    deck.style.width = width;
    
    parent:
    for (var i = 0; i < 8; i++) {
    	for (var j = 0; j < 12; j++) {
    		keynumber = (i * 12) + j;
    		if (keynumber >= 88) break parent;
    		keys[keynumber] = document.createElement('div');
    		keys[keynumber].style.border = "1px solid black";
    		keys[keynumber].style.position = "absolute";
    		id = (octave[j][1] == '#') ? octave[j] + i + 's' : octave[j] + i;
    		keys[keynumber].id = id;
    
    		switch(j%12) {
    			case 1: 
    			case 3: 
    			case 6: 
    			case 8:
    			case 10:
    				keys[keynumber].style.backgroundColor = 'black';
    				keys[keynumber].style.left = ((width / 50 * whitekeys) - (width / 200)) + 'px';
    				keys[keynumber].style.width = width/100 + "px";
    				keys[keynumber].style.height = "200px";
    				keys[keynumber].style.zIndex = 10;
    				break;
    			default:
    				keys[keynumber].style.backgroundColor = 'white';
    				keys[keynumber].style.left = (width / 50 * whitekeys) + 'px';
    				keys[keynumber].style.width = width/50 + "px";
    				keys[keynumber].style.height = "300px";
    				whitekeys++;
    
    		}
    
    		deck.appendChild(keys[keynumber]);
    
    	}
    }
    
    document.body.appendChild(deck);
    

    И вновь превратим нормальный код в нечитабельное говнище применим небольшую оптимизацию.

    var width = 1000, octave = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'], id = "", div, whitekeys=0, keys = [];
    parent: for (var i = 0; i < 8; i++) {
    	for (var j = 0; j < 12; j++) {
    		if ((i * 12) + j >= 88) break parent;
    		div = document.createElement('div');
    		div.id = (octave[j][1] == '#') ? octave[j][0] + ((((i * 12) + j + 9) / 12)|0) + 's' : octave[j] + ((((i * 12) + j + 9) / 12)|0);
    		if (j % 12 == 1 || j % 12 == 4 || j % 12 == 6 || j % 12 == 9 || j % 12 == 11) {
    			div.setAttribute('style', 'border:1px solid black; position:absolute; background-color:black; left:' + ((width / 50 * whitekeys) - (width / 200)) + 'px; width:' + width/100 + 'px; height: 200px; z-index:1;');}
    		else {
    			div.setAttribute('style', 'border:1px solid black; position:absolute; background-color:white; left:' + (width / 50 * whitekeys) + 'px; width:' + width/50 + 'px; height:300px;');
    			whitekeys++;
    		}
    		document.body.appendChild(div);}}
    


    Мы израсходовали ещё 13 строк.

    Научим пианино издавать звуки

    Для этого нам понадобится Web Audio API, который на сей момент поддерживается только Webkit-based браузерами и Firefox.

    Добавим в строку объявления глобальных переменных создание аудиоконтекста:
    context = window.AudioContext ? new AudioContext() : new webkitAudioContext();
    

    добавим обработчик нажатий на клавиши:
    document.body.addEventListener('click', play);
    

    а саму функцию play изменим следующим образом:
    function play(e) {
    	var controctave = { 'C': 32.703, 'Cs': 34.648, 'D': 36.708, 'Ds': 38.891, 'E': 41.203, 'F': 43.654, 'Fs': 46.249, 'G': 48.999, 'Gs': 51.913, 'A': 55, 'As': 58.27, 'B': 61.735}, osc = context.createOscillator();
    	osc.frequency.value = e.target.id[2] == 's' ? controctave[e.target.id[0] + 's'] * Math.pow(2, (e.target.id[1]|0) - 1) : controctave[e.target.id[0]] * Math.pow(2, (e.target.id[1]|0) - 1);
    	osc.type = "square";
    	osc.connect(context.destination);
    	osc.start(0);
    	setTimeout(function() {
    	    osc.stop(0);
    	    osc.disconnect(context.destination);
    	}, 1000 / 2);}
    


    Здесь мы создали осциллятор: osc = context.createOscillator();, установили ему необходимую частоту звучания: osc.frequency.value = e.target.id[2] == 's' ? controctave[e.target.id[0] + 's'] * Math.pow(2, (e.target.id[1]|0) - 1) : controctave[e.target.id[0]] * Math.pow(2, (e.target.id[1]|0) - 1); (ну, мы же не следим за чистотой и опрятностью кода, не так ли?), установили форму сигнала: osc.type = "square"; (по умолчанию был синусоидальный) соединили его с устройством вывода звука: osc.connect(context.destination);, и дали команду начать воспроизведение: osc.start(0);. После этого нам необходимо заставить клавишу замолчать через некоторое время (500мс), а то она так и будет противно пищать. Для этого используем osc.stop(0), завёрнутый в интервал. Обязательный элемент — osc.disconnect(context.destination); — отключаем осциллятор от устройства вывода.

    Резюмируем: у нас получился вот такой нехитрый код:

    var width = 1000, octave = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'], id = "", div, whitekeys=0, keys = [],context = window.AudioContext ? new AudioContext() : new webkitAudioContext();
    parent: for (var i = 0; i < 8; i++) {
    	for (var j = 0; j < 12; j++) {
    		if ((i * 12) + j >= 88) break parent;
    		div = document.createElement('div');
    		div.id = (octave[j][1] == '#') ? octave[j][0] + ((((i * 12) + j + 9) / 12)|0) + 's' : octave[j] + ((((i * 12) + j + 9) / 12)|0);
    		if (j % 12 == 1 || j % 12 == 4 || j % 12 == 6 || j % 12 == 9 || j % 12 == 11) {
    			div.setAttribute('style', 'border:1px solid black; position:absolute; background-color:black; left:' + ((width / 50 * whitekeys) - (width / 200)) + 'px; width:' + width/100 + 'px; height: 200px; z-index:1;');}
    		else {
    			div.setAttribute('style', 'border:1px solid black; position:absolute; background-color:white; left:' + (width / 50 * whitekeys) + 'px; width:' + width/50 + 'px; height:300px;');
    			whitekeys++;
    		}
    		document.body.appendChild(div);}}
    document.body.addEventListener('click', play);
    function play(e) {
    	var controctave = { 'C': 32.703, 'Cs': 34.648, 'D': 36.708, 'Ds': 38.891, 'E': 41.203, 'F': 43.654, 'Fs': 46.249, 'G': 48.999, 'Gs': 51.913, 'A': 55, 'As': 58.27, 'B': 61.735}, osc = context.createOscillator();
    	osc.frequency.value = e.target.id[2] == 's' ? controctave[e.target.id[0] + 's'] * Math.pow(2, (e.target.id[1]|0) - 1) : controctave[e.target.id[0]] * Math.pow(2, (e.target.id[1]|0) - 1);
    	osc.type = "square";
    	osc.connect(context.destination);
    	osc.start(0);
    	setTimeout(function() {
    	    osc.stop(0);
    	    osc.disconnect(context.destination);
    	}, 1000 / 2);}
    


    В заключение хочу сказать, что теперь меня надо называть Страдивари XXI века Web Audio API — штука очень классная и интересная. Почитать про него можно, естественно, на MDN, могу посоветовать милый туториал на HTML5Rocks и ещё один забавный эксперимент.

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

    Поиграть

    Посмотреть код

    P.S. динамики макбука, например, отказываются издавать слышимые звуки вплоть до малой октавы (т.е. до 130 Гц), что неудивительно. В общем, не удивляйтесь, если левая часть клавиатуры будто бы вообще не звучит.
    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 54
    • +8
      Вызов принят.
      • +12
        Вот почему было все со змейки: якобы сейчас каждый новый пост, это как еда для той же змейки, в итоге хабр растет с постами о играх в 30+ строк.
      • +10
        Можно ещё добавить немного для поддержки игры с клавиатуры:
        document.body.addEventListener('keydown', press);
        function press(e) {
          var map = 'zsxdcvgbhnjmq2w3er5t6y7ui9o0p', key = map.indexOf(String.fromCharCode(e.which).toLowerCase());
          if (key + 1) play({target: document.querySelector('div:nth-child(' + (29 + key)  + ')')});}
        


        jsfiddle.net/c62Pe/3/embedded/result/ (сначала айфрейму надо дать фокус)
      • +6
        А как же кривая громкости ADSR (attack-decay-sustain-release)?
        • +22
          Я всегда балдею, когда читаю что-то вроде «я вообще нуб в этом деле», а потом следует что-то вроде «покрывающих диапазон от A0 (Ля суб-контр-октавы, частота звучания 27.5 Гц) до C8 (До пятой октавы, частота 4186 Гц)».
          Я хоть и играю на настроенной гитаре и значительно больше десятка песен, но для меня большая часть этой фразы — магия, и больше всего мне ясны слова «частота звучания» и «герц» =)
          • +1
            Где же тут магия? Эти страшные слова — по сути всего лишь названия клавиш. Все что нужно знать для написания эмулятора — то, что такие клавиши есть.
            • 0
              Вообще суть многих страшных слов очень часто сводится к простым принципам. Взять хотя бы те же синхрофазотрон и интеграл.
              Но все-таки после слов автора «моё музыкальное образование ограничивается десятком блатных песенок на расстроенной гитаре» как-то не очень ожидалось появление термина «суб-контр-октава» без пояснения, что это заклинание значит =)
            • +5
              Эммм. Ну я уж не знаю какой-то более базовой вещи в теории музыки.

              image
              • 0
                Ну так автор и не упоминал о том, что он знаком с теорией музыки. Я как гитарист-самоучка могу определенно сказать, что для блатных песен под гитару никакие понятия о теории музыки не нужны. А для примитивной настройки гитары достаточно только понимать явление резонанса.
                Еще раз процитирую автора «моё музыкальное образование ограничивается десятком блатных песенок на расстроенной гитаре».
                Именно поэтому я приготовился к некоторому уровню изложения. И тут на тебе — суб-контр-октава! Да еще и Ля суб-контр-октавы! Для меня это было несколько внезапно =)
                И вообще, предлагаю закончить оффтопить =) Мне кажется, я уже достаточно аргументировал свое высказывание. Оставим место для полезных комментариев =)
                • 0
                  Не, я удивился что для вас «большая часть этой фразы — магия» при том, что «хоть и играю на настроенной гитаре и значительно больше десятка песен».

                  Хотя конечно прекрасно представляю как можно играть на скажем гитаре без теории этой всей. Я-то и сам музыкант-самоучка (барабаны), правда базовое сольфеджио осилил самостоятельно, даже гаммы на фортепиано поиграл пока не надоело.
                  • +1
                    Да это просто мечта детства была — научиться на гитаре играть.
                    Потом гитара появилась, я взял самоучитель… А там чего-то втирают, втирают… А как после этого «Что такое осень» сыграть — непонятно. А вот аппликатуры аккордов и надписи С, D, Аm мне понравились. Так что теория пошла лесом, началась сугубая практика. А потом еще и гитарпро развратил =)
                    Поэтому когда при мне начинают говорить словами «сольфеджио», «суб-контр-октава», «сустейн», и т.п., да еще и вроде как со знанием дела, у меня к таким людям сразу уважение просыпается, потому что я это шаманство не осилил =)
                  • +9
                    Да я это все нагуглил. Ещё вчера я бы не смог так отборно материться.
              • 0
                О! Моя курсовая, правда делал еще на паскале)
                • +3
                  Тоже была в 30 строк?
                  • +1
                    Да разве-ж я помню?) 14 лет прошло…
                • +11
                  А зачем забивать частоты нот хардкодом? Насколько я помню, частота ноты равна 440*2^{n/12} Гц, где n — смещение ноты от ля первой октавы в полутонах.
                  • +1
                    Ну, не каждый лад выражается такой простой формулой… Если мы захотим поиграть в одном оркестре с духовыми инструментами, то придется брать рациональные отношения частот.

                    Хотя, с точки зрения минимальности кода, идея использовать формулу здравая.
                    • +3
                      Для пианино — все клавиши описываются этой формулой. Почему бы и нет, в соревновании то на краткость? ;)
                      • 0
                        Для пианино — все клавиши звучат так, как их настроят, и формулой описываются только в том случае, если их по этой формуле и настраивали
                        • +1
                          Вообще-то уже давно принят как стандарт равномерно-темперированный строй ru.wikipedia.org/wiki/%D0%A0%D0%B0%D0%B2%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D0%BD%D0%BE_%D1%82%D0%B5%D0%BC%D0%BF%D0%B5%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D1%80%D0%BE%D0%B9

                          есть конечно споры писал ли Бах ХТК именно под этот строй или под строй Веркмейстера но факт что с 18-го века используется именно этот строй.
                          • 0
                            На некоторых музыкальных инструментах можно извлечь только звуки натурального звукоряда, cреди них фанфара (и горн), рог (охотничий рог, альпийский рог, почтовый рожок, шофар и т.п.), натуральная труба (особенно её старинные разновидности, например, лур), натуральная валторна, так называемые обертоновые флейты (молдавская тилинка, некоторые разновидности общетюркского шогура) и другие духовые инструменты, а также варган. По отношению к этим и подобным инструментам говорят, что они звучат в «натуральном строе».
                            И если такие инструменты нужно добавить в оркестр — то и остальные придется под них подстраивать. А так — да, равномерно-темперированный строй давно уже стандарт…
                            • +1
                              но мы же про пианино — там натуральный звукоряд не используют
                    • +3
                      Большое спасибо. Сказывается отсутствие базовых знаний.
                    • +7
                      Что, на хабре неделя JavaScript?
                      • +6
                        Прирост 30-ти строчных игр удвоен!
                      • +8
                        Я вот уже какой пост читаю и не понимаю, честно говоря, в чём смысл. Искаверкать код не невозможности, но вместиться в N строчек?

                        То есть как, мне понятно, что всё это — just for fun. Но так же мне понятно, что если сжать этот с помощью ADVANCED_OPTIMIZATIONS в GCC, то вообще 4 строчки получится. И читабельность примерно такая же :). А можно ещё переносы строк убрать! :)

                        Тот код, который получился — он уже нечитабелен никому, кроме автора (и автору через два месяца тоже будет нечитабелен). Если это — нечто для конкурса типа js1k, то тут очевидно фиговая оптимизация, есть уже описанные здесь на хабре гораздо более прикольные техники.
                        • +1
                          У меня звук был на максимуме. А это пианино меня кипричом по ушам!
                          1. Можно ли избавиться от задержек?
                          2. Пусть строк будет больше, а звук поживее :)
                          • +2
                            Можно ли избавиться от задержек?

                            А это не задержка, а звук на Release, хотя логичнее — на Press.
                            • 0
                              Да, конечно, mousedown здесь куда уместнее, вы правы.
                          • +1
                            установили форму сигнала: osc.type = «square»; (по умолчанию был синусоидальный)

                            Почему отказались от синуса? Он вроде приятнее звучит. Да и звук пианино всё-же более близок к синусу чем к прямоугольным колебаниям.
                            • 0
                              Зато получается очень нинтендовско.
                              • 0
                                Не-не, синус не пойдёт! Без гармоник это очень глухое и невнятное звучание.
                                Но квадрат тоже, да, «компьютерный» звук от 8-битных игрушек.
                                Вот пила бы очень подошла — звучит ярко, выразительно, похоже на скрипку.
                              • +1
                                До пианино расти и расти :( Электро-орган, и то примитивный :( Я так понимаю нормального звука в API нет?
                                • +2
                                  Действительно, спонтанный конкурс лучше выдерживать не в стиле «30 строк» а 1024, 4096 и 10кб — хотя такие соревнования уже есть:
                                  js1k.com
                                  10k.aneventapart.com
                                  даже с призами вроде…
                                  • +1
                                    А это уже демосцена получается!
                                  • 0
                                    Вот, японцы развлекаются: www.g200kg.com/en/webaudio/
                                    Хотя тут далеко не 30 строк, конечно
                                    • +6
                                      Многовато лишнего кода. Можно же и короче, и читабельнее: jsfiddle.net/c62Pe/29/ А с треугольными волнами ещё и ламповее.
                                      • 0
                                        Вообще красота.
                                        • 0
                                          а сложно сделать так что бы длительность ноты задавалась длительностью нажатия клавиши?
                                          Или что бы вообще клавиши соответствовали обычной клавиатуре?
                                          • 0
                                            Сегодня буду переписывать и дописывать.
                                            Разумеется, уже не в 30-строчных рамках, и с нормальным читабельным кодом :)
                                            • 0
                                              да всё равно сколько кода не мне же его писать:-) самое главное звучание и возможность сыграть на обычной клавиатуре без миди — к примеру было бы очень круто если бы клавиши
                                              от «y» до "]" — были до ре ми фа соль ля си — первой октавы
                                              соответственно:
                                              «7» — до диез
                                              «8» — ре диез
                                              «0» — фа диез
                                              "-" — соль диез
                                              "=" — ля диез
                                              ну и от «z» до «m» — до ре ми фа соль ля си малой октавы
                                              «s» — до диез
                                              «d» — ре диез
                                              «g» — фа диез
                                              «h» — соль диез
                                              «j» — ля диез
                                              а если ещё будет возможность задавать октавы (повышать понижать другими клавишами — то вообще жесть:-)

                                              что-то я размечтался — захотелось сыграть что-то на обычной клавиатуре как на настоящем пианино — наверное надо самому этим занятся
                                        • +2
                                          Кто о чём, а я о матмузчасти.
                                          Нота «си» у пианистов (по крайней мере, у тех, кто меня учил бряцать на клавишах в музыкальной школе 15 лет тому назад) обозначается вовсе не B, как обычно пишут в табах гитаристы, а H. B — это си-бемоль, т.е. на клавиатуре фо-но соответствует A# (ля-диезу). Например, вот тут про это в двух словах сказано. И на жизнерадостной картиночке, приведённой в комментах выше, тоже везде H, если приглядеться.
                                          Каждая октава на клавиатуре состоит из двенадцати нот:
                                          До, До-диез, Ре, Ре-диез, Ми, Фа, Фа-диез, Соль, Соль-диез, Ля, Ля-диез/Си-бемоль, Си

                                          Тогда уж так:
                                          До, До-диез/Ре-бемоль, Ре, Ре-диез/Ми-бемоль, Фа, Фа-диез/Соль-бемоль, Соль, Соль-диез/Ля-бемоль, Ля, Ля-диез/Си-бемоль.
                                          А то чего это, си-бемоль есть, а остальных бемолей нету… Непорядок!
                                          • 0
                                            А оно по-разному.
                                            В классике — где всё считается от камертонного «ля» 440 герц — и оттуда обозначается последовательными буквами алфавита (A,B,C,D...). Очень логично и последовательно. Легко запомнить! H при этом обозначает Bb или A#. Но есть и да, другой вариант. Где B и H меняются местами, и звукоряд обозначается уже A,H,C,D..., а B это Hb или A#.
                                            Я одинаково часто сталкивался с обоими вариантами. Поэтому утверждать, что один из них более «правильный» — не стану. Просто факт в том, что есть оба!
                                            • 0
                                              Я вовсе и не утверждаю, что какой-то один вариант правильный. Просто лично мне это «B» в качестве «си» царапает глаз, потому что меня учили по-другому (хотя знакомые гитаристы и используют нотацию, как у автора, но тут речь всё же про фо-но). Я и источники своего знания привожу — это программа муз. школы (т.е. учебники по сольфеджио, которые гуглибельны) и вики. А вы где «одинаково часто сталкивались», если не секрет? :)
                                              • 0
                                                Да как раз в песенных сборниках. Там обычно мелодическая линия для голоса + обозначена гармония для аккомпанемента. И вот там порой полная вольность обозначений H и B. Иногда даже в пределах одного сборника! Вполне обычно, когда доминанта к E обозначается как H. А в другом произведении там же субдоминанта к F — всё та же H.
                                          • +2
                                            Писал похожее для конкурса JS1K. Вот демо: js1k.com/2012-love/demo/1209 Вроде даже исходник сохранился но он очень подрезанный. Надо поискать или полный исходить есть где-то… У меня там даже есть режим самопроигрывания :)
                                            • +4
                                              Как-то оно надоело уже. Ну и не меряется код строками.
                                              • 0
                                                Я попытался сделать статью информативной, а не просто «Смотрите, пацаны, чо я написал».
                                                Если не получилось — извините, над комментами есть кнопочка со стрелочкой вниз.

                                                И код не меряется строками если вы разрабатываете что-то для реальной жизни. В продакшн бы я в жизни ничего похожего не написал.
                                                А это — просто развлечение, если хотите, своего рода искусство. Занятие, родственное демосцене. Тут код может меряться и строками, и байтами, и соотношением количеств букв «a» и «b» в коде.
                                              • 0
                                                Если добавить немного кода и переписать соединение источников, будет 30 строк, но звучать будет получше благодаря непостоянной огибающей.

                                                gainNode = context.createGain();
                                                //<...>
                                                osc.type = "square";
                                                osc.connect(gainNode);   //Меняем дестинейшен вот тут
                                                //ниже - энвелоуп
                                                gainNode.connect(context.destination);
                                                gainNode.gain.value = 0.5;
                                                gainNode.gain.linearRampToValueAtTime(0, context.currentTime);
                                                gainNode.gain.setTargetAtTime(1,context.currentTime,0.1);
                                                gainNode.gain.setTargetAtTime(0,context.currentTime+0.1,0.3);
                                                osc.start(0);
                                                	setTimeout(function() {
                                                	    osc.stop(0);
                                                	    osc.disconnect(gainNode); //Меняем дестинейшен вот тут, заодно делаем звучание подлиннее
                                                	}, 5000 / 2);}
                                                
                                              • 0
                                                Не проверил уровень звука. Открыл демо. Нажал клавишу. Навалил кирпичей. Закрыл демо.
                                                • 0
                                                  В общем, не удивляйтесь, если левая часть клавиатуры будто бы вообще не звучит.

                                                  Вы так пишите, как-будто бы маковские ноуты у всех, и все как один будут писать почему работает только половина клавы)

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.