Sådan opbygges et klavertastatur ved hjælp af Vanilla JavaScript

Oprettelse af et spilbart klaver keyboard kan være en fantastisk måde at lære et programmeringssprog (udover at være masser af sjov). Denne vejledning viser dig, hvordan du koder en ved hjælp af vanille JavaScript uden behov for eksterne biblioteker eller rammer.

Her er JavaScript klaver keyboard, jeg lavede, hvis du først vil tjekke slutproduktet.

Denne vejledning forudsætter, at du har en grundlæggende forståelse af JavaScript, såsom funktioner og begivenhedshåndtering, samt kendskab til HTML og CSS. Ellers er det helt nybegyndervenligt og rettet mod dem, der ønsker at forbedre deres JavaScript-færdigheder gennem projektbaseret læring (eller bare vil lave et sejt projekt!).

Det klaver keyboard vi laver til dette projekt er baseret på det dynamisk genererede syntetiske keyboard lavet af Keith William Horwood. Vi udvider antallet af tilgængelige nøgler til 4 oktaver og indstiller nye tastebindinger.

Selvom hans keyboard kan afspille lyde fra andre instrumenter, holder vi tingene enkle og holder os bare med klaver.

Her er de trin, vi vil tage for at tackle dette projekt:

1. Få arbejdsfiler

2. Opsæt tastebindinger

3. Generer tastatur

4. Håndtag tastetryk

Lad os komme igang!

1. Få arbejdsfiler

Denne tutorial bruger følgende filer:

· Audiosynth.js

· PlayKeyboard.js

Som nævnt vil vi basere vores klaver keyboard fra det, der er lavet af Keith. Naturligvis låner vi også noget af hans kode, som han venligt har givet tilladelse til audiosynth.js.

Vi inkorporerer audiosynth.js i playKeyboard.js (min modificerede version af nogle af Keiths kode), der håndterer al vores JavaScript. Denne vejledning giver en detaljeret forklaring i de følgende afsnit om de vigtigste punkter i, hvordan koden i denne fil skaber et fuldt fungerende klaver keyboard.

Vi efterlader filen audiosynth.js uberørt, da den er eneansvarlig for lydgenerering.

Koden i denne fil adskiller dette klaver keyboard fra andre, der findes online ved hjælp af Javascript til dynamisk at generere den passende lyd, når brugeren trykker på en tast. Koden behøver således ikke indlæse eksterne lydfiler.

Keith giver allerede en forklaring på, hvordan lydgenerationen fungerer på hans hjemmeside, så vi kommer ikke ind på detaljerne her.

I en nøddeskal involverer det at bruge Math.sin()funktionen i JS til at skabe sinusformede bølgeformer og transformere dem, så de lyder mere som rigtige instrumenter gennem en fancy matematik.

Opret en indeks HTML-fil, og lad os linke til JS-filerne i overskriften:

I kroppen kan vi oprette et tomt element til at fungere som vores tastatur “container”:

Vi giver det et id-navn, så vi kan henvise til det senere, når vi opretter tastaturet ved hjælp af JS. Vi kan også køre vores JS-kode ved at kalde den i kroppen:

playKeyboard()

Vi bruger playKeyboard.js som en stor funktion. Det kører, så snart browseren kommer til den kodelinje og genererer et fuldt fungerende tastatur i elementet med

id = “keyboard”.

De første par linjer i playKeyboard.js konfigurerer til mobilenhedsfunktionalitet (valgfrit) og opretter et nyt AudioSynth()objekt. Vi bruger dette objekt til at kalde metoderne til audiosynth.js, som vi linkede til tidligere. Vi bruger en af ​​disse metoder i begyndelsen til at indstille lydstyrken til lyden.

På linje 11 indstiller vi positionen for midten C til den 4. oktav.

2. Opsæt tastebindinger

Før vi genererer tastaturet, skal vi oprette vores tastebindinger, da de bestemmer, hvor mange nøgler der skal genereres.

Jeg ville oprindeligt prøve at spille åbningsnoterne til 'Für Elise', så jeg valgte en række på 4 oktaver til i alt 48 sorte og hvide taster. Dette krævede næsten alle nøgler på mit (pc) tastatur, og du er velkommen til at medtage færre.

