You can manipulate the amplitude of a PCM audio stream by applying a multiplier to each sample. You can do this just prior to passing each buffer (sample set) to PortAudio. It is as simple as:
float buffer[SAMPLES_PER_BUFFER];
const float volumeMultiplier = 0.2f;
for(int i = 0; i < SAMPLES_PER_BUFFER; ++i)
{
buffer[i] *= volumeMultiplier;
}
However, the trick is in how you calculate your multiplier. Generally you won't notice much change in the signal level until you cut the overall signal level in half volumeMultiplier = 0.5f
. As you may be aware the human ear does not perceive changes in volume level linearly, but logarithmically. The following links may help explain this concept:
- http://en.wikipedia.org/wiki/Decibel#Acoustics
- http://en.wikipedia.org/wiki/Sound_pressure
- http://www.sengpielaudio.com/calculator-levelchange.htm
Using this information one might change the code above as such:
float buffer[SAMPLES_PER_BUFFER];
//volume in dB 0db = unity gain, no attenuation, full amplitude signal
// -20db = 10x attenuation, significantly more quiet
float volumeLevelDb = -6.f; //cut amplitude in half; same as 0.5 above
const float VOLUME_REFERENCE = 1.f;
const float volumeMultiplier = (VOLUME_REFERENCE * pow(10, (volumeLevelDb / 20.f);
for(int i = 0; i < SAMPLES_PER_BUFFER; ++i)
{
buffer[i] *= volumeMultiplier;
}
For your purposes this may not be important, but if you were to attach the value of volumeLevelDb or volumeMultiplier to a user interface like a slider widget, the difference would be apparent.
You can apply this algorithm to any data type.