ftp.nice.ch/pub/next/unix/audio/cmusic.bs.N.tar.gz#/src/sigsf/denoise.c

This is denoise.c in view mode; [Download] [Up]

#include <math.h>
#include <stdio.h>
#include <carl/carl.h>
#include <carl/defaults.h>
#include <carl/procom.h>
#include <carl/sndio.h>

extern int sferror;
extern CSNDFILE *rootsfd;

/*------------------------------------------------------
			denoise.c

Experimental noise reduction scheme using frequency-
	domain noise-gating.  This program should work
	best in the case of high signal-to-noise with
	hiss-type noise.  The algorithm is that suggested
	by Moorer & Berger in "Linear-Phase Bandsplitting:
	Theory and Applications" presented at the 76th
	Convention 1984 October 8-11 New York of the Audio
	Engineering Society (preprint #2132) except that
	it uses the Weighted Overlap-Add formulation for
	short-time Fourier analysis-synthesis in place of
	the recursive formulation suggested by Moorer &
	Berger.  The gain in each frequency bin is computed
	independently according to 

	gain = g0 + (1-g0) * [avg / (avg + th*th*nref)] ^ sh

	where avg and nref are the mean squared signal and
	noise respectively for the bin in question.  (This
	is slightly different than in Moorer & Berger.)  The
	critical parameters th and g0 are specified in dB
	and internally converted to decimal values.  The nref
	values are computed at the start of the program on the
	basis of a noise_soundfile (specified in the command
	line) which contains noise without signal.  The avg
	values are computed over a rectangular window of m
	FFT frames looking both ahead and behind the current
	time.  This corresponds to a temporal extent of m*D/R
	(which is typically (m*N/8)/R).  The default settings
	of N, M, and D should be appropriate for most uses.  A
	higher sample rate than 16KHz might indicate a higher N.

To compile:  cc denoise.c -lieee -lI77 -lF77 -lsf -lcarl -lm -o denoise

------------------------------------------------------*/

