Loading Symbol

JS Synthesizer Part 4: Processing Computer Keyboard Input

Virtual Synthesizer

In part four, we’ll learn how to process computer keyboard input. This will enable users to play on the computer keyboard instead of clicking with their mouse. Here’s what we’ll cover in part four:

  • Change how we add voices to the activeVoices object
  • Add the key code to note map
  • Create keyup/keydown EventListeners

This tutorial is part four in a series that will show you how to create a virtual synthesizer using JavaScript and the Web Audio API. Click here to clone or download the working code examples from the GitHub repository. Click here to view the finished product that uses the concepts and code discussed in this series. To get a better idea of the complete process, checkout the overview.

Key Code to Note Map

First, we’ll add this map to synth.js. It’s a JavaScript object that maps each keyboard key code (not every single key, just the ones we want to play on) to a note and octave.

var keyboard = {
    192: 'C,2', /* ~ */
    49: 'C#,2', /* 1 */
    50: 'D,2', /* 2 */
    51: 'D#,2', /* 3 */
    52: 'E,2', /* 4 */
    53: 'F,2', /* 5 */
    54: 'F#,2', /* 6 */
    55: 'G,2', /* 7 */
    56: 'G#,2', /* 8 */
    57: 'A,2', /* 9 */
    48: 'A#,2', /* 0 */
    189: 'B,2', /* - */
    187: 'C,3', /* = */
    81: 'C#,3', /* Q */
    87: 'D,3', /* W */
    69: 'D#,3',/* E */
    82: 'E,3',/* R */
    84: 'F,3',/* T */
    89: 'F#,3',/* Y */
    85: 'G,3',/* U */
    73: 'G#,3',/* I */
    79: 'A,3',/* O */
    80: 'A#,3',/* P */
    219: 'B,3',/* [ */
    221: 'C,4',/* ] */
    65: 'C#,4',/* A */
    83: 'D,4', /* S */
    68: 'D#,4', /* D */
    70: 'E,4', /* F */
    71: 'F,4', /* G */
    72: 'F#,4', /* H */
    74: 'G,4', /* J */
    75: 'G#,4', /* K */
    76: 'A,4', /* L */
    186: 'A#,4', /* ; */
    222: 'B,4', /* " */
    90: 'C,5', /* Z */
    88: 'C#,5', /* X */
    67: 'D,5', /* C */
    86: 'D#,5', /* V */
    66: 'E,5', /* B */
    78: 'F,5', /* N */
    77: 'F#,5', /* M */
    188: 'G,5', /* , */
    190: 'G#,5', /* . */
    191: 'A,5', /* / */
    37: 'A#,5', /* <- */
    39: 'B,5'  /* -> */
};

Add Keyup and Keydown EventListeners to Detect Keyboard Input

Now we need to add the EventListeners. These will read the keycode from the event object (event.which) and look up the note/octave on the keyboard object.

Keydown Events

To convert keydown events into sound, we will to first add the EventListener. Inside the event listener, we need to do several things:

  1. Get the key code using event.which.
  2. Look up the corresponding note/octave in the ‘keyboard’ object.
  3. Split that value from the ‘keyboard’ object into note and octave.
  4. Update the synth.octave property to reflect the note that was just played.
  5. Compare the note that was just played with the currently selected octave from the octave dropdown.
  6. Update the keyboard octave dropdown if the corresponding note on the keyboard is not currently in view.
  7. Figure out which octave to highlight the key in and highlight it.
  8. Create the new Voice, add it to activeVoices and start it.
// Computer keyboard keypress
$(document).keydown(function(event){
    var keycode = (event.which);
    // console.log('pressed key' + keycode);
    
    if (keyboard.hasOwnProperty(keycode)) {
        var noteOctave = keyboard[keycode];
        var both = noteOctave.split(','); 
        var rawNote = both[0]; // Get raw note e.g. 'C'
        var octave = both[1]; // Get octave

        // Already playing this note. Exit.
        if (activeVoices.hasOwnProperty(rawNote+octave)) return false; 

        // Update synth to correct octave
        synth.octave = parseInt(octave);
        // If the key pressed doesn't correspond to a note on the keyboard that we can already see,
        // update the octave range
        var current = parseInt($('#octaveSelect').val());
        if (synth.octave !== current && synth.octave !== (current + 1)) {
            $('#octaveSelect').val(synth.octave); 
        }

        // Value currently showing in the oactave select dropdown
        var current = parseInt($('#octaveSelect').val()); 
        // How many actaves to offset from the first octave (from the left)
        var oOffset = synth.octave - current; 
        // Which key on the keyboard will get highlighted
        var kbNote = rawNote; 

        // Choose whether to highlight key in first, second or third octave of keyboard
        switch(oOffset) {
        case 1:
            kbNote = 2 + rawNote; // Third octave
            break;
        case 2:
            kbNote = 3 + rawNote; // Second octave
            break;
        default: // offset = 0 or error
            kbNote = rawNote; // First octave
        }

        $('[data-note="'+kbNote+'"]').addClass('active'); // Highlight key on keyboard


        var voice = new Voice(rawNote, kbNote);
        activeVoices[rawNote+synth.octave] = voice;
        voice.start();
    }
});

Keyup Events

Next we need to stop the sound when the user lets a key up. This is slightly more in-depth than the ‘mouseup’ event code. Whereas we stopped all notes on the ‘mouseup’ event, here we need to stop only the note corresponding to the key that is firing the ‘keyup’ event. This is due to the fact that there are many keys and only one mouse click (not differentiating between left and right mouse clicks). Here are the things we need to do on a keyup event:

  1. Figure out the key code using event.which.
  2. Lookup note/octave in keyboard object.
  3. Using the note/octave, find the voice in the activeVoices object and stop it. If it can’t be found, call stopAllKeys() just to be safe – having keys ringing that can’t be stopped is really annoying.
// Computer keyboard keyup
$(document).keyup(function(event){
    var keycode = (event.which);
    // console.log('keyup' + keycode);

    if (keyboard.hasOwnProperty(keycode)) {
        var noteOctave = keyboard[keycode];
        var both = noteOctave.split(',');
        var rawNote = both[0];
        var octave = both[1];

        if(activeVoices.hasOwnProperty(rawNote+octave)) {
            var voice = activeVoices[rawNote+octave];

            voice.stop();

            var kbNote = voice.kbNote;

            // Delete voice after it's finished sounding
            // Remember the value is stored in the activeVoices object as
            // a note + octave e.g. 'C,3'
            delete activeVoices[rawNote+octave];

            $('[data-note="'+kbNote+'"]').removeClass('active');

        } else {
            stopAllKeys();
        }
    }
});

Now you can play through C2 – C7 using the computer keyboard input! You should see the keys highlight/unhighlight and see the octave select dropdown updating automatically.


Loading Symbol


Leave a Reply

Your email address will not be published. Required fields are marked *