## PCM Audio | Part 3: Basic Audio Effects – Volume Control

So now we know what data is stored in a PCM stream, let’s look at some real waveform examples. The easiest is a simple sine wave:

Now if we “amplify” that wave by 5, we’d get a much louder sound, represented by a wave that looked like this:

So if you want to increase the volume of your PCM stream, just multiply every PCM value by some number. If we had 2048 **bytes** of audio (remember… that’s 1024 samples since each sample is two bytes), we could amplify the stream with this type of code:

```
```

```
int16_t pcm[1024] = read in some pcm data;
for (ctr = 0; ctr < 1024; ctr++) {
pcm[ctr] *= 2;
}
```

```
```

Volume control is *almost* that simple. There's two catches.

#### Clipping

Clipping occurs when your resulting value increases above the maximum value for a sample. So since we're dealing with signed 16 bit integers our maximum positive sample is 32767. If we have a PCM sample value of 5000 and we multiplied it by 10, the resulting value is -15536, not the expected 50000. When clipping occurs, you end up with noise in the audio. You should always check to see if the result of your multiplication would cause clipping, and if so, set the value to 32767 (or -32768) instead.

So our code above becomes:

```
```

```
int16_t pcm[1024] = read in some pcm data;
int32_t pcmval;
for (ctr = 0; ctr < 1024; ctr++) {
pcmval = pcm[ctr] * 2;
if (pcmval < 32767 && pcmval > -32768) {
pcm[ctr] = pcmval
} else if (pcmval > 32767) {
pcm[ctr] = 32767;
} else if (pcmval < -32768) {
pcm[ctr] = -32768;
}
}
```

```
```

#### Volume Is Logarithmic

The other catch is that volume as perceived by humans (measured in decibels) is logarithmic, not linear. Your first instinct would be to think "Well if I wanted to double the volume, I should just multiply the samples by 2." Unfortunately, it's not quite that easy.

Multiplying a value by 1 will obviously give you no amplification. So to decrease volume, you would multiply by a value less than 1 and greater than 0. To increase volume, multiply by a number greater than one. Unfortunately, I didn't pay enough attention to logarithms in school, so I don't have a clever answer as to how to implement a proper volume control, but I've found that this function works pretty well:

```
```

```
int some_level;
float multiplier = tan(some_level/100.0);
```

```
```

If some_level is set to a value between 0 and 148 or so, this will give you a rather linear sounding multiplier. 79 is almost a multiplier of 1 (no amplification). It is far -- really far -- from perfect, but it worked well enough for my needs of implementing a volume slider. Graphing that function from 0 to 148 gives you this:

So to set an appropriate level, now we have a volume slider at 39 (roughly half volume):

```
```

```
int16_t pcm[1024] = read in some pcm data;
int32_t pcmval;
uint8_t level = 39; // half as loud
// uint8_t level = 118 // twice as loud (79 * 1.5)
float multiplier = tan(level/100.0);
for (ctr = 0; ctr < 1024; ctr++) {
pcmval = pcm[ctr] * multiplier;
if (pcmval < 32767 && pcmval > -32768) {
pcm[ctr] = pcmval
} else if (pcmval > 32767) {
pcm[ctr] = 32767;
} else if (pcmval < -32768) {
pcm[ctr] = -32768;
}
}
```

```
```

I wasn't able to find a simple logarithmic slider example, so if you have one, **please** post in the comments. I'd love to replace my hack.

Using some simple algorithms and that function above, you could easily implement a fade-in/out effect on PCM data by stepping through all 148 possible values over a period of time. And don't worry, we'll get to "time" later in the series.

That's pretty much all there is to know about volume, in the next part of the series, we're going to discuss mixing two streams together to create one stream.