Help - Search - Members - Calendar
Full Version: Writing a normalizer DSP
Hydrogenaudio Forums > Hydrogenaudio Forum > General Audio
China_DemonViper
i want to write an audio dsp to normalize audio samples(16bit pcm) after EQ, but i don't know the algorithm.
i have tried to use the Advanced Limiter(Foobar SDK, named Monkee Limiter), but i think it doesn't work, it has no effect!
perhaps someone know how to do it, thanks a lot~

Moderation: Changed topic title. See TOS#6.
DVDdoug
Limiting is different from normalization.

Limiting is non-linear. It lowers the peaks without affecting lower-level sounds.

Normalization is linear. It increases or decreases the overall level, setting the peaks to 0dB (or slightly below 0dB).

Normalization is simple (with one complication). You simply scan the file for the existing peak (positive or negative). Then, you apply a gain adjustment to the entire file as required to increase (or decrease) the peak to 0dB.

For example, if you scan the file and determine the current peak is -3.2dB, you apply a gain increase of 3.2dB to the entire file. In practice, the software won't be working with dB. It will be working with integers or floating-point numbers, and you will be making gain adjustments in terms of a gain-factor rather than dB. Continuing with the above numbers... With 16-bit integers, 0dB is 32,767 and the peak scanned value of -3.2dB would be ~22,670. The needed gain factor is 32,767/22,670, which means you would simply multiply each sample by 1.445. (You can do the same thing with floating-point numbers, as long as you know what floating-point value represents 0dB.)

But, here's the complication... If you are working with 16-bit files and you boost any frequencies with the equalizer, you can easily drive the level into clipping. You must normalize before the signal is clipped! (A clipped file is already normalized, but it's distorted too.) Most audio editors use floating point numbers for temporary-internal processing, so the file is not clipped until you save it... You need to normalize the file before it is converted back to 16-bits. In order to do that, you need to know the scale-factor the editor uses (i.e. the floating-point value of 0dB) and you need access to the internal data, which usually means writing a plug-in.
China_DemonViper
QUOTE(DVDdoug @ Dec 28 2007, 03:14) *

Limiting is different from normalization.

Limiting is non-linear. It lowers the peaks without affecting lower-level sounds.

Normalization is linear. It increases or decreases the overall level, setting the peaks to 0dB (or slightly below 0dB).

Normalization is simple (with one complication). You simply scan the file for the existing peak (positive or negative). Then, you apply a gain adjustment to the entire file as required to increase (or decrease) the peak to 0dB.

For example, if you scan the file and determine the current peak is -3.2dB, you apply a gain increase of 3.2dB to the entire file. In practice, the software won't be working with dB. It will be working with integers or floating-point numbers, and you will be making gain adjustments in terms of a gain-factor rather than dB. Continuing with the above numbers... With 16-bit integers, 0dB is 32,767 and the peak scanned value of -3.2dB would be ~22,670. The needed gain factor is 32,767/22,670, which means you would simply multiply each sample by 1.445. (You can do the same thing with floating-point numbers, as long as you know what floating-point value represents 0dB.)

But, here's the complication... If you are working with 16-bit files and you boost any frequencies with the equalizer, you can easily drive the level into clipping. You must normalize before the signal is clipped! (A clipped file is already normalized, but it's distorted too.) Most audio editors use floating point numbers for temporary-internal processing, so the file is not clipped until you save it... You need to normalize the file before it is converted back to 16-bits. In order to do that, you need to know the scale-factor the editor uses (i.e. the floating-point value of 0dB) and you need access to the internal data, which usually means writing a plug-in.


Thank you for explaining this to me!
Now if I want to write a limiter, whether I should do as following:
Convert the 16-bit pcm samples to float-point[ *out = (float *)in/32767.00f ]
Scan the buffer(float-point samples) and find out the samples which absolute value is larger than 0.9999f, then multiply this samples by 0.9999f or others.

Am i right?
Thanks a lot~
China_DemonViper
This is my source code:

CODE

extern float lastgainLeft;
extern float lastgainRight;

