Вопрос

I'm currently using libsndfile in combination with PortAudio V19 to read audio data from a file and play it back. (Please note I am doing this on a Raspberry Pi running Raspbian.) The problem I'm encountering is that I need to dynamically control the playback volume in real time for each audio sample played in this way. I've attempted to use system calls to manipulate the global playback volume via alsamixer, which would be an acceptable solution in my use case, but the latency is too high for this to work.

What I'm seeking is one of two things:

  • A library which can handle modifying audio volume in real time, either by acting on the raw audio data retrieved by libsndfile, or by setting the global playback volume with minimal latency (sub-millisecond). The library must be free (gratis) and usable on Raspbian; licensing is not a concern.

  • The mathematical transformations that need to be applied to the audio data retrieved by libsndfile, in order to modify the volume level of the data, preferably where the target volume is in the range [0.0f, 1.0f], with 0.0f being silent and 1.0f being the original volume from the file.

I've tried to look all over for useful (free) material on this subject and have failed to locate anything that helps. Any assistance is greatly appreciated!

Это было полезно?

Решение

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:

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top