04 – Javascript – Web Audio API – Construindo um Sintetizador Web

04 – Javascript – Web Audio API – Construindo um Sintetizador Web

Construindo um Sintetizador

Construindo um Sintetizador Web

Página principal

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

WEB AUDIO API

Vamos construir um Sintetizador Web pra rodar no browser?

É um teclado sintetizador simples, a cara dele é essa.

Clique na imagem do teclado abaixo para testar o sintetizador no seu browser.

Sintetizador Web

https://toticavalcanti.github.io/web-synth/

A estrutura do projeto é:

Estrutura da aplicação

Estrutura da aplicação

Usaremos o arquivo javascript qwerty-hancock.min.js já pronto, acesse o link abaixo para mais detalhes:

http://stuartmemo.com/qwerty-hancock

No meu github você pode baixar o código completo, com o arquivo qwerty-hancock.min.js com algumas pequenas alterações que fiz e com os comentários em português.

Os códigos são esses:

3-Criando-um-Sintetizador-Web/scripts/qwerty-hancock.min.js


/*
 * Qwerty Hancock keyboard library v0.6.0
 * The web keyboard for now people.
 * Copyright 2012-18, Stuart Memo
 *
 * Licensed under the MIT License
 * http://opensource.org/licenses/mit-license.php
 *
 * http://stuartmemo.com/qwerty-hancock
 */

