Welcome, guest | Sign In | My Account | Store | Cart
#include <stdio.h>
/*
gcc -o autolvl4.out autolvl4.c;./autolvl4.out
*/

// Auto-adjust the gain to keep a fairly constant volume.

#define g_numsamples 11000 // Min 12000 ok 
#define g_max_clip_level 32767
#define d_max_ampl 5000.0 // 4000 -> -17dB, 5000 -> -15dB, 5500 -> -14dB

char g_input[] = "seesound.wav";
char g_output[100] = {0};
double gain_ceiling=5.0;

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;
}


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;

	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;

	sumL/=count;
	sumR/=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;
			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;
		}
	}

	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 97% 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)
			{
				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;
			}

			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);
	}
	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 3 2013-04-16 13:57:04
+++ revision 4 2013-04-18 15:04:25
@@ -1,17 +1,17 @@
 #include <stdio.h>
-#include <math.h>
 /*
-gcc -o autolvl2.out autolvl2.c;./autolvl2.out
+gcc -o autolvl4.out autolvl4.c;./autolvl4.out
 */
-// Measure the decibel level of a file.
-
-#define g_numsamples 11000
+
+// Auto-adjust the gain to keep a fairly constant volume.
+
+#define g_numsamples 11000 // Min 12000 ok 
 #define g_max_clip_level 32767
-
-char g_input[] = "odetofam.wav";
+#define d_max_ampl 5000.0 // 4000 -> -17dB, 5000 -> -15dB, 5500 -> -14dB
+
+char g_input[] = "seesound.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)
 {
@@ -37,165 +37,6 @@
 }
 
 
-
-
-
-
-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;
@@ -217,7 +58,6 @@
 	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;
@@ -291,19 +131,17 @@
 	}
 	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.
+/*__________________________________________
+
+
+		Make sure that no interval clips or
+		exceeds the gain specification.
+
+____________________________________________*/
+
 	fseek(fin,0,SEEK_SET);
 	gainptr=0;
 	double locdatal=0.0;
@@ -329,14 +167,11 @@
 		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;
@@ -394,6 +229,7 @@
 
 	// 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])
@@ -405,13 +241,6 @@
 			r_gains[i]=(r_gains[i]+r_gains[i-1]*3.0)/4.0;
 		}
 	}
-
-	fclose(fgaintxt);
-
-
-
-
-
 
 	long block=0;
 	double d_ldata=0.0;
@@ -458,13 +287,13 @@
 
 			// leave the dynamics at the end alone.
 			// could make this a percentage too.
-			// when we are 85% of the way through the song,
+			// when we are 97% 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)
+			if (dpercent_in_file < 0.97)
 			{
 				dlastgains[0]=dgains[0];
 				dlastgains[1]=dgains[1];
@@ -497,12 +326,6 @@
 				rptgain += rgaininc;
 			}
 
-			fix_clipping(ndata);
-			fix_clipping(&ndata[1]);
-
-			fix_bottom_clipping(ndata);
-			fix_bottom_clipping(&ndata[1]);
-
 			fwrite(ndata,sizeof(ndata),1,fout);
 		}
 
@@ -517,7 +340,6 @@
 		}
 
 		fclose(fout);
-		// fclose(fgain);
 	}
 	else
 	{

History