main(argc, argv)
	int	argc;
	char	**argv;
{
	float fsndi();		/* for reading in samples of noise_sndfile */


	float	*input,		/* input buffer */
		*output,	/* output buffer */
		*anal,		/* analysis buffer */
		*nextIn,	/* pointer to next empty word in input */
		*nextOut,	/* pointer to next empty word in output */
		*analWindow,	/* pointer to center of analysis window */
		*synWindow,	/* pointer to center of synthesis window */
		*mbuf,		/* m most recent frames of FFT */
		*nref,		/* noise reference buffer */
		*rsum;		/* running sum of magnitude-squared spectrum */

	int	N = 1024,	/* number of points in FFT */
		Np2,		/* N+2 */
		M = 0,		/* length of analWindow impulse response (M=N)*/
		L = 0,		/* length of synWindow impulse response (L=M)*/
		D = 0,		/* decimation factor (default is M/8) */
		I = 0,		/* interpolation factor (should be I=D) */
		m = 5,		/* number of frames to save in mbuf */
		mi = 0,		/* frame offset index in mbuf */
		mj,		/* delayed offset index in mbuf */
		md,		/* number of frames of delay in mbuf (m/2) */
		mp,		/* mi * Np2 */
		analWinLen,	/* half-length of analysis window */
		synWinLen;	/* half-length of synthesis window */

	long	outCount,	/* number of samples written to output */
		ibuflen,	/* length of input buffer */
		obuflen,	/* length of output buffer */
		beg = 0,	/* first sample in noise soundfile */
		end = 0,	/* last sample in noise soundfile */
		nI,		/* current input (analysis) sample */
		nO,		/* current output (synthesis) sample */
		nId,		/* delayed input (analysis) sample */
		nOd,		/* delayed output (synthesis) sample */
		nMin = 0,	/* first input (analysis) sample */
		nMax = 100000000;	/* last input sample (unless EOF) */

	char	ch;		/* needed for crack (commandline interpreter)*/

	float	Pi,		/* 3.14159... */
		TwoPi,		/* 2*Pi */
		gain,		/* gain of noise gate */
		g0 = -40.,	/* minimum gain for noise gate */
		g0m,		/* 1. - g0 */
		th = 30.,	/* threshold above noise reference (dB) */
		avg,		/* average square energy */
		fac,		/* factor in gain computation */
		minv,		/* 1 / m */
		sum = 0.,	/* sum for averaging */
		rIn,		/* decimated sampling rate */
		rOut,		/* pre-interpolated sampling rate */
		Ninv,		/* 1. / N */
		R = 0.;		/* input sampling rate */

	int	i,j,k,		/* index variables */
		i0,i1,		/* indices for real and imaginary FFT values */
		Dd,		/* number of new inputs to read (Dd <= D) */
		Ii,		/* number of new outputs to write (Ii <= I) */
		N2,		/* N/2 */
		Mf = 0,		/* flag for even M */
		Lf = 0,		/* flag for even L */
		sh = 1,		/* sharpness control for noise gate gain */
		v = 0,		/* flag for verifying noise reference values */
		flag;		/* end-of-input flag */

	float	srate;		/* sample rate from header on stdin */

	char	cbuf[72];	/* buffer for strings to write to header */

	char	*dbuf;		/* buffer for strings to read from header */

	char *file = "test";

	char *cbeg = NULL, *cend = NULL, *cdur = NULL;

	CSNDFILE *sfd, *opensf();

	PROP	*proplist;	/* from header on stdin */

/* call crack to interpret commandline */

	if (isatty(0))
		usage(1);

	while ((ch = crack(argc,argv,"N|M|L|D|R|b|e|d|t|s|n|m|vh",0))
		 != NULL){
		switch (ch) {
			case 'N': N = sfexpr(arg_option,1.0); break;
			case 'M': M = sfexpr(arg_option,1.0); break;
			case 'L': L = sfexpr(arg_option,1.0); break;
			case 'D': D = sfexpr(arg_option,1.0); break;
			case 'R': R = sfexpr(arg_option,1.0); break;
			case 't': th = sfexpr(arg_option,1.0); break;
			case 's': sh = sfexpr(arg_option,1.0); break;
			case 'm': g0 = sfexpr(arg_option,1.0); break;
			case 'n': m = sfexpr(arg_option,1.0); break;
			case 'b': cbeg = arg_option; break;
			case 'e': cend = arg_option; break;
			case 'd': cdur = arg_option; break;
			case 'v': v = 1; break;
			case 'h': usage(0);	/* then exits normally */
			default: usage(1);	/* this exits with error */
		}
	}

/* read header from stdin */

	if ((proplist = getheader(stdin)) != NULL) {	/* there is a header */
		noautocp();				/* suppress hdr copy */

		if ((dbuf = getprop(stdin, H_SRATE)) != NULL){
			srate = atof(dbuf);	/* get input srate */
			if (R == 0.)
				R = srate;
			srate = R;
		}

	}

/* Open reference soundfile for noise reduction. */

	if (arg_index < (argc - 1)){
		fprintf(stderr,"denoise: too many filenames specified \n");
		exit(1);
	}

	if (arg_index < argc)
		file = argv[arg_index];
	else {
		fprintf(stderr,"denoise: reference soundfile not specified\n");
		exit(1);
	}

	if ((sfd = opensf(file, "-r")) == NULL) { 
		fprintf(stderr,"denoise: opensf failed on %s\n", file);
		(void) sfallclose(); 
		exit(1); 
	}

	/* calculate times */
	if (cbeg != NULL) 
		beg = sfexpr(cbeg, sfd->sr)*sfd->nc;
	if (cend != NULL)
		end = sfexpr(cend, sfd->sr)*sfd->nc;
	else if (cdur != NULL)
		end = sfexpr(cdur, sfd->sr)*sfd->nc + beg;

	/* check boundaries */
	if (beg < 0 || beg >= sfd->fs) {
		fprintf(stderr, "denoise: begin time out of range\n");
		quit();
	}
	if (end < 0 || end >= sfd->fs) {
		fprintf(stderr, "denoise: end time out of range\n");
		quit();
	}

	if (end == 0)
		end = sfd->fs;

/* set up parameter values */

	for (N2 = 2; N2 <= 16384; N2 *= 2)
		if (N2 >= N)
			break;

	if (N2 > 16384){
		fprintf(stderr,"denoise: N too large\n");
		exit(1);
	}

	N = N2;
	N2 = N / 2;
	Ninv = 1. / N;
	Np2 = N + 2;

	if (M == 0)
		M = N;
	if ((M%2) == 0)
		Mf = 1;
	if (L == 0)
		L = M;
	if ((L%2) == 0)
		Lf = 1;

	ibuflen = 4 * M;
	obuflen = 4 * L;

	if (D == 0)
		D = ((float) M / 8.);
	I = D;

	Pi = 4.*atan(1.);
	TwoPi = 2. * Pi;

	minv = 1. / m;
	md = m / 2;
	g0 = pow(10.,(.05*g0));
	g0m = 1. - g0;
	th = pow(10.,(.05*th));

/* write header to stdout */

	cpoheader(proplist,stdout);
	putheader(stdout);

/* set up analysis window: The window is assumed to be symmetric
	with M total points.  After the initial memory allocation,
	analWindow always points to the midpoint of the window (or
	one half sample to the right, if M is even); analWinLen is
	half the true window length (rounded down). If the window
	duration is longer than the transform (M > N), then the window
	is multiplied by a sin(x)/x function to meet the condition:
	analWindow[Ni] = 0 for i != 0.  In either case, the window
	is renormalized so that the amplitude estimates are properly
	scaled.  The maximum allowable window duration is ibuflen/2. */

	if ((analWindow = (float *) calloc(M+Mf,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);
	analWindow += (analWinLen = M/2);

	hamming(analWindow,analWinLen,Mf);
	for (i = 1; i <= analWinLen; i++)
		*(analWindow - i) = *(analWindow + i - Mf);

	if (M > N) {
		if (Mf)
			*analWindow *= N * sin((double) Pi*.5/N) /( Pi*.5);
		for (i = 1; i <= analWinLen; i++) 
			*(analWindow + i) *=
			N * sin((double) Pi*(i+.5*Mf)/N) / (Pi*(i+.5*Mf));
		for (i = 1; i <= analWinLen; i++)
			*(analWindow - i) = *(analWindow + i - Mf);
	}

	sum = 0.;
	for (i = -analWinLen; i <= analWinLen; i++)
		sum += *(analWindow + i);

	sum = 2. / sum;		/*factor of 2 comes in later in trig identity*/

	for (i = -analWinLen; i <= analWinLen; i++)
		*(analWindow + i) *= sum;


/* set up synthesis window:  For the minimal mean-square-error
	formulation (valid for N >= M), the synthesis window
	is identical to the analysis window (except for a
	scale factor), and both are even in length.  If N < M,
	then an interpolating synthesis window is used. */

	if ((synWindow = (float *) calloc(L+Lf,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);
	synWindow += (synWinLen = L/2);

	if (M <= N){
		hamming(synWindow,synWinLen,Lf);
		for (i = 1; i <= synWinLen; i++)
			*(synWindow - i) = *(synWindow + i - Lf);

		for (i = -synWinLen; i <= synWinLen; i++)
			*(synWindow + i) *= sum;

		sum = 0.;
		for (i = -synWinLen; i <= synWinLen; i+=I)
			sum += *(synWindow + i) * *(synWindow + i);

		sum = 1. / sum;

		for (i = -synWinLen; i <= synWinLen; i++)
			*(synWindow + i) *= sum;
	}
	else {
		hamming(synWindow,synWinLen,Lf);
		for (i = 1; i <= synWinLen; i++)
			*(synWindow - i) = *(synWindow + i - Lf);

		if (Lf)
			*synWindow *= I * sin((double) Pi*.5/I) / (Pi*.5);
		for (i = 1; i <= synWinLen; i++) 
			*(synWindow + i) *=
			I * sin((double) Pi*(i+.5*Lf)/I) / (Pi*(i+.5*Lf));
		for (i = 1; i <= synWinLen; i++)
			*(synWindow - i) = *(synWindow + i - Lf);

		sum = 1. / sum;

		for (i = -synWinLen; i <= synWinLen; i++)
			*(synWindow + i) *= sum;
	}

      
/* set up input buffer:  nextIn always points to the next empty
	word in the input buffer (i.e., the sample following
	sample number (n + analWinLen)).  If the buffer is full,
	then nextIn jumps back to the beginning, and the old
	values are written over. */

	if ((input = (float *) calloc(ibuflen,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);
	
	nextIn = input;


/* set up output buffer:  nextOut always points to the next word
	to be shifted out.  The shift is simulated by writing the
	value to the standard output and then setting that word
	of the buffer to zero.  When nextOut reaches the end of
	the buffer, it jumps back to the beginning.  */

	if ((output = (float *) calloc(obuflen,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);

	nextOut = output;


/* set up analysis buffer for (N/2 + 1) channels: The input is real,
	so the other channels are redundant. */

	if ((anal = (float *) calloc(Np2,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);

/* noise reduction: calculate noise reference by taking as many 
	consecutive FFT's as possible in noise soundfile, and
	averaging them all together.  Multiply by th*th to
	establish threshold for noise-gating in each bin. */

	if ((nref = (float *) calloc((N2+1),sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);

	if ((mbuf =(float *)calloc(m*Np2,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);

	if ((rsum =(float *)calloc(N2+1,sizeof(float))) == NULL)
		malerr("denoise: insufficient memory",1);

	k = 0;
	j = beg;
	while(j < end){
		for (i = 0; i < N; i++,j++){
			if (j < end)
				anal[i] = fsndi(sfd, j);
			if (sferror)
				quit();
		}

		cfast(anal,N);

		for (i = 0; i <= N+1; i++)
			anal[i] *= Ninv;

		for (i = 0; i <= N2; i++){
			fac = anal[2*i] * anal[2*i];
			fac += anal[2*i+1] * anal[2*i+1];
			nref[i] += fac;
		}
		k++;
	}
	fac = th * th / k;
	for (i = 0; i <= N2; i++)
		nref[i] *= fac;

/* initialization: input time starts negative so that the rightmost
	edge of the analysis filter just catches the first non-zero
	input samples; output time is always T times input time.
	nI and nO count time with regard to input, while nId and
	nOd count time according to the output.  The latter are all
	that really matter except for catching the EOF on input. */

	outCount = 0;
	nI = -(analWinLen / D) * D;	/* input time (in samples) */
	nId = nI - md * D;		/* subtract additional delay in mbuf*/
	nO = nI;
	nOd = nId;
	Dd = analWinLen + nI + 1;	/* number of new inputs to read */
	Ii = 0;				/* number of new outputs to write */
	flag = 1;
	mi = 0;
	mj = m - md;
	if (mj >= m)
		mj = 0;
	mp = mi * Np2;

/* main loop:  If nMax is not specified it is assumed to be very large
	and then readjusted when getfloat detects the end of input. */

	while(nId < (nMax + analWinLen)){

		for (i = 0; i < Dd; i++){
			if (getfloat(nextIn++) <= 0)
				Dd = i;			/* EOF ? */
			if (nextIn >= (input + ibuflen))
				nextIn -= ibuflen;
		}

		if (nI > 0)
			for (i = Dd; i < D; i++){	/* zero fill at EOF */
				*(nextIn++) = 0.;
				if (nextIn >= (input + ibuflen))
					nextIn -= ibuflen;
			}


	/* analysis: The analysis subroutine computes the complex output at
		time n of (N/2 + 1) of the FFT bins.  It operates on input
		samples (n - analWinLen) thru (n + analWinLen) and expects
		to find these in input[(n +- analWinLen) mod ibuflen].
		It expects analWindow to point to the center of a
		symmetric window of length (2 * analWinLen +1).  It is the
		responsibility of the main program to ensure that these values
		are correct!  The results are returned in anal as succesive
		pairs of real and imaginary values for the lowest (N/2 + 1)
		channels. */


		for (i = 0; i < N+2; i++) *(anal + i) = 0.;	/*initialize*/

		j = (nI - analWinLen - 1 + ibuflen) % ibuflen;	/*input pntr*/

		k = nI - analWinLen - 1;			/*time shift*/
		while (k < 0)
			k += N;
		k = k % N;

		for (i = -analWinLen; i <= analWinLen; i++) {
			if (++j >= ibuflen)
				j -= ibuflen;
			if (++k >= N)
				k -= N;
			anal[k] += analWindow[i] * input[j];
		}

		cfast(anal,N);

	/* noise reduction: for each bin, calculate average magnitude-squared
		and calculate corresponding gain.  Apply this gain to delayed
		FFT values in mbuf[mj*Np2 + i?]. */

		for (i = 0; i <= N2; i++){
			i0 = 2 * i;
			i1 = i0 + 1;
			rsum[i] -= mbuf[mp + i0] * mbuf[mp + i0];
			rsum[i] -= mbuf[mp + i1] * mbuf[mp + i1];
			rsum[i] += anal[i0] * anal[i0];
			rsum[i] += anal[i1] * anal[i1];
			avg = minv * rsum[i];
			if (avg < 0.)
				avg = 0.;
			if (avg == 0.)
				fac = 0.;
			else
				fac = avg / (avg + nref[i]);
			for (j = 1; j < sh; j++)
				fac *= fac;
			gain = g0m * fac + g0;
			mbuf[mp + i0] = anal[i0];
			mbuf[mp + i1] = anal[i1];
			anal[i0] = gain * mbuf[mj*Np2 + i0];
			anal[i1] = gain * mbuf[mj*Np2 + i1];
		}

		if (++mi >= m)
			mi = 0;
		if (++mj >= m)
			mj = 0;
		mp = mi * Np2;

	/* synthesis: The synthesis employs the Weighted Overlap-Add
		technique to reconstruct the time-domain signal.  The
		(N/2 + 1) filter outputs at time n are inverse Fourier
		transformed, windowed, and added into the output array.  The
		subroutine thinks of output as a shift register in which 
		locations are referenced modulo obuflen.  Therefore, the main
		program must take care to zero each location which it "shifts"
		out (to standard output). */

		cfsst(anal,N);

		j = nOd - synWinLen - 1;
		while (j < 0)
			j += obuflen;
		j = j % obuflen;

		k = nOd - synWinLen - 1;
		while (k < 0)
			k += N;
		k = k % N;

		for (i = -synWinLen; i <= synWinLen; i++) {	/*overlap-add*/
			if (++j >= obuflen)
				j -= obuflen;
			if (++k >= N)
				k -= N;
			output[j] += anal[k] * synWindow[i];
		}


		for (i = 0; i < Ii; i++){	/* shift out next Ii values */
			putfloat(nextOut);
			*(nextOut++) = 0.;
			if (nextOut >= (output + obuflen))
				nextOut -= obuflen;
			outCount++;
		}

		if (flag)
			if ((nI > 0) && (Dd < D)){	/* EOF detected */
				flag = 0;
				nMax = nI + analWinLen - (D - Dd);
		}

		nI += D;				/* increment time */
		nO += I;
		nId += D;				/* increment time */
		nOd += I;

		if ((nI + analWinLen) <= nMax)
			Dd = D;
		else
			if ((nI + analWinLen - D) <= nMax)
				Dd = nMax -(nI + analWinLen - D);
			else
				Dd = 0;

		if (nOd > (synWinLen + I))
			Ii = I;
		else
			if (nOd > synWinLen)
				Ii = nOd - synWinLen;
			else {
				Ii = 0;
				for (i=nOd+synWinLen; i<obuflen; i++)
					if (i > 0)
						output[i] = 0.;
			}
	}

	while (outCount <= nMax){
		outCount++;
		putfloat(nextOut++);
		if (nextOut >= (output + obuflen))
			nextOut -= obuflen;
	}

	flushfloat();

	exit(0);

}

usage(exitcode)
	int exitcode;
{
	fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
		"usage: denoise [flags] noise_soundfile < floatsams > floatsams\n",
		"input and output must be files or pipes\n",
		"flags:\n",
		"R = input sample rate (automatically read from stdin)\n",
		"N = # of bandpass filters (size of FFT) (1024)\n",
		"M = analysis window length (N-1)\n",
		"L = synthesis window length (M) \n",
		"D = decimation factor (M/8)\n",
		"b = begin time in noise reference soundfile (0)\n",
		"e = end time in noise reference soundfile (end)\n",
		"d = duration in noise reference soundfile (end - begin)\n",
		"t = threshold above noise reference in dB (30)\n",
		"s = sharpness of noise-gate turnoff (1) (1 to 5)\n",
		"n = number of FFT frames to average over (5)\n",
		"m = minimum gain of noise-gate when off in dB (-40)\n",
		"filename must follow all flags\n"
		);

	exit(exitcode);
}

hamming(win,winLen,even)
	float *win; 
	int winLen, even;
{
	float Pi,ftmp;
	int i;

	Pi = 4.*atan(1.);
	ftmp = Pi/winLen;

if (even) {
	for (i=0; i<winLen; i++)
		*(win+i) = .54 + .46*cos(ftmp*(i+.5));
	*(win+winLen) = 0.;
}

else {
	*(win) = 1.;
	for (i=1; i<=winLen; i++)
		*(win+i) = *(win-i) = .54 + .46*cos(ftmp*i);
}

	return;
}

malerr(str, ex)
	char *str;
	int ex;
{
	fprintf(stderr, "%s\n", str);
	exit(ex);
}

quit() {
	fprintf(stderr, "exiting\n");
	(void) sfallclose(); 
	exit(1); 
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.