(function () {
    const root = this;
    /* No contexto do script, `this` é a janela.
     * No contexto do nó (browserify), `this` é o nó global.
     */
    const globalWindow = typeof global === 'undefined' ? root : root.window;
    let version = '0.6.0',
        settings = {},
        mouse_is_down = false,
        keysDown = {},
        //Mapeia os números ASCII das teclas do teclado do computador para as notas.
        key_map = {
            // 65 é o 'A' na tabela ASCII
            65: 'Cl',
            // 87 é o 'W'
            87: 'C#l',
            // 83 é o 'S'
            83: 'Dl',
            // 69 o 'E'
            69: 'D#l',
            // 68 o 'D'
            68: 'El',
            // 70 o 'F'
            70: 'Fl',
            // 84 o 'T'
            84: 'F#l',
            // 71 o 'G'
            71: 'Gl',
            // 89 o 'Y'
            89: 'G#l',
            // 90 o 'Z', tá comentado, porque não quero habilitar a tecla 'Z'
            //90: 'G#l',
            // 72 o 'H'
            72: 'Al',
            // 85 o 'U'
            85: 'A#l',
            // 74 o 'J'
            74: 'Bl',
            // 75 o 'K'
            75: 'Cu',
            // 79 o 'O'
            79: 'C#u',
            // 76 o 'L'
            76: 'Du',
            // 80 o 'P'
            80: 'D#u',
            // 59 o ';' obs: não funciona em teclado pt-br, por isso tá comentado.
            //59: 'Eu',
            // 186 o '║' ou 'Ç' no teclado em português pt-br
            186: 'Eu',
            // 222 o '~' no teclado em português pt-br
            222: 'Fu',
            // 221 o '[' no teclado em português pt-br
            221: 'F#u',
            // 220 o ']' no teclado em português pt-br
            220: 'Gu'
        },
        keyDownCallback,
        keyUpCallback;

    /**
     * Calcula a largura da tecla branca.
     * @return {number} Largura de uma única tecla branca em pixel.
     */
    const getWhiteKeyWidth = function (number_of_white_keys) {
        return Math.floor((settings.width - number_of_white_keys) / number_of_white_keys);
    };

    /**
     * Mesclar configurações de usuário com padrões.
     * @param  {object} user_settings
     */
    const init = function (us) {
        let container;

        user_settings = us || {};

        settings = {
            // atribui a chave id a string keyboard, caso não seja passado nada pelo user_settings.id
            id:             user_settings.id || 'keyboard',
            // define que o teclado terá 3 oitavas, caso não seja passado nada pelo user_settings.octaves
            octaves:        user_settings.octaves || 3,
            // pega o valor da largura pelo user_settings.width
            width:          user_settings.width,
            // Mesma coisa para o height
            height:         user_settings.height,
            // Define que o teclado vai começar pela nota A3 (Lá 3)
            startNote:      user_settings.startNote || 'A3',
            // Define a cor das teclas brancas do teclado em hexa
            whiteKeyColour: user_settings.whiteKeyColour || '#fff',
            // Define a cor das teclas pretas do teclado em hexa
            blackKeyColour: user_settings.blackKeyColour || '#000',
            // Define a cor da tecla ativa, isto é, que tá sendo pressionada, para a cor azul.
            activeColour:   user_settings.activeColour || '#428bca',
            // Define a cor da borda das teclas como preto
            borderColour:   user_settings.borderColour || '#000'
        };
        // Pega o id do elemento no html, no nosso caso, o id é keyboard
        container = document.getElementById(settings.id);
        // Se a largura(width) não for definido no settings, ele pega a largura 
        // através do container, que é o elemento keyboard, com o offsetWidth
        if (typeof settings.width === 'undefined') {
            settings.width = container.offsetWidth;
        }
        // Mesma coisa para a altura(height)
        if (typeof settings.height === 'undefined') {
            settings.height = container.offsetHeight;
        }
        // define a oitava inicial, pegando o 3 da string A3, que é a nota inicial.
        // "A3".charAt(1), 10) retorna só o "3", que passa no parseInt e vira 3.
        settings.startOctave = parseInt(settings.startNote.charAt(1), 10);
        // Define a oitava inicial com 3, pego no comando anterior, caso não seja passado
        // um valor para user_settings.keyOctave no synth.js como parâmetro octaves.
        settings.keyOctave = user_settings.keyOctave || settings.startOctave;

        // Adiciona getters e setters
        this.setKeyOctave = function(octave){
            settings.keyOctave = octave;
            return settings.keyOctave;
        }
        this.getKeyOctave = function(){
            return settings.keyOctave;
        }
        this.keyOctaveUp = function(){
            settings.keyOctave++;
            return settings.keyOctave;
        }
        this.keyOctaveDown = function(){
            settings.keyOctave--;
            return settings.keyOctave;
        }
        this.getKeyMap = function(){
            return key_map;
        }
        this.setKeyMap = function(newKeyMap){
            key_map = newKeyMap;
            return key_map;
        }

        createKeyboard();
        addListeners.call(this, container);
    };

    /**
     * Obtém frequência de uma determinada nota.
     * @param  {string} note Nota musical para converter em hertz.
     * @return {number} Frequência de nota em hertz.
     */
    const getFrequencyOfNote = function (note) {
        let notes = ['A', 'A#', 'B', 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#'],
            key_number,
            octave;

        if (note.length === 3) {
            octave = note.charAt(2);
        } else {
            octave = note.charAt(1);
        }

        key_number = notes.indexOf(note.slice(0, -1));

        if (key_number < 3) {
            key_number = key_number + 12 + ((octave - 1) * 12) + 1;
        } else {
            key_number = key_number + ((octave - 1) * 12) + 1;
        }

        return 440 * Math.pow(2, (key_number - 49) / 12);
    };

    /**
     * Alterar a cor de uma tecla.
     * @param  {element} el Elemento DOM para mudar a cor.
     */
    const lightenUp = function lightenUp (el) {
        if (el !== null || typeof el === undefined) {
            el.style.backgroundColor = settings.activeColour;
        }
    };

    /**
     * Reverte a tecla para a cor original.
     * @param  {element} el Elemento DOM para mudar a cor.
     */
    const darkenDown = function darkenDown (el) {
        if (el !== null) {
            if (el.getAttribute('data-note-type') === 'white') {
                el.style.backgroundColor = settings.whiteKeyColour;
            } else {
                el.style.backgroundColor = settings.blackKeyColour;
            }
        }
    };

    /**
     * Ordena as notas na ordem definida pela tecla inicial nas configurações.
     * @param {array} notes_to_order Notas a serem ordenadas.
     * @return {array{ ordered_notes Notas ordenadas.
     */
    const orderNotes = function (notes_to_order) {
        let i,
            keyOffset = 0,
            number_of_notes_to_order = notes_to_order.length,
            ordered_notes = [];

        for (i = 0; i < number_of_notes_to_order; i++) {
            if (settings.startNote.charAt(0) === notes_to_order[i]) {
                keyOffset = i;
                break;
            }
        }

        for (i = 0; i < number_of_notes_to_order; i++) { if (i + keyOffset > number_of_notes_to_order - 1) {
                ordered_notes[i] = notes_to_order[i + keyOffset - number_of_notes_to_order];
            } else {
                ordered_notes[i] = notes_to_order[i + keyOffset];
            }
        }

        return ordered_notes;
    };

    /**
     * Adiciona estilo a uma tecla branca individual.
     * @param  {element} el Elemento DOM tecla branca.
     */
    const styleWhiteKey = function (key) {
        key.el.style.backgroundColor = settings.whiteKeyColour;
        key.el.style.border = '1px solid ' + settings.borderColour;
        key.el.style.borderRight = 0;
        key.el.style.height = settings.height + 'px';
        key.el.style.width = key.width + 'px';
        key.el.style.borderRadius = '0 0 5px 5px';

        if (key.noteNumber === getTotalWhiteKeys() - 1) {
            key.el.style.border = '1px solid ' + settings.borderColour;
        }
    };

    /**
     * Adiciona estilo a uma tecla preta individual.
     * @param  {element} el Elemento DOM tecla preta.
     */
    const styleBlackKey = function (key) {
        const white_key_width = getWhiteKeyWidth(getTotalWhiteKeys()),
            black_key_width = Math.floor(white_key_width / 2);

        key.el.style.backgroundColor = settings.blackKeyColour;
        key.el.style.border = '1px solid ' + settings.borderColour;
        key.el.style.position = 'absolute';
        key.el.style.left = Math.floor(((white_key_width + 1) * (key.noteNumber + 1)) - (black_key_width / 2)) + 'px';
        key.el.style.width = black_key_width + 'px';
        key.el.style.height = (settings.height / 1.5) + 'px';
        key.el.style.borderRadius = '0 0 3px 3px';
    };

    /**
    * Adiciona um estilo a uma tecla individual no teclado.
    * @param  {object} key Elemento tecla.
    */
    const styleKey = function (key) {
        key.el.style.display = 'inline-block';
        key.el.style['-webkit-user-select'] = 'none';

        if (key.colour === 'white') {
            styleWhiteKey(key);
        } else {
            styleBlackKey(key);
        }
    };

    /**
    * Redefine estilos no container do teclado e no elemento da  lista.
    * @param {element} keyboard container do elemento DOM teclado.
    */
    const styleKeyboard = function (keyboard) {
        const styleElement = function (el) {
            el.style.cursor = 'default';
            el.style.fontSize = '0px';
            el.style.height = settings.height + 'px';
            el.style.padding = 0;
            el.style.position = 'relative';
            el.style.listStyle = 'none';
            el.style.margin = 0;
            el.style.width = settings.width + 'px';
            el.style['-webkit-user-select'] = 'none';
        };

        styleElement(keyboard.container);
        styleElement(keyboard.el);
    };

    /**
    * Chama o evento mouseDown do usuário.
    */
    const mouseDown = function (element, callback) {
        if (element.tagName.toLowerCase() == 'li') {
            mouse_is_down = true;
            lightenUp(element);
            callback(element.title, getFrequencyOfNote(element.title));
        }
    };

    /**
    * Chama o evento mouseUp do usuário.
    */
    const mouseUp = function (element, callback) {
        if (element.tagName.toLowerCase() == 'li') {
            mouse_is_down = false;
            darkenDown(element);
            callback(element.title, getFrequencyOfNote(element.title));
        }
    };

    /**
    * Chama mouseOver do usuário, se necessário, então, podemos até comentar se quiser, 
    *  pois no caso do nosso synth, ela não tá sendo usada.
    */
    const mouseOver = function (element, callback) {
        if (mouse_is_down) {
            lightenUp(element);
            callback(element.title, getFrequencyOfNote(element.title));
        }
    };

    /**
    * Chama o mouseUp do usuário.
    */
    const mouseOut = function (element, callback) {
        if (mouse_is_down) {
            darkenDown(element);
            callback(element.title, getFrequencyOfNote(element.title));
        }
    };

    /**
    * Cria o elemento DOM tecla e seus atributos HTML.
    * @return {object} Key DOM element.
    */
    const createKey = function (key) {
        key.el = document.createElement('li');
        key.el.id = key.id;
        key.el.title = key.id;
        key.el.setAttribute('data-note-type', key.colour);

        styleKey(key);

        return key;
    };
    
    // Retorna o total de teclas brancas fazendo uma conta simples,
    // cada oitava tem 7 notas brancas, então multiplica-se o 7 pelo 
    // número de oitavas que queremos no teclado
    const getTotalWhiteKeys = function () {
        return settings.octaves * 7;
    };
    // Cria a quantidade certa de teclas do teclado
    const createKeys = function () {
        let that = this,
            i,
            key,
            keys = [],
            note_counter = 0,
            octave_counter = settings.startOctave,
            total_white_keys = getTotalWhiteKeys();
        // total_white_keys é igual a 28 (Teclado com 4 oitavas)
        for (i = 0; i < total_white_keys; i++) { // whiteNotes.length é igual a 5 ==> ['C', 'D', 'E', 'F', 'G', 'A', 'B']
            // então se i % 5 for ==> 0 atribui zero a note_counter
            // só vai entrar no if quando i for igual a 0 ou a 5
            if (i % this.whiteNotes.length === 0) {
                note_counter = 0;
            }
            // bizarre_note_counter recebe o valor de whiteNotes ['C', 'D', 'E', 'F', 'G', 'A', 'B']
            // na posição note_counter
            bizarre_note_counter = this.whiteNotes[note_counter];
            // Se bizarre_note_counter for Dó(C) e i for diferente de zero,
            // soma uma unidade em octave_counter
            if ((bizarre_note_counter === 'C') && (i !== 0)) {
                octave_counter++;
            }
            // Cria uma tecla branca pra ser adicionada as teclas do teclado
            key = createKey({
                colour: 'white',
                octave: octave_counter,
                width: getWhiteKeyWidth(total_white_keys),
                id: this.whiteNotes[note_counter] + octave_counter,
                noteNumber: i
            });
            // Insere no HTML o elemento(el) DOM tecla
            keys.push(key.el);

            if (i !== total_white_keys - 1) {
                this.notesWithSharps.forEach(function (note, index) {
                    if (note === that.whiteNotes[note_counter]) {
                        key = createKey({
                            colour: 'black',
                            octave: octave_counter,
                            width: getWhiteKeyWidth(total_white_keys) / 2,
                            id: that.whiteNotes[note_counter] + '#' + octave_counter,
                            noteNumber: i
                        });

                        keys.push(key.el);
                    }
                });
            }
            note_counter++;
        }

        return keys;
    };
    // Insere as teclas DOM dentro do HTML
    const addKeysToKeyboard = function (keyboard) {
        keyboard.keys.forEach(function (key) {
            keyboard.el.appendChild(key);
        });
    };

    // Trata da entrada de notas via teclado do computador
    const setKeyPressOffset = function (sorted_white_notes) {
        settings.keyPressOffset = sorted_white_notes[0] === 'C' ? 0 : 1;
    };
    // Definição do dicionário keyboard
    // Note o el, através dele as teclas são mostradas no HTML como uma lista('ul')
    const createKeyboard = function () {
        const keyboard = {
            container: document.getElementById(settings.id),
            el: document.createElement('ul'),
            whiteNotes: orderNotes(['C', 'D', 'E', 'F', 'G', 'A', 'B']),
            notesWithSharps: orderNotes(['C', 'D', 'F', 'G', 'A']),
        };
        // Com call(), um objeto pode usar um método pertencente a outro objeto.
        // Aqui chamamos o createKeys do próprio objeto teclado instanciado, através do call().
        keyboard.keys = createKeys.call(keyboard);
        // Chama setKeyPressOffset para tratar as notas via teclado do computador
        setKeyPressOffset(keyboard.whiteNotes);
        // Chama a styleKeyboard para dá o estilo do teclado(CSS via função javascript)
        styleKeyboard(keyboard);

        // Adiciona as teclas ao teclado e o teclado ao contêiner.
        addKeysToKeyboard(keyboard);
        keyboard.container.appendChild(keyboard.el);
        // Retorna o teclado formatado, já todo certinho
        return keyboard;
    };

    const getKeyPressed = function (keyCode) {
        return key_map[keyCode]
                .replace('l', parseInt(settings.keyOctave, 10) + settings.keyPressOffset)
                .replace('u', (parseInt(settings.keyOctave, 10) + settings.keyPressOffset + 1)
                .toString());
    };

    /**
     * Manipular uma tecla do teclado sendo pressionada.
     * @param {object} key Evento de teclado - tecla atualmente pressionada.
     * @param {callback} callback A função noteDown do usuário.
     * @return {boolean} true se foi uma tecla (combo) usada por qwerty-hancock
     */
    const keyboardDown = function (key, callback) {
        let key_pressed;

        if (key.keyCode in keysDown) {
           return false;
        }

       keysDown[key.keyCode] = true;

       if (typeof key_map[key.keyCode] !== 'undefined') {
            key_pressed = getKeyPressed(key.keyCode);

            // Chama a função noteDown do usuário.
            callback(key_pressed, getFrequencyOfNote(key_pressed));
            lightenUp(document.getElementById(key_pressed));
            return true;
       }
       return false;
    };

    /**
     * Lida com uma tecla do teclado sendo liberada.
     * @param {element} key O elemento DOM da tecla que foi liberada.
     * @param {callback} callback A função noteDown do usuário.
     * @return {boolean} true se foi uma tecla  (combo) usada por qwerty-hancock
     */
    const keyboardUp = function (key, callback) {
        let key_pressed;

        delete keysDown[key.keyCode];

        if (typeof key_map[key.keyCode] !== 'undefined') {
            key_pressed = getKeyPressed(key.keyCode);
            // Chama a função noteDown do usuário.
            callback(key_pressed, getFrequencyOfNote(key_pressed));
            darkenDown(document.getElementById(key_pressed));
            return true;
        }
        return false;
    };

    /**
     * Determine se a tecla pressionada é uma tecla modificadora ou não 
     * exemplo de teclas modificadoras: Control, Shift, Alt / Option (Apple),
     * AltGr, Meta, Command (Apple) / Windows (Microsoft), Super, Hyper, Fn.
     * @param {KeyboardEvent} O evento keydown de uma tecla pressionada.
     */
    const isModifierKey = function (key) {
        return key.ctrlKey ||  key.metaKey || key.altKey;
    };

    /**
     * Adiciona event listeners ao teclado.
     * @param {element} keyboard_element
     */
    const addListeners = function (keyboard_element) {
        const that = this;

        // A tecla é pressionada no teclado.
        //verifica se a tecla pressionada foi um alt, ou Shift, etc.
        globalWindow.addEventListener('keydown', function (key) {
            if (isModifierKey(key)) {
              return;
            }
            //através da instância do keyboard(this)
            if (keyboardDown(key, that.keyDown)) {
                key.preventDefault();
            }
        });

        // A tecla é liberada no teclado.
        // O método preventDefault () 
        //cancela o evento se for cancelável, 
        //o que significa que a ação padrão que pertence ao evento não ocorrerá.
        globalWindow.addEventListener('keyup', function (key) {
            if (isModifierKey(key)) {
              return;
            }
            if (keyboardUp(key, that.keyUp)) {
                key.preventDefault();
            }
        });

          
        keyboard_element.addEventListener('mousedown', function (event) {
            mouseDown(event.target, that.keyDown);
        });

        // Mouse é liberado do elemento de teclado.
        keyboard_element.addEventListener('mouseup', function (event) {
            mouseUp(event.target, that.keyUp);
        });

        // Mouse é movido sobre o elemento do teclado.
        keyboard_element.addEventListener('mouseover', function (event) {
            mouseOver(event.target, that.keyDown);
        });

        // Mouse é movido para fora do elemento do teclado.
        keyboard_element.addEventListener('mouseout', function (event) {
            mouseOut(event.target, that.keyUp);
        });

        // O dispositivo suporta eventos de toque.
        if ('ontouchstart' in document.documentElement) {
            keyboard_element.addEventListener('touchstart', function (event) {
                mouseDown(event.target, that.keyDown);
            });

            keyboard_element.addEventListener('touchend', function (event) {
                mouseUp(event.target, that.keyUp);
            });

            keyboard_element.addEventListener('touchleave', function (event) {
                mouseOut(event.target, that.keyUp);
            });

            keyboard_element.addEventListener('touchcancel', function (event) {
                mouseOut(event.target, that.keyUp);
            });
        }
    };

    /**
     * Construtor do Qwerty Hancock.
     * @param {object} settings Configurações do usuário opcionais.
     */
    const QwertyHancock = function (settings) {
        this.version = version;
        init.call(this, settings);
    };

    if (typeof exports !== 'undefined') {
      if (typeof module !== 'undefined' && module.exports) {
        exports = module.exports = QwertyHancock;
      }
      exports.QwertyHancock = QwertyHancock;
    } else {
      root.QwertyHancock = QwertyHancock;
    }
})(this);

3-Criando-um-Sintetizador-Web/scripts/synth.js


// Cria uma instância do QwertyHancock
const keyboard = new QwertyHancock({
     id: 'keyboard',
     width: 800,
     height: 150,
     octaves: 4
});
// Cria o contexto de audio
const context = new AudioContext(),
    masterVolume = context.createGain(),
    oscillators = {};
    
masterVolume.gain.value = 0.2;

masterVolume.connect(context.destination);

keyboard.keyDown = function (note, frequency) {
    const osc = context.createOscillator(),
        osc2 = context.createOscillator();

        osc.frequency.value = frequency;
        osc.type = 'sawtooth';
        osc.detune.value = -10;

        osc2.frequency.value = frequency;
        osc2.type = 'triangle';
        osc2.detune.value = 10;

        osc.connect(masterVolume);
        osc2.connect(masterVolume);

        masterVolume.connect(context.destination);

        oscillators[frequency] = [osc, osc2];

        osc.start(context.currentTime);
        osc2.start(context.currentTime);


};

keyboard.keyUp = function (note, frequency) {
    oscillators[frequency].forEach(function (oscillator) {
        oscillator.stop(context.currentTime);
    });
};

3-Criando-um-Sintetizador-Web/styles/main.css


body {
    font-family: sans-serif;
}

.container {
    margin: auto;
    width: 800px;
}

.container h1 {
	text-align: center;
}

3-Criando-um-Sintetizador-Web/index.html


<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title>Sintetizador Web</title>
        <link rel="stylesheet" href="styles/main.css">
    </head>
    <body>
        <div class="container">
            <h1>Construindo um sintetizador com a Web Audio API</h1>
            <div id="keyboard"></div>
        </div>

        <script src="scripts/qwerty-hancock.min.js"></script>
        <script src="scripts/synth.js"></script>
    </body>
</html>

O Criando-um-Sintetizador-Web/index.html e o Criando-um-Sintetizador-Web/styles/main.css são arquivos bem simples, por isso, vou pular a explicação, qualquer dúvida deixe nos comentários.

No vídeo, como sempre, vou passar rapidamente por eles, mas na parte textual vou falar apenas dos dois pricipais arquivos: o Criando-um-Sintetizador-Web/scripts/synth.js e o 3-Criando-um-Sintetizador-Web/scripts/qwerty-hancock.min.js.

Criando-um-Sintetizador-Web/scripts/synth.js

No Criando-um-Sintetizador-Web/scripts/synth.js é criada uma instância do QwertyHancock (nosso teclado) logo na linha 2.

São passados alguns parâmetros para o construtor do QwertyHancock:

id, width, height e octaves.

Um contexto de áudio é criado(context) e por meio dele, é criado o masterVolume, que é conectado ao context.destination.

Através da instância do QwertyHancock que tá na variável keyboard, é definido o que é feito quando determinada tecla é pressionada (keyboard.keyDown) ou liberada (keyboard.keyUp).

keyboard.keyDown

Quando a tecla é pressionada, são criados dois osciladores osc e osc2, logo depois são definidos os tipos de onda e um valor para o detune.

O detune é uma propriedade da interface AudioBufferSourceNode, que é um AudioParam de taxa k que representa a desafinação do oscilador em cent.

O cent é uma unidade de medida logarítmica usada para intervalos musicais.

Por exemplo, valores de detune de +100 e -100 alteram a afinação da fonte para cima ou para baixo em um semitom:

Um semitom acima, detune = +100

Um semitom acima, detune = +100

Um semitom abaixo, detune = -100

Um semitom abaixo, detune = -100

Enquanto valores de detune de +1200 e -1200 muda a afinação em uma oitava para cima ou para baixo.

Uma oitava acima, detune = +1200

Uma oitava acima, detune = +1200

Uma oitava abaixo, detune = -1200

Uma oitava abaixo, detune = -1200

O valor atribuído ao detune foi de -10 em um oscilador e 10 no outro, esse valor é ínfimo, ele serve só pra dá uma levíssima desafinada entre a frequência de um oscilador e do outro, para dá uma enriquecida no timbre final, que é a soma dos dois.

O tipo de onda do osc é sawtooth e do osc2 é triangle.

Depois vem a conexão dos dois osciladores ao masterVolume e o masterVolume ao context.destination.

No dicionário vazio chamado oscillators, criado no início do arquivo na linha 10, na chave frequency é atribuída a lista [osc, osc2] com os dois osciladores criados.

Em seguida os dois osciladores são iniciados, linhas 35 e 36.

keyboard.keyUp

Quando a tecla é liberada, é dado um stop em cada oscilador na lista frequency.

3-Criando-um-Sintetizador-Web/scripts/qwerty-hancock.min.js

O 3-Criando-um-Sintetizador-Web/scripts/qwerty-hancock.min.js é mais complexo, vou tentar dá uma pincelada.

Na linha 600, onde temos var QwertyHancock = function (settings) … é onde o teclado é construído, por isso chamamos de construtor, isso é um conceito de orientação a objetos.

Em seguida é definida a versão com: this.version = version; pegando o valor atribuído na linha 18 com var version = ‘0.6.0’,

Em seguida chama o método init dele mesmo, passando ele mesmo e o resto dos parâmetros: id, width, height e octaves para terminar de construir o objeto QwertyHancock .

O trecho de código abaixo expõe o QwertyHancock( nosso teclado ), como um módulo.


    if (typeof exports !== 'undefined') {
      if (typeof module !== 'undefined' && module.exports) {
        exports = module.exports = QwertyHancock;
      }
      exports.QwertyHancock = QwertyHancock;
    } else {
      root.QwertyHancock = QwertyHancock;
    }

O Javascript utiliza módulo para imitar o conceito de classes, já que JavaScript não suporta nativamente esse conceito.

Com isso, podemos ter variáveis e métodos públicos e privados semelhante ao conceito de classes de outras linguagens de programação como Java, Python, C#, etc.

Módulos em geral, são altamente auto-suficientes, com funcionalidades distintas, o que lhes permite ser reutilizado em vários projetos.

Então podemos pensar em módulo como uma peça de código reutilizável.

No caso do QwertyHancock, ele é uma peça de software, já pronto, que estamos utilizando para criar nosso sintetizador web.

Ele pode ser utilizado em vários outros projetos.

Em 3-Criando-um-Sintetizador-Web/scripts/qwerty-hancock.min.js foi usado função anônima.

Justamente por isso, precisamos expor nosso módulo QwertyHancock com module.exports para acessar seus métodos e propriedades fora da função anônima em que ele está encapsulado.

No código do teclado, primeiro é verificado se o exports e o módulo estão definidos (linha 605 e 606):  typeof exports !== ‘undefined’ e typeof module !== ‘undefined’ && module.exports, e através do exports.QwertyHancock expomos o QwertyHancock construído.

Se o export estiver indefinido(undefined) ainda, definimos ele com: root.QwertyHancock = QwertyHancock;

O (this) é a instância do teclado construído.

Na linha 13 root recebe a instância do próprio teclado (this): var root = this;

Quando não está no contexto do navegador, geralmente o contexto raiz é chamado global, então você pode usar isso (linha 17).

var globalWindow = typeof global === ‘undefined’ ? root : root.window;

Se global não estiver definido, definimos globalWindow como root, a janela raiz.

Se já estiver definida, atribuímos root.window à globalWindow.

Vou ficando por aqui.

Os códigos estão comentados e qualquer dúvida deixe nos comentários.

Para entender melhor assista o vídeo da aula.

Para baixar os códigos é só acessar meu github.

Se gostarem do conteúdo dêem um joinha 👍 na página do Código Fluente no
Facebook

Esse é o link do código fluente no Pinterest

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado, até e bons estudos. 🙂

 

 

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>