DEV Community

Cover image for 11. Playing audio
Rustam Apay
Rustam Apay

Posted on

11. Playing audio

Prepare audio files

We will use a short mp3 files for each key value. In the folder keyboardData create folders: en, ru, ar. Move audio files with numbers (for 3 langs) to the folders.

GITHUB

Note:

If you need to make many files from one, I highly recommend free audio editor Audacity. There you can select parts of an audio and attach text labels to them (ctrl+b). Then in menu: File --> Export --> Export Multiple --> Split based on: Label.

For example, you have 1 audio file with numbers from 0 to 9.

Image description

You will get files: 0.mp3, 1.mp3, ... 9.mp3.

HTML5 audio element

In App.js methods, at the beginning of setActiveKey add 2 lines:

App.js methods

setActiveKey(keyContent) {
  const audio = new Audio(`./keyboardData/en/1.mp3`);
  audio.play();
  ...
}
Enter fullscreen mode Exit fullscreen mode

That's how audio element works.

Now when you click on any button, will be played one file en/1.mp3. You guess that we need to play different files. But there is a problem to identify them in our data model. If you remember, each key is represented by object:

const key = {
    code,
    label,
    main,
    shifted
}
Enter fullscreen mode Exit fullscreen mode

Data model extension

Key data aren't filled in the same way. Keys have such different set of props:
file naming

  1. Only code
{
    code: 'F1'
}
Enter fullscreen mode Exit fullscreen mode

File name F1.mp3 is good for such keys.

  1. main and shifted
{
    code: 'Digit1',
    main: '1',
    shifted: '!',
    // should add:
    shiftedName: 'exclamation mark'
},
Enter fullscreen mode Exit fullscreen mode

Here we cannot use code as before. Because there is only 1 code, but we need 2 audio files for main and shifted values.

Furthermore, ! is forbidden symbol for file names. So it would be good to have an additional field shiftedName: 'exclamation mark', that we'll use in a file name.

Value of main is good for filename 1.mp3, but sometimes we will need an additional field mainName.

1.mp3 or exclamation mark.mp3 would be good filenames for keyContent above.

  1. lower and upper case letters
{
    code: 'KeyQ',
    main: 'q',
    shifted: 'Q',
    // add:
    shiftedName: 'q'
},
Enter fullscreen mode Exit fullscreen mode

Here it is enough to have only 1 file q.mp3 for both values q and Q.

How do we fill our data now? We add to every main and shifted values that we can't or don't want to use as a file name, additional values: mainName and/or shiftedName.

The idea behind this different approach to each key type, instead of writing filename for every key value, is that we are trying to avoid overloading of our keyboardData/lang.js files. So we will add a minimum info do keyboard data file, and then calculate filenames from that minimum.

getAudioFileName

const getAudioFileName = (keyContent, shiftKey) => {
    const { main, mainName, shifted, shiftedName, code } = keyContent

    let fileName

    if (shiftKey) {
        // will be returned 1 of 3 values (if it exist). priority to the first one
        fileName = shiftedName || shifted || code
    } else {
        fileName = mainName || main || code
    }

    // to have a guarantee, that everything is written in the same (lower) case
    return fileName.toLowerCase()
}
Enter fullscreen mode Exit fullscreen mode

Notice, that we call any audio file by its lower case name. So we don't need to change keyboard data for values like q-Q. When you name files make sure they are in lowercase.

Testing

When you wrote a function, and you're not sure if it works or not, you should test it.

The simplest way: copy/paste the function to console (Chrome --> DevTools --> Console). (code above without word const)

Also copy to the console keyContent examples that we wrote before. Put them to the array input:

const input = [
    { code: 'F1' },
    {
        code: 'Digit1',
        main: '1',
        shifted: '!',
        shiftedName: 'exclamation mark'
    },
    {
        code: 'KeyH',
        main: 'h',
        shifted: 'H'
    }
]
Enter fullscreen mode Exit fullscreen mode

Then call getAudioFileName with these data entities and different shiftKey values, in the console.

getAudioFileName(input[0], false) // f1
getAudioFileName(input[0], true) // f1
getAudioFileName(input[1], false) // 1
getAudioFileName(input[1], true) // exclamation mark
getAudioFileName(input[2], false) // h
getAudioFileName(input[2], true) // h
Enter fullscreen mode Exit fullscreen mode

That is called testing. Programmers save such a code with:

  • input,
  • call(input),
  • correct output

to special files -- called tests. Then, after codebase has changed, we run the tests to check that we haven't broken anything.

Dynamic audio playing

Add that function definition at the top of Keyboard.js, just after imports:

Keyboard.js

import Keyboard from './components/Keyboard.js'
import LangSwitcher from './components/LangSwitcher.js'

const getAudioFileName = (keyContent, shiftKey) => {
    ...
}
Enter fullscreen mode Exit fullscreen mode

And call it where we played static audio before.

Keyboard.js methods

setActiveKey(keyContent) {
    const fileName = getAudioFileName(keyContent, this.shiftKey)
    const audio = new Audio(
        `./keyboardData/${this.currentLang}/${fileName}.mp3`
    )
    audio.play()
    ...
}
Enter fullscreen mode Exit fullscreen mode

Now when you click on different buttons, you hear a particular key sound, even when you switch languages. Don't forget, that for now we have files only for numbers 0, 1, ..., 9 -- they are correct for en and ru. But for playing Arabic numbers you should add their mainNames to keyboardData/ar.js.

keyboardData/ar.js

{
    code: 'Digit1',
    main: '١',
    // add:
    mainName: '1',
    shifted: '!'
},
...

Enter fullscreen mode Exit fullscreen mode

Or you should rename files to ١.mp3, ٢.mp3 e.t.c.

As you can see, our approach of file naming and data filling is flexible.

Diffs in code 11

Entire code after the chapter 11

Top comments (0)