/* * Copyright (c) 1983-2013 Composers Desktop Project Ltd * http://www.composersdesktop.com * This file is part of the CDP System. * The CDP System is free software; you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * The CDP System is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with the CDP System; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* delay.c * generalised delay line featuring * feedthrough, feedforward and feedback * A. Bentley, Composers' Desktop Project, Nov 1987. * Revised and extended to stereo and command-line mode * by Richard Dobson Oct 1993. */ static char *rcsid = "$Id: delay.c%v 3.3 1994/10/31 22:30:12 martin Exp $"; /* * $Log: delay.c%v $ * Revision 3.3 1994/10/31 22:30:12 martin * first rcs version * */ #include #include #include #include "sfsys.h" #include "cdplib.h" static char *sfsys_h_rcsid = SFSYS_H_RCSID; static char *cdplib_h_rcsid = CDPLIB_H_RCSID; #define remainder my_remainder #define BUFLEN (4096) #define DELBUFSIZ (176400) /* allows 2sec delay in stereo */ #define ONE_FLT (65536.0) void domono(); void dostereo(); void usage(); void delinit(int cmndline,char *argv[]); void initfbuf(); void initlbuf(); int Funcflag = 0; int Quickflag = 1; long Nchans; long Delaysamps; long bufcnt; long remainder; float *Fdelbuf; long *Ldelbuf; double Sr; double Delay; float Feedforgain; float Feedbckgain; float Delaygain; float Prescale = 1.0F; double Invert =1.0; double Rescale; long Lfeedforgain; long Lfeedbckgain; long Ldelaygain; long Lprescale; double Trailtime = 0.; long Trailsamps = 0; int ifd; /* input file descriptor */ int ofd; /* output file descriptor */ long isr; long sampsize; short *inbuf; short *outbuf; static char version[] = "~CDPVER~GROUCHO: DELAY Portable version 3.0, REVISED BY R. DOBSON OCT 1993."; PROGRAM_NUMBER(0xf93c2705); void main(argc,argv) int argc; char *argv[]; { int cmndline = 0; char *ptr; if(sflinit("delay")) { sfperror("Delay: initialisation"); exit(1); } while(argv[1] != 0 && argv[1][0] == '-') { switch(argv[1][1]) { case 'f': Quickflag--; break; case 'p': ptr = (char *) &(argv[1][2]); if(*ptr==0) { usage(); } Prescale = (float)atof(ptr); if(Prescale==0.0) { printf("\ndelay: -p error, prescale = 0.0!\n"); usage(); } break; case 'i': Invert = -1.0; break; default: usage(); break; } argv++; argc--; } if(argc < 3) usage(); if(argc>4) { if(argc<7) { fprintf(stderr,"\ndelay: incorrect no of parameters"); usage(); } else { cmndline=1; } } if((inbuf = (short *) malloc((BUFLEN) * sizeof(short))) == NULL) { fprintf(stderr,"\ndelay: not enough memory for input buffer"); exit(1); } if((outbuf = (short *) malloc(BUFLEN * sizeof(short))) == NULL) { fprintf(stderr,"\ndelay: not enough memory for output buffer"); exit(1); } if((ifd = sndopen(argv[1])) < 0) { fprintf(stderr,"\ndelay: unable to open input file\n"); exit(1); } if(sndgetprop(ifd,"sample rate",(char *) &isr, sizeof(long)) < 0) { fprintf(stderr,"\ndelay: failed to get sample rate"); exit(1); } if(sndgetprop(ifd,"channels",(char *) &Nchans, sizeof(long)) < 0) { fprintf(stderr,"\ndelay: failed to get channel data"); exit(1); } if(sndgetprop(ifd,"sample type",(char *) &sampsize, sizeof(long)) < 0){ fprintf(stderr,"\ndelay: failed to get sample type"); exit(1); } if(Nchans > 2) { fprintf(stderr,"\ndelay: too many channels! Mono or stereo only.\n"); exit(1); } Sr = (double) isr; delinit(cmndline,argv); if((ofd = sndcreat(argv[2],-1,sampsize)) < 0) { fprintf(stderr,"\ndelay: unable to open output file\n"); exit(1); } if(!Quickflag) initfbuf(); else initlbuf(); stopwatch(1); if(Nchans==1) {domono();} else {dostereo();} stopwatch(0); sndclose(ifd); if(sndputprop(ofd,"sample rate",(char *) &isr, sizeof(long)) < 0){ fprintf(stderr,"\ndelay: failed to write sample rate"); } if(sndputprop(ofd,"channels", (char *) &Nchans, sizeof(long)) < 0){ fprintf(stderr,"\ndelay: failed to write channel data"); } free(inbuf); free(outbuf); if(!Quickflag) {free(Fdelbuf);} else {free(Ldelbuf);} sndclose(ofd); sffinish(); } void domono() { int i; long rdsamps; float input,output; long linput,loutput; long ltemp1,ltemp2; long ipptr=0,opptr=0; do /* while rdsamps */ { if((rdsamps = fgetsbuf(inbuf, BUFLEN, ifd)) < 0) { fprintf(stderr,"\ndelay: failure to read input file\n"); exit(1); } for(i=0;i= Delaysamps) ipptr -= Delaysamps; if(opptr >= Delaysamps) opptr -= Delaysamps; if(ipptr < 0 || opptr < 0) { printf( "\ninternal error, ipptr=%d,opptr=%d\n",ipptr,opptr); } } if(fputsbuf(outbuf,rdsamps,ofd) < rdsamps) { fprintf(stderr, "\ndelay: failure in writing to output file\n"); exit(1); } inform(rdsamps,Sr); } while(rdsamps > 0); /* now do trailer */ rdsamps=BUFLEN; if (Trailsamps>0) { do { /* while bufcnt */ if(!bufcnt) rdsamps = remainder; for(i=0;i= Delaysamps) ipptr -= Delaysamps; if(opptr >= Delaysamps) opptr -= Delaysamps; } if(fputsbuf(outbuf,rdsamps,ofd) < rdsamps){ fprintf(stderr, "\ndelay: failure in writing to output file\n"); exit(1); } inform(BUFLEN,Sr); bufcnt -=1; } while(bufcnt>=0); } /* end if Trailsamps */ } /* end domono */ void dostereo() /* do stereo delay */ { int i; long rdsamps; float Linput,Rinput; float Loutput,Routput; long Llinput,Rlinput; long Lloutput,Rloutput; long Ltemp1,Ltemp2; long Rtemp1,Rtemp2; long Lipptr = 0,Ripptr = 1; long Lopptr = 0,Ropptr = 1; do /* while rdsamps */ { if((rdsamps = fgetsbuf(inbuf,BUFLEN,ifd)) < 0) { fprintf(stderr,"\ndelay: failure to read input file\n"); exit(1); } for(i=0;i<(rdsamps-1);i+=2) { if(!Quickflag) { Linput=inbuf[i]*Prescale; Rinput=inbuf[i+1]*Prescale; Loutput=(Linput*Feedforgain) + (Fdelbuf[Lopptr]*Delaygain); Routput=(Rinput*Feedforgain) + (Fdelbuf[Ropptr]*Delaygain); Fdelbuf[Lipptr] = Fdelbuf[Lopptr]*Feedbckgain; Lopptr+=2; Fdelbuf[Ripptr] = Fdelbuf[Ropptr]*Feedbckgain; Ropptr+=2; Fdelbuf[Lipptr]+=Linput; Fdelbuf[Ripptr]+=Rinput; Lipptr+=2; Ripptr+=2; outbuf[i] = (short) Loutput; outbuf[i+1] = (short) Routput; if(Lipptr >= Delaysamps) Lipptr -= Delaysamps; if(Ripptr >= Delaysamps) Ripptr -= Delaysamps; if(Lopptr >= Delaysamps) Lopptr -= Delaysamps; if(Ropptr >= Delaysamps) Ropptr -= Delaysamps; } else { Llinput = inbuf[i] * Lprescale; Rlinput = inbuf[i+1] * Lprescale; Ltemp1 = Llinput >> 16; Rtemp1 = Rlinput >> 16; Ltemp2 = Ldelbuf[Lopptr] >> 16; Rtemp2 = Ldelbuf[Ropptr] >> 16; Lopptr += 2; Ropptr += 2; Lloutput = (Ltemp1 * Lfeedforgain) + (Ltemp2 * Ldelaygain); Rloutput = (Rtemp1 * Lfeedforgain) + (Rtemp2 * Ldelaygain); Ldelbuf[Lipptr] = Ltemp2 * Lfeedbckgain; Ldelbuf[Ripptr] = Rtemp2 * Lfeedbckgain; Ldelbuf[Lipptr] += Llinput; Ldelbuf[Ripptr] += Rlinput; Lipptr += 2; Ripptr += 2; outbuf[i] = (short)(Lloutput>>16); outbuf[i+1] = (short)(Rloutput>>16); if(Lipptr >= Delaysamps) Lipptr -= Delaysamps; if(Ripptr >= Delaysamps) Ripptr -= Delaysamps; if(Lopptr >= Delaysamps) Lopptr -= Delaysamps; if(Ropptr >= Delaysamps) Ropptr -= Delaysamps; } } if(fputsbuf(outbuf,rdsamps,ofd) < rdsamps) { fprintf(stderr,"\ndelay: failure in writing to output file\n"); exit(1); } inform(rdsamps/2,Sr); }while(rdsamps > 0); /* end do */ /* now do trailer */ rdsamps=BUFLEN; if(Trailsamps>0) { do { if(!bufcnt) rdsamps = remainder; if(!Quickflag) { for(i=0;i<(rdsamps-1);i+=2) { Loutput = Fdelbuf[Lopptr]*Delaygain; Routput = Fdelbuf[Ropptr]*Delaygain; Fdelbuf[Lipptr] = Fdelbuf[Lopptr]*Feedbckgain; Fdelbuf[Ripptr] = Fdelbuf[Ropptr]*Feedbckgain; Lopptr+=2; Ropptr+=2; Lipptr+=2; Ripptr+=2; outbuf[i] = (short) Loutput; outbuf[i+1] = (short) Routput; if(Lipptr >= Delaysamps) Lipptr -= Delaysamps; if(Ripptr >= Delaysamps) Ripptr -= Delaysamps; if(Lopptr >= Delaysamps) Lopptr -= Delaysamps; if(Ropptr >= Delaysamps) Ropptr -= Delaysamps; } } else { for(i=0;i<(rdsamps-1);i+=2) { Ltemp2 = Ldelbuf[Lopptr] >> 16; Lopptr+=2; Rtemp2 = Ldelbuf[Ropptr] >> 16; Ropptr+=2; Lloutput = Ltemp2 * Ldelaygain; Rloutput = Rtemp2 * Ldelaygain; Ldelbuf[Lipptr] = Ltemp2*Lfeedbckgain; Ldelbuf[Ripptr] = Rtemp2*Lfeedbckgain; Lipptr +=2; Ripptr +=2; outbuf[i] = (short)(Lloutput>>16); outbuf[i+1] = (short)(Rloutput>>16); if(Lipptr >= Delaysamps) Lipptr -= Delaysamps; if(Ripptr >= Delaysamps) Ripptr -= Delaysamps; if(Lopptr >= Delaysamps) Lopptr -= Delaysamps; if(Ropptr >= Delaysamps) Ropptr -= Delaysamps; } } if(fputsbuf(outbuf,rdsamps,ofd) < rdsamps) { fprintf(stderr,"\ndelay: failure in writing to output file\n"); exit(1); } inform(rdsamps/2,Sr); bufcnt -=1; } while (bufcnt>=0); } /* end if */ } /* end dostereo */ void usage() { fprintf(stderr,"delay - A Groucho Program: $Revision: 3.3 $\n"); fprintf(stderr, "usage: delay [-f][-pN][-i] infile outfile [4 cmndline parameters]\n"); fprintf(stderr, " where -f sets output in floatsams, -p sets prescale = N for infile,\n"); fprintf(stderr, " and -i inverts dry signal (for allpass filters).\n"); fprintf(stderr,"\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "Cmndline option: after [flags] filenames, enter all parameters in this order:", " delaytime mix feedbackgain trailertime", " 0.0 < delaytime (msecs) <= maxdelay", " 0.0 <= mix <= 1.0", " -1.0 <= feedbackgain <= 1.0", " 0.0 <= trailertime (secs) <= 30.0", "where maxdelay = 8000ms @22050 mono", " = 4000ms @22050 stereo, and 44100 mono", " = 2000ms @44100 stereo"); exit(0); } void delinit(int cmndlin, char *argv[]) { double temp,maxdelay,mix; char msg[80]; /* Some jiggery pokery is needed in setting the * fixed point variables to avoid overloading * the input to the delay line. The input is * divided by two in conjunction with prescaling * and the feedthrough and delay outputs are * raised by two to compensate. */ maxdelay = ((double) (DELBUFSIZ) / Sr) * 1000.0; /* milliseconds */ if(Nchans==2) {maxdelay /= 2;} if(cmndlin) { temp = atof(argv[3]); if (temp > maxdelay) { printf("delay: delay time too long\n"); exit(1); } Delaysamps = (long) ((Sr *temp) / 1000.0); mix = atof(argv[4]); if((mix < 0.0) || (mix > 1.0)) { printf("delay: mix value out of range\n"); exit(1); } Feedbckgain = (float)atof(argv[5]); if((Feedbckgain < -1.0) || (Feedbckgain > 1.0)) { printf("delay: feedbackgain out of range\n"); exit(1); } Trailtime = atof(argv[6]); if((Trailtime < 0.0) || (Trailtime > 1000.0)) { printf("delay: trailtime out of range\n"); exit(1); } } else /* ask for params from terminal */ { printf("max delay in msec = %f\n",maxdelay); sprintf(msg, "Give delay time in milliseconds :"); temp = accexpr(msg,0.0,maxdelay,0.0,0,Sr); if(temp==0.0) { printf("\nzero delay time!!! No point in running program. Bye!\n"); exit(1); } Delaysamps = (long) ((Sr * temp) / 1000.0); printf("\ndelay length in samples = %ld \n",Delaysamps); sprintf(msg, "Give mix proportion (0.0 <= level <= 1.0) :"); mix = accexpr(msg,0.0,1.0,0.0,0,Sr); sprintf(msg, "Give feedback gain (-1.0 <= level <= 1.0) :"); Feedbckgain = (float)accexpr(msg,-1.0,1.0,0.0,0,Sr); sprintf(msg, "Give trailer time (default = 0 <= trailtime <= 30.0) :"); Trailtime = accexpr(msg, 0.0,30.0,0.0,1,Sr); } /* now massage parameters */ /* nb delaysamps is per channel here */ Feedforgain = (float)((1.0 - mix)*Invert); Lfeedbckgain = (long) (Feedbckgain * ONE_FLT); Delaygain = (float)mix; Ldelaygain = (long) (Delaygain * ONE_FLT * 2.0); Rescale = (1.0 / (Feedbckgain + 1.0)); /* i/p compensation */ Prescale *= (float)Rescale; Lprescale= (long) (Prescale * ONE_FLT * .5); Feedforgain /= (float)Rescale; Lfeedforgain = (long) (Feedforgain * ONE_FLT * 2.0); Trailsamps = (long) (Trailtime * Sr); if(Nchans==2) Trailsamps *= 2; bufcnt = Trailsamps/BUFLEN; remainder = Trailsamps % BUFLEN; } void initfbuf() { Delaysamps*=Nchans; if((Fdelbuf = (float *) calloc(Delaysamps+2, sizeof(float))) == NULL){ fprintf(stderr,"\ndelay: not enough memory for main delay buffer"); exit(1); } } void initlbuf() { Delaysamps*=Nchans; if((Ldelbuf = (long *) calloc(Delaysamps+4, sizeof(long))) == NULL){ fprintf(stderr,"\ndelay: not enough memory for main delay buffer"); exit(1); } }