aboutsummaryrefslogtreecommitdiff
path: root/files/pt-br/web/api/web_audio_api/simple_synth/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/pt-br/web/api/web_audio_api/simple_synth/index.html')
-rw-r--r--files/pt-br/web/api/web_audio_api/simple_synth/index.html579
1 files changed, 579 insertions, 0 deletions
diff --git a/files/pt-br/web/api/web_audio_api/simple_synth/index.html b/files/pt-br/web/api/web_audio_api/simple_synth/index.html
new file mode 100644
index 0000000000..b0fdf2a0c4
--- /dev/null
+++ b/files/pt-br/web/api/web_audio_api/simple_synth/index.html
@@ -0,0 +1,579 @@
+---
+title: 'Tutorial e exemplo: Teclado de Sintetizador Simples'
+slug: Web/API/API_Web_Audio/Sintetizador_simples
+tags:
+ - Audio
+ - Exemplo
+ - Guía
+ - Media
+ - Oscilador
+ - Piano
+ - Sintetizador
+ - Tutorial
+ - Web Audio API
+translation_of: Web/API/Web_Audio_API/Simple_synth
+---
+<div>{{DefaultAPISidebar("Web Audio API")}}</div>
+
+<p>Este artigo apresenta o código e uma demonstração funcional de um teclado que você pode tocar usando seu mouse. O teclado lhe permite alternar entre formas de onda padrões e customizadas. Esse exemplo utiliza das seguintes interfaces de Web API: {{domxref("AudioContext")}}, {{domxref("OscillatorNode")}}, {{domxref("PeriodicWave")}}, e {{domxref("GainNode")}}.</p>
+
+<p>Já que {{domxref("OscillatorNode")}} é baseado no {{domxref("AudioScheduledSourceNode")}}, isso até certo ponto também é um exemplo pra isto.</p>
+
+<h2 id="The_video_keyboard" name="The_video_keyboard">O Teclado Visual</h2>
+
+<h3 id="HTML">HTML</h3>
+
+<p>Existem três componentes primários para o display do nosso teclado virtual. O primeito do qual é o teclado musical em si. Nós extraimos em um par de elementos {{HTMLElement("div")}} aninhados para permitir a rolagem horizontal caso as teclas não encaixem na tela.</p>
+
+<h4 id="O_Teclado">O Teclado</h4>
+
+<p>Primeiro, criamos o espaço no qual construiremos o teclado. Estaremos construindo o teclado programaticamente, considerando que ao fazer desse jeito teremos a flexibilidade de configurar cada tecla conforme determinamos as informações apropriadas para tecla correspondente. No nosso caso, pegamos a frequência de cada tecla através de uma tabela, mas poderia ser calculado de forma algoritmica também.</p>
+
+<pre class="brush: html notranslate">&lt;div class="container"&gt;
+ &lt;div class="keyboard"&gt;&lt;/div&gt;
+&lt;/div&gt;
+</pre>
+
+<p>O {{HTMLElement("div")}} nomeado de <code>"container"</code> é a barra de rolagem que permite o teclado ser rolado horizontalmente se for largo demais para o espaço disponivel. As teclas em si serão inseridas no bloco de classe <code>"keyboard"</code>.</p>
+
+<h4 id="A_barra_de_opções">A barra de opções</h4>
+
+<p>Abaixo do teclado, colocaremos alguns controles para configurar o camada. Por enquanto, teremos dois controles: Um para controlar o volume e outro para selecionar a forma de onda periodica usada ao gerar as notas.</p>
+
+<h5 id="O_controle_de_volume">O controle de volume</h5>
+
+<p>Primeiro criamos o <code>&lt;div&gt;</code> para conter a barra de opções, para ser personalizado conforme preciso. Então estabelecemos uma caixa que será apresentada no lado esquerdo da barra e colocar um rotulo e um elemento {{HTMLElement("input")}} do tipo <code>"range"</code>. O elemento range será tipicamente apresentado como o controle da barra de rolagem ; configuramos ele para permitir qualquer valor entre 0.0 e 1.0 em cada posição.</p>
+
+<pre class="brush: html notranslate">&lt;div class="settingsBar"&gt;
+ &lt;div class="left"&gt;
+ &lt;span&gt;Volume: &lt;/span&gt;
+ &lt;input type="range" min="0.0" max="1.0" step="0.01"
+ value="0.5" list="volumes" name="volume"&gt;
+ &lt;datalist id="volumes"&gt;
+ &lt;option value="0.0" label="Mute"&gt;
+ &lt;option value="1.0" label="100%"&gt;
+ &lt;/datalist&gt;
+ &lt;/div&gt;
+</pre>
+
+<p>Especificamos um valor padrão de 0.5, e provemos um elemento {{HTMLElement("datalist")}} no qual é conectado ao range usando o atributo {{htmlattrxref("name")}} para achar uma lista de opções cujo ID encaixa; nesse caso, o conjunto de informações é nomeado de <code>"volume"</code>. isso nos permite prover um conjunto de valores comuns e strings especiais que o browser pode de forma opcional escolher mostrar de alguma maneira; e então atribuimos nomes aos valores 0.0 ("Mute") e 1.0 ("100%").</p>
+
+<h5 id="A_seleção_de_forma_de_onda">A seleção de forma de onda</h5>
+
+<p>E no lado da barra de configurações, colocamos um rótulo e um elemento {{HTMLElement("select")}} nomeado de <code>"waveform"</code> cujas opções correspondem as formas de onda disponiveis.</p>
+
+<pre class="brush: html notranslate"> &lt;div class="right"&gt;
+ &lt;span&gt;Current waveform: &lt;/span&gt;
+ &lt;select name="waveform"&gt;
+ &lt;option value="sine"&gt;Sine&lt;/option&gt;
+ &lt;option value="square" selected&gt;Square&lt;/option&gt;
+ &lt;option value="sawtooth"&gt;Sawtooth&lt;/option&gt;
+ &lt;option value="triangle"&gt;Triangle&lt;/option&gt;
+ &lt;option value="custom"&gt;Custom&lt;/option&gt;
+ &lt;/select&gt;
+ &lt;/div&gt;
+&lt;/div&gt;</pre>
+
+<div class="hidden">
+<h3 id="CSS">CSS</h3>
+
+<pre class="brush: css notranslate">.container {
+ overflow-x: scroll;
+ overflow-y: hidden;
+ width: 660px;
+ height: 110px;
+ white-space: nowrap;
+ margin: 10px;
+}
+
+.keyboard {
+ width: auto;
+ padding: 0;
+ margin: 0;
+}
+
+.key {
+ cursor: pointer;
+ font: 16px "Open Sans", "Lucida Grande", "Arial", sans-serif;
+ border: 1px solid black;
+ border-radius: 5px;
+ width: 20px;
+ height: 80px;
+ text-align: center;
+ box-shadow: 2px 2px darkgray;
+ display: inline-block;
+ position: relative;
+ margin-right: 3px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+}
+
+.key div {
+ position: absolute;
+ bottom: 0;
+ text-align: center;
+ width: 100%;
+ pointer-events: none;
+}
+
+.key div sub {
+ font-size: 10px;
+ pointer-events: none;
+}
+
+.key:hover {
+ background-color: #eef;
+}
+
+.key:active {
+ background-color: #000;
+ color: #fff;
+}
+
+.octave {
+ display: inline-block;
+ padding: 0 6px 0 0;
+}
+
+.settingsBar {
+ padding-top: 8px;
+ font: 14px "Open Sans", "Lucida Grande", "Arial", sans-serif;
+ position: relative;
+ vertical-align: middle;
+ width: 100%;
+ height: 30px;
+}
+
+.left {
+ width: 50%;
+ position: absolute;
+ left: 0;
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.left span, .left input {
+ vertical-align: middle;
+}
+
+.right {
+ width: 50%;
+ position: absolute;
+ right: 0;
+ display: table-cell;
+ vertical-align: middle;
+}
+
+.right span {
+ vertical-align: middle;
+}
+
+.right input {
+ vertical-align: baseline;
+}</pre>
+</div>
+
+<h3 id="JavaScript">JavaScript</h3>
+
+<p>O código em JavaScript começa inicializando algumas váriaveis.</p>
+
+<pre class="brush: js notranslate">let audioContext = new (window.AudioContext || window.webkitAudioContext)();
+let oscList = [];
+let masterGainNode = null;
+</pre>
+
+<ol>
+ <li><code>audioContext</code> é colocado para referenciar o objeto global {{domxref("AudioContext")}} (ou <code>webkitAudioContext</code> se  necessário).</li>
+ <li><code>oscillators</code> está colocado para conter uma lista de todos os osciladores atualmente tocando. Ele começa nulo, afinal não há nenhum oscilador tocando ainda.</li>
+ <li><code>masterGainNode</code> é colocado como nulo; durante o processo de setup, ele será configurado para contar um {{domxref("GainNode")}} no quall todos os osciladores irão se conectar para permitir o volume geral a ser controlado por apenas uma barra de rolagem.</li>
+</ol>
+
+<pre class="brush: js notranslate">let keyboard = document.querySelector(".keyboard");
+let wavePicker = document.querySelector("select[name='waveform']");
+let volumeControl = document.querySelector("input[name='volume']");
+</pre>
+
+<p>Referencias aos elementos que precisaremos acessar são obtidas através dp:</p>
+
+<ul>
+ <li><code>keyboard</code> que é o elemento que irá alojar as teclas.</li>
+ <li><code>wavePicker</code> é o elemento {{HTMLElement("select")}} usado para seleção da forma de onda das notas.</li>
+ <li><code>volumeControl</code> É o elemento {{HTMLElement("input")}} (do tipo <code>"range"</code>) usado para controlar o volume geral.</li>
+</ul>
+
+<pre class="brush: js notranslate">let noteFreq = null;
+let customWaveform = null;
+let sineTerms = null;
+let cosineTerms = null;
+</pre>
+
+<p>Enfim, variaveis globais que serão usadas quando as formas de onda são criadas:</p>
+
+<ul>
+ <li><code>noteFreq</code> será uma matriz de matrizes; cada matriz representa uma oitava, cada uma possuindo um valor nota daquela oitava. O valor de cada é a frequência, em Hertz, do tom da nota.</li>
+ <li><code>customWaveform</code> será arrumado como um {{domxref("PeriodicWave")}} descrevendo a forma de onda quando o usuário selecionar "Custom" na seleção de forma de onda.</li>
+ <li><code>sineTerms</code> e <code>cosineTerms</code> será utilizado para guardar a informação para gerar a forma de onda; cada um irá conter uma matriz que será gerada caso o usuário escolha "Custom".</li>
+</ul>
+
+<h3 id="Criando_a_tabela_de_notas">Criando a tabela de notas</h3>
+
+<p>A função <code>createNoteTable()</code> constrói a matriz <code>noteFreq</code> para conter uma matriz de objetos representando cada oitava. Cada oitava, possui uma propriedade para cada nota nessa oitava; O nome dessa propriedade é o nome da nota (utilizando da notação em inglês, como "C" para representar "dó"), e o valor é a frequência, em Hertz, daquela nota.</p>
+
+<pre class="brush: js notranslate">function createNoteTable() {
+ let noteFreq = [];
+ for (let i=0; i&lt; 9; i++) {
+ noteFreq[i] = [];
+ }
+
+ noteFreq[0]["A"] = 27.500000000000000;
+ noteFreq[0]["A#"] = 29.135235094880619;
+ noteFreq[0]["B"] = 30.867706328507756;
+
+ noteFreq[1]["C"] = 32.703195662574829;
+ noteFreq[1]["C#"] = 34.647828872109012;
+ noteFreq[1]["D"] = 36.708095989675945;
+ noteFreq[1]["D#"] = 38.890872965260113;
+ noteFreq[1]["E"] = 41.203444614108741;
+ noteFreq[1]["F"] = 43.653528929125485;
+ noteFreq[1]["F#"] = 46.249302838954299;
+ noteFreq[1]["G"] = 48.999429497718661;
+ noteFreq[1]["G#"] = 51.913087197493142;
+ noteFreq[1]["A"] = 55.000000000000000;
+ noteFreq[1]["A#"] = 58.270470189761239;
+ noteFreq[1]["B"] = 61.735412657015513;
+</pre>
+
+<p>... várias oitavas não mostradas para manter breve ...</p>
+
+<div class="hidden">
+<pre class="brush: js notranslate"> noteFreq[2]["C"] = 65.406391325149658;
+ noteFreq[2]["C#"] = 69.295657744218024;
+ noteFreq[2]["D"] = 73.416191979351890;
+ noteFreq[2]["D#"] = 77.781745930520227;
+ noteFreq[2]["E"] = 82.406889228217482;
+ noteFreq[2]["F"] = 87.307057858250971;
+ noteFreq[2]["F#"] = 92.498605677908599;
+ noteFreq[2]["G"] = 97.998858995437323;
+ noteFreq[2]["G#"] = 103.826174394986284;
+ noteFreq[2]["A"] = 110.000000000000000;
+ noteFreq[2]["A#"] = 116.540940379522479;
+ noteFreq[2]["B"] = 123.470825314031027;
+
+ noteFreq[3]["C"] = 130.812782650299317;
+ noteFreq[3]["C#"] = 138.591315488436048;
+ noteFreq[3]["D"] = 146.832383958703780;
+ noteFreq[3]["D#"] = 155.563491861040455;
+ noteFreq[3]["E"] = 164.813778456434964;
+ noteFreq[3]["F"] = 174.614115716501942;
+ noteFreq[3]["F#"] = 184.997211355817199;
+ noteFreq[3]["G"] = 195.997717990874647;
+ noteFreq[3]["G#"] = 207.652348789972569;
+ noteFreq[3]["A"] = 220.000000000000000;
+ noteFreq[3]["A#"] = 233.081880759044958;
+ noteFreq[3]["B"] = 246.941650628062055;
+
+ noteFreq[4]["C"] = 261.625565300598634;
+ noteFreq[4]["C#"] = 277.182630976872096;
+ noteFreq[4]["D"] = 293.664767917407560;
+ noteFreq[4]["D#"] = 311.126983722080910;
+ noteFreq[4]["E"] = 329.627556912869929;
+ noteFreq[4]["F"] = 349.228231433003884;
+ noteFreq[4]["F#"] = 369.994422711634398;
+ noteFreq[4]["G"] = 391.995435981749294;
+ noteFreq[4]["G#"] = 415.304697579945138;
+ noteFreq[4]["A"] = 440.000000000000000;
+ noteFreq[4]["A#"] = 466.163761518089916;
+ noteFreq[4]["B"] = 493.883301256124111;
+
+ noteFreq[5]["C"] = 523.251130601197269;
+ noteFreq[5]["C#"] = 554.365261953744192;
+ noteFreq[5]["D"] = 587.329535834815120;
+ noteFreq[5]["D#"] = 622.253967444161821;
+ noteFreq[5]["E"] = 659.255113825739859;
+ noteFreq[5]["F"] = 698.456462866007768;
+ noteFreq[5]["F#"] = 739.988845423268797;
+ noteFreq[5]["G"] = 783.990871963498588;
+ noteFreq[5]["G#"] = 830.609395159890277;
+ noteFreq[5]["A"] = 880.000000000000000;
+ noteFreq[5]["A#"] = 932.327523036179832;
+ noteFreq[5]["B"] = 987.766602512248223;
+
+ noteFreq[6]["C"] = 1046.502261202394538;
+ noteFreq[6]["C#"] = 1108.730523907488384;
+ noteFreq[6]["D"] = 1174.659071669630241;
+ noteFreq[6]["D#"] = 1244.507934888323642;
+ noteFreq[6]["E"] = 1318.510227651479718;
+ noteFreq[6]["F"] = 1396.912925732015537;
+ noteFreq[6]["F#"] = 1479.977690846537595;
+ noteFreq[6]["G"] = 1567.981743926997176;
+ noteFreq[6]["G#"] = 1661.218790319780554;
+ noteFreq[6]["A"] = 1760.000000000000000;
+ noteFreq[6]["A#"] = 1864.655046072359665;
+ noteFreq[6]["B"] = 1975.533205024496447;
+</pre>
+</div>
+
+<pre class="brush: js notranslate"> noteFreq[7]["C"] = 2093.004522404789077;
+ noteFreq[7]["C#"] = 2217.461047814976769;
+ noteFreq[7]["D"] = 2349.318143339260482;
+ noteFreq[7]["D#"] = 2489.015869776647285;
+ noteFreq[7]["E"] = 2637.020455302959437;
+ noteFreq[7]["F"] = 2793.825851464031075;
+ noteFreq[7]["F#"] = 2959.955381693075191;
+ noteFreq[7]["G"] = 3135.963487853994352;
+ noteFreq[7]["G#"] = 3322.437580639561108;
+ noteFreq[7]["A"] = 3520.000000000000000;
+ noteFreq[7]["A#"] = 3729.310092144719331;
+ noteFreq[7]["B"] = 3951.066410048992894;
+
+ noteFreq[8]["C"] = 4186.009044809578154;
+ return noteFreq;
+}
+</pre>
+
+<p>O resultado é uma matriz, <code>noteFreq</code>, com um objeto para cada oitava. Cada objeto de oitava tem propriedades nomeadas nela onde a propriedade é o nome da nota com a notação em inglês (Como "C" para representar "dó") e o valor da propriedade é a frequência da nota em Hertz.. o objeto resultando se parece com isso:</p>
+
+<table class="standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Octave</th>
+ <td colspan="8" rowspan="1">Notes</td>
+ <td rowspan="1"></td>
+ <td rowspan="1"></td>
+ <td rowspan="1"></td>
+ <td rowspan="1"></td>
+ </tr>
+ <tr>
+ <th scope="row">0</th>
+ <td>"A" ⇒ 27.5</td>
+ <td>"A#" ⇒ 29.14</td>
+ <td>"B" ⇒ 30.87</td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ <tr>
+ <th scope="row">1</th>
+ <td>"C" ⇒ 32.70</td>
+ <td>"C#" ⇒ 34.65</td>
+ <td>"D" ⇒ 36.71</td>
+ <td>"D#" ⇒ 38.89</td>
+ <td>"E" ⇒ 41.20</td>
+ <td>"F" ⇒ 43.65</td>
+ <td>"F#" ⇒ 46.25</td>
+ <td>"G" ⇒ 49</td>
+ <td>"G#" ⇒ 51.9</td>
+ <td>"A" ⇒ 55</td>
+ <td>"A#" ⇒ 58.27</td>
+ <td>"B" ⇒ 61.74</td>
+ </tr>
+ <tr>
+ <th scope="row">2</th>
+ <td colspan="12" rowspan="1" style="text-align: center;">. . .</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Com esta tabela no lugar, podemos descobrir a frequência para uma dada nota em uma oitava particular relativamente fácil. Se queremos a frequência pra nota G# na primeira oitava, nós simplesmente usamos  <code>noteFreq[1]["G#"]</code> e conseguimos o valor 51.9 como resultado.</p>
+
+<div class="note">
+<p>Os valores na tabela de exemplo acima foram arredondados para duas casas decimais.</p>
+</div>
+
+<div class="hidden">
+<p> Esse polyfill  é utilizado quando <code>Object.entries()</code> não existir.</p>
+
+<pre class="brush: js notranslate">if (!Object.entries) {
+ Object.entries = function entries(O) {
+ return reduce(keys(O), (e, k) =&gt; concat(e, typeof k === 'string' &amp;&amp; isEnumerable(O, k) ? [[k, O[k]]] : []), []);
+ };
+}
+</pre>
+</div>
+
+<h3 id="Construindo_o_teclado">Construindo o teclado</h3>
+
+<p>A função <code>setup()</code> é responsavel por construir o teclado e preparar a aplicação para tocar a música.</p>
+
+<pre class="brush: js notranslate">function setup() {
+ noteFreq = createNoteTable();
+
+ volumeControl.addEventListener("change", changeVolume, false);
+
+ masterGainNode = audioContext.createGain();
+ masterGainNode.connect(audioContext.destination);
+ masterGainNode.gain.value = volumeControl.value;
+
+ // Create the keys; skip any that are sharp or flat; for
+ // our purposes we don't need them. Each octave is inserted
+ // into a &lt;div&gt; of class "octave".
+
+ noteFreq.forEach(function(keys, idx) {
+ let keyList = Object.entries(keys);
+ let octaveElem = document.createElement("div");
+ octaveElem.className = "octave";
+
+ keyList.forEach(function(key) {
+ if (key[0].length == 1) {
+ octaveElem.appendChild(createKey(key[0], idx, key[1]));
+ }
+ });
+
+ keyboard.appendChild(octaveElem);
+ });
+
+ document.querySelector("div[data-note='B'][data-octave='5']").scrollIntoView(false);
+
+ sineTerms = new Float32Array([0, 0, 1, 0, 1]);
+ cosineTerms = new Float32Array(sineTerms.length);
+ customWaveform = audioContext.createPeriodicWave(cosineTerms, sineTerms);
+
+ for (i=0; i&lt;9; i++) {
+ oscList[i] = {};
+ }
+}
+
+setup();</pre>
+
+<ol>
+ <li>A tabela que mapeia o nome e oitavas das notas para suas respectivas frequências é criado ao chamar <code>createNoteTable()</code>.</li>
+ <li>Um manipulador de eventos é estabelecido ao chamar nosso velho amigo {{domxref("EventTarget.addEventListener", "addEventListener()")}} para cuidar dos eventos do {{event("change")}} no controle de ganho geral. Isso vai simplesmente atualizar o módulo de ganho de volume para o novo valor.</li>
+ <li>Em seguida, nós replicamos cada oitava na tabela de frequências das notas. Para cada oitava, usamos {{jsxref("Object.entries()")}} para conseguir uma lista de notas daquela oitava.</li>
+ <li>Criar um {{HTMLElement("div")}} para contar as notas daquela oitava (para ter um pouco de espaço entre as oitavas), e mudar o nome de classe para "octave".</li>
+ <li>Para cada tecla na oitava, checamos para ver se o nome daquela nota há mais de um caractere. Nós pulamos essas, pois estamos deixando notas sustenidas de fora deste exemplo. Do contrário, chamamos <code>createKey()</code>, especificando uma string, oitava, e frequência. O elemento retornado é anexado na elemento da oitava criada no passo 4.</li>
+ <li>Quando o elemento da oitava é construido, é então anexada ao teclado.</li>
+ <li>Uma vez que o teclado foi construido, nós rolamos para nota "B" na quinta oitava; isso tem o efeito de garantir que o C médio é visivel junto das notas ao redor.</li>
+ <li>Então uma forma de onda customizada é construida usando {{domxref("AudioContext.createPeriodicWave()")}}. Essa forma de onda será usada toda vez que o usuário selecionar "Custom" da seleção de formas de onda.</li>
+ <li>Enfim, a lista de osciladores é iniciada para garantir que está pronta para receber informação identificando quais osciladores estão associados com que teclas.</li>
+</ol>
+
+<h4 id="Criando_uma_tecla">Criando uma tecla</h4>
+
+<p>A função <code>createKey()</code>  é chamada toda vez que queremos que uma tecla seja apresentada no nosso teclado virtual. Ela cria elementos da tecla e seu rótulo, adiciona informação dos atributos ao elemento para uso posterior, e coloca modificadores de eventos para os eventos que nos importam.</p>
+
+<pre class="brush: js notranslate">function createKey(note, octave, freq) {
+ let keyElement = document.createElement("div");
+ let labelElement = document.createElement("div");
+
+ keyElement.className = "key";
+ keyElement.dataset["octave"] = octave;
+ keyElement.dataset["note"] = note;
+ keyElement.dataset["frequency"] = freq;
+
+ labelElement.innerHTML = note + "&lt;sub&gt;" + octave + "&lt;/sub&gt;";
+ keyElement.appendChild(labelElement);
+
+ keyElement.addEventListener("mousedown", notePressed, false);
+ keyElement.addEventListener("mouseup", noteReleased, false);
+ keyElement.addEventListener("mouseover", notePressed, false);
+ keyElement.addEventListener("mouseleave", noteReleased, false);
+
+ return keyElement;
+}
+</pre>
+
+<p>Após criar  os elementos representando as teclas e seus rótulos, nós configuramos o elemento das teclas ao configurar sua classe para "key" (Que estabelece a aparência). Então adicionamos atributos {{htmlattrxref("data-*")}}  que contém a string da oitava da nota (attribute <code>data-octave</code>), representando a nota a ser tocada (attribute <code>data-note</code>), e frequência (attribute <code>data-frequency</code>) em Hertz. Isso irá nos permitir facilmente pegar informação conforme necessário ao cuidar de eventos.</p>
+
+<h3 id="Fazendo_música">Fazendo música</h3>
+
+<h4 id="Tocando_um_tom">Tocando um tom</h4>
+
+<p>O trabalho da função <code>playTone()</code> é tocar um tom em uma dada frequência. Isso será usado pelo modificador para eventos acionados nas teclas do teclado, para que toquem as notas apropriadas.</p>
+
+<pre class="brush: js notranslate">function playTone(freq) {
+ let osc = audioContext.createOscillator();
+ osc.connect(masterGainNode);
+
+ let type = wavePicker.options[wavePicker.selectedIndex].value;
+
+ if (type == "custom") {
+ osc.setPeriodicWave(customWaveform);
+ } else {
+ osc.type = type;
+ }
+
+ osc.frequency.value = freq;
+ osc.start();
+
+ return osc;
+}
+</pre>
+
+<p>O <code>playTone()</code> começa criando um novo {{domxref("OscillatorNode")}} ao chamar o método {{domxref("AudioContext.createOscillator()")}}. Então conectamos ele para o módulo de ganha geral ao chamar o novo método de osciladores {{domxref("OscillatorNode.connect()")}} method;, Que determina ao oscilador onde ele irá mandar seu output. Ao fazer isso, mudar o valor do ganho do módulo de ganho geral irá mudar o volume de todos os toms gerados.</p>
+
+<p>Então conseguimos o tipo de forma de onda para usar ao checar o valor do controle de seleção de formas de onda na barra de opções. Se o usuário estiver colocado como <code>"custom"</code>, chamamos {{domxref("OscillatorNode.setPeriodicWave()")}} para configurar os osciladores para usar nossa forma de onda customizada. Fazer isso automáticamente coloca o {{domxref("OscillatorNode.type", "type")}} do oscilador como <code>custom</code>. Se qualquer outro tipo de forma de onda é selecionado na seleção de formas de ondas, nós simplesmente colocamos os tipos de osciladores no valor da seleção, esse valor será um entre <code>sine</code>, <code>square</code>, <code>triangle</code>, e <code>sawtooth</code>.</p>
+
+<p>A frequência do oscilador é colocada no valor especificado no paramêtro <code>freq</code> ao colocar o valor dos objetos {{domxref("Oscillator.frequency")}} {{domxref("AudioParam")}} . Então, enfim, o oscilador é iniciado e começa a produzir sons ao chamar o método {{domxref("AudioScheduledSourceNode.start()")}} .</p>
+
+<h4 id="Tocando_um_tom_2">Tocando um tom</h4>
+
+<p>Quando o evento {{event("mousedown")}} ou {{domxref("mouseover")}} ocorre em uma tecla, queremos que toque a nota correspondente. A função <code>notePressed()</code> é usada como o modificador de eventos para esses eventos.</p>
+
+<pre class="brush: js notranslate">function notePressed(event) {
+ if (event.buttons &amp; 1) {
+ let dataset = event.target.dataset;
+
+ if (!dataset["pressed"]) {
+ let octave = +dataset["octave"];
+ oscList[octave][dataset["note"]] = playTone(dataset["frequency"]);
+ dataset["pressed"] = "yes";
+ }
+ }
+}
+</pre>
+
+<p>Começamos checando se o botão esquerdo do mouse é pressionado, por dois motivos. Primeiro, queremos que apenas o botão esquerdo acione as notas. Segundo, e mais importante, estamos usando isso para cuidar do {{event("mouseover")}} para casos onde o usuário arrasta de tecla a tecla, e só queremos tocar uma nota se o mouse estiver pressionado quando entrar no elemento.</p>
+
+<p>Se o botão do mouse estiver de fato sendo pressionado, recebemos o atributo de tecla pressionada {{htmlattrxref("dataset")}} ; isso torna fácil o acesso das informações de atributo customizadas no elemento. Procuramos por um atributo <code>data-pressed</code> ; caso não haja um(o que indica que a nota não está tocando ainda), chamamos <code>playTone()</code> para começar a tocar a nota, passando no valor dos elementos do atributo <code>data-frequency</code>. O valor retornado do oscilador é guardado no <code>oscList</code> para refêrencia futura, e <code>data-pressed</code> é colocado como <code>yes</code> para indicar que a nota está tocando para que não iniciemos novamente na próxima vez que isso for chamado.</p>
+
+<h4 id="Parando_um_tom">Parando um tom</h4>
+
+<p>A função <code>noteReleased()</code> é o modificador de eventos chamado quando o usuário solta o botão do mouse ou move o mouse para fora da tecla que ele está tocando.</p>
+
+<pre class="brush: js notranslate">function noteReleased(event) {
+ let dataset = event.target.dataset;
+
+ if (dataset &amp;&amp; dataset["pressed"]) {
+ let octave = +dataset["octave"];
+ oscList[octave][dataset["note"]].stop();
+ delete oscList[octave][dataset["note"]];
+ delete dataset["pressed"];
+ }
+}
+</pre>
+
+<p><code>noteReleased()</code> usa os atributos customizados <code>data-octave</code> and <code>data-note</code>  para procurar os osciladores das teclas, e então chama o método de oscilador {{domxref("AudioScheduledSourceNode.stop", "stop()")}} para parar de tocar a nota. Finalmente, a entrada <code>oscList</code> para nota é limpa e o atributo <code>data-pressed</code> é removido do elemento da tecla (como identificado pelo {{domxref("event.target")}}), para indicar que a nota não está tocando no momento.</p>
+
+<h4 id="Mudando_o_volume_geral">Mudando o volume geral</h4>
+
+<p>A barra de rolagem do volume na barra de opções dá uma simples interface para mudar o valor do ganho no módulo de ganho geral, então mudando o volume de todas as notas sendo tocadas. O metódo <code>changeVolume()</code> é o modificador do evento {{event("change")}} na barra de rolagem.</p>
+
+<pre class="brush: js notranslate">function changeVolume(event) {
+ masterGainNode.gain.value = volumeControl.value
+}
+</pre>
+
+<p>Isso simplesmente coloca o valor do módulo de ganho geral <code>gain</code> {{domxref("AudioParam")}} para o novo valor na barra de rolagem.</p>
+
+<h3 id="Resultado">Resultado</h3>
+
+<p>Coloque tudo junto, o resultado é um simples e funcional teclado virtual que funciona com o clique:</p>
+
+<p>{{ EmbedLiveSample('The_video_keyboard', 680, 200) }}</p>
+
+<h2 id="Veja_também">Veja também</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/API/Web_Audio_API">Web Audio API</a></li>
+ <li>{{domxref("OscillatorNode")}}</li>
+ <li>{{domxref("GainNode")}}</li>
+ <li>{{domxref("AudioContext")}}</li>
+</ul>