static void Limiter(float *Samples, short int *OutBuf, int CH, int Num)
//Samples were not clipped yet! smile.gif
{
float *Data = Samples;
short int *Out = OutBuf;
int X = Num;
float Max = 0;
float val, delta, i;
if(CH!=2) return;
_Left:
while(X--)
{
if(fabs(Data[0])>Max) Max = fabs(Data[0]);
Data+=2;
}
if(Max<32767.00f)
{
Data = Samples;
Out = OutBuf;
X = Num;
val = 1.00f;
i = lastgainLeft;
delta = (float)(((float)val-(float)lastgainLeft)/(float)Num);
while(X--)
{
Out[0] = (float)Data[0]*(float)i;
if(i!=val) i+=delta;
Data+=2;
Out+=2;
}
lastgainLeft = val;
goto _Right;
}
Data = Samples;
Out = OutBuf;
val = (float)((float)32767.00f/(float)Max);
X = Num;
i = lastgainLeft;
delta = (float)(((float)val-(float)lastgainLeft)/1000.00f);
while(X--)
{
if(i!=val) i+=delta;
Out[0] = (float)Data[0]*(float)i;
Data+=2;
Out+=2;
}
lastgainLeft = val;
_Right:
Max = 0;
X = Num;
Data = Samples;
Out = OutBuf;
while(X--)
{
if(fabs(Data[1])>Max) Max = fabs(Data[1]);
Data+=2;
}
if(Max<32767.00f)
{
Data = Samples;
Out = OutBuf;
X = Num;
val = 1.00f;
i = lastgainRight;
delta = (float)(((float)val-(float)lastgainRight)/(float)Num);
while(X--)
{
Out[1] = (float)Data[1]*(float)i;
if(i!=val) i+=delta;
Data+=2;
Out+=2;
}
lastgainRight = val;
return;
}
Data = Samples;
Out = OutBuf;
val = (float)((float)32767.00f/(float)Max);
X = Num;
i = lastgainRight;
delta = (float)(((float)val-(float)lastgainRight)/Speed);
while(X--)
{
if(i!=val) i+=delta;
Out[1] = (float)Data[1]*(float)i;
Data+=2;
Out+=2;
}
lastgainRight = val;
return;
}


crying.gif But i think it doesn't work very well!
it sounds bad!

Moderation: Use the codebox, Luke!
[JAZ]
A) Learn C. Don't you know there's a thing named "else"? "goto" is a rarely used thing nowadays.

B) May I ask what are lastgainLeft/Right? You put them as externals (i don't know why you need to store them between calls), don't know if you initialize them, but definitely, when the function ends, the only value it can have is 1.0f
QUOTE

val = 1.00f;
[...]
lastgainLeft = val;


C) delta? what are you calculating, if i can ask? the orbit of the moon?
* Normalization: Multiplication of all the values by a *constant*, in order to achieve one peak value.
* Limiter: Process where the peaks are reduced dinamically in order to avoid clipping. The reduction depends on the current sample value. I can't see where lastgainLeft, val, or Num have anything to do with the current sample value.
(Concretely, you're just doing a ramp up, based on an initial lastgainLeft/Right value)
China_DemonViper
Thanks a lot!

The "lastgainLeft/Right" was used to store the previous reduction of left/right channel, they were initialized to 1.00f.
The "val" was used to store the current(means current sample) gain factor(reduction) of left and right channel.
"Num": the number of the input samples.

I ever tried not to use the lastgainLeft/Right, as [JAZ] said, the reduction only depends on the current sample value, but the result was out of my expectation!

I think, if the maximum peak of the current buffer is 62851 and the reduction is 0.521344f, the maximum peak of the next buffer is 120000 and the reduction is 0.27305f, then the gain factor(reduction) changes form 0.521344f to 0.27305f directly, as a result it sounds bad, it has a noise!

So, i use the lastgainLeft/Right to make the gain factor changed smoothly(linear), but it still has a lot of noise!

Is there any problems in my algorithm?
Concretely, if i want to do a limiting in a small buffer, how do i calculate the reduction?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2008 Invision Power Services, Inc.