En advarsel: Jeg har ikke de bedste tastebindinger, så de kan føle sig intuitive, når du rent faktisk prøver at spille. Måske er det prisen på at prøve at oprette et 4-oktav tastatur.

For at opsætte tastebindingerne skal du først oprette et objekt, der bruger nøglekoden som dets nøgler og den note, der skal afspilles som dens nøgleværdier (startlinje 15):

var keyboard = { /* ~ */ 192: 'C,-2', /* 1 */ 49: 'C#,-2', /* 2 */ 50: 'D,-2', /* 3 */ 51: 'D#,-2', //...and the rest of the keys } 

Kommentarerne angiver de taster, som en bruger kan trykke på på et computertastatur. Hvis en bruger trykker på tilde-tasten, er den tilsvarende nøglekode 192. Du kan få nøglekoden ved hjælp af et værktøj såsom keycode.info.

Nøgleværdien er den note, der skal afspilles og skrives i formatet 'note, oktavmodifikator', hvor oktavmodifikatoren repræsenterer den relative oktavposition fra oktaven, der indeholder mellemste C. F.eks. Er 'C, -2' C-noten 2 oktaver under midten C.

Bemærk, at der ikke er nogen 'flade' taster. Hver note er repræsenteret med en 'skarp'.

For at gøre vores klavertastatur funktionelt, er vi nødt til at forberede en omvendt opslagstabel, hvor vi skifter key: valueparene, så den tone, der skal spilles, bliver nøglen og nøglekoden bliver værdien.

Vi har brug for et sådant bord, fordi vi vil gentage musikken for let at generere vores keyboard.

Her er det, hvor tingene kan blive vanskelige: vi har faktisk brug for 2 omvendte opslagstabeller.

Vi bruger en tabel til at slå op på den etiket, vi vil have vist til computertasten, vi trykker på for at spille en note (erklæret som reverseLookupTextpå linje 164) og en anden til at slå op på den aktuelle tast, der blev trykket på (erklæret som reverseLookuppå linje 165).

Den kloge kan indse, at begge opslagstabeller har nøglekoder som værdier, så hvad er forskellen mellem dem?

It turns out that (for reasons unknown to me) when you get a keycode that corresponds to a key and you try to use String.fromCharCode() method on that keycode, you don’t always get back the same string representing the pressed key.

For example, pressing left open bracket yields keycode 219 but when you actually try to convert the keycode back to a string using String.fromCharCode(219) it returns "Û". To get "[", you have to use key code 91. We replace the incorrect codes starting on line 168.

Getting the right keycode initially involved a bit of trial and error, but later I realized you can just use another function (getDispStr() on line 318) to force the correct string to be displayed.

The majority of the keys do behave properly but you can choose to start with a smaller keyboard so you don’t have to deal with incorrect keycodes.

3. Generate Keyboard

We start the keyboard generation process by selecting our element keyboard container with document.getElementById(‘keyboard’) on line 209.

On the next line, we declare the selectSound object and set the value property to zero to have audioSynth.js load the sound profile for piano. You may wish to enter a different value (can be 0-3) if you want to try out other instruments. See line 233 of audioSynth.js with Synth.loadSoundProfile for more details.

On line 216 with var notes, we retrieve the available notes for one octave (C, C#, D…B) from audioSynth.js.

We generate our keyboard by looping through each octave and then each note in that octave. For each note, we create a element to represent the appropriate key using document.createElement(‘div’).

To distinguish whether we need to create a black or white key, we look at the length of the note name. Adding a sharp sign makes the length of the string greater than one (ex. ‘C#’) which indicates a black key and vice versa for white.

For each key we can set a width, height, and an offset from the left based on key position. We can also set appropriate classes for use with CSS later.

Next, we label the key with the computer key we need to press to play its note and store it in another element. This is where reverseLookupText comes in handy. Inside the same , we also display the note name. We accomplish all of this by setting the label’s innerHTML property and appending the label to the key (lines 240-242).

label.innerHTML = '' + s + '' + '

' + n.substr(0,1) + '' + (__octave + parseInt(i)) + '' + (n.substr(1,1)?n.substr(1,1):'');

Similarly, we add an event listener to the key to handle mouse clicks (line 244):

thisKey.addEventListener(evtListener[0], (function(_temp) { return function() { fnPlayKeyboard({keyCode:_temp}); } })(reverseLookup[n + ',' + i]));

The first parameter evtListener[0] is a mousedown event declared much earlier on line 7. The second parameter is a function that returns a function. We need reverseLookup to get us the correct keycode and we pass that value as a parameter _temp to the inner function. We will not need reverseLookup to handle actual keydown events.

This code is pre-ES2015 (aka ES6) and the updated, hopefully clearer equivalent is:

const keyCode = reverseLookup[n + ',' + i]; thisKey.addEventListener('mousedown', () => { fnPlayKeyboard({ keyCode }); }); 

After creating and appending all necessary keys to our keyboard, we will need to handle the actual playing of a note.

4. Handle Key Presses

We handle key presses the same way whether the user clicks the key or presses the corresponding computer key through use of the function fnPlayKeyboard on line 260. The only difference is the type of event we use in addEventListener to detect the key press.

We set up an array called keysPressed in line 206 to detect what keys are being pressed/clicked. For simplicity, we will assume that a key being pressed can include it being clicked as well.

We can divide the process of handling key presses into 3 steps: adding the keycode of the pressed key to keysPressed, playing the appropriate note, and removing the keycode from keysPressed.

The first step of adding a keycode is easy:

keysPressed.push(e.keyCode);

where e is the event detected by addEventListener.

If the added keycode is one of the key bindings we assigned, then we call fnPlayNote() on line 304 to play the note associated with that key.

In fnPlayNote(), we first create a new Audio() element container for our note using the generate() method from audiosynth.js. When the audio loads, we can then play the note.

Lines 308-313 are legacy code and seem they can just be replaced by container.play(), though I have not done any extensive testing to see what the difference is.

Removing a key press is also quite straightforward, as you can just remove the key from the keysPressed array with the splice method on line 298. For more details, see the function called fnRemoveKeyBinding().

The only thing we have to watch out for is when the user holds down a key or multiple keys. We have to make sure that the note only plays once while a key is held down (lines 262-267):

var i = keysPressed.length; while(i--) { if(keysPressed[i]==e.keyCode) { return false; } } 

Returning false prevents the rest of fnPlayKeyboard() from executing.

Summary

We have created a fully functioning piano keyboard using vanilla JavaScript!

To recap, here are the steps we took:

  1. We set up our index HTML file to load the appropriate JS files and execute

    playKeyboard() in to generate and make the keyboard functional. We have a element with id= "keyboard" where the keyboard will be displayed on the page.

  2. In our JavaScript file playKeyboard.js, we set up our key bindings with keycodes as keys and musical notes as values. We also create two reverse lookup tables in which one is responsible for looking up the appropriate key label based on the note and the other for looking up the correct keycode.

  3. We dynamically generate the keyboard by looping through every note in each octave range. Each key is created as its own element. We use the reverse lookup tables to generate the key label and correct keycode. Then an event listener on mousedown uses it to call fnPlayKeyboard() to play the note. The

    keydown event calls the same function but does not need a reverse lookup table to get the keycode.

  4. Vi håndterer tastetryk som følge af museklik eller computertastetryk i 3 trin: tilføj nøglekoden for den trykkede tast til en matrix, afspil den passende note og fjern nøglekoden fra den matrix. Vi skal være omhyggelige med ikke gentagne gange at afspille en note (fra starten), mens brugeren kontinuerligt holder en tast nede.

Tastaturet er nu fuldt funktionelt, men det kan se lidt kedeligt ud. Jeg overlader CSS-delen til dig?

Igen er her JavaScript klaver keyboard jeg lavede til reference.

Hvis du vil lære mere om webudvikling og tjekke nogle pæne projekter, kan du besøge min blog på 1000 Mile World.

Tak for læsning og glad kodning!