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

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

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

#define NCMAX 262144

extern int sferror;
extern CSNDFILE *rootsfd;

/*-------------------------------------------------------
			convolvesf.c

This program performs fast convolution via the FFT.  In
	contrast to fastflt, the filter impulse response
	is contained in a soundfile as opposed to a filter
	file.  One potential problem is getting sufficient
	memory for the program to run.  NCMAX = 262144
	gives a maximum impulse response of 5.3 seconds
	at 48K, but this requires over 5 Mbytes of memory
	and probably won't run!  NOTE: This program depends
	upon the C versions of the IEEE FFT subroutines
	fast and fsst in order to perform FFT's of greater
	than 32K.

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

main(argc, argv)
	int argc;
	char **argv;
{

	float fsndi();

	float 	*sbuf,		/* array for input and FFT */
		*tbuf,		/* array for overlap-adding output */
		*filt;		/* array for filter transform */

	float	a,		/* temporary */
		b,		/* temporary */
		temp,		/* temporary */
		srate,		/* sample rate from header */
		gain = 1.,	/* gain */
		max = 0.;	/* maximum */

	long	i,
		j,
		icnt,		/* counts input samples */
		ocnt = 0,	/* counts output samples */
		begin = 0,	/* first sample of soundfile */
		end = 0,	/* last sample of soundfile */
		N,		/* FFT size is twice impulse response */
		N2,		/* N / 2 */
		ip,		/* temporary pointer */
		ip1;		/* temporary pointer */

	int	nchan;		/* number of channels from header */

	CSNDFILE *sfd, *opensf();

	char ch;

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

	char *file = "test";

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

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

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

/* Interpret commandline by calling dgl's subroutine crack. */

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

	while ((ch = crack(argc, argv, "b|d|e|g|h", 0)) != NULL) {
		switch (ch) {
			case 'b': cbeg = arg_option; break;
			case 'e': cend = arg_option; break;
			case 'd': cdur = arg_option; break;
			case 'g': gain = sfexpr(arg_option,1.); break;
			case 'h':  usage(0);
			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);
		if ((dbuf=getprop(stdin, H_NCHANS)) != NULL)nchan = atoi(dbuf);
	}


/* Read in soundfile with room response. */

	if (arg_index < argc)
		file = argv[arg_index];
	else fprintf(stderr,"convolvesf: no impulse response\n");

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

	/* calculate times */
	if (cbeg != NULL) 
		begin = 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 + begin;

	/* check boundaries */
	if (begin < 0 || begin >= sfd->fs) {
		fprintf(stderr, "convolvesf: begin time out of range\n");
		quit();
	}
	if (end < 0 || end >= sfd->fs) {
		fprintf(stderr, "convolvesf: end time out of range\n");
		quit();
	}
	if (end == 0)
		end = sfd->fs;
	if ((end - begin) > NCMAX){
		end = begin + NCMAX;
	fprintf(stderr,"convolvesf: warning - impulse response too long\n");
	}

/* Write header to stdout. */

	if (srate != sfd->sr)
	fprintf(stderr,"convolvesf: warning - incompatible sample rates\n");

	if ((nchan > 1) || (sfd->nc > 1))
	fprintf(stderr,"convolvesf: warning - mono expected and not found\n");

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

/* Set up buffers. */

	for (N2 = 2; N2 < NCMAX; N2 *= 2)
		if (N2 >= (end - begin)) break;

	N = 2 * N2;

	if ((sbuf = (float *) calloc(N+2, sizeof(float))) == NULL)
		malerr("convolvesf: insufficient memory", 1);
	if ((tbuf = (float *) calloc(N2, sizeof(float))) == NULL)
		malerr("convolvesf: insufficient memory", 1);
	if ((filt = (float *) calloc(N+2, sizeof(float))) == NULL)
		malerr("convolvesf: insufficient memory", 1);

	/* fill filter buffer */
	for (i = begin, j = 0; i < end; i++, j++) {
		temp = fsndi(sfd, i);
		if (sferror) quit();
		*(filt+j) = temp;
	}

	/* close all open files */
	(void) sfallclose();

/* Finish initialization: fill buffers, take FFT of filt, normalize */

	for (i=j; i<N+2; i++)
		*(filt+i) = 0.;

	cfast(filt,N);

	for (i=0; i<=N2; i++){
		a = *(filt + 2*i);
		b = *(filt + 2*i + 1);
		temp = a*a + b*b;
		if (temp > max)
			max = temp;
	}

	if (max != 0.)
		max = gain/(sqrt(max));
	else {
		fprintf(stderr,"convolvesf: impulse response is all zeros\n");
		exit(1);
	}

	for (i=0; i< N+2; i++)
		*(filt + i) *= max;

	for (i=0; i<N2; i++)
		if (getfloat(sbuf+i) <= 0)
			break;

	icnt = i;

	for (; i<N+2; i++)
		*(sbuf+i) = 0.;

	if (icnt == 0){
		fprintf(stderr,"convolvesf: no valid input on stdin\n");
		exit(1);
	}

/* Main loop:  Take N-point FFT of next N/2 input samples and multiply
	by FFT of filter impulse response.  Inverse FFT and add first N/2
	resulting samples to last N/2 samples of previous FFT.  */

	while(ocnt < icnt){

		cfast(sbuf,N);

		for (i=0; i<=N2; i++){
			ip = 2*i;
			ip1 = ip + 1;
			a = *(sbuf+ip)* *(filt+ip) - *(sbuf+ip1)* *(filt+ip1);
			b = *(sbuf+ip)* *(filt+ip1) + *(sbuf+ip1)* *(filt+ip);
			*(sbuf+ip) = a;
			*(sbuf+ip1) = b;
		}

		cfsst(sbuf,N);

		for (i=0; i<N2; i++)
			*(tbuf+i) += *(sbuf+i);

		for (i=0; i<N2; i++)
			if (++ocnt <= icnt)
				putfloat(tbuf+i);

		for (i=0; i<N2; i++)
			*(tbuf+i) = *(sbuf+N2+i);

		for (i=0; i<N2; i++)
			if (getfloat(sbuf+i) <= 0)
				break;

		icnt += i;

		for (; i<N+2; i++)
			*(sbuf+i) = 0.;

	}

	flushfloat();

	exit(0);
}

usage(exitcode)
	int exitcode;
{
	fprintf(stderr,"%s%s%s%s%s%s",
	"\nusage: convolvesf [flags] impulse_response_soundfile < floatsams > floatsams\n",
	"\nflags:\n",
	"\tg = gain factor (1.)\n",
	"\tb = begin time in impulse response (first sample to use) (0.)\n",
	"\te = end time in impulse response (last sample to use) (end)\n",
	"\td = duration of impulse response (end - begin)\n\n");
	exit(exitcode);
}

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

malerr(str, ex)
	char *str;
	int ex;
{
	fprintf(stderr, "%s\n", str);
	fprintf(stderr,"\nconvolvesf: impulse response too long\n");
	exit(ex);
}

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