#include <stdio.h>
#include <math.h>
/*
gcc -o autolvl2.out autolvl2.c;./autolvl2.out
*/
// Measure the decibel level of a file.
#define g_numsamples 11000
#define g_max_clip_level 32767
char g_input[] = "odetofam.wav";
char g_output[100] = {0};
double gain_ceiling=5.0;
double g_max_gain_increase_rate=0.7;
short signed int scale_data(short signed int data, double gain)
{
short signed int n_result=0;
double d_result=(double)data * gain;
if (d_result < -32768.0)
{
d_result = -32768.0;
}
if (d_result > 32767.0)
{
d_result = 32767.0;
}
n_result = (short signed int)d_result;
return n_result;
}
double abs_scale_data(short signed int data, double gain)
{
double d_result=(double)data * gain;
if (d_result<0.0) d_result=-d_result;
return d_result;
}
void fix_bottom_clipping(short signed int *ndata)
{
long i=0;
char this_clip=0;
char last_clip=0;
long clip_start=0;
long clip_end=0;
// Left channel positive clipping.
for (i=0;i<g_numsamples;i+=2)
{
if (ndata[i]<=-32768)
{
if (this_clip==0)
{
this_clip=1;
clip_start=i;
}
}
else
{
if (this_clip==1)
{
clip_end=i;
this_clip=0;
// from here, find the outer points.
// zero crossing or upward turn
// first outer point.
long outer_start=-1;
long outer_end=-1;
long j=clip_start;
long last_j=j;
short signed int last_data=ndata[j];
outer_start = clip_start-4;
if (outer_start < 0) outer_start=0;
outer_end=clip_end+4;
if (outer_end >= g_numsamples-2) outer_end = g_numsamples-2;
double max_high_value = -32766.0;
if (outer_start >=0 && outer_end >=0)
{
// Construct a sine wave hump.
long midpoint=(outer_end-outer_start)/2+outer_start;
midpoint-=(midpoint%2);
double dmag = (double)(max_high_value - ndata[outer_start]);
if (dmag<0) dmag=-dmag;
double parabola = (outer_end-outer_start)/2.0;
parabola *= parabola;
double new_data=0.0;
long k=outer_start;
for (k=outer_start; k<=midpoint; k+=2)
{
new_data = max_high_value + (dmag * (double)(midpoint-k) * (double)(midpoint-k) /parabola);
ndata[k] = new_data;
}
dmag = (double)(max_high_value - ndata[outer_end]);
if (dmag<0) dmag=-dmag;
for (k=midpoint; k<=outer_end; k+=2)
{
new_data = max_high_value + (dmag * (double)(k-midpoint) * (double)(k-midpoint) /parabola);
ndata[k] = new_data;
}
}
}
}
last_clip=this_clip;
}
}
void fix_clipping(short signed int *ndata)
{
long i=0;
char this_clip=0;
char last_clip=0;
long clip_start=0;
long clip_end=0;
// Left channel positive clipping.
for (i=0;i<g_numsamples;i+=2)
{
if (ndata[i]>=32767)
{
if (this_clip==0)
{
this_clip=1;
clip_start=i;
}
}
else
{
if (this_clip==1)
{
clip_end=i;
this_clip=0;
// from here, find the outer points.
// zero crossing or upward turn
// first outer point.
long outer_start=-1;
long outer_end=-1;
long j=clip_start;
long last_j=j;
short signed int last_data=ndata[j];
outer_start = clip_start-4;
if (outer_start < 0) outer_start=0;
outer_end=clip_end+4;
if (outer_end >= g_numsamples-2) outer_end = g_numsamples-2;
double max_high_value = 32766.0;
if (outer_start >=0 && outer_end >=0)
{
// Construct a sine wave hump.
long midpoint=(outer_end-outer_start)/2+outer_start;
midpoint-=(midpoint%2);
double dmag = (double)(max_high_value - ndata[outer_start]);
double parabola = (outer_end-outer_start)/2.0;
parabola *= parabola;
double new_data=0.0;
long k=outer_start;
for (k=outer_start; k<=midpoint; k+=2)
{
new_data = max_high_value - (dmag * (double)(midpoint-k) * (double)(midpoint-k) /parabola);
ndata[k] = new_data;
}
dmag = (double)(max_high_value - ndata[outer_end]);
for (k=midpoint; k<=outer_end; k+=2)
{
new_data = max_high_value - (dmag * (double)(k-midpoint) * (double)(k-midpoint) /parabola);
ndata[k] = new_data;
}
}
}
}
last_clip=this_clip;
ndata[i+1];
}
}
int main(void)
{
FILE *fin;
long i=0;
long j=0;
double sumL=0.0;
double sumR=0.0;
double count=0.0;
g_output[0] = '0';
g_output[1] = '_';
for (i=0; i<99; i++)
{
g_output[i+2]=g_input[i];
}
double locgainl=0.0;
double locgainr=0.0;
long loccount=0;
double d_max_ampl=5000.0; // 4000 -> -17dB 5500 -> -14dB
short signed int ndata[g_numsamples]={0};
short signed int cheader[88]={0};
char b_make_output=0;
long num_blocks=0;
long num_gains=0;
double l_gains[40000]={0.0};
double r_gains[40000]={0.0};
double l_avg_gains[40000]={0.0};
double r_avg_gains[40000]={0.0};
fin=fopen(g_input,"rb");
if (!fin)
{
printf("Error opening input file\n");
return 0;
}
else
{
printf("Opening %s\n", g_input);
}
fseek(fin,88,SEEK_SET);
/*_____________________________
MAIN LOOP
_____________________________*/
double dgains[2]={0.0};
long int gainptr=0;
while(1)
{
if (feof(fin)) break;
num_blocks++;
fread(ndata,sizeof(ndata),1,fin);
i=0;
long int j=0;
locgainl=0.0;
locgainr=0.0;
loccount=0;
for (i=0;i<g_numsamples;i++)
if (ndata[i]<0) ndata[i]=-ndata[i];
for (i=0;i<g_numsamples;i+=2)
{
sumL+=(double)ndata[i];
sumR+=(double)ndata[i+1];
locgainl+=(double)ndata[i];
locgainr+=(double)ndata[i+1];
count+=1.0;
loccount++;
}
// calculate the average
locgainl/=(double)loccount;
locgainr/=(double)loccount;
// scale the average to meet the gain.
locgainl=d_max_ampl/locgainl;
locgainr=d_max_ampl/locgainr;
dgains[0]=locgainl;
dgains[1]=locgainr;
if (locgainl > gain_ceiling) locgainl = gain_ceiling;
if (locgainr > gain_ceiling) locgainr = gain_ceiling;
l_gains[gainptr]=locgainl;
r_gains[gainptr]=locgainr;
gainptr++;
}
num_gains=gainptr;
FILE *fgaintxt;
fgaintxt=fopen("gains.txt", "wt");
sumL/=count;
sumR/=count;
printf("Left=%f\n",sumL);
printf("Right=%f\n",sumR);
printf("Count=%f\n",count);
// Make sure that no interval clips or exceeds the gain specification.
fseek(fin,0,SEEK_SET);
gainptr=0;
double locdatal=0.0;
double locdatar=0.0;
double maxlocdata=0.0;
while(1)
{
if (feof(fin)) break;
fread(ndata,sizeof(ndata),1,fin);
i=0;
long int j=0;
maxlocdata=0;
for (i=0;i<g_numsamples;i++)
if (ndata[i]<0) ndata[i]=-ndata[i];
for (i=0;i<g_numsamples;i+=2)
{
locdatal=abs_scale_data(ndata[i], l_gains[gainptr]);
locdatar=abs_scale_data(ndata[i+1], r_gains[gainptr]);
if (locdatal > maxlocdata) maxlocdata = locdatal;
if (locdatar > maxlocdata) maxlocdata = locdatar;
}
if (maxlocdata >= g_max_clip_level)
{
double factor=g_max_clip_level / maxlocdata;
fprintf(fgaintxt, "max = %.2f, factor = %.2f\n", maxlocdata, factor);
l_gains[gainptr] *= factor;
r_gains[gainptr] *= factor;
}
gainptr++;
}
// l_avg_gains
long llim=0;
long ulim=0;
for (i=0; i<num_gains; i++)
{
double dlavg=0.0;
double dravg=0.0;
long j=0;
double dcount=0.0;
llim=i-10;
ulim=i+10;
dlavg=0.0;
dravg=0.0;
if (llim < 0)
{
llim=0;
ulim-=llim;
}
for (j=llim; j<ulim; j++)
{
dlavg+=l_gains[j];
dravg+=r_gains[j];
dcount+=1.0;
}
dlavg/=dcount;
dravg/=dcount;
l_avg_gains[i]=dlavg;
r_avg_gains[i]=dravg;
}
for (i=0; i<num_gains; i++)
{
double dfactor=0.0;
double dnewgain=0.0;
if (l_gains[i] > l_avg_gains[i])
{
dfactor=l_gains[i]/l_avg_gains[i];
dnewgain=(l_gains[i] * dfactor) + l_avg_gains[i];
dnewgain /= (dfactor + 1);
l_gains[i] = dnewgain;
}
if (r_gains[i] > r_avg_gains[i])
{
dfactor=r_gains[i]/r_avg_gains[i];
dnewgain=(r_gains[i] * dfactor) + r_avg_gains[i];
dnewgain /= (dfactor + 1);
r_gains[i] = dnewgain;
}
}
// Make it so that the gains can decrease rapidly, but not increase as fast.
// If a future gain is a lot bigger than a previous gain, then scale it back.
for (i=1; i<num_gains; i++)
{
if (l_gains[i]>l_gains[i-1])
{
l_gains[i]=(l_gains[i]+l_gains[i-1]*3.0)/4.0;
}
if (r_gains[i]>r_gains[i-1])
{
r_gains[i]=(r_gains[i]+r_gains[i-1]*3.0)/4.0;
}
}
fclose(fgaintxt);
long block=0;
double d_ldata=0.0;
double d_rdata=0.0;
double lgain=0.0;
double rgain=0.0;
/*_____________________________________
Do the dynamic compression here
_______________________________________*/
i=0;
printf("Auto-scale volume\n");
lgain=d_max_ampl/sumL;
rgain=d_max_ampl/sumR;
double lgaininc=0.0;
double rgaininc=0.0;
double dlastgains[2] = {0.0};
double lptgain=0.0;
double rptgain=0.0;
double dpercent_in_file=0.0;
FILE *fout;
fout=fopen(g_output,"wb");
if (fout)
{
fseek(fin,0,SEEK_SET);
fread(cheader,sizeof(cheader),1,fin);
fwrite(cheader,sizeof(cheader),1,fout);
gainptr=0;
dgains[0]=l_gains[gainptr];
dgains[1]=r_gains[gainptr++];
for (block=0; block<num_blocks; block++)
{
fread(ndata,sizeof(ndata),1,fin);
// leave the dynamics at the end alone.
// could make this a percentage too.
// when we are 85% of the way through the song,
// just let it ride out and don't adjust the gain
// from what it was.
dpercent_in_file = (double)block / (double)num_blocks;
if (dpercent_in_file < 0.97) // (num_blocks-block > 30)
{
dlastgains[0]=dgains[0];
dlastgains[1]=dgains[1];
lptgain=dlastgains[0];
rptgain=dlastgains[1];
if (lptgain > gain_ceiling) lptgain = gain_ceiling;
if (rptgain > gain_ceiling) rptgain = gain_ceiling;
dgains[0]=l_gains[gainptr];
dgains[1]=r_gains[gainptr++];
lgaininc=(dgains[0]-dlastgains[0]) / (double)g_numsamples;
rgaininc=(dgains[1]-dlastgains[1]) / (double)g_numsamples;
}
else
{
lgaininc=0;
rgaininc=0;
}
long i=0;
for (i=0;i<g_numsamples;i+=2)
{
ndata[i] = scale_data(ndata[i] , lptgain);
ndata[i+1] = scale_data(ndata[i+1], rptgain);
lptgain += lgaininc;
rptgain += rgaininc;
}
fix_clipping(ndata);
fix_clipping(&ndata[1]);
fix_bottom_clipping(ndata);
fix_bottom_clipping(&ndata[1]);
fwrite(ndata,sizeof(ndata),1,fout);
}
while(1)
{
if (feof(fin)) break;
fread(ndata,sizeof(short signed int),1,fin);
short int i=0;
ndata[i]=scale_data(ndata[i], lgain);
ndata[i+1]=scale_data(ndata[i+1], rgain);
fwrite(ndata,sizeof(short signed int),1,fout);
}
fclose(fout);
// fclose(fgain);
}
else
{
printf("Error opening output file\n");
}
printf("lgain = %.2f\n", lgain);
printf("rgain = %.2f\n", rgain);
fclose(fin);
return 0;
}
Diff to Previous Revision
--- revision 2 2013-04-11 16:31:54
+++ revision 3 2013-04-16 13:57:04
@@ -1,12 +1,14 @@
#include <stdio.h>
#include <math.h>
-// gcc -o autolvl.out autolvl.c;./autolvl.out
+/*
+gcc -o autolvl2.out autolvl2.c;./autolvl2.out
+*/
// Measure the decibel level of a file.
-// Min 12000 ok
#define g_numsamples 11000
-
-char g_input[] = "testsine.wav";
+#define g_max_clip_level 32767
+
+char g_input[] = "odetofam.wav";
char g_output[100] = {0};
double gain_ceiling=5.0;
double g_max_gain_increase_rate=0.7;
@@ -27,9 +29,10 @@
return n_result;
}
-double dbl_scale_data(short signed int data, double gain)
+double abs_scale_data(short signed int data, double gain)
{
double d_result=(double)data * gain;
+ if (d_result<0.0) d_result=-d_result;
return d_result;
}
@@ -298,6 +301,43 @@
printf("Right=%f\n",sumR);
printf("Count=%f\n",count);
+
+
+ // Make sure that no interval clips or exceeds the gain specification.
+ fseek(fin,0,SEEK_SET);
+ gainptr=0;
+ double locdatal=0.0;
+ double locdatar=0.0;
+ double maxlocdata=0.0;
+ while(1)
+ {
+ if (feof(fin)) break;
+ fread(ndata,sizeof(ndata),1,fin);
+ i=0;
+ long int j=0;
+ maxlocdata=0;
+ for (i=0;i<g_numsamples;i++)
+ if (ndata[i]<0) ndata[i]=-ndata[i];
+ for (i=0;i<g_numsamples;i+=2)
+ {
+ locdatal=abs_scale_data(ndata[i], l_gains[gainptr]);
+ locdatar=abs_scale_data(ndata[i+1], r_gains[gainptr]);
+ if (locdatal > maxlocdata) maxlocdata = locdatal;
+ if (locdatar > maxlocdata) maxlocdata = locdatar;
+ }
+
+ if (maxlocdata >= g_max_clip_level)
+ {
+ double factor=g_max_clip_level / maxlocdata;
+ fprintf(fgaintxt, "max = %.2f, factor = %.2f\n", maxlocdata, factor);
+ l_gains[gainptr] *= factor;
+ r_gains[gainptr] *= factor;
+ }
+ gainptr++;
+ }
+
+
+
// l_avg_gains
long llim=0;
long ulim=0;
@@ -368,6 +408,11 @@
fclose(fgaintxt);
+
+
+
+
+
long block=0;
double d_ldata=0.0;
double d_rdata=0.0;