11. Playing audio
Tags: webdev, javascript, beginners, tutorial
Published: 2022-08-18 05:43:31

---
published: true
title: 11. Playing audio
tags: webdev, javascript, beginners, tutorial
cover_image: https://res.cloudinary.com/practicaldev/image/fetch/s--2Ycgb9E_--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t7manuew9dwi5jlrf8p5.png
series: keyboard-learning-app
published_at: '2022-08-18T05:43:31Z'
---

- [Prepare audio files](#prepare-audio-files)
- [HTML5 audio element](#html5-audio-element)
- [Data model extension](#data-model-extension)
- [getAudioFileName](#getaudiofilename)
- [Testing](#testing)
- [Dynamic audio playing](#dynamic-audio-playing)

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhdk124li854p0hi63od.png)

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

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

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:

```javascript
const key = {
	code,
	label,
	main,
	shifted
}
```

## Data model extension

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

1. Only `code`

```js
{
	code: 'F1'
}
```

File name `F1.mp3` is good for such keys.

2. `main` and `shifted`

```js
{
	code: 'Digit1',
	main: '1',
	shifted: '!',
	// should add:
	shiftedName: 'exclamation mark'
},
```

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.

3. lower and upper case letters

```js
{
	code: 'KeyQ',
	main: 'q',
	shifted: 'Q',
	// add:
	shiftedName: 'q'
},
```

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

```js
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()
}
```

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`:

```js
const input = [
	{ code: 'F1' },
	{
		code: 'Digit1',
		main: '1',
		shifted: '!',
		shiftedName: 'exclamation mark'
	},
	{
		code: 'KeyH',
		main: 'h',
		shifted: 'H'
	}
]
```

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

```js
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
```

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

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

const getAudioFileName = (keyContent, shiftKey) => {
	...
}
```

And call it where we played static audio before.

Keyboard.js methods

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

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 `mainName`s to `keyboardData/ar.js`.

keyboardData/ar.js

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

```

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](https://github.com/ApayRus/keyboard/commit/c9521ff9bb79f8d8c10db7066c09f1dfc9461e6f)

[Entire code after the chapter 11](https://github.com/ApayRus/keyboard/tree/11.-Playing-audio-)