Browse Source

initial commit

richarddobson 3 years ago
parent
commit
32513577ea
80 changed files with 31724 additions and 0 deletions
  1. 8 0
      dev/externals/CMakeLists.txt
  2. 21 0
      dev/externals/fastconv/CMakeLists.txt
  3. 768 0
      dev/externals/fastconv/fconv-fftw.cpp
  4. 752 0
      dev/externals/fastconv/fconv.cpp
  5. 235 0
      dev/externals/fastconv/genrespframe2.cpp
  6. 883 0
      dev/externals/fastconv/mxfftd.c
  7. 110 0
      dev/externals/fastconv/readme.txt
  8. 258 0
      dev/externals/include/portsf.h
  9. 81 0
      dev/externals/mctools/CMakeLists.txt
  10. 189 0
      dev/externals/mctools/abfdcode.cpp
  11. 180 0
      dev/externals/mctools/abfdcode2.cpp
  12. 276 0
      dev/externals/mctools/abfpan.cpp
  13. 296 0
      dev/externals/mctools/abfpan2.cpp
  14. 302 0
      dev/externals/mctools/channel.c
  15. 297 0
      dev/externals/mctools/chorder.c
  16. 760 0
      dev/externals/mctools/chxformat.c
  17. 604 0
      dev/externals/mctools/copysf.c
  18. 400 0
      dev/externals/mctools/fmdcode.c
  19. 106 0
      dev/externals/mctools/fmdcode.h
  20. 32 0
      dev/externals/mctools/fmhcube1.txt
  21. 712 0
      dev/externals/mctools/fmhfuncs.c
  22. 504 0
      dev/externals/mctools/interlx.c
  23. 698 0
      dev/externals/mctools/njoin.c
  24. 333 0
      dev/externals/mctools/nmix.c
  25. 320 0
      dev/externals/mctools/rmsinfo.cpp
  26. 378 0
      dev/externals/mctools/sfprops.c
  27. 5 0
      dev/externals/paprogs/CMakeLists.txt
  28. BIN
      dev/externals/paprogs/dx9mgw.zip
  29. 26 0
      dev/externals/paprogs/listaudevs/CMakeLists.txt
  30. 69 0
      dev/externals/paprogs/listaudevs/devs.c
  31. 32 0
      dev/externals/paprogs/listaudevs/orig_Makefile
  32. BIN
      dev/externals/paprogs/pa_stable_v19_20140130.tgz
  33. 37 0
      dev/externals/paprogs/palinuxbuild.txt
  34. 44 0
      dev/externals/paprogs/pamacbuild.txt
  35. 74 0
      dev/externals/paprogs/pamingwbuild.txt
  36. 30 0
      dev/externals/paprogs/paplay/CMakeLists.txt
  37. 106 0
      dev/externals/paprogs/paplay/fmdcode.h
  38. 712 0
      dev/externals/paprogs/paplay/fmhfuncs.c
  39. 1593 0
      dev/externals/paprogs/paplay/paplay.c
  40. 248 0
      dev/externals/paprogs/pvplay/.gdb_history
  41. 30 0
      dev/externals/paprogs/pvplay/CMakeLists.txt
  42. BIN
      dev/externals/paprogs/pvplay/Toolkit.pdf
  43. 106 0
      dev/externals/paprogs/pvplay/fmdcode.h
  44. 667 0
      dev/externals/paprogs/pvplay/fmhfuncs.c
  45. 831 0
      dev/externals/paprogs/pvplay/mxfft.c
  46. 43 0
      dev/externals/paprogs/pvplay/pvdefs.h
  47. 1300 0
      dev/externals/paprogs/pvplay/pvfileio.c
  48. 131 0
      dev/externals/paprogs/pvplay/pvfileio.h
  49. 43 0
      dev/externals/paprogs/pvplay/pvoc.h
  50. 1541 0
      dev/externals/paprogs/pvplay/pvoc2.cpp
  51. 1435 0
      dev/externals/paprogs/pvplay/pvplay.cpp
  52. 233 0
      dev/externals/paprogs/pvplay/pvplay.h
  53. 247 0
      dev/externals/paprogs/pvplay/pvpp.h
  54. 487 0
      dev/externals/paprogs/pvplay/pvthreads.cpp
  55. 27 0
      dev/externals/paprogs/recsf/CMakeLists.txt
  56. 974 0
      dev/externals/paprogs/recsf/recsf.c
  57. 14 0
      dev/externals/portsf/CMakeLists.txt
  58. 149 0
      dev/externals/portsf/ieee80.c
  59. 17 0
      dev/externals/portsf/ieee80.h
  60. 3573 0
      dev/externals/portsf/portsf.c
  61. 31 0
      dev/externals/reverb/CMakeLists.txt
  62. 7 0
      dev/externals/reverb/LARGERM.TXT
  63. 7 0
      dev/externals/reverb/MEDIUMRM.TXT
  64. 7 0
      dev/externals/reverb/POS.TXT
  65. 252 0
      dev/externals/reverb/allpass.c
  66. 546 0
      dev/externals/reverb/delay.c
  67. 62 0
      dev/externals/reverb/diffuse.h
  68. 169 0
      dev/externals/reverb/lpcomb.cpp
  69. 121 0
      dev/externals/reverb/readme.txt
  70. 189 0
      dev/externals/reverb/reflect.cpp
  71. 33 0
      dev/externals/reverb/reflect.h
  72. 745 0
      dev/externals/reverb/reverb.cpp
  73. 1488 0
      dev/externals/reverb/reverberator.cpp
  74. 360 0
      dev/externals/reverb/reverberator.h
  75. 949 0
      dev/externals/reverb/rmverb.cpp
  76. 713 0
      dev/externals/reverb/roomresp.cpp
  77. 95 0
      dev/externals/reverb/smalldiff.cpp
  78. 688 0
      dev/externals/reverb/tdelaymain.cpp
  79. 809 0
      dev/externals/reverb/wavetable.cpp
  80. 126 0
      dev/externals/reverb/wavetable.h

+ 8 - 0
dev/externals/CMakeLists.txt

@@ -0,0 +1,8 @@
+add_subdirectory(portsf)
+add_subdirectory(fastconv)
+add_subdirectory(reverb)
+add_subdirectory(mctools)
+
+##if(USE_LOCAL_PORTAUDIO)
+  add_subdirectory(paprogs)
+##endif()

+ 21 - 0
dev/externals/fastconv/CMakeLists.txt

@@ -0,0 +1,21 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.8 -Dunix -fomit-frame-pointer -funroll-loops")
+  set(CMAKE_CXX_FLAGS "-O2 -Wall -mmacosx-version-min=10.8 -Dunix -fomit-frame-pointer -funroll-loops  -std=c++11 -stdlib=libc++")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -fomit-frame-pointer  -funroll-loops")
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+  endif()
+endif()
+
+link_directories(../lib)
+
+include_directories(../include)
+
+add_executable(fconv fconv.cpp genrespframe2.cpp mxfftd.c)
+
+target_link_libraries(fconv portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(fconv)
+

+ 768 - 0
dev/externals/fastconv/fconv-fftw.cpp

@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+/* fastconv.cpp: */
+/*  self-contained prog for fast convolution (reverb, FIR filtering, bformat etc) 
+ *  
+*/
+
+/* um, there is nothing C++ish about this code apart from use of new, delete! */
+
+/*TODO: control wet/dry mix with something... */
+/* auto-rescale arbitrary impulse responses? */
+/* Feb 2013: rebuilt with updated portsf */
+/* August 2013 epanded usage message re text file. */
+extern "C"
+{
+
+#include <portsf.h>
+
+}
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+
+
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+
+#ifdef unix
+#include <ctype.h>
+int stricmp(const char *a, const char *b);
+int strnicmp(const char *a, const char *b, const int length);
+#endif
+
+
+#include <rfftw.h>
+
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT (0x0003)
+#endif
+
+//#define FFTTEST
+
+/* convert from mono text impulse file */
+long genimpframe1(double *insbuf, double*** outbuf, long npoints, double scalefac);
+/* convert from multichan soundfile */
+long genimpframe2(int ifd,double*** outframe, double* rms,long imchans,double scalefac);
+void mc_split(double* inbuf,double** out,long insize,long chans);
+void mc_interl(double** in,double* out,long insize,long chans);
+void complexmult(double *frame,const double *impulse,long length);
+
+#ifdef FFTTEST
+extern "C"{
+void fft_(double *, double *,int,int,int,int);
+void fftmx(double *,double *,int,int,int,int,int,int *,double *,double *,double *,double *,int *,int[]);
+void reals_(double *,double *,int,int);
+}
+#endif
+
+#define DEFAULT_BUFLEN (32768)
+
+enum {ARG_NAME,ARG_INFILE,ARG_IMPFILE,ARG_OUTFILE,ARG_NARGS};
+
+void usage(const char *progname)
+{
+
+    printf("\n\n%s v1.2 RWD,CDP July 2010,2013",progname);
+	printf("\nMulti-channel FFT-based convolver");
+
+	printf("\nUsage: \n    %s [-aX][-f] infile impulsefile outfile [dry]\n"		               
+           "   -aX        : scale output amplitude by X\n"
+           "   -f         : write output as floats (no clipping)\n"
+           "  infile      : input soundfile to be processed.\n"
+           "  impulsefile : m/c soundfile or mono text file containing impulse response,\n"
+           "                  e.g. reverb or FIR filter.\n"
+           "                Text file name must have extension .txt.\n"
+		   "                  File must contain 1 column of floating point values,\n"
+		   "                  typically in the range -1.0 to 1.0.\n"
+           "              Supported channel combinations:\n"
+           "               (a) mono infile, N-channel impulsefile;\n"
+           "               (b) channels are the same;\n"
+           "               (c) mono impulsefile, N-channel infile.\n"
+           "  [dry]       :  set dry/wet mix (e.g. for reverb)\n"
+           "                 Range: 0.0 - 1.0,  default = 0.0\n"
+           "                 (uses sin/cos law for constant power mix)\n"
+           "Note: some recorded reverb impulses effectively include the direct signal.\n"
+           "In such cases  <dry>  need not be used\n"
+           "Where impulsefile is filter response (FIR), optimum length is power-of-two - 1.\n",progname
+		   );		   
+}
+
+
+double
+timer()
+{
+	struct timeb now;
+	double secs, ticks;	
+	ftime(&now);
+	ticks = (double)now.millitm/(1000.0);
+	secs = (double) now.time;
+
+	return secs + ticks;
+}
+
+
+void			
+stopwatch(int flag) 
+{
+	static double start;		    
+	long mins=0,hours=0;
+	double end, secs=0;
+
+	if(flag)
+		start = timer();	
+	else 
+	{
+		end    = timer(); 
+		secs   = end - start;
+		mins   = (long)(secs/60.0);
+		secs  -= mins*60.0; 
+		hours  = mins/60;
+		mins  -= hours*60;
+
+		fprintf(stderr,"\nElapsed time: ");
+		if(hours > 0)
+			fprintf(stderr,"%ld h ", hours);
+		if(mins > 0)
+			fprintf(stderr,"%ld m ", mins);
+		fprintf(stderr,"%2.3lf s\n\n",secs);
+	}
+}
+
+/* how do we want this to work?
+
+  (1)  multi-chan impulse:  EITHER: mono infile  OR infile with same chan count
+  (2)  mono impulse:  expanded to infile chancount
+     Therefore: need to open both infiles before deciding action
+*/
+#define PI2 (1.570796327)
+
+int main(int argc,char **argv)
+{
+
+	long fftlen = 1,imlen = 0,chans = 0,srate;
+	long buflen = 0;
+	double scalefac = 1.0f;
+	double ampfac = 1.0;
+    double Ninv = 1.0;
+	int insndfile = -1,inimpfile = -1,outsndfile = -1; 
+    int dorms = 0;
+    int nameoffset = 0;
+	PSF_PROPS inprops,outprops, impulseprops;
+    psf_format format;
+    PSF_CHPEAK  *fpeaks = NULL;
+	MYLONG peaktime;
+	int i,jj,minheader = 0,do_timer = 1;
+    int writefloats = 0;
+    long framesneeded;
+	double oneovrsr;
+    double *inmonobuf = 0;
+	double *insbuf=0,*outsbuf = 0;
+    double **inbuf = 0, **outbuf = 0;    
+	double **imbuf = 0;
+	double **overlapbuf = 0;
+    double rms = 0.0;
+    double dry = 0.0;
+    double wet = 1.0;
+#ifdef FFTTEST
+    double *anal = 0;
+#endif
+    rfftwnd_plan* forward_plan = 0;
+    rfftwnd_plan* inverse_plan = 0;
+	
+    if(argv[0][0]=='.' && argv[0][1]=='/'){
+        nameoffset  = 2;
+    }
+	
+    if(argc==2){
+        if(strcmp(argv[1],"--version")==0){
+            printf("1.2.0.\n");
+            return 0;
+        }
+    }
+
+	if(psf_init()){
+		puts("unable to start portsf\n");
+		return 1;
+	}
+
+
+	if(argc < ARG_NARGS){		 
+		usage(argv[0]+nameoffset);
+		return(1);
+	}
+
+    
+
+    while(argc > 1 && argv[1][0]=='-'){				
+		switch(argv[1][1]){
+        case 'a':
+            scalefac =  atof(&(argv[1][2]));
+            if(scalefac <=0.0){
+                printf("Error: scalefac must be positive!\n");
+                return 1;
+            }
+            break;        
+        case 'f':
+            writefloats = 1;
+            break;
+        default:
+            break;
+        }
+        argv++;
+        argc--;
+    }		
+	/* 2 legal possibilities: infile and outfile, or -I used with infile only */
+	if(argc< ARG_NARGS){
+		fprintf(stderr,"\nInsufficient arguments.");
+		usage(argv[0]+nameoffset);
+		return(1);
+	}
+    if(argc==ARG_NARGS+1){
+        double dryarg;
+        dryarg = atof(argv[ARG_NARGS]);
+        if(dryarg < 0.0 || dryarg > 1.0){
+            printf("dry value out of range.\n");
+            return 0;
+        }
+        if(dryarg==1.0){
+            printf("Warning: dry=1 copies input!\n");
+            wet = 0.0;
+        }
+        else{
+            dry = sin(PI2 * dryarg);
+            wet = cos(PI2 * dryarg);
+        }
+    }
+
+		
+
+/* TODO:  where -F[file] is combined with -A, it signifies create analysis file
+           compatible with impulse file (e.g 50% overlap, etc) */
+
+	
+	/* open infile, check props */	
+		
+	if((insndfile = psf_sndOpen(argv[ARG_INFILE],&inprops, 0)) < 0){
+		fprintf(stderr,"\nUnable to open input soundfile %s",argv[1]);
+        delete []imbuf;
+		return 1;
+	}
+    srate = inprops.srate;
+	if(srate <=0){
+		fprintf(stderr,"\nBad srate found: corrupted file?\n");
+        delete []imbuf;
+		return 1;
+	}
+	chans = inprops.chans;
+    framesneeded = psf_sndSize(insndfile);
+    if(framesneeded <= 0){
+        printf("Error in input file - no data!\n");
+        psf_sndClose(insndfile);
+        return 1;
+    }
+    /* open impulse file */
+    /* check for soundfile */
+      
+    format = psf_getFormatExt(argv[ARG_IMPFILE]);
+    if(format==PSF_FMT_UNKNOWN){  /* must be a text file */
+        FILE *fp = 0;
+        char tmp[80];
+        char* dot;
+        int size = 0,got = 0;
+        int read = 0;
+
+        dot = strrchr(argv[ARG_IMPFILE],'.');
+        if(dot==NULL || stricmp(dot,".txt") != 0){
+            fprintf(stderr,"Error: impulse text file must have .txt extension.\n");
+            return 1;
+        }
+        fp = fopen(argv[ARG_IMPFILE],"r");
+        if(fp==0){
+            printf("Cannot open impulse text file %s\n.",argv[ARG_IMPFILE]);
+            return 1;
+        }
+        /* count lines! */
+        while(fgets(tmp,80,fp) != NULL)
+            size++;
+        if(size==0){
+            printf("Impulse textfile %s has no data!.\n",argv[ARG_IMPFILE]);
+            return 1;
+        }
+        rewind(fp);
+            
+        inmonobuf = new double[size+1];
+        for(i=0;i < size;i++)  {
+            read = fscanf(fp,"%lf",&inmonobuf[i]);
+            if(read==0){
+                i--;
+                continue;
+            }
+            if(read==EOF)
+                break;
+            got++;
+        }
+        imlen = got;
+        impulseprops.chans = 1;
+        fclose(fp);
+    }
+    else{
+        if((inimpfile = psf_sndOpen(argv[ARG_IMPFILE],&impulseprops, 0))< 0){
+            fprintf(stderr,"\nUnable to open impulse file %s",argv[ARG_IMPFILE]);       
+            return 0;
+        }
+        //printf("impulse file sr = %d\n",impulseprops.srate);
+        if(srate != impulseprops.srate){
+            printf("Error: files must have same sample rate");
+            delete []imbuf;
+            return 1;
+        }
+        long  len = psf_sndSize(inimpfile);
+        if(len <= 0){
+            printf("Error in impulse soundfile - no data!\n");
+            return 1;
+        }
+        if(impulseprops.chans > 1){
+            if(! (chans == 1 || chans == impulseprops.chans)){
+                fprintf(stderr,"\nChannel mismatch.\n    Infile must be mono or have same channels as irfile.\n");
+                return(1);
+            }
+            chans = impulseprops.chans;
+        }    
+    }
+
+    imbuf = new double*[chans];
+
+    /* if impulse file is mono, we will need to copy data to the other buffers */
+    // allocate  impulseprops.chans buffers ; may need more
+
+    if(inimpfile >=0){
+	    if((imlen = genimpframe2(inimpfile,&imbuf,&rms,impulseprops.chans, scalefac)) == 0){
+	    	fprintf(stderr,"Error reading impulse file\n");
+	    	return(1);
+	    }
+    }
+    
+    else if(imlen){        
+        genimpframe1(inmonobuf,&imbuf,imlen,scalefac);       
+    }
+    printf("mean rms level = %.4lf (%.2lf dB)\n",rms, 20.0 * log10(rms));
+    
+
+    framesneeded +=  imlen;        /* can  close outfile, when this length reached */
+
+    while(fftlen < imlen*2)			/* convolution style - double-length */
+		fftlen <<= 1;
+    double norm = sqrt(2.0);       /* relative: rms of 0dBFS sine is 0.707 = 1/root2 */
+    Ninv = 1.0 / sqrt(imlen*2);
+    Ninv *= norm;
+    Ninv /= imlen;
+    // take simple avg of  rms for adjustment factor.
+    // may not adequately represent some responses, e.g. with strong attack/earlies, soft reverb tail */
+    double rmsdif2 =  (-20.0 * log10(rms)) * 0.5;
+    double rmsadjust = pow(10, rmsdif2/20.0);
+#ifdef _DEBUG
+    printf("rescaling factor = %.6e\n",Ninv);   
+    printf("rmsadjust = %.4lf\n",rmsadjust);
+#endif
+   Ninv /= rmsadjust; 
+
+
+    /* copy buffers if required */
+    for(i = impulseprops.chans;i < chans;i++){
+        imbuf[i] = new double[fftlen+2];
+        memcpy(imbuf[i],imbuf[0],(fftlen+2)* sizeof(double));
+    }         
+    oneovrsr = 1.0 / (double) srate;
+    
+    /*make sure buflen is at least fftlen */
+//	if(fftlen > buflen)
+//		buflen = fftlen;
+
+    
+	/* main i/o buffers */
+	
+	insbuf     = new double [fftlen  * chans];
+    outsbuf    = new double [fftlen  * chans];
+	inbuf      = new double*[chans];
+    outbuf     = new double*[chans];  /* overlap-add buffers */
+    overlapbuf = new double*[chans];
+    
+    for(i=0;i < chans;i++){
+        inbuf[i]  = new double[fftlen+2];   // main in-place buf 
+        outbuf[i] = new double[fftlen+2];        
+	    /* channel buffers */	
+        overlapbuf[i] = new double[fftlen/2];
+        memset(overlapbuf[i],0,sizeof(double)*(fftlen/2));        
+    }
+#ifdef FFTTEST
+    anal = new double[fftlen+2];
+#endif
+    
+    
+    forward_plan = new rfftwnd_plan[chans];
+    inverse_plan = new rfftwnd_plan[chans];
+    int insize = (int) fftlen;
+    for(i=0;i < chans;i++){		    
+	    memset(inbuf[i], 0, sizeof(double) * (fftlen+2));
+        memset(outbuf[i],0,sizeof(double) * (fftlen+2));
+        
+        forward_plan[i] = rfftwnd_create_plan_specific(1,&insize, 
+		FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE | FFTW_IN_PLACE,
+		inbuf[i],1,NULL,1);
+        inverse_plan[i] = rfftwnd_create_plan_specific(1,&insize, 
+		FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE | FFTW_IN_PLACE,
+		inbuf[i],1,NULL,1);
+    }
+
+    
+	/* use generic init function */
+/*bool phasevocoder::init(long outsrate,long fftlen,long winlen,long decfac,float scalefac,
+						pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode)
+*/
+	
+	/*create output sfile */
+	psf_stype smptype;
+	psf_format outformat;
+	/* will it be aiff, wav, etc? */
+	outformat = psf_getFormatExt(argv[ARG_OUTFILE]);
+	if(outformat==PSF_FMT_UNKNOWN){
+		fprintf(stderr,"\nOutfile name has unrecognized extension.");
+        delete []imbuf;
+		return(1);
+	}
+		
+		
+	smptype = inprops.samptype;
+    if(writefloats)
+        smptype= PSF_SAMP_IEEE_FLOAT;
+
+	/*the one gotcha: if output is floats and format is aiff, change to aifc */
+	if(smptype==PSF_SAMP_IEEE_FLOAT && outformat==PSF_AIFF){
+		fprintf(stderr,"Warning: AIFF output written as AIFC for float samples\n");
+		outformat = PSF_AIFC;
+	}
+
+	outprops          = inprops;
+	outprops.chans    = chans;
+	outprops.srate    = srate;
+	outprops.format   = outformat;
+	outprops.samptype = smptype;
+	outprops.chformat = STDWAVE;
+    /* if irfile is BFormat, need to set outfile fmt likewise */
+    if(impulseprops.chformat==MC_BFMT)
+        outprops.chformat = MC_BFMT;
+    /* I suppose people will want automatic decoding too...grrr */
+
+    fpeaks = new PSF_CHPEAK[chans];
+	if(fpeaks==NULL){
+		puts("no memory for fpeak data buffer\n");
+		return 1;
+	}
+
+	/*last arg: no clipping of f/p samples: we use PEAK chunk */
+	if((outsndfile = psf_sndCreate(argv[ARG_OUTFILE],&outprops,0,minheader,PSF_CREATE_RDWR)) <0 ){
+		fprintf(stderr,"\nUnable to open outfile %s",argv[ARG_OUTFILE]);
+        delete []imbuf;
+		return(1);
+	}
+				
+
+	
+	printf("\n");
+	
+	
+	stopwatch(1);
+
+	long written,thisblock,framesread;
+    long frameswritten = 0;
+	double intime= 0.0,outtime = 0.0;
+    int last = 0;
+				
+	while((framesread = psf_sndReadDoubleFrames(insndfile,insbuf,imlen)) > 0){        
+		written = thisblock =  0;
+
+		for(i = framesread * inprops.chans; i< fftlen * inprops.chans; i++)
+			insbuf[i] = 0.0f;
+        framesread = imlen;
+		memset(inbuf[0],0,(fftlen+2) * sizeof(double));
+        if(chans == inprops.chans)  {
+            /* must clean buffers! */
+		    for(i=0;i < chans;i++)
+                memset(inbuf[i],0,(fftlen+2) * sizeof(double));
+            mc_split(insbuf,inbuf,imlen * chans, chans);
+
+        }
+        else{
+            for(i=0;i < chans;i++) {
+                memset(inbuf[i],0,(fftlen+2) * sizeof(double));
+                memcpy(inbuf[i],insbuf,imlen * sizeof(double));
+                memset(outbuf[i],0,sizeof(double) * (fftlen+2));
+
+            }
+        }
+        if(impulseprops.chans==1){
+            for(jj = 0; jj < chans;jj++){
+#ifdef FFTTEST
+                int zz;
+                memcpy(anal,inbuf[jj],(fftlen+2) * sizeof(double));
+                fft_(anal,anal+1,1,fftlen/2,1,-2);
+	            reals_(anal,anal+1,fftlen/2,-2);
+                for(zz=0;zz < fftlen+2;zz++)
+                    anal[zz] *= 0.001;
+#endif
+                rfftwnd_one_real_to_complex(forward_plan[jj],inbuf[jj],NULL);			    				
+                complexmult(inbuf[jj],imbuf[0],fftlen+2);                
+			    rfftwnd_one_complex_to_real(inverse_plan[jj],(fftw_complex * )inbuf[jj],NULL);				
+            }           
+        }
+        else{
+            for(jj = 0; jj < chans;jj++){
+				rfftwnd_one_real_to_complex(forward_plan[jj],inbuf[jj],NULL);                   				
+                complexmult(inbuf[jj],imbuf[jj],fftlen+2);                
+				rfftwnd_one_complex_to_real(inverse_plan[jj],(fftw_complex * )inbuf[jj],NULL);    				    			    
+            }            
+        }
+        
+        /* overlap-add  for each channel */
+        /* TODO: verify  use of imlen here - should it be fftlen/2 -1 ? */
+        for(jj=0;jj < chans;jj++){
+            for(i=0;i < imlen;i++) {
+                outbuf[jj][i] = inbuf[jj][i] + overlapbuf[jj][i];
+                overlapbuf[jj][i] = inbuf[jj][i+imlen];
+            }
+        }
+		mc_interl(outbuf,outsbuf,imlen,chans);
+        if(inprops.chans == chans){
+            for(i=0;i < framesread; i++) {
+                for(jj=0;jj < chans;jj++){
+                    long  outindex = i*chans + jj;            
+                    outsbuf[outindex] *= Ninv;
+                    outsbuf[outindex] *= wet;
+                    outsbuf[outindex] += dry * insbuf[outindex];
+                }
+            }
+        }
+        /* elso mono input */
+        else {
+            for(i=0;i < framesread; i++) {
+                for(jj=0;jj < chans;jj++){
+                    long  outindex = i*chans + jj;
+                    double inval = dry *  insbuf[i];
+                    outsbuf[outindex] *= Ninv;
+                    outsbuf[outindex] *= wet;
+                    outsbuf[outindex] += inval;
+                }
+            }
+        }
+
+        if((written = psf_sndWriteDoubleFrames(outsndfile,outsbuf,framesread)) != framesread){
+		    fprintf(stderr,"\nerror writing to outfile");
+		    return(1);		               
+        }
+        frameswritten += written;
+
+		if(do_timer){
+			intime += (double)framesread * oneovrsr;
+			printf("Input time: %.2lf\r",intime);
+		}    
+    }
+    /* complete tail */
+
+    if(frameswritten < framesneeded){
+        // TODO: imlen? see above
+        mc_interl(overlapbuf,outsbuf,imlen,chans);
+        long towrite = framesneeded - frameswritten; 
+        for(i=0;i < towrite * chans; i++) {
+            outsbuf[i] *= Ninv;
+            outsbuf[i] *= wet;
+        }
+        if((written = psf_sndWriteDoubleFrames(outsndfile,outsbuf,towrite)) != towrite){
+	        fprintf(stderr,"\nerror writing to outfile");
+	        return(1);
+	    }
+    }
+
+	stopwatch(0);
+    if(psf_sndReadPeaks( outsndfile,fpeaks,&peaktime)){
+        double peakmax = 0.0;
+		printf("PEAK values:\n");
+		for(i=0; i < chans; i++)	{
+            peakmax = fpeaks[i].val > peakmax ? fpeaks[i].val : peakmax;
+			if(fpeaks[i].val < 0.0001f)
+				printf("CH %d:\t%e (%.2lf dB)\tat frame %10lu :\t%.4lf secs\n",i+1,
+				fpeaks[i].val,20.0*log10(fpeaks[i].val),fpeaks[i].pos,(double) (fpeaks[i].pos) / (double)srate);
+			else
+				printf("CH %d:\t%.4lf (%.2lf dB)\tat frame %10lu :\t%.4lf secs\n",i+1,
+				fpeaks[i].val,20.0 * log10(fpeaks[i].val),fpeaks[i].pos,(double) (fpeaks[i].pos) / (double)srate);		
+		}
+        if(peakmax > 1.0)
+            printf("Overflows!  Rescale by %.10lf\n",1.0 / peakmax);
+	}
+
+	if(insndfile >=0)
+		psf_sndClose(insndfile);
+	if(outsndfile >=0)
+		psf_sndClose(outsndfile);
+    if(inimpfile >=0)
+	    psf_sndClose(inimpfile);
+	psf_finish();
+    delete [] fpeaks;
+	if(insbuf)
+		delete [] insbuf;
+	if(outsbuf)
+		delete [] outsbuf;
+    
+
+    for(i=0;i < chans;i++){
+        if(inbuf){       
+		    delete [] inbuf[i];
+        }           
+        if(outbuf){
+		    delete [] outbuf[i];
+        }       	
+        if(imbuf){        
+	        delete [] imbuf[i];
+        }
+        if(overlapbuf)
+            delete [] overlapbuf[i];
+        if(forward_plan)
+            rfftwnd_destroy_plan(forward_plan[i]);
+        if(inverse_plan)
+            rfftwnd_destroy_plan(inverse_plan[i]);
+    }
+    delete [] outbuf;
+    delete [] inbuf;
+    delete [] imbuf;
+    delete [] overlapbuf;
+	return 0;
+}
+
+
+// insize is raw samplecount,buflen is insize/chans
+void mc_split(double* inbuf,double** out,long insize,long chans)
+{
+    long i,j,buflen = insize/chans;
+    double* pinbuf;
+
+    
+    for(j=0;j < chans;j++){
+        pinbuf = inbuf+j;
+        for(i=0;i < buflen;i++){
+            out[j][i] = *pinbuf;
+            pinbuf += chans;
+        }
+    }
+}
+
+
+/* insize is m/c frame count */
+void mc_interl(double** in,double* out,long insize,long chans)
+{
+    long i,j;
+    double* poutbuf;
+
+    for(j = 0;j < chans;j++){
+        poutbuf = out+j;
+        for(i=0;i < insize;i++){
+            *poutbuf = in[j][i];
+            poutbuf += chans;
+        }
+    }
+}
+
+/* OR:  apply scalefac to impulse responses */
+void complexmult(double *frame,const double *impulse,long length)
+{
+	double re,im;
+	
+	int i,j;
+	
+
+	for(i=0,j = 1;i < length;i+=2,j+=2){
+		re = frame[i] * impulse[i] - frame[j] * impulse[j];
+		im = frame[i] * impulse[j] + frame[j]* impulse[i];
+		frame[i] = re;
+		frame[j] = im;
+	}
+}
+
+#ifdef unix
+int stricmp(const char *a, const char *b)
+{
+	while(*a != '\0' && *b != '\0') {
+		int ca = islower(*a) ? toupper(*a) : *a;
+		int cb = islower(*b) ? toupper(*b) : *b;
+        
+		if(ca < cb)
+			return -1;
+		if(ca > cb)
+			return 1;
+        
+		a++;
+		b++;
+	}
+	if(*a == '\0' && *b == '\0')
+		return 0;
+	if(*a != '\0')
+		return 1;
+	return -1;
+}
+
+int
+strnicmp(const char *a, const char *b, const int length)
+{
+	int len = length;
+    
+	while(*a != '\0' && *b != '\0') {
+		int ca = islower(*a) ? toupper(*a) : *a;
+		int cb = islower(*b) ? toupper(*b) : *b;
+        
+		if(len-- < 1)
+			return 0;
+        
+		if(ca < cb)
+			return -1;
+		if(ca > cb)
+			return 1;
+        
+		a++;
+		b++;
+	}
+	if(*a == '\0' && *b == '\0')
+		return 0;
+	if(*a != '\0')
+		return 1;
+	return -1;
+}
+#endif
+
+
+
+
+
+

+ 752 - 0
dev/externals/fastconv/fconv.cpp

@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+/* fastconv.cpp: */
+/*  self-contained prog for fast convolution (reverb, FIR filtering, bformat etc) 
+ *  
+*/
+
+/* um, there is nothing C++ish about this code apart from use of new, delete! */
+
+/*TODO: control wet/dry mix with something... */
+/* auto-rescale arbitrary impulse responses? */
+/* Feb 2013: rebuilt with updated portsf */
+/* August 2013 epanded usage message re text file. */
+extern "C"
+{
+
+#include <portsf.h>
+
+}
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+
+
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+
+#ifdef unix
+#include <ctype.h>
+int stricmp(const char *a, const char *b);
+int strnicmp(const char *a, const char *b, const int length);
+#endif
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT (0x0003)
+#endif
+
+/* convert from mono text impulse file */
+int genimpframe1(double *insbuf, double*** outbuf, int npoints, double scalefac);
+/* convert from multichan soundfile */
+int genimpframe2(int ifd,double*** outframe, double* rms,int imchans,double scalefac);
+void mc_split(double* inbuf,double** out,int insize,int chans);
+void mc_interl(double** in,double* out,int insize,int chans);
+void complexmult(double *frame,const double *impulse,int length);
+
+
+extern "C"{
+void fft_(double *, double *,int,int,int,int);
+void fftmx(double *,double *,int,int,int,int,int,int *,double *,double *,double *,double *,int *,int[]);
+void reals_(double *,double *,int,int);
+}
+
+
+#define DEFAULT_BUFLEN (32768)
+
+enum {ARG_NAME,ARG_INFILE,ARG_IMPFILE,ARG_OUTFILE,ARG_NARGS};
+
+void usage(const char *progname)
+{
+
+    printf("\n\n%s v1.2 RWD,CDP July 2010,2013",progname);
+	printf("\nMulti-channel FFT-based convolver");
+
+	printf("\nUsage: \n    %s [-aX][-f] infile impulsefile outfile [dry]\n"		               
+           "   -aX        : scale output amplitude by X\n"
+           "   -f         : write output as floats (no clipping)\n"
+           "  infile      : input soundfile to be processed.\n"
+           "  impulsefile : m/c soundfile or mono text file containing impulse response,\n"
+           "                  e.g. reverb or FIR filter.\n"
+           "                Text file name must have extension .txt.\n"
+		   "                  File must contain 1 column of floating point values,\n"
+		   "                  typically in the range -1.0 to 1.0.\n"
+           "              Supported channel combinations:\n"
+           "               (a) mono infile, N-channel impulsefile;\n"
+           "               (b) channels are the same;\n"
+           "               (c) mono impulsefile, N-channel infile.\n"
+           "  [dry]       :  set dry/wet mix (e.g. for reverb)\n"
+           "                 Range: 0.0 - 1.0,  default = 0.0\n"
+           "                 (uses sin/cos law for constant power mix)\n"
+           "Note: some recorded reverb impulses effectively include the direct signal.\n"
+           "In such cases  <dry>  need not be used\n"
+           "Where impulsefile is filter response (FIR), optimum length is power-of-two - 1.\n",progname
+		   );		   
+}
+
+
+double
+timer()
+{
+	struct timeb now;
+	double secs, ticks;	
+	ftime(&now);
+	ticks = (double)now.millitm/(1000.0);
+	secs = (double) now.time;
+
+	return secs + ticks;
+}
+
+
+void			
+stopwatch(int flag) 
+{
+	static double start;		    
+	int mins=0,hours=0;
+	double end, secs=0;
+
+	if(flag)
+		start = timer();	
+	else 
+	{
+		end    = timer(); 
+		secs   = end - start;
+		mins   = (int)(secs/60.0);
+		secs  -= mins*60.0; 
+		hours  = mins/60;
+		mins  -= hours*60;
+
+		fprintf(stderr,"\nElapsed time: ");
+		if(hours > 0)
+			fprintf(stderr,"%d h ", hours);
+		if(mins > 0)
+			fprintf(stderr,"%d m ", mins);
+		fprintf(stderr,"%2.3lf s\n\n",secs);
+	}
+}
+
+/* how do we want this to work?
+
+  (1)  multi-chan impulse:  EITHER: mono infile  OR infile with same chan count
+  (2)  mono impulse:  expanded to infile chancount
+     Therefore: need to open both infiles before deciding action
+*/
+#define PI2 (1.570796327)
+
+int main(int argc,char **argv)
+{
+
+	int fftlen = 1,imlen = 0,chans = 0,srate;
+	double scalefac = 1.0f;
+    double Ninv = 1.0;
+	int insndfile = -1,inimpfile = -1,outsndfile = -1; 
+    int nameoffset = 0;
+	PSF_PROPS inprops,outprops, impulseprops;
+    psf_format format;
+    PSF_CHPEAK  *fpeaks = NULL;
+	MYLONG peaktime;
+	int i,jj,minheader = 0,do_timer = 1;
+    int writefloats = 0;
+    int framesneeded;
+	double oneovrsr;
+    double *inmonobuf = 0;
+	double *insbuf=0,*outsbuf = 0;
+    double **inbuf = 0, **outbuf = 0;    
+	double **imbuf = 0;
+	double **overlapbuf = 0;
+    double rms = 0.0;
+    double dry = 0.0;
+    double wet = 1.0;
+#ifdef FFTTEST
+    double *anal = 0;
+#endif
+    	
+    if(argv[0][0]=='.' && argv[0][1]=='/'){
+        nameoffset  = 2;
+    }
+	
+    if(argc==2){
+        if(strcmp(argv[1],"--version")==0){
+            printf("1.2.0.\n");
+            return 0;
+        }
+    }
+
+	if(psf_init()){
+		puts("unable to start portsf\n");
+		return 1;
+	}
+
+
+	if(argc < ARG_NARGS){		 
+		usage(argv[0]+nameoffset);
+		return(1);
+	}
+
+    
+
+    while(argc > 1 && argv[1][0]=='-'){				
+		switch(argv[1][1]){
+        case 'a':
+            scalefac =  atof(&(argv[1][2]));
+            if(scalefac <=0.0){
+                printf("Error: scalefac must be positive!\n");
+                return 1;
+            }
+            break;        
+        case 'f':
+            writefloats = 1;
+            break;
+        default:
+            break;
+        }
+        argv++;
+        argc--;
+    }		
+	/* 2 legal possibilities: infile and outfile, or -I used with infile only */
+	if(argc< ARG_NARGS){
+		fprintf(stderr,"\nInsufficient arguments.");
+		usage(argv[0]+nameoffset);
+		return(1);
+	}
+    if(argc==ARG_NARGS+1){
+        double dryarg;
+        dryarg = atof(argv[ARG_NARGS]);
+        if(dryarg < 0.0 || dryarg > 1.0){
+            printf("dry value out of range.\n");
+            return 0;
+        }
+        if(dryarg==1.0){
+            printf("Warning: dry=1 copies input!\n");
+            wet = 0.0;
+        }
+        else{
+            dry = sin(PI2 * dryarg);
+            wet = cos(PI2 * dryarg);
+        }
+    }
+
+		
+
+/* TODO:  where -F[file] is combined with -A, it signifies create analysis file
+           compatible with impulse file (e.g 50% overlap, etc) */
+
+	
+	/* open infile, check props */	
+		
+	if((insndfile = psf_sndOpen(argv[ARG_INFILE],&inprops, 0)) < 0){
+		fprintf(stderr,"\nUnable to open input soundfile %s",argv[1]);
+        delete []imbuf;
+		return 1;
+	}
+    srate = inprops.srate;
+	if(srate <=0){
+		fprintf(stderr,"\nBad srate found: corrupted file?\n");
+        delete []imbuf;
+		return 1;
+	}
+	chans = inprops.chans;
+    framesneeded = psf_sndSize(insndfile);
+    if(framesneeded <= 0){
+        printf("Error in input file - no data!\n");
+        psf_sndClose(insndfile);
+        return 1;
+    }
+    /* open impulse file */
+    /* check for soundfile */
+      
+    format = psf_getFormatExt(argv[ARG_IMPFILE]);
+    if(format==PSF_FMT_UNKNOWN){  /* must be a text file */
+        FILE *fp = 0;
+        char tmp[80];
+        char* dot;
+        int size = 0,got = 0;
+        int read = 0;
+
+        dot = strrchr(argv[ARG_IMPFILE],'.');
+        if(dot==NULL || stricmp(dot,".txt") != 0){
+            fprintf(stderr,"Error: impulse text file must have .txt extension.\n");
+            return 1;
+        }
+        fp = fopen(argv[ARG_IMPFILE],"r");
+        if(fp==0){
+            printf("Cannot open impulse text file %s\n.",argv[ARG_IMPFILE]);
+            return 1;
+        }
+        /* count lines! */
+        while(fgets(tmp,80,fp) != NULL)
+            size++;
+        if(size==0){
+            printf("Impulse textfile %s has no data!.\n",argv[ARG_IMPFILE]);
+            return 1;
+        }
+        rewind(fp);
+            
+        inmonobuf = new double[size+1];
+        for(i=0;i < size;i++)  {
+            read = fscanf(fp,"%lf",&inmonobuf[i]);
+            if(read==0){
+                i--;
+                continue;
+            }
+            if(read==EOF)
+                break;
+            got++;
+        }
+        imlen = got;
+        impulseprops.chans = 1;
+        fclose(fp);
+    }
+    else{
+        if((inimpfile = psf_sndOpen(argv[ARG_IMPFILE],&impulseprops, 0))< 0){
+            fprintf(stderr,"\nUnable to open impulse file %s",argv[ARG_IMPFILE]);       
+            return 0;
+        }
+        //printf("impulse file sr = %d\n",impulseprops.srate);
+        if(srate != impulseprops.srate){
+            printf("Error: files must have same sample rate");
+            delete []imbuf;
+            return 1;
+        }
+        int  len = psf_sndSize(inimpfile);
+        if(len <= 0){
+            printf("Error in impulse soundfile - no data!\n");
+            return 1;
+        }
+        if(impulseprops.chans > 1){
+            if(! (chans == 1 || chans == impulseprops.chans)){
+                fprintf(stderr,"\nChannel mismatch.\n    Infile must be mono or have same channels as irfile.\n");
+                return(1);
+            }
+            chans = impulseprops.chans;
+        }    
+    }
+
+    imbuf = new double*[chans];
+
+    /* if impulse file is mono, we will need to copy data to the other buffers */
+    // allocate  impulseprops.chans buffers ; may need more
+
+    if(inimpfile >=0){
+	    if((imlen = genimpframe2(inimpfile,&imbuf,&rms,impulseprops.chans, scalefac)) == 0){
+	    	fprintf(stderr,"Error reading impulse file\n");
+	    	return(1);
+	    }
+    }
+    
+    else if(imlen){        
+        genimpframe1(inmonobuf,&imbuf,imlen,scalefac);       
+    }
+    printf("mean rms level = %.4lf (%.2lf dB)\n",rms, 20.0 * log10(rms));
+    
+
+    framesneeded +=  imlen;        /* can  close outfile, when this length reached */
+
+    while(fftlen < imlen*2)			/* convolution style - double-length */
+		fftlen <<= 1;
+    double norm = sqrt(2.0);       /* relative: rms of 0dBFS sine is 0.707 = 1/root2 */
+    // scale factor: most of this sheer guesswork!
+    Ninv = fftlen;
+    Ninv /= sqrt(imlen*2);
+    Ninv *= norm;
+    Ninv /= imlen;
+    
+    // take simple avg of  rms for adjustment factor.
+    // may not adequately represent some responses, e.g. with strong attack/earlies, soft reverb tail */
+    double rmsdif2 =  (-20.0 * log10(rms)) * 0.5;
+    double rmsadjust = pow(10, rmsdif2/20.0);
+#ifdef _DEBUG
+    printf("rescaling factor = %.6e\n",Ninv);   
+    printf("rmsadjust = %.4lf\n",rmsadjust);
+#endif
+   Ninv /= rmsadjust; 
+
+
+    /* copy buffers if required */
+    for(i = impulseprops.chans;i < chans;i++){
+        imbuf[i] = new double[fftlen+2];
+        memcpy(imbuf[i],imbuf[0],(fftlen+2)* sizeof(double));
+    }         
+    oneovrsr = 1.0 / (double) srate;
+    
+   
+	/* main i/o buffers */
+	
+	insbuf     = new double [fftlen  * chans];
+    outsbuf    = new double [fftlen  * chans];
+	inbuf      = new double*[chans];
+    outbuf     = new double*[chans];  /* overlap-add buffers */
+    overlapbuf = new double*[chans];
+    
+    for(i=0;i < chans;i++){
+        inbuf[i]  = new double[fftlen+2];   // main in-place buf 
+        outbuf[i] = new double[fftlen+2];        
+	    /* channel buffers */	
+        overlapbuf[i] = new double[fftlen/2];
+        memset(overlapbuf[i],0,sizeof(double)*(fftlen/2));        
+    }
+#ifdef FFTTEST
+    anal = new double[fftlen+2];
+#endif
+    
+    for(i=0;i < chans;i++){		    
+	    memset(inbuf[i], 0, sizeof(double) * (fftlen+2));
+        memset(outbuf[i],0,sizeof(double) * (fftlen+2));
+    }
+
+    
+	/* use generic init function */
+/*bool phasevocoder::init(int outsrate,int fftlen,int winlen,int decfac,float scalefac,
+						pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode)
+*/
+	
+	/*create output sfile */
+	psf_stype smptype;
+	psf_format outformat;
+	/* will it be aiff, wav, etc? */
+	outformat = psf_getFormatExt(argv[ARG_OUTFILE]);
+	if(outformat==PSF_FMT_UNKNOWN){
+		fprintf(stderr,"\nOutfile name has unrecognized extension.");
+        delete []imbuf;
+		return(1);
+	}
+		
+		
+	smptype = inprops.samptype;
+    if(writefloats)
+        smptype= PSF_SAMP_IEEE_FLOAT;
+
+	/*the one gotcha: if output is floats and format is aiff, change to aifc */
+	if(smptype==PSF_SAMP_IEEE_FLOAT && outformat==PSF_AIFF){
+		fprintf(stderr,"Warning: AIFF output written as AIFC for float samples\n");
+		outformat = PSF_AIFC;
+	}
+
+	outprops          = inprops;
+	outprops.chans    = chans;
+	outprops.srate    = srate;
+	outprops.format   = outformat;
+	outprops.samptype = smptype;
+	outprops.chformat = STDWAVE;
+    /* if irfile is BFormat, need to set outfile fmt likewise */
+    if(impulseprops.chformat==MC_BFMT)
+        outprops.chformat = MC_BFMT;
+    /* I suppose people will want automatic decoding too...grrr */
+
+    fpeaks = new PSF_CHPEAK[chans];
+	if(fpeaks==NULL){
+		puts("no memory for fpeak data buffer\n");
+		return 1;
+	}
+
+	/*last arg: no clipping of f/p samples: we use PEAK chunk */
+	if((outsndfile = psf_sndCreate(argv[ARG_OUTFILE],&outprops,0,minheader,PSF_CREATE_RDWR)) <0 ){
+		fprintf(stderr,"\nUnable to open outfile %s",argv[ARG_OUTFILE]);
+        delete []imbuf;
+		return(1);
+	}
+				
+
+	
+	printf("\n");
+	
+	
+	stopwatch(1);
+
+	int written,thisblock,framesread;
+    int frameswritten = 0;
+	double intime= 0.0;
+				
+	while((framesread = psf_sndReadDoubleFrames(insndfile,insbuf,imlen)) > 0){        
+		written = thisblock =  0;
+
+		for(i = framesread * inprops.chans; i< fftlen * inprops.chans; i++)
+			insbuf[i] = 0.0f;
+        framesread = imlen;
+		memset(inbuf[0],0,(fftlen+2) * sizeof(double));
+        if(chans == inprops.chans)  {
+            /* must clean buffers! */
+		    for(i=0;i < chans;i++)
+                memset(inbuf[i],0,(fftlen+2) * sizeof(double));
+            mc_split(insbuf,inbuf,imlen * chans, chans);
+
+        }
+        else{
+            for(i=0;i < chans;i++) {
+                memset(inbuf[i],0,(fftlen+2) * sizeof(double));
+                memcpy(inbuf[i],insbuf,imlen * sizeof(double));
+                memset(outbuf[i],0,sizeof(double) * (fftlen+2));
+
+            }
+        }
+        if(impulseprops.chans==1){
+			
+            for(jj = 0; jj < chans;jj++){
+#ifdef FFTTEST
+                int zz;
+                memcpy(anal,inbuf[jj],(fftlen+2) * sizeof(double));
+                fft_(anal,anal+1,1,fftlen/2,1,-2);
+	            reals_(anal,anal+1,fftlen/2,-2);
+                for(zz=0;zz < fftlen+2;zz++)
+                    anal[zz] *= 0.001;
+#endif
+                //rfftwnd_one_real_to_complex(forward_plan[jj],inbuf[jj],NULL);	
+				double *danal = inbuf[jj];
+				fft_(danal,danal+1,1,fftlen/2,1,-2);
+				reals_(danal,danal+1,fftlen/2,-2);
+                complexmult(inbuf[jj],imbuf[0],fftlen+2);                
+			    //rfftwnd_one_complex_to_real(inverse_plan[jj],(fftw_complex * )inbuf[jj],NULL);
+				reals_(danal,danal+1,fftlen/2,2);
+				fft_(danal,danal+1,1,fftlen/2,1,2);
+            }           
+        }
+        else{
+            for(jj = 0; jj < chans;jj++){
+				//rfftwnd_one_real_to_complex(forward_plan[jj],inbuf[jj],NULL);                   				
+                double *danal = inbuf[jj];
+				fft_(danal,danal+1,1,fftlen/2,1,-2);
+				reals_(danal,danal+1,fftlen/2,-2);
+				complexmult(inbuf[jj],imbuf[jj],fftlen+2);                
+				//rfftwnd_one_complex_to_real(inverse_plan[jj],(fftw_complex * )inbuf[jj],NULL); 
+				reals_(danal,danal+1,fftlen/2,2);
+				fft_(danal,danal+1,1,fftlen/2,1,2);
+            }            
+        }
+        
+        /* overlap-add  for each channel */
+        /* TODO: verify  use of imlen here - should it be fftlen/2 -1 ? */
+        for(jj=0;jj < chans;jj++){
+            for(i=0;i < imlen;i++) {
+                outbuf[jj][i] = inbuf[jj][i] + overlapbuf[jj][i];
+                overlapbuf[jj][i] = inbuf[jj][i+imlen];
+            }
+        }
+		mc_interl(outbuf,outsbuf,imlen,chans);
+
+        if(inprops.chans == chans){
+            for(i=0;i < framesread; i++) {
+                for(jj=0;jj < chans;jj++){
+                    int  outindex = i*chans + jj;            
+                    outsbuf[outindex] *= Ninv;
+                    outsbuf[outindex] *= wet;
+                    outsbuf[outindex] += dry * insbuf[outindex];
+                }
+            }
+        }
+        /* elso mono input */
+        else {
+            for(i=0;i < framesread; i++) {
+                for(jj=0;jj < chans;jj++){
+                    int  outindex = i*chans + jj;
+                    double inval = dry *  insbuf[i];
+                    outsbuf[outindex] *= Ninv;
+                    outsbuf[outindex] *= wet;
+                    outsbuf[outindex] += inval;
+                }
+            }
+        }
+
+        if((written = psf_sndWriteDoubleFrames(outsndfile,outsbuf,framesread)) != framesread){
+		    fprintf(stderr,"\nerror writing to outfile");
+		    return(1);		               
+        }
+        frameswritten += written;
+
+		if(do_timer){
+			intime += (double)framesread * oneovrsr;
+			printf("Input time: %.2lf\r",intime);
+		}    
+    }
+    /* complete tail */
+
+    if(frameswritten < framesneeded){
+        // TODO: imlen? see above
+        mc_interl(overlapbuf,outsbuf,imlen,chans);
+        int towrite = framesneeded - frameswritten; 
+        for(i=0;i < towrite * chans; i++) {
+            outsbuf[i] *= Ninv;
+            outsbuf[i] *= wet;
+        }
+        if((written = psf_sndWriteDoubleFrames(outsndfile,outsbuf,towrite)) != towrite){
+	        fprintf(stderr,"\nerror writing to outfile");
+	        return(1);
+	    }
+    }
+
+	stopwatch(0);
+    if(psf_sndReadPeaks( outsndfile,fpeaks,&peaktime)){
+        double peakmax = 0.0;
+		printf("PEAK values:\n");
+		for(i=0; i < chans; i++)	{
+            peakmax = fpeaks[i].val > peakmax ? fpeaks[i].val : peakmax;
+			if(fpeaks[i].val < 0.0001f)
+				printf("CH %d:\t%e (%.2lf dB)\tat frame %10u :\t%.4lf secs\n",i+1,
+				fpeaks[i].val,20.0*log10(fpeaks[i].val),fpeaks[i].pos,(double) (fpeaks[i].pos) / (double)srate);
+			else
+				printf("CH %d:\t%.4lf (%.2lf dB)\tat frame %10u :\t%.4lf secs\n",i+1,
+				fpeaks[i].val,20.0 * log10(fpeaks[i].val),fpeaks[i].pos,(double) (fpeaks[i].pos) / (double)srate);		
+		}
+        if(peakmax > 1.0)
+            printf("Overflows!  Rescale by %.10lf\n",1.0 / peakmax);
+	}
+
+	if(insndfile >=0)
+		psf_sndClose(insndfile);
+	if(outsndfile >=0)
+		psf_sndClose(outsndfile);
+    if(inimpfile >=0)
+	    psf_sndClose(inimpfile);
+	psf_finish();
+    delete [] fpeaks;
+	if(insbuf)
+		delete [] insbuf;
+	if(outsbuf)
+		delete [] outsbuf;
+    
+
+    for(i=0;i < chans;i++){
+        if(inbuf){       
+		    delete [] inbuf[i];
+        }           
+        if(outbuf){
+		    delete [] outbuf[i];
+        }       	
+        if(imbuf){        
+	        delete [] imbuf[i];
+        }
+        if(overlapbuf)
+            delete [] overlapbuf[i];
+    }
+    delete [] outbuf;
+    delete [] inbuf;
+    delete [] imbuf;
+    delete [] overlapbuf;
+	return 0;
+}
+
+
+// insize is raw samplecount,buflen is insize/chans
+void mc_split(double* inbuf,double** out,int insize,int chans)
+{
+    int i,j,buflen = insize/chans;
+    double* pinbuf;
+
+    
+    for(j=0;j < chans;j++){
+        pinbuf = inbuf+j;
+        for(i=0;i < buflen;i++){
+            out[j][i] = *pinbuf;
+            pinbuf += chans;
+        }
+    }
+}
+
+
+/* insize is m/c frame count */
+void mc_interl(double** in,double* out,int insize,int chans)
+{
+    int i,j;
+    double* poutbuf;
+
+    for(j = 0;j < chans;j++){
+        poutbuf = out+j;
+        for(i=0;i < insize;i++){
+            *poutbuf = in[j][i];
+            poutbuf += chans;
+        }
+    }
+}
+
+/* OR:  apply scalefac to impulse responses */
+void complexmult(double *frame,const double *impulse,int length)
+{
+	double re,im;
+	
+	int i,j;
+	
+
+	for(i=0,j = 1;i < length;i+=2,j+=2){
+		re = frame[i] * impulse[i] - frame[j] * impulse[j];
+		im = frame[i] * impulse[j] + frame[j]* impulse[i];
+		frame[i] = re;
+		frame[j] = im;
+	}
+}
+
+#ifdef unix
+int stricmp(const char *a, const char *b)
+{
+	while(*a != '\0' && *b != '\0') {
+		int ca = islower(*a) ? toupper(*a) : *a;
+		int cb = islower(*b) ? toupper(*b) : *b;
+        
+		if(ca < cb)
+			return -1;
+		if(ca > cb)
+			return 1;
+        
+		a++;
+		b++;
+	}
+	if(*a == '\0' && *b == '\0')
+		return 0;
+	if(*a != '\0')
+		return 1;
+	return -1;
+}
+
+int
+strnicmp(const char *a, const char *b, const int length)
+{
+	int len = length;
+    
+	while(*a != '\0' && *b != '\0') {
+		int ca = islower(*a) ? toupper(*a) : *a;
+		int cb = islower(*b) ? toupper(*b) : *b;
+        
+		if(len-- < 1)
+			return 0;
+        
+		if(ca < cb)
+			return -1;
+		if(ca > cb)
+			return 1;
+        
+		a++;
+		b++;
+	}
+	if(*a == '\0' && *b == '\0')
+		return 0;
+	if(*a != '\0')
+		return 1;
+	return -1;
+}
+#endif
+
+
+
+
+
+

+ 235 - 0
dev/externals/fastconv/genrespframe2.cpp

@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* genrespframe2.cpp */
+/* generate m/c pvoc frames containing impulse response */
+extern "C"
+{
+#include <portsf.h>
+}
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+
+
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+
+/* deinterleave input buffer of insize samps to array of chans buffers */
+void mc_split(double* inbuf,double** out,int insize,int chans);
+extern "C"{
+	void fft_(double *, double *,int,int,int,int);
+	void fftmx(double *,double *,int,int,int,int,int,int *,double *,double *,double *,double *,int *,int[]);
+	void reals_(double *,double *,int,int);
+}
+
+/*  read impulse data from soundfile */
+/* return 0 for error, or size of impulse */
+/*we assume pvsys initialized by caller */
+int genimpframe2(int ifd, double*** outbufs, double* rms,int imchans, double scalefac)
+{
+	int i,j;	
+	double *insbuf = 0;
+    double **inbuf = *outbufs;
+	int fftsize = 1;
+    int inframes;
+    double*  rmsfac = 0;
+    double* ampsum = 0;
+    double  maxrmsfac = 0.0;
+#ifdef _DEBUG
+    double *re,*im,*mag;
+#endif
+	inframes = psf_sndSize(ifd);  // m/c frames
+	if(inframes <= 0)
+        return 0;
+	while(fftsize < inframes*2)			/* convolution style - double-length */
+		fftsize <<= 1;
+	printf("impulse length = %d frames, fftsize = %d\n",inframes,fftsize);
+    insbuf = new double[inframes * imchans];
+#ifdef _DEBUG
+    re = new double[fftsize/2+1];
+    im = new double[fftsize/2+1];
+    mag = new double[fftsize/2+1];
+#endif
+    for(i=0;i < imchans;i++){	
+	    inbuf[i] = new double[fftsize+2];
+	    if(inbuf[i]==NULL){
+		    puts("no memory for file buffer!\n");                    
+		    return 0;
+	    }
+	    memset(inbuf[i],0,(fftsize+2)* sizeof(double));
+    }
+
+
+	if(inframes != psf_sndReadDoubleFrames(ifd,insbuf,inframes)){
+		fprintf(stderr,"Error reading impulse data\n");
+		
+		delete [] insbuf;
+        // and do other cleanup!
+        
+		return 0;
+	}
+    rmsfac = new double[imchans];
+    ampsum = new double[imchans];
+    for(i=0;i< imchans;i++) {
+        rmsfac[i] = 0.0;
+        ampsum[i] = 0.0;
+    }
+    /* do user scaling first */
+    for(i = 0;i < inframes * imchans;i++)
+        insbuf[i] *= scalefac;
+    /* now try to adjust rms  */
+
+    for(i = 0;i < inframes;i++) {
+        for(j=0;j < imchans;j++){
+            double val = insbuf[i*imchans + j];
+            ampsum[j] += fabs(val);
+            rmsfac[j] += val*val; 
+        }
+    }
+    for(j=0;j < imchans;j++){
+        //    rmsfac = sqrt(rmsfac);
+        rmsfac[j] /= inframes;
+        rmsfac[j] = sqrt(rmsfac[j]);
+        ampsum[j] /= inframes;
+        if(rmsfac[j] > maxrmsfac)
+            maxrmsfac = rmsfac[j];
+#ifdef _DEBUG
+        if(ampsum[j] > maxampsum)
+            maxampsum = ampsum[j];
+#endif
+    }
+    /* do the rescaling! */
+          
+#ifdef _DEBUG        
+        printf("ampsum = %.4f\n",maxampsum);
+#endif
+    
+    // now deinterleave to each inbuf
+    mc_split(insbuf,inbuf,inframes * imchans,imchans);
+    for(i=0;i < imchans;i++){
+		double *anal = inbuf[i];
+	    //rfftwnd_one_real_to_complex(forward_plan[i],inbuf[i],NULL);
+		fft_(anal,anal+1,1,fftsize/2,1,-2);
+		reals_(anal,anal+1,fftsize/2,-2);
+    }
+#ifdef _DEBUG
+    /* in order to look at it all */
+    double  magmax = 0.0;
+    double magsum = 0.0;
+    for(i=0;i < fftsize/2+1;i++){
+        double thisre, thisim;
+        thisre =inbuf[0][i*2];        
+        thisim = inbuf[0][i*2+1]; 
+        re[i] = thisre;
+        im[i] = thisim;
+        mag[i] = sqrt(thisre*thisre + thisim*thisim); 
+        magsum += (mag[i] * mag[i]);
+        if(mag[i] > magmax)
+            magmax = mag[i];
+    }
+    magsum = sqrt(magsum);
+    printf("maxamp of FFT = %.4f\n",magmax);
+    printf("mean level of FFT = %.4f\n",magsum / (fftsize/2+1));
+#endif	
+    
+    delete [] rmsfac;
+    delete [] ampsum;
+
+#ifdef _DEBUG
+    delete [] re;
+    delete [] im;
+    delete [] mag;
+#endif
+    *rms = maxrmsfac;
+	return inframes;
+}
+/* convert from input double buffer (read from mono text impulse file) */
+int genimpframe1(double *insbuf, double*** outbuf, int npoints, double scalefac)
+{
+	int i;	
+    double **inbuf = *outbuf;
+	int fftsize = 1;
+    int insamps;
+#ifdef _DEBUG
+    double *re,*im,*mag;
+#endif
+	insamps = npoints;  // m/c frames
+	if(insamps <= 0)
+        return 0;
+	while(fftsize < insamps*2)			/* convolution style - double-length */
+		fftsize <<= 1;
+	printf("infile size = %d,impulse framesize = %d\n",insamps,fftsize);
+    
+#ifdef _DEBUG
+    re = new double[fftsize/2+1];
+    im = new double[fftsize/2+1];
+    mag = new double[fftsize/2+1];
+#endif
+    	
+	inbuf[0] = new double[fftsize+2];
+	if(inbuf[0]==NULL){
+	    puts("no memory for file buffer!\n");                    
+	    return 0;
+	}
+	memset(inbuf[0],0,(fftsize+2)* sizeof(double)); 
+#ifdef _DEBUG
+    double ampsum = 0.0;
+#endif
+    for(i = 0;i < insamps;i++) {
+#ifdef _DEBUG
+        ampsum += fabs(insbuf[i]);
+#endif        
+        insbuf[i] *= scalefac;
+    }
+#ifdef _DEBUG
+    printf("amplitude sum of impulse file = %f\n",ampsum);
+#endif
+    memcpy(inbuf[0],insbuf,npoints* sizeof(double));
+	//rfftwnd_one_real_to_complex(forward_plan,inbuf[0],NULL);
+	double *anal = inbuf[0];
+	fft_(anal,anal+1,1,fftsize/2,1,-2);
+	reals_(anal,anal+1,fftsize/2,-2);
+    
+#ifdef _DEBUG
+    /* in order to look at it all */
+    for(i=0;i < fftsize/2+1;i++){
+        double thisre, thisim;
+        thisre =inbuf[0][i*2];        
+        thisim = inbuf[0][i*2+1]; 
+        re[i] = thisre;
+        im[i] = thisim;
+        mag[i] = sqrt(thisre*thisre + thisim*thisim);
+    }
+#endif	
+    
+    
+#ifdef _DEBUG
+    delete [] re;
+    delete [] im;
+    delete [] mag;
+#endif
+   
+
+	return insamps;
+}

+ 883 - 0
dev/externals/fastconv/mxfftd.c

@@ -0,0 +1,883 @@
+/*  
+    mxfftd.c: double precision version of mxfft.c
+	 
+	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
+
+*/
+
+/* This program converted from the FORTRAN routines by Singleton in
+ * Section 1.4 of  "Programs for Digital Signal Processing", IEEE Press, 1979.
+ *  Conversion by Trevor Wishart and Keith Henderson, York Univ.
+ */
+
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+
+void fft_(double *, double *, int, int, int, int);
+void fftmx(double *, double *, int, int, int, int, int,
+      int*, double *, double *, double *, double *, int *, int[]);
+void reals_(double *, double *, int, int);
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  fft
+ * multivariate complex fourier transform, computed in place
+ * using mixed-radix fast fourier transform algorithm.
+ *-----------------------------------------------------------------------
+ *
+ *      this is the call from C:
+ *              fft_(anal,banal,&one,&N2,&one,&mtwo);
+ *      CHANGED TO:-
+ *              fft_(anal,banal,one,N2,one,mtwo);
+ */
+
+void
+fft_(double *a, double *b, int nseg, int n, int nspn, int isn)
+  /*    *a,       pointer to array 'anal'  */
+  /*    *b;       pointer to array 'banal' */
+{
+    int nfac[16];               /*  These are one bigger than needed   */
+                                /*  because wish to use Fortran array  */
+                                /* index which runs 1 to n, not 0 to n */
+
+    int         m = 0,
+                nf,
+                k,
+                kt,
+                ntot,
+                j,
+                jj,
+                maxf, maxp=-1;
+
+/* work space pointers */
+    double       *at, *ck, *bt, *sk;
+    int *np;
+
+
+/* reduce the pointers to input arrays - by doing this, FFT uses FORTRAN
+   indexing but retains compatibility with C arrays */
+    a--;        b--;
+
+/*
+ * determine the factors of n
+ */
+    k=nf=abs(n);
+    if (nf==1)
+      return;
+
+    nspn=abs(nf*nspn);
+    ntot=abs(nspn*nseg);
+
+    if (isn*ntot == 0) {
+      printf("\nerror - zero in fft parameters %d %d %d %d",
+              nseg, n, nspn, isn);
+      return;
+    }
+    for (m=0; !(k%16); nfac[++m]=4,k/=16);
+    for (j=3,jj=9; jj<=k; j+=2,jj=j*j)
+      for (; !(k%jj); nfac[++m]=j,k/=jj);
+
+    if (k<=4) {
+      kt = m;
+      nfac[m+1] = k;
+      if (k != 1)
+        m++;
+    }
+    else {
+      if (k%4==0) {
+        nfac[++m]=2;
+        k/=4;
+      }
+
+      kt = m;
+      maxp = (kt+kt+2 > k-1 ? kt+kt+2 : k-1);
+      for (j=2; j<=k; j=1+((j+1)/2)*2)
+        if (k%j==0) {
+          nfac[++m]=j;
+          k/=j;
+        }
+    }
+    if (m <= kt+1)
+      maxp = m + kt + 1;
+    if (m+kt > 15) {
+      printf("\nerror - fft parameter n has more than 15 factors : %d", n);
+      return;
+    }
+    if (kt!=0) {
+      j = kt;
+      while (j)
+        nfac[++m]=nfac[j--];
+    }
+    maxf = nfac[m-kt];
+    if (kt > 0 && maxf <nfac[kt])
+      maxf = nfac[kt];
+
+/*  allocate workspace - assume no errors! */
+    at = (double *) calloc(maxf,sizeof(double));
+    ck = (double *) calloc(maxf,sizeof(double));
+    bt = (double *) calloc(maxf,sizeof(double));
+    sk = (double *) calloc(maxf,sizeof(double));
+    np = (int *) calloc(maxp,sizeof(int));
+
+/* decrement pointers to allow FORTRAN type usage in fftmx */
+    at--;       bt--;   ck--;   sk--;   np--;
+
+/* call fft driver */
+
+    fftmx(a,b,ntot,nf,nspn,isn,m,&kt,at,ck,bt,sk,np,nfac);
+
+/* restore pointers before releasing */
+    at++;       bt++;   ck++;   sk++;   np++;
+
+/* release working storage before returning - assume no problems */
+    free(at);
+    free(sk);
+    free(bt);
+    free(ck);
+    free(np);
+    return;
+}
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  fftmx
+ * called by subroutine 'fft' to compute mixed-radix fourier transform
+ *-----------------------------------------------------------------------
+ */
+void
+fftmx(double *a, double *b, int ntot, int n, int nspan, int isn, int m,
+      int *kt, double *at, double *ck, double *bt, double *sk, int *np, int nfac[])
+{
+    int i,inc,
+      j,jc,jf, jj,
+      k, k1, k2, k3, k4,
+      kk,klim,ks,kspan, kspnn,
+      lim,
+      maxf,mm,
+      nn,nt;
+    double  aa, aj, ajm, ajp, ak, akm, akp,
+      bb, bj, bjm, bjp, bk, bkm, bkp,
+      c1, c2, c3, c72, cd,
+      dr,
+      rad,
+      sd, s1, s2, s3, s72, s120;
+
+    double      xx;     /****** ADDED APRIL 1991 *********/
+    inc=abs(isn);
+    nt = inc*ntot;
+    ks = inc*nspan;
+/******************* REPLACED MARCH 29: ***********************
+                                        rad = atan((double)1.0);
+**************************************************************/
+    rad = 0.785398163397448278900;
+/******************* REPLACED MARCH 29: ***********************
+                                        s72 = rad/0.625;
+                                        c72 = cos(s72);
+                                        s72 = sin(s72);
+**************************************************************/
+    c72 = 0.309016994374947451270;
+    s72 = 0.951056516295153531190;
+/******************* REPLACED MARCH 29: ***********************
+                                        s120 = sqrt((double)0.75);
+**************************************************************/
+    s120 = 0.866025403784438707600;
+
+/* scale by 1/n for isn > 0 ( reverse transform ) */
+
+    if (isn < 0) {
+      s72 = -s72;
+      s120 = -s120;
+      rad = -rad;}
+    else {
+      ak = 1.0/(double)n;
+      for (j=1; j<=nt;j += inc) {
+        a[j] *= (double)ak;
+        b[j] *= (double)ak;
+      }
+    }
+    kspan = ks;
+    nn = nt - inc;
+    jc = ks/n;
+
+/* sin, cos values are re-initialised each lim steps  */
+
+    lim = 32;
+    klim = lim * jc;
+    i = 0;
+    jf = 0;
+    maxf = m - (*kt);
+    maxf = nfac[maxf];
+    if ((*kt) > 0 && maxf < nfac[*kt])
+      maxf = nfac[*kt];
+
+/*
+ * compute fourier transform
+ */
+
+ lbl40:
+    dr = (8.0 * (double)jc)/((double)kspan);
+/*************************** APRIL 1991 POW & POW2 not WORKING.. REPLACE *******
+                    cd = 2.0 * (pow2 ( sin((double)0.5 * dr * rad)) );
+*******************************************************************************/
+    xx =  sin((double)0.5 * dr * rad);
+    cd = 2.0 * xx * xx;
+    sd = sin(dr * rad);
+    kk = 1;
+    if (nfac[++i]!=2) goto lbl110;
+/*
+ * transform for factor of 2 (including rotation factor)
+ */
+    kspan /= 2;
+    k1 = kspan + 2;
+    do {
+      do {
+        k2 = kk + kspan;
+        ak = a[k2];
+        bk = b[k2];
+        a[k2] = (a[kk]) - (double)ak;
+        b[k2] = (b[kk]) - (double)bk;
+        a[kk] = (a[kk]) + (double)ak;
+        b[kk] = (b[kk]) + (double)bk;
+        kk = k2 + kspan;
+      } while (kk <= nn);
+      kk -= nn;
+    } while (kk <= jc);
+    if (kk > kspan) goto lbl350;
+ lbl60:
+    c1 = 1.0 - cd;
+    s1 = sd;
+    mm = (k1/2 < klim ? k1/2 :klim);
+    goto lbl80;
+ lbl70:
+    ak = c1 - ((cd*c1)+(sd*s1));
+    s1 = ((sd*c1)-(cd*s1)) + s1;
+    c1 = ak;
+lbl80:
+    do {
+      do {
+        k2 = kk + kspan;
+        ak = a[kk] - a[k2];
+        bk = b[kk] - b[k2];
+        a[kk] = a[kk] + a[k2];
+        b[kk] = b[kk] + b[k2];
+        a[k2] = (double)((c1 * ak) - (s1 * bk));
+        b[k2] = (double)((s1 * ak) + (c1 * bk));
+        kk = k2 + kspan;
+      } while (kk < nt);
+      k2 = kk - nt;
+      c1 = -c1;
+      kk = k1 - k2;
+    } while (kk > k2);
+    kk += jc;
+    if (kk <= mm) goto lbl70;
+    if (kk < k2)  goto lbl90;
+    k1 += (inc + inc);
+    kk = ((k1-kspan)/2) + jc;
+    if (kk <= (jc+jc)) goto lbl60;
+    goto lbl40;
+ lbl90:
+    s1 = ((double)((kk-1)/jc)) * dr * rad;
+    c1 = cos(s1);
+    s1 = sin(s1);
+    mm = (k1/2 < mm+klim ? k1/2 : mm+klim);
+    goto lbl80;
+/*
+ * transform for factor of 3 (optional code)
+ */
+
+
+ lbl100:
+    k1 = kk + kspan;
+    k2 = k1 + kspan;
+    ak = a[kk];
+    bk = b[kk];
+    aj = a[k1] + a[k2];
+    bj = b[k1] + b[k2];
+    a[kk] = (double)(ak + aj);
+    b[kk] = (double)(bk + bj);
+    ak += (-0.5 * aj);
+    bk += (-0.5 * bj);
+    aj = (a[k1] - a[k2]) * s120;
+    bj = (b[k1] - b[k2]) * s120;
+    a[k1] = (double)(ak - bj);
+    b[k1] = (double)(bk + aj);
+    a[k2] = (double)(ak + bj);
+    b[k2] = (double)(bk - aj);
+    kk = k2 + kspan;
+    if (kk < nn)     goto lbl100;
+    kk -= nn;
+    if (kk <= kspan) goto lbl100;
+    goto lbl290;
+
+/*
+ * transform for factor of 4
+ */
+
+ lbl110:
+    if (nfac[i] != 4) goto lbl230;
+    kspnn = kspan;
+    kspan = kspan/4;
+ lbl120:
+    c1 = 1.0;
+    s1 = 0;
+    mm = (kspan < klim ? kspan : klim);
+    goto lbl150;
+ lbl130:
+    c2 = c1 - ((cd*c1)+(sd*s1));
+    s1 = ((sd*c1)-(cd*s1)) + s1;
+/*
+ * the following three statements compensate for truncation
+ * error.  if rounded arithmetic is used, substitute
+ * c1=c2
+ *
+ * c1 = (0.5/(pow2(c2)+pow2(s1))) + 0.5;
+ * s1 = c1*s1;
+ * c1 = c1*c2;
+ */
+    c1 = c2;
+ lbl140:
+    c2 = (c1 * c1) - (s1 * s1);
+    s2 = c1 * s1 * 2.0;
+    c3 = (c2 * c1) - (s2 * s1);
+    s3 = (c2 * s1) + (s2 * c1);
+ lbl150:
+    k1 = kk + kspan;
+    k2 = k1 + kspan;
+    k3 = k2 + kspan;
+    akp = a[kk] + a[k2];
+    akm = a[kk] - a[k2];
+    ajp = a[k1] + a[k3];
+    ajm = a[k1] - a[k3];
+    a[kk] = (double)(akp + ajp);
+    ajp = akp - ajp;
+    bkp = b[kk] + b[k2];
+    bkm = b[kk] - b[k2];
+    bjp = b[k1] + b[k3];
+    bjm = b[k1] - b[k3];
+    b[kk] = (double)(bkp + bjp);
+    bjp = bkp - bjp;
+    if (isn < 0) goto lbl180;
+    akp = akm - bjm;
+    akm = akm + bjm;
+    bkp = bkm + ajm;
+    bkm = bkm - ajm;
+    if (s1 == 0.0) goto lbl190;
+ lbl160:
+    a[k1] = (double)((akp*c1) - (bkp*s1));
+    b[k1] = (double)((akp*s1) + (bkp*c1));
+    a[k2] = (double)((ajp*c2) - (bjp*s2));
+    b[k2] = (double)((ajp*s2) + (bjp*c2));
+    a[k3] = (double)((akm*c3) - (bkm*s3));
+    b[k3] = (double)((akm*s3) + (bkm*c3));
+    kk = k3 + kspan;
+    if (kk <= nt)   goto lbl150;
+ lbl170:
+    kk -= (nt - jc);
+    if (kk <= mm)   goto lbl130;
+    if (kk < kspan) goto lbl200;
+    kk -= (kspan - inc);
+    if (kk <= jc)   goto lbl120;
+    if (kspan==jc)  goto lbl350;
+    goto lbl40;
+lbl180:
+    akp = akm + bjm;
+    akm = akm - bjm;
+    bkp = bkm - ajm;
+    bkm = bkm + ajm;
+    if (s1 != 0.0)  goto lbl160;
+ lbl190:
+    a[k1] = (double)akp;
+    b[k1] = (double)bkp;
+    a[k2] = (double)ajp;
+    b[k2] = (double)bjp;
+    a[k3] = (double)akm;
+    b[k3] = (double)bkm;
+    kk = k3 + kspan;
+    if (kk <= nt) goto lbl150;
+    goto lbl170;
+ lbl200:
+    s1 = ((double)((kk-1)/jc)) * dr * rad;
+    c1 = cos(s1);
+    s1 = sin(s1);
+    mm = (kspan < mm+klim ? kspan : mm+klim);
+    goto lbl140;
+
+/*
+ * transform for factor of 5 (optional code)
+ */
+
+ lbl210:
+    c2 = (c72*c72) - (s72*s72);
+    s2 = 2.0 * c72 * s72;
+ lbl220:
+    k1 = kk + kspan;
+    k2 = k1 + kspan;
+    k3 = k2 + kspan;
+    k4 = k3 + kspan;
+    akp = a[k1] + a[k4];
+    akm = a[k1] - a[k4];
+    bkp = b[k1] + b[k4];
+    bkm = b[k1] - b[k4];
+    ajp = a[k2] + a[k3];
+    ajm = a[k2] - a[k3];
+    bjp = b[k2] + b[k3];
+    bjm = b[k2] - b[k3];
+    aa = a[kk];
+    bb = b[kk];
+    a[kk] = (double)(aa + akp + ajp);
+    b[kk] = (double)(bb + bkp + bjp);
+    ak = (akp*c72) + (ajp*c2) + aa;
+    bk = (bkp*c72) + (bjp*c2) + bb;
+    aj = (akm*s72) + (ajm*s2);
+    bj = (bkm*s72) + (bjm*s2);
+    a[k1] = (double)(ak - bj);
+    a[k4] = (double)(ak + bj);
+    b[k1] = (double)(bk + aj);
+    b[k4] = (double)(bk - aj);
+    ak = (akp*c2) + (ajp*c72) + aa;
+    bk = (bkp*c2) + (bjp*c72) + bb;
+    aj = (akm*s2) - (ajm*s72);
+    bj = (bkm*s2) - (bjm*s72);
+    a[k2] = (double)(ak - bj);
+    a[k3] = (double)(ak + bj);
+    b[k2] = (double)(bk + aj);
+    b[k3] = (double)(bk - aj);
+    kk = k4 + kspan;
+    if (kk < nn)     goto lbl220;
+    kk -= nn;
+    if (kk <= kspan) goto lbl220;
+    goto lbl290;
+
+/*
+ * transform for odd factors
+ */
+
+ lbl230:
+    k = nfac[i];
+    kspnn = kspan;
+    kspan /= k;
+    if (k==3)   goto lbl100;
+    if (k==5)   goto lbl210;
+    if (k==jf)  goto lbl250;
+    jf = k;
+    s1 = rad/(((double)(k))/8.0);
+    c1 = cos(s1);
+    s1 = sin(s1);
+    ck[jf] = 1.0;
+    sk[jf] = 0.0;
+    for (j=1; j<k ; j++) {
+      ck[j] = (double)((ck[k])*c1 + (sk[k])*s1);
+      sk[j] = (double)((ck[k])*s1 - (sk[k])*c1);
+      k--;
+      ck[k] = ck[j];
+      sk[k] = -(sk[j]);
+    }
+ lbl250:
+    k1 = kk;
+    k2 = kk + kspnn;
+    aa = a[kk];
+    bb = b[kk];
+    ak = aa;
+    bk = bb;
+    j = 1;
+    k1 += kspan;
+    do {
+      k2 -= kspan;
+      j++;
+      at[j] = a[k1] + a[k2];
+      ak = at[j] + ak;
+      bt[j] = b[k1] + b[k2];
+      bk = bt[j] + bk;
+      j++;
+      at[j] = a[k1] - a[k2];
+      bt[j] = b[k1] - b[k2];
+      k1 += kspan;
+    } while (k1 < k2);
+    a[kk] = (double)ak;
+    b[kk] = (double)bk;
+    k1 = kk;
+    k2 = kk + kspnn;
+    j = 1;
+ lbl270:
+    k1 += kspan;
+    k2 -= kspan;
+    jj = j;
+    ak = aa;
+    bk = bb;
+    aj = 0.0;
+    bj = 0.0;
+    k = 1;
+    do {
+      k++;
+      ak = (at[k] * ck[jj]) + ak;
+      bk = (bt[k] * ck[jj]) + bk;
+      k++;
+      aj = (at[k] * sk[jj]) + aj;
+      bj = (bt[k] * sk[jj]) + bj;
+      jj += j;
+      if (jj > jf)
+        jj -= jf;
+    } while (k < jf);
+    k = jf - j;
+    a[k1] = (double)(ak - bj);
+    b[k1] = (double)(bk + aj);
+    a[k2] = (double)(ak + bj);
+    b[k2] = (double)(bk - aj);
+    j++;
+    if (j < k)     goto lbl270;
+    kk += kspnn;
+    if (kk <= nn)  goto lbl250;
+    kk -= nn;
+    if (kk<=kspan) goto lbl250;
+
+/*
+ * multiply by rotation factor (except for factors of 2 and 4)
+ */
+
+ lbl290:
+    if (i==m) goto lbl350;
+    kk = jc + 1;
+ lbl300:
+    c2 = 1.0 - cd;
+    s1 = sd;
+    mm = (kspan < klim ? kspan : klim);
+    goto lbl320;
+ lbl310:
+    c2 = c1 - ((cd*c1) + (sd*s1));
+    s1 = s1 + ((sd*c1) - (cd*s1));
+ lbl320:
+    c1 = c2;
+    s2 = s1;
+    kk += kspan;
+ lbl330:
+    ak = a[kk];
+    a[kk] = (double)((c2*ak) - (s2 * b[kk]));
+    b[kk] = (double)((s2*ak) + (c2 * b[kk]));
+    kk += kspnn;
+    if (kk <= nt) goto lbl330;
+    ak = s1*s2;
+    s2 = (s1*c2) + (c1*s2);
+    c2 = (c1*c2) - ak;
+    kk -= (nt - kspan);
+    if (kk <= kspnn) goto lbl330;
+    kk -= (kspnn - jc);
+    if (kk <= mm)   goto lbl310;
+    if (kk < kspan) goto lbl340;
+    kk -= (kspan - jc - inc);
+    if (kk <= (jc+jc)) goto lbl300;
+    goto lbl40;
+ lbl340:
+    s1 = ((double)((kk-1)/jc)) * dr * rad;
+    c2 = cos(s1);
+    s1 = sin(s1);
+    mm = (kspan < mm+klim ?  kspan :mm+klim);
+    goto lbl320;
+
+/*
+ * permute the results to normal order---done in two stages
+ * permutation for square factors of n
+ */
+
+ lbl350:
+    np[1] = ks;
+    if (!(*kt)) goto lbl440;
+    k = *kt + *kt + 1;
+    if (m < k)
+      k--;
+    np[k+1] = jc;
+    for (j=1; j < k; j++,k--) {
+      np[j+1] = np[j] / nfac[j];
+      np[k] = np[k+1] * nfac[j];
+    }
+    k3 = np[k+1];
+    kspan = np[2];
+    kk = jc + 1;
+    k2 = kspan + 1;
+    j = 1;
+    if (n != ntot) goto lbl400;
+/*
+ * permutation for single-variate transform (optional code)
+ */
+ lbl370:
+    do {
+      ak = a[kk];
+      a[kk] = a[k2];
+      a[k2] = (double)ak;
+      bk = b[kk];
+      b[kk] = b[k2];
+      b[k2] = (double)bk;
+      kk += inc;
+      k2 += kspan;
+    } while (k2 < ks);
+lbl380:
+    do {
+      k2 -= np[j++];
+      k2 += np[j+1];
+    } while (k2 > np[j]);
+    j = 1;
+ lbl390:
+    if (kk < k2) {
+      goto lbl370;
+    }
+    kk += inc;
+    k2 += kspan;
+    if (k2 < ks) goto lbl390;
+    if (kk < ks) goto lbl380;
+    jc = k3;
+    goto lbl440;
+/*
+ * permutation for multivariate transform
+ */
+ lbl400:
+    do {
+      do {
+        k = kk + jc;
+        do {
+          ak = a[kk];
+          a[kk] = a[k2];
+          a[k2] = (double)ak;
+          bk = b[kk];
+          b[kk] = b[k2];
+          b[k2] = (double)bk;
+          kk += inc;
+          k2 += inc;
+        } while (kk < k);
+        kk += (ks - jc);
+        k2 += (ks - jc);
+      } while (kk < nt);
+      k2 -= (nt - kspan);
+      kk -= (nt - jc);
+    } while (k2 < ks);
+ lbl420:
+    do {
+      k2 -= np[j++];
+      k2 += np[j+1];
+    } while (k2 > np[j]);
+    j = 1;
+ lbl430:
+    if (kk < k2)         goto lbl400;
+    kk += jc;
+    k2 += kspan;
+    if (k2 < ks)      goto lbl430;
+    if (kk < ks)      goto lbl420;
+    jc = k3;
+ lbl440:
+    if ((2*(*kt))+1 >= m)
+      return;
+
+    kspnn = *(np + *(kt) + 1);
+    j = m - *kt;
+    nfac[j+1] = 1;
+ lbl450:
+    nfac[j] = nfac[j] * nfac[j+1];
+    j--;
+    if (j != *kt) goto lbl450;
+    *kt = *(kt) + 1;
+    nn = nfac[*kt] - 1;
+    jj = 0;
+    j = 0;
+    goto lbl480;
+ lbl460:
+    jj -= k2;
+    k2 = kk;
+    kk = nfac[++k];
+ lbl470:
+    jj += kk;
+    if (jj >= k2) goto lbl460;
+    np[j] = jj;
+ lbl480:
+    k2 = nfac[*kt];
+    k = *kt + 1;
+    kk = nfac[k];
+    j++;
+    if (j <= nn) goto lbl470;
+/* Determine permutation cycles of length greater than 1 */
+    j = 0;
+    goto lbl500;
+ lbl490:
+    k = kk;
+    kk = np[k];
+    np[k] = -kk;
+    if (kk != j) goto lbl490;
+    k3 = kk;
+ lbl500:
+    kk = np[++j];
+    if (kk < 0)  goto lbl500;
+    if (kk != j) goto lbl490;
+    np[j] = -j;
+    if (j != nn) goto lbl500;
+    maxf *= inc;
+    /* Perform reordering following permutation cycles */
+    goto lbl570;
+ lbl510:
+    j--;
+    if (np[j] < 0) goto lbl510;
+    jj = jc;
+ lbl520:
+    kspan = jj;
+    if (jj > maxf)
+      kspan = maxf;
+    jj -= kspan;
+    k = np[j];
+    kk = (jc*k) + i + jj;
+    k1 = kk + kspan;
+    k2 = 0;
+ lbl530:
+    k2++;
+    at[k2] = a[k1];
+    bt[k2] = b[k1];
+    k1 -= inc;
+    if (k1 != kk) goto lbl530;
+ lbl540:
+    k1 = kk + kspan;
+    k2 = k1 - (jc * (k + np[k]));
+    k = -(np[k]);
+ lbl550:
+    a[k1] = a[k2];
+    b[k1] = b[k2];
+    k1 -= inc;
+    k2 -= inc;
+    if (k1 != kk) goto lbl550;
+    kk = k2;
+    if (k != j)   goto lbl540;
+    k1 = kk + kspan;
+    k2 = 0;
+ lbl560:
+    k2++;
+    a[k1] = at[k2];
+    b[k1] = bt[k2];
+    k1 -= inc;
+    if (k1 != kk) goto lbl560;
+    if (jj)       goto lbl520;
+    if (j  != 1)  goto lbl510;
+lbl570:
+    j = k3 + 1;
+    nt -= kspnn;
+    i = nt - inc + 1;
+    if (nt >= 0)  goto lbl510;
+    return;
+}
+
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:
+  reals
+ * used with 'fft' to compute fourier transform or inverse for real data
+ *-----------------------------------------------------------------------
+ *      this is the call from C:
+
+ *              reals_(anal,banal,N2,mtwo);
+ *      which has been changed from CARL call
+ *              reals_(anal,banal,&N2,&mtwo);
+ */
+
+void
+reals_(double *a, double *b, int n, int isn)
+
+  /*    *a,       a refers to an array of floats 'anal'   */
+  /*    *b;       b refers to an array of floats 'banal'  */
+/* See IEEE book for a long comment here on usage */
+
+{
+    int inc,
+      j,
+      k,
+      lim,
+      mm,ml,
+      nf,nk,nh;
+
+    double      aa,ab,
+      ba,bb,
+      cd,cn,
+      dr,
+      em,
+      rad,re,
+      sd,sn;
+    double      xx;     /******* ADDED APRIL 1991 ******/
+    /* adjust  input array pointers (called from C) */
+    a--;        b--;
+    inc=abs(isn);
+    nf=abs(n);
+    if (nf*isn==0) {
+      printf("\nerror - zero in reals parameters : %d : %d ",n,isn);
+      return;
+    }
+    nk = (nf*inc) + 2;
+    nh = nk/2;
+/*****************************
+        rad  = atan((double)1.0);
+******************************/
+    rad = 0.785398163397448278900;
+    dr = -4.0/(double)(nf);
+/********************************** POW2 REMOVED APRIL 1991 *****************
+                                cd = 2.0 * (pow2(sin((double)0.5 * dr * rad)));
+*****************************************************************************/
+    xx = sin((double)0.5 * dr * rad);
+    cd = 2.0 * xx * xx;
+    sd = sin(dr * rad);
+/*
+ * sin,cos values are re-initialised each lim steps
+ */
+    lim = 32;
+    mm = lim;
+    ml = 0;
+    sn = 0.0;
+    if (isn<0) {
+      cn = 1.0;
+      a[nk-1] = a[1];
+      b[nk-1] = b[1]; }
+    else {
+      cn = -1.0;
+      sd = -sd;
+    }
+    for (j=1;j<=nh;j+=inc)      {
+      k = nk - j;
+      aa = a[j] + a[k];
+      ab = a[j] - a[k];
+      ba = b[j] + b[k];
+      bb = b[j] - b[k];
+      re = (cn*ba) + (sn*ab);
+      em = (sn*ba) - (cn*ab);
+      b[k] = (double)((em-bb)*0.5);
+      b[j] = (double)((em+bb)*0.5);
+      a[k] = (double)((aa-re)*0.5);
+      a[j] = (double)((aa+re)*0.5);
+      ml++;
+      if (ml!=mm) {
+        aa = cn - ((cd*cn)+(sd*sn));
+        sn = ((sd*cn) - (cd*sn)) + sn;
+        cn = aa;}
+      else {
+        mm +=lim;
+        sn = ((double)ml) * dr * rad;
+        cn = cos(sn);
+        if (isn>0)
+          cn = -cn;
+        sn = sin(sn);
+      }
+    }
+    return;
+}
+
+

+ 110 - 0
dev/externals/fastconv/readme.txt

@@ -0,0 +1,110 @@
+Preliminary documentation  for fastconv.
+
+
+FASTCONV:  multi-channel fast convolution (using FFTs)
+
+version: 1.0 2010
+
+usage message: 
+fastconv [-aX][-f] infile impulsefile outfile [dry]
+   -aX        : scale output amplitude by X
+   -f         : write output as floats (no clipping)
+  infile      : input soundfile to be processed.
+  impulsefile : soundfile or text file containing impulse response,
+                  e.g. reverb or FIR filter.
+                (text file name must have extension .txt)
+              Supported channel combinations:
+               (a) mono infile, N-channel impulsefile;
+               (b) channels are the same;
+               (c) mono impulsefile, N-channel infile.
+  [dry]       :  set dry/wet mix (e.g. for reverb)
+                 Range: 0.0 - 1.0,  default = 0.0
+                 (uses sin/cos law for constant power mix)
+Note: some recorded reverb impulses effectively include the direct signal.
+In such cases  <dry>  need not be used
+Where impulsefile is a filter response (FIR), optimum length is power-of-two - 1.
+
+
+The primary application of fastconv is convolution reverberation using a 
+sampled impulse response of a building or other responsive space. The term "fast"  
+refers to the use of the Fast Fourier transform (FFT) to perfume the convolution. 
+The program can also be used more experimentally, as the impulse response input can 
+be any mono or multi-channel file (see details of available channel combinations below); 
+a file can also be convolved with itself. 
+The program uses double-precision processing throughout, and files of considerable 
+length can be processed cleanly.
+Note however that the FFT of the impulse response is stored in main memory, 
+so very large files may raise memory demands to critical levels.
+
+
+More on channel options:
+
+  Impulse soundfile can be multi-channel.
+  The .amb format is supported, also Logic Pro SDIR files for reading (change the file extension to .aifc).
+
+  When the infile is multi-channel, impulse file must be mono, or the same number of channels.
+        Where the impulse file is mono, data is duplicated for all input channels.
+		Typical usage: linear-phase filtering. Output should be 100% "wet".
+	The optimum length for a filter impulse response is power-of-two-1, 
+	e.g. 127,255, 511 etc. Most filter creation tools will output a file of this size.
+
+	(More tools to support linear-phase filtering are in preparation!).
+
+
+  When infile is mono, impulse soundfile can be multi-channel, outfile has channel count of
+      impulse response.
+    Typical usage: reverb convolution for spatialization, B-Format convolution
+	It will be usual to supply a non-zero value for "dry", e.g. 0.5. Note however that some recorded 
+       or synthetic impulse responses  may already include a "direct" component. In such cases, 
+	a "dry" value may not be needed.
+
+  The program employs an rms-based gain scaling algorithm which attempts to ensure all outputs are
+ approximately at the same level as the input. In normal use (e.g. a naturally decaying reverb impulse response),
+ the -a flag should not be needed.
+When the -f flag is used, output is forced to floats, with no clipping. The program reports 
+the output level together with a suggested corrective gain factor. This will be of particular relevance to more
+experimental procedures, such as convolving a soundfile with itself or with some other arbitrary source.
+
+
+Note for advanced users - use for FIR linear-phase filtering.
+
+Convolution implements a filtering process - equivalent to multiplication of the spectra of the two inputs. 
+The most common filter in audio work is recursive - it recycles output samples back into the input. The output 
+continues in principle for ever, hence the term "infinite impulse response" (IIR). The advantage of this technique 
+(as employed for example in CDP's "filter' package) is that even a low-order filter (i.e. using a small 
+number of delayed inputs and outputs) can be very powerful in its effects. A disadvantage in some applications 
+can be that an IIR filter changes the phase of components in the input (frequency components are 
+delayed by different amounts). This means among the things that the waveform shape of the input is not 
+preserved. The timbre of the sound (even in regions  not directly boosted 
+or attenuated by the filter) will therefore be changed. In common parlance, such a filter "colours" the signal.
+
+The alternative is a linear phase filter, which preserves all phase relationships in the signal. 
+All frequency components are delayed equally. To achieve this the impulse response must have a symmetrical 
+shape (see illustration). The response decays identically either side of the central peak.  
+
+This requires that there be no recirculated outputs reinjected into the filter. 
+Such a filter has a Finite Impulse Response (FIR). The impulse response data now comprises literally the 
+response of the filter to a single input sample (impulse). An impulse response of 31 samples means 
+that the filter generates and adds together 31 delayed copies of the input, to create each output sample. 
+While IIR filter coefficients may involve only two delayed samples ("a "second-order" filter), FIR responses 
+need to employ many more samples to achieve similar effects. It would not be unusual to use a 511-th order FIR filter. 
+This also means that the overall delay ("latency") of a FIR filter is much longer than that of an IIR filter. 
+
+A FIR filter cannot resonate as an IIR filter can. By computing only delayed inputs, It is unconditionally 
+"stable" - whereas a badly designed IIR filter can "blow up" with output values rapidly exceeding the sample limit.
+
+Fastconv supports the use of FIR coefficient files either in the form of either a short soundfile, or a 
+plain text file containing (in a single column)  the list of coefficients as floating-point numbers within 
+the "normalised" range -1.0 to 1.0. For orthodox filtering purposes a mono soundfile should be used, to process 
+all channels identically. FIR coefficient text files are generated by many engineering-oriented filter design 
+applications.  User may be tempted to  write response files by hand; this can be done, but the results will be 
+virtually impossible to predict or control. 
+
+For maximum efficiency, such files should ideally have a size that is a power-of-two less one: e.g. 255, 511, 1023, etc.
+
+For more information about FIR filters, see :
+
+http://www.labbookpages.co.uk/audio/firWindowing.html
+
+
+August 23 2010

+ 258 - 0
dev/externals/include/portsf.h

@@ -0,0 +1,258 @@
+/* Copyright (c) 2009, 2014 Richard Dobson
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* POST_BOOK!*/
+/* RWD Oct 2009: added MC_CUBE to listy of supported speaker layouts */
+/* corrected 7.1 speaker value (hex) */
+/* Aug 2012 corrected SPKRS_MONO value */
+/* Nov 2013: added SPKRS_6_1 and MC_SURR_6_1 to list */
+
+#ifndef __RIFFWAV_H_INCLUDED
+#define __RIFFWAV_H_INCLUDED
+
+/* Revision 16th September 2003: added TPDF dither support */
+/* revision Dec 2005: support new B-Format extensions */
+/* RWD Nov 1 2006: extended with double sread/write for research purposes! */
+/* July 2009: attempt portability with 64bit platforms */
+/* unable to test Win64 yet - but the symbol to check is simply _WIN64  */
+#ifdef __GNUC__ 
+# ifdef __LP64__
+#   define CPLONG64
+# endif
+#endif
+/* failing that, manually define CPLONG64 in makefile or project setting */
+#ifdef CPLONG64
+// all this effort is for readPeaks() and ctime only!
+# define MYLONG int
+#else
+# define MYLONG long
+#endif
+
+#ifdef __cplusplus
+extern "C" {	
+#endif	   
+/* compatible with <windows.h> */
+#ifndef DWORD
+typedef unsigned MYLONG DWORD;
+typedef unsigned short WORD;
+#endif
+/* NB: AIFF spec always illustrates chunksize as (signed) long; 
+   even though nFrames is always unsigned long!
+   So we make everything DWORD here.
+ */
+
+/* the file sample formats we could support */
+
+typedef enum {
+	PSF_SAMP_UNKNOWN	=	0,
+	PSF_SAMP_8,				   /* not yet supported! */
+	PSF_SAMP_16,
+	PSF_SAMP_24,
+	PSF_SAMP_32,
+	PSF_SAMP_IEEE_FLOAT
+} psf_stype;
+
+/* the file format */
+/* currently based only on file extension. 
+   To be friendly, we should parse the header to get the format.
+*/
+typedef enum {
+	PSF_FMT_UNKNOWN = 0,		/* e.g if no extension given. This could also signify 'raw' */
+	PSF_STDWAVE,
+	PSF_WAVE_EX,
+	PSF_AIFF,
+	PSF_AIFC
+} psf_format;
+
+/* provisional stab at error codes */
+enum {
+	PSF_E_NOERROR		= 0,
+	PSF_E_CANT_OPEN		= -1,
+	PSF_E_CANT_CLOSE	= -2,
+	PSF_E_CANT_WRITE	= -3,
+	PSF_E_CANT_READ		= -4,
+	PSF_E_NOT_WAVE		= -5,
+	PSF_E_BAD_TYPE		= -6,
+	PSF_E_BAD_FORMAT	= -7,
+	PSF_E_UNSUPPORTED	= -8,
+	PSF_E_NOMEM			= -9,
+	PSF_E_BADARG		= -10,
+	PSF_E_CANT_SEEK		= -11,
+	PSF_E_TOOMANYFILES  = -12,
+	PSF_E_FILE_READONLY = -13,
+	PSF_E_SEEK_BEYOND_EOF = -14
+};
+
+#define NUM_SPEAKER_POSITIONS (18)
+
+#define SPEAKER_FRONT_LEFT				0x1
+#define SPEAKER_FRONT_RIGHT				0x2
+#define SPEAKER_FRONT_CENTER			0x4
+#define SPEAKER_LOW_FREQUENCY			0x8
+#define SPEAKER_BACK_LEFT				0x10
+#define SPEAKER_BACK_RIGHT				0x20
+#define SPEAKER_FRONT_LEFT_OF_CENTER	0x40
+#define SPEAKER_FRONT_RIGHT_OF_CENTER	0x80
+#define SPEAKER_BACK_CENTER				0x100
+#define SPEAKER_SIDE_LEFT				0x200
+#define SPEAKER_SIDE_RIGHT				0x400
+#define SPEAKER_TOP_CENTER				0x800
+#define SPEAKER_TOP_FRONT_LEFT			0x1000
+#define SPEAKER_TOP_FRONT_CENTER		0x2000
+#define SPEAKER_TOP_FRONT_RIGHT			0x4000
+#define SPEAKER_TOP_BACK_LEFT			0x8000
+#define SPEAKER_TOP_BACK_CENTER			0x10000
+#define SPEAKER_TOP_BACK_RIGHT			0x20000
+#define SPEAKER_RESERVED      			0x80000000
+
+/* my extras*/
+#define SPKRS_UNASSIGNED	(0)
+#define SPKRS_MONO			(0x00000004)
+#define SPKRS_STEREO		(0x00000003)
+#define SPKRS_GENERIC_QUAD	(0x00000033)
+#define SPKRS_SURROUND_LCRS	(0x00000107)
+#define SPKRS_SURR_5_0      (0x00000037)
+#define SPKRS_DOLBY5_1		(0x0000003f)
+#define SPKRS_6_1			(0x0000013f)
+#define SPKRS_7_1           (0x000000ff)
+#define SPKRS_CUBE          (SPKRS_GENERIC_QUAD | SPEAKER_TOP_FRONT_LEFT | SPEAKER_TOP_FRONT_RIGHT | SPEAKER_TOP_BACK_LEFT | SPEAKER_TOP_BACK_RIGHT)
+#define SPKRS_ACCEPT_ALL	(0xffffffff)	 /*???? no use for a file*/
+
+
+/* support for the PEAK chunk */
+typedef struct psf_chpeak {	
+	float val;
+	DWORD pos;   /* OK for all WAVE and AIFF <= 4GB */
+} PSF_CHPEAK;
+
+/* second two are speculative at present! */
+typedef enum  {PSF_CREATE_RDWR,PSF_CREATE_TEMPORARY,PSF_CREATE_WRONLY} psf_create_mode;
+/* the speakerfeed format */
+/* MC_WAVE_EX is a possibly temporary one to cover abstruse infile formats! */
+typedef enum { STDWAVE,MC_STD,MC_MONO,MC_STEREO,MC_QUAD,MC_LCRS,MC_BFMT,MC_DOLBY_5_1,
+				MC_SURR_5_0,MC_SURR_6_1,MC_SURR_7_1,MC_CUBE,MC_WAVE_EX } psf_channelformat;
+
+/* read access support */
+/* for psf_sndSeek(); ~should~ map directly to fseek mode flags*/
+enum { PSF_SEEK_SET=0,PSF_SEEK_CUR,PSF_SEEK_END};
+
+enum {PSF_DITHER_OFF,PSF_DITHER_TPDF};
+
+/* main structure to define a soundile. Extended props must be asked for separately */
+typedef struct psf_props 
+{
+	int		srate;
+	int		chans;	
+	psf_stype	samptype;		
+	psf_format	format;			
+	psf_channelformat chformat;	
+	/* probably add more stuff...esp for full WAVE-EX support */
+} PSF_PROPS;
+
+
+/*************** PUBLIC FUNCS */
+
+/* init sfs system. return 0 for success */
+int psf_init(void);
+/* close sfs-system. Does auto cleanup of open files, etc. return 0 for success */
+int psf_finish(void);
+/* Create soundfile from props.
+   Supports clipping or non-clipping of floats to 0dbFS, 
+   set minimum header (or use PEAK)
+   returns Sf descriptor >= 0, or some PSF_E_*  on error.
+*/
+/* using WIN32, it is possible to share for reading, but not under ANSI */
+/* maybe we just abandon all that */ 
+/* TODO (?): enforce non-destructive creation */
+int psf_sndCreate(const char *path,const PSF_PROPS *props, int clip_floats,int minheader,int mode);
+
+
+/* open existing soundfile. Receive format info in props. Supports auto rescale from PEAK
+   data, with floats files. Only RDONLY access supported.
+   Return sf descriptor >= 0, or some PSF_E_+ on error.
+  */
+int psf_sndOpen(const char *path,PSF_PROPS *props, int rescale);
+/*snd close. Updates PEAK data if used. return 0 for success, or some PSF_E_+ on error.*/
+int psf_sndClose(int sfd);
+
+/* all data read/write is counted in multi-channel sample 'frames', NOT in raw samples.*/
+
+/* get size of file, in m/c frames. Return size, or PSF_E_BADARG on bad sfd  */
+int psf_sndSize(int sfd);
+
+/* write m/c frames of floats. this updates internal PEAK data automatically.
+   return num frames written, or  some PSF_E_* on error. 
+  */
+int psf_sndWriteFloatFrames(int sfd, const float *buf, DWORD nFrames);
+int psf_sndWriteDoubleFrames(int sfd, const double *buf, DWORD nFrames);
+/* as above, with 16bit data */
+int psf_sndWriteShortFrames(int sfd, const short *buf, DWORD nFrames);
+
+/* 	get current m/c frame position in file, or PSF_E_BADARG with bad sfd*/    
+int psf_sndTell(int sfd);
+
+/* m/c frame wrapper for stdio fseek. return 0 for success. Offset counted in m/c frames.
+ seekmode must be one of PSF_SEEK_* options, to ensure correct mapping to SEEK_CUR, etc */
+int psf_sndSeek(int sfd,int offset,int seekmode);
+
+/* read m/c sample frames into floats buffer. return nFrames, or some PSF_E_*.
+    if file opened with rescale = 1, over-range floats data, as indicated by PEAK chunk,
+	is automatically scaled to 0dbFS.
+	NB we could add a facility to define 'headroom', as some level below 0dBFS.
+ */
+int psf_sndReadFloatFrames(int sfd, float *buf, DWORD nFrames);
+int psf_sndReadDoubleFrames(int sfd, double *buf, DWORD nFrames);
+/* can add ReadShortFrames if we really need it! */
+
+int psf_sndReadPeaks(int sfd,PSF_CHPEAK peakdata[],MYLONG *peaktime);
+
+/* find the soundfile format from the filename extension */
+/* currently supported: .wav, .aif, .aiff,.aifc,.afc, .amb */
+psf_format psf_getFormatExt(const char *path);
+
+
+/* set/unset dither. 
+   Returns 0 on success, -1 if error (unrecognised type, or read-only)
+   no-op for input files
+*/
+
+int psf_sndSetDither(int sfd,unsigned int dtype);
+/* get current dither setting */
+int psf_sndGetDither(int sfd);
+
+/* RWD NEW Nov 2009 */
+int psf_speakermask(int sfd);
+
+/* Feb 2010 */
+int psf_getWarning(int sfd,const char** warnstring);
+
+/* Sept 2010  return 1 for true, 0 for false */
+    /* NB tests up to 32bit size for now! */
+int is_legalsize(unsigned long nFrames, const PSF_PROPS *props);
+    
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 81 - 0
dev/externals/mctools/CMakeLists.txt

@@ -0,0 +1,81 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.8 -Dunix -fomit-frame-pointer -funroll-loops")
+  set(CMAKE_CXX_FLAGS "-O2 -Wall -mmacosx-version-min=10.8 -Dunix -fomit-frame-pointer -funroll-loops -std=c++11 -stdlib=libc++")
+#  SET(CMAKE_EXE_LINKER_FLAGS "-static")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -fomit-frame-pointer  -funroll-loops")
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+  endif()
+endif()
+
+link_directories(../lib)
+
+include_directories(../../../include ../include)
+
+add_executable(abfpan abfpan.cpp)
+target_link_libraries(abfpan portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(abfpan)
+
+add_executable(abfpan2 abfpan2.cpp)
+target_link_libraries(abfpan2 portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(abfpan2)
+
+add_executable(abfdcode abfdcode.cpp)
+target_link_libraries(abfdcode portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(abfdcode)
+
+add_executable(fmdcode fmdcode.c fmhfuncs.c)
+target_link_libraries(fmdcode portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(fmdcode)
+
+add_executable(channelx channel.c)
+target_link_libraries(channelx portsf sfsys m ${EXTRA_LIBRARIES})
+
+my_install(channelx)
+
+add_executable(chorder chorder.c)
+target_link_libraries(chorder portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(chorder)
+
+add_executable(chxformat chxformat.c)
+target_link_libraries(chxformat portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(chxformat)
+
+add_executable(copysfx copysf.c)
+target_link_libraries(copysfx portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(copysfx)
+
+add_executable(interlx interlx.c)
+target_link_libraries(interlx portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(interlx)
+
+add_executable(nmix nmix.c)
+target_link_libraries(nmix portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(nmix)
+
+add_executable(njoin njoin.c)
+target_link_libraries(njoin portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(njoin)
+
+add_executable(rmsinfo rmsinfo.cpp)
+target_link_libraries(rmsinfo portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(rmsinfo)
+
+add_executable(sfprops sfprops.c)
+target_link_libraries(sfprops portsf sfsys ${EXTRA_LIBRARIES})
+
+my_install(sfprops)
+

+ 189 - 0
dev/externals/mctools/abfdcode.cpp

@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+//abfdcode.cpp  : write mono wave file into ambisonic B-format
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "portsf.h"
+
+#ifdef unix
+/* in portsf.lib */
+extern  "C" {
+    int stricmp(const char *a, const char *b);
+}
+#endif
+
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;	
+} ABFSAMPLE;
+
+int main(int argc,char *argv[])
+{
+	int i,ifd, ofd;
+	long outchans = 4;
+	long outsize;
+	char *sfname;
+	bool write_wavex = false;
+	ABFSAMPLE abfsample;
+	float frame[4];
+	PSF_PROPS props;
+	PSF_CHPEAK peaks[4];
+	long inchans;
+    MYLONG peaktime;
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.2.1\n");
+        return 0;
+    }
+    
+	if(argc < 3){
+		printf("\nCDP MCTOOLS: ABFDCODE v 1.2.1: CDP 1999,2004,2005\n"
+				"Horizontal B Format Decoder\n"
+				"usage: abfdcode [-x] infile outfile\n"
+				"       -x    :   write WAVE_EX (Quad) outfile format\n"
+				"                (requires .wav extension)\n"
+				"NB: infile must have 3 or 4 channels\n"
+                "UPDATE 2009: This program is now replaced by FMDCODE, \n"
+                "and may be omitted in future updates to the Toolkit.\n"
+                "For this task use fmdcode with e.g. layout 4.\n"
+				);
+		return 1;
+	}
+	while(argv[1][0] =='-'){		
+		switch(argv[1][1]){
+		case('x'):
+			write_wavex = true;
+			break;
+		default:
+			fprintf(stderr,"abfdecode: error: illegal flag option %s\n",argv[1]);
+			return 1;
+		}
+		argc--; argv++;
+	}
+	if(argc < 3){
+		fprintf(stderr,"CDP MCTOOLS: ABFDCODE.EXE: CDP 1999,2005\nHorizontal B Format Decoder\nusage: abfdcode [-x] infile outfile\n");
+		return 1;
+	}
+	if(psf_init()){
+		fprintf(stderr,"abfdcode: startup failure.\n");
+		return 1;
+	}
+	
+	sfname = argv[2];
+	
+	ifd = psf_sndOpen(argv[1],&props,0);
+	if(ifd < 0){
+		fprintf(stderr,"unable toopen infile %s\n",argv[1]);
+		return 1;
+	}
+
+	inchans = props.chans;
+	if(!(inchans == 3 || inchans == 4)){
+		fprintf(stderr,"Sorry: infile is not first-order\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+
+	outsize = psf_sndSize(ifd);
+	if(outsize <= 0){
+		fprintf(stderr,"infile is empty!\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+	
+	props.chformat = STDWAVE;
+	props.chans = 4;
+    if(!is_legalsize(outsize,&props)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+    
+    
+	if(write_wavex){
+		props.chformat = MC_QUAD;
+		props.format = PSF_WAVE_EX;
+	}
+	//ofd = sndcreat_formatted(sfname,outsize * outchans,stype,outchans,srate,CDP_CREATE_NORMAL);
+	ofd = psf_sndCreate(sfname,&props,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		fprintf(stderr,"can't create outfile %s.\n",sfname);
+		psf_sndClose(ifd);
+		return 1;
+	}
+	
+
+	int got, halfsec = props.srate / 2;
+	unsigned int framepos = 0;
+	printf("\ndoing b-format decoding:\n");
+	while((got = psf_sndReadFloatFrames(ifd,(float *) &abfsample,1))==1){
+		//this_samp	= 0.5 * p_iosc->tick(1000);
+		float aw = abfsample.W;
+		float ax = abfsample.X * 0.707f;
+		float ay = abfsample.Y * 0.707f;
+		frame[0] = 0.3333f * (aw + ax + ay);
+		frame[1] = 0.3333f * (aw + ax - ay);
+		frame[2] = 0.3333f * (aw - ax + ay);
+		frame[3] = 0.3333f * (aw - ax - ay);
+
+		if(0 > psf_sndWriteFloatFrames(ofd,frame,1)){
+			fprintf(stderr,"error writing sample block  %ld\n",got * outchans);
+			psf_sndClose(ifd);
+			psf_sndClose(ofd);
+			return 1;
+		}
+		if(framepos % halfsec==0) {
+			printf("%.2lf secs\r",(double)framepos / (double) props.srate);
+            fflush(stdout);
+        }
+		framepos++;
+	}
+
+	if(got != 0){
+		fprintf(stderr,"warning: not all data was read\n");
+	}
+	printf("\n%.4lf secs\nWritten %d quad frames to %s\n",(double)framepos / (double) props.srate,framepos,sfname);
+    if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+        printf("PEAK values:\n");
+        for(i=0; i < outchans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                        val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                    val,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate); 
+            }
+        }
+              
+    }
+    psf_sndClose(ifd);
+	psf_sndClose(ofd);
+	psf_finish();
+	return 0;
+}

+ 180 - 0
dev/externals/mctools/abfdcode2.cpp

@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+//abfdcode2.cpp  : decode to std wavex speaker positions
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+extern "C"{
+#include <sfsys.h>
+
+}
+
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;	
+} ABFSAMPLE;
+
+int main(int argc,char *argv[])
+{
+	int ifd, ofd;
+	long outchans = 4;
+	long outsize;
+	char *sfname;
+	bool write_wavex = false;
+	ABFSAMPLE abfsample;
+	float frame[4];
+	SFPROPS props;
+	CHPEAK peaks[4];
+	long inchans;
+
+	if(argc < 3){
+		printf("\nCDP MCTOOLS: ABFDCODE2 v 1.0: RWD,CDP 2009\n"
+				"Horizontal B Format Decoder\n"
+				"usage: abfdcode2 infile outfile layout\n"
+				"       (wavex layouts require .wav extension)\n"
+				
+				);
+		return 1;
+	}
+	while(argv[1][0] =='-'){		
+		switch(argv[1][1]){
+		case('x'):
+			write_wavex = true;
+			break;
+		default:
+			fprintf(stderr,"abfdecode: error: illegal flag option %s\n",argv[1]);
+			return 1;
+		}
+		argc--; argv++;
+	}
+	if(argc < 3){
+		fprintf(stderr,"CDP MCTOOLS: ABFDCODE.EXE: CDP 1999,2005\nHorizontal B Format Decoder\nusage: abfdcode [-x] infile outfile\n");
+		return 1;
+	}
+	if(sflinit("abfdcode")){
+		fprintf(stderr,"unable to initialize sfsys\n");
+		return 1;
+	}
+	
+	sfname = argv[2];
+	
+	ifd = sndopenEx(argv[1],0,CDP_OPEN_RDONLY);
+	if(ifd < 0){
+		fprintf(stderr,"unable toopen infile %s\n",argv[1]);
+		return 1;
+	}
+
+	if(!snd_headread(ifd,&props)){
+		fprintf(stderr,"unable to read infile header data\n");
+		sndcloseEx(ifd);
+		return 1;
+	}
+	if(!props.type==wt_wave){
+		fprintf(stderr,"infile is not a soundfile\n");
+		sndcloseEx(ifd);
+		return 1;
+	}
+	inchans = props.chans;
+	if(!(inchans == 3 || inchans == 4)){
+		fprintf(stderr,"Sorry: infile is not first-order\n");
+		sndcloseEx(ifd);
+		return 1;
+	}
+
+	outsize = sndsizeEx(ifd) / inchans;
+	if(outsize <= 0){
+		fprintf(stderr,"infile is empty!\n");
+		sndcloseEx(ifd);
+		return 1;
+	}
+	
+
+	//srate = props.srate;
+	//stype = props.samptype == FLOAT32 ? SAMP_FLOAT : SAMP_SHORT;
+	props.chformat = STDWAVE;
+	props.chans = 4;
+	if(write_wavex){
+		props.chformat = MC_QUAD;
+		props.format = WAVE_EX;
+	}
+	//ofd = sndcreat_formatted(sfname,outsize * outchans,stype,outchans,srate,CDP_CREATE_NORMAL);
+	ofd = sndcreat_ex(sfname,outsize * outchans,&props,SFILE_CDP,CDP_CREATE_NORMAL);
+	if(ofd < 0){
+		fprintf(stderr,"can't create outfile %s : %s\n",sfname,sferrstr());
+		//delete p_iosc;
+		//delete p_sintable;
+		sndcloseEx(ifd);
+		return 1;
+	}
+	peaks[0].value = peaks[1].value = peaks[2].value = peaks[3].value = 0.0f;
+	peaks[0].position = peaks[1].position = peaks[2].position = peaks[3].position = 0;
+
+	int got,quartersec = props.srate / 4;
+	unsigned int framepos = 0;
+	printf("\ndoing b-format decoding:\n");
+	while((got = fgetfbufEx((float *) &abfsample,inchans,ifd,0))==inchans){
+		int i;
+		//this_samp	= 0.5 * p_iosc->tick(1000);
+		float aw = abfsample.W;
+		float ax = abfsample.X * 0.707f;
+		float ay = abfsample.Y * 0.707f;
+		frame[0] = 0.3333f * (aw + ax + ay);
+		frame[1] = 0.3333f * (aw + ax - ay);
+		frame[2] = 0.3333f * (aw - ax + ay);
+		frame[3] = 0.3333f * (aw - ax - ay);
+
+		if(0 > fputfbufEx(frame,outchans,ofd)){
+			fprintf(stderr,"error writing sample block  %ld\n",got * outchans);
+			sndcloseEx(ifd);
+			sndcloseEx(ofd);
+			return 1;
+		}
+		for(i=0;i < 4;i++){
+			float val;
+			val= (float)fabs(frame[i]);
+			if(val > peaks[i].value) {
+				peaks[i].value = val;
+				peaks[i].position = framepos;
+			}
+		}
+		if(framepos % quartersec==0)
+			printf("%.2lf secs\r",(double)framepos / (double) props.srate);
+		framepos++;
+	}
+
+	if(got != 0){
+		fprintf(stderr,"warning: not all data was read\n");
+	}
+	printf("\n%.4lf secs\nWritten %d quad frames to %s\n",(double)framepos / (double) props.srate,framepos,sfname);
+	printf("PEAK values:\n");
+	printf("CH 1: %.4f at frame %ld:\t%.4f secs\n",peaks[0].value,peaks[0].position,(double)peaks[0].position / (double) props.srate);
+	printf("CH 2: %.4f at frame %ld:\t%.4f secs\n",peaks[1].value,peaks[1].position,(double)peaks[1].position / (double) props.srate);
+	printf("CH 3: %.4f at frame %ld:\t%.4f secs\n",peaks[2].value,peaks[2].position,(double)peaks[2].position / (double) props.srate);
+	printf("CH 4: %.4f at frame %ld:\t%.4f secs\n",peaks[3].value,peaks[3].position,(double)peaks[3].position / (double) props.srate);
+	sndputpeaks(ofd,4,peaks);
+	sndcloseEx(ifd);
+	sndcloseEx(ofd);
+	sffinish();
+	return 0;
+}

+ 276 - 0
dev/externals/mctools/abfpan.cpp

@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+//abfpan.cpp  : write mono wave file into ambisonic B-format
+// Dec 2005 support .amb extension, and 3ch output
+// Jan 2010: corrected usage message about -b and -x
+// Nov 2013: updated usage message formatting, infile must be mono
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "portsf.h"
+
+#ifdef unix
+/* in portsf.lib */
+extern "C" {
+    int stricmp(const char *a, const char *b);
+}
+#endif
+
+#ifndef TWOPI
+#define TWOPI	(6.283185307)
+#endif
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;	
+} ABFSAMPLE;
+
+
+enum {ARG_PROGNAME, ARG_INFILE, ARG_OUTFILE,ARG_STARTPOS, ARG_ENDPOS,ARG_NARGS};
+void usage()
+{
+	fprintf(stderr,"CDP MCTOOLS V1.5.3 (c) RWD,CDP 2009,2010,2013\n"
+				   "ABFPAN:  apply fixed or orbiting 1st-order B-Format pan to infile\n");
+	fprintf(stderr,"usage : abfpan [-b][-x][-oN] infile outfile startpos endpos\n");
+	fprintf(stderr,"    infile:   input soundfile, must be mono\n");
+	fprintf(stderr,"    startpos: 0.0 <= startpos <= 1.0 (0.0 and 1.0 = Centre Front)\n");
+	fprintf(stderr,"    endpos:   endpos < 0.0 gives anticlockwise rotation,\n"
+				   "                endpos > 0.0 gives clockwise rotation.\n"
+				   "                Units give number of revolutions, fraction gives final position\n"
+				   "                Set endpos = startpos for fixed pan\n");
+	fprintf(stderr,"    -b:       write output as horizontal Ambisonic B-format (.amb),\n"
+                   "                (i.e. bypass B-Format decoding).\n"
+			       "    -x:       write (WAVE_EX) format file (.wav). \n"			   
+				   "                Default: write standard m/c soundfile (WAVE,AIFF).\n"
+                   "                NB: -b overrides -x - no meaning in using both.\n"
+				   "    -oN:      number of B-Format output channels: N = 3 or 4 only.\n"
+				   "                Default: write 4-ch file.\n"
+                   "    See also ABFPAN2 for 2nd order version with fixed height.\n"
+	);
+    fprintf(stderr,"\n");
+}
+
+
+
+#define SYN_BUFLEN	(32768)
+#define TABLEN (1024)
+int main(int argc,char *argv[])
+{
+	int ifd, ofd;
+	int srate = 44100;
+	int outchans = 4;
+    MYLONG peaktime;
+	long outsize;	
+	float this_samp;
+	char *sfname;
+	double angle_incr;
+	double start_angle = 0.0;
+	double startpos,endpos;
+	
+	ABFSAMPLE abfsample;
+	PSF_PROPS props;
+	PSF_CHPEAK peaks[4];
+
+	bool write_bformat = false;
+	bool write_wavex = false;
+	int iarg = outchans;
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.5.2.\n");
+        return 0;
+    }
+    
+	if(argc < 5){
+		usage();
+		return 1;
+	}
+
+	if(psf_init()){
+		fprintf(stderr,"abfpan2: Startup failure.\n");
+		return 1;
+	}
+	
+	while(argv[1][0] =='-'){
+		
+		switch(argv[1][1]){
+		case('b'):
+			write_bformat = true;
+			break;
+
+		case('x'):
+			write_wavex = true;
+			break;
+		case('o'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"abfpan: error: -o flag requires channel value\n");
+				return 1;
+			}
+			iarg = atoi(&(argv[1][2]));
+			if(!(iarg == 3 || iarg == 4)){
+				fprintf(stderr,"abfpan: error: invalid channel value for -o flag\n");
+				return 1;
+			}
+
+			break;
+		default:
+			fprintf(stderr,"abfpan: error: illegal flag option %s.\n",argv[1]);
+			return 1;
+		}
+
+		argc--; argv++;
+	}
+
+	if(argc != 5){
+		usage();
+		return 1;
+	}
+
+	if(write_bformat)
+		outchans = iarg;
+    
+	sfname = argv[ARG_INFILE];
+	startpos = atof(argv[ARG_STARTPOS]);
+	if(startpos < 0.0 || startpos > 1.0){
+		fprintf(stderr,"abfpan: startpos %.4lf out of range: must be between 0.0 and 1.0.\n",startpos);
+		return 1;
+	}
+
+	endpos = atof(argv[ARG_ENDPOS]);
+
+
+
+	ifd = psf_sndOpen(sfname,&props,0);
+	if(ifd < 0){
+		fprintf(stderr,"abfpan: unable to open infile %s.\n",argv[1]);
+		return 1;
+	}
+	if(props.chans != 1){
+		fprintf(stderr,"abfpan: infile must be mono.\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+
+	outsize = psf_sndSize(ifd);
+	if(outsize <= 0){
+		fprintf(stderr,"abfpan: infile is empty!\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+    srate = props.srate;
+	props.chans = outchans;
+    if(!is_legalsize(outsize,&props)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+                
+	start_angle = - (TWOPI * startpos);	   //we think of positive as clockwise at cmdline!
+	angle_incr = TWOPI / outsize;
+	angle_incr *= (endpos - startpos);
+	
+	if(write_wavex){
+		props.format = PSF_WAVE_EX;
+		props.chformat = MC_QUAD;
+	}
+	if(write_bformat)
+		props.chformat = MC_BFMT;
+	ofd = psf_sndCreate(argv[ARG_OUTFILE],&props,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		fprintf(stderr,"abfpan: can't create outfile %s.\n",argv[ARG_OUTFILE]);	
+		return 1;
+	}
+
+	int i,got;
+	int half_sec = srate / 4;
+	long total_frames = 0;
+	double d_srate = (double)srate;
+
+	abfsample.Z = 0.0f;
+	for(i=0; i < outchans; i++){
+		peaks[i].val = 0.0f;
+		peaks[i].pos = 0;
+	}
+	//should make one 360deg rotate over duration
+	//TODO: make lookup_sin and lookup_cos ugens, taking angle arg
+	if(write_bformat)
+		printf("\nWriting B-Format file:\n");
+	else
+		printf("\n");
+	while((got = psf_sndReadFloatFrames(ifd,&this_samp,1))==1){		
+		abfsample.W = (float) (this_samp * 0.707);
+        abfsample.X = (float) (this_samp * cos(start_angle));
+		abfsample.Y	= (float) (this_samp * sin(start_angle));
+
+		//set this for periphonic; otherwise = 0.0 for horizontal_only
+		//abfsample.Z = (float) (this_samp*0.25);
+		if(!write_bformat){
+			//decode into quad file
+			float aw = abfsample.W;
+			float ax = abfsample.X * 0.707f;
+			float ay = abfsample.Y * 0.707f;
+            /* ignore wxyz names  - this is now handy container for quad speaker feeds*/
+			abfsample.W = 0.33333f * (aw + ax + ay); /* front L */
+			abfsample.X = 0.33333f * (aw + ax - ay); /* front R */
+			abfsample.Y = 0.33333f * (aw - ax + ay); /* rear  L */
+			abfsample.Z = 0.33333f * (aw - ax - ay); /* rear  R */
+		}
+		if(0 > psf_sndWriteFloatFrames(ofd,(float*)  &abfsample,1)){
+			fprintf(stderr,"error writing abf sample frame  %ld\n",total_frames);
+			return 1;
+		}
+		start_angle -= angle_incr;
+		total_frames++;		
+		if(total_frames % half_sec ==0) {
+			printf("%.2lf secs\r",(double)total_frames / d_srate);
+            fflush(stdout);
+        }
+	}
+
+	if(got != 0){
+		fprintf(stderr,"abfpan: warning: not all data was read.\n");
+	}
+
+	printf("%.4lf secs\nwritten %ld %d-ch sample frames to %s\n",(double)outsize / d_srate,outsize,outchans, argv[ARG_OUTFILE]);
+	if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+		printf("PEAK values:\n");
+		for(i=0; i < outchans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                       val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                        val, (unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate); 
+            }
+        }
+        
+	}
+	printf("\n");
+	psf_sndClose(ifd);
+	psf_sndClose(ofd);
+	psf_finish();
+	return 0;
+}

+ 296 - 0
dev/externals/mctools/abfpan2.cpp

@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+//abfpan2.cpp  : write mono wave file into ambisonic B-format
+// Dec 2005 support .amb extension, and 3ch output
+// OCT 2009 portsf version, with 2nd-order encoding
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <memory.h>
+#include "portsf.h"
+
+#ifndef M_PI
+#define M_PI (3.141592654)
+#endif
+#ifndef TWOPI
+#define TWOPI	(6.283185307)
+#endif
+
+#ifdef unix
+/* in portsf.lib */
+extern "C" {
+    int stricmp(const char *a, const char *b);
+}
+#endif
+
+enum {W,X,Y,Z,R,S,T,U,V};
+
+
+enum {ARG_PROGNAME, ARG_INFILE, ARG_OUTFILE,ARG_STARTPOS,ARG_ENDPOS,ARG_NARGS};
+
+void usage()
+{
+	fprintf(stderr,"\nCDP MCTOOLS V1.0.1 beta (c) RWD,CDP 2010"
+				   "\n\nABFPAN2:  apply fixed or orbiting 2nd-order B-Format pan to infile\n");
+	fprintf(stderr,"usage : abfpan2 [-gGAIN][-w] [-p[DEG]] infile outfile startpos endpos\n"
+                   "    infile : mono source.\n"
+                   "    outfile: 2nd order B-format output.\n"        
+	               "        0.0 <= startpos <= 1.0 (0.0 and 1.0 = Centre Front).\n");
+	fprintf(stderr,"    endpos : endpos < 0.0 gives anticlockwise rotation,\n"
+				   "            endpos > 0.0 gives clockwise rotation.\n"
+				   "            Units give number of revolutions, fraction gives final position\n"
+				   "            Set endpos = startpos for fixed pan\n"
+                   "    -gGAIN : scale infile amplitude by GAIN (GAIN > 0).\n"
+                   "    -w     : Write standard soundfile (wave, aiff)\n"
+                   "            Default: WAVEX B-Format; use .amb extension.\n"
+                   "    -p[DEG]: write full 9-channel (periphonic) B-Format file.\n"
+                   "        Default: write 5-channel (2nd-order horizontal) file.\n"
+                   "        DEG:  optional fixed height argument (degrees).\n"
+                   "             Range = -180  to +180,\n"
+                   "             where  -90 = nadir, +90 = zenith (directly above).\n"
+                   "             Default: DEG=0;  height channels (Z,R,S,T)  will be empty.\n"
+                   "    NB: this program does not create a decoded output.\n"
+                   "    Use FMDCODE to decode to choice of speaker layouts.\n"
+				   );
+    fprintf(stderr,"\n");
+}
+
+int main(int argc,char *argv[])
+{
+	int i,got,ifd, ofd;
+	long srate = 44100;
+	long outchans = 5;
+    long do_peri = 0;
+    int write_wav = 1;
+	MYLONG peaktime;
+	long outsize;	
+	float this_samp;
+	int half_sec;
+	long total_frames;
+	double d_srate;
+	char *sfname;
+	double angle_incr;
+	double start_angle = 0.0;
+	double startpos,endpos;
+    double gain = 1.0;
+    double degree=0.0,elevation = 0.0;  
+	
+	float abfsample[9];
+    float outframe[5];
+    float *p_frame;
+    
+	PSF_PROPS props;
+	PSF_CHPEAK *peaks = NULL;
+	
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.0.1b\n");
+        return 0;
+    }
+    
+	if(argc < 5){
+		usage();
+		return 1;
+	}
+
+	if(psf_init()){
+		fprintf(stderr,"\nabfpan2: Startup failure");
+		return 1;
+	}
+	
+	while(argv[1][0] =='-'){
+		switch(argv[1][1]){
+        case 'g':
+            if(argv[1][2] == '\0'){
+                fprintf(stderr,"abfpan2 Error: -g flag requires a value.\n");
+                return 1;
+            }
+            gain = atof(&argv[1][2]);
+            if(gain <= 0.0){
+                printf("abfpan2: gain value must be positive!\n");
+                return 1;
+            }
+            break;
+        case 'p':
+            if(argv[1][2] != '\0'){
+                degree = atof(&argv[1][2]);
+                if(degree < -180.0 || degree > 180.0){
+                    fprintf(stderr,"-p: degree value out of range.\n");
+                    return 1;
+                }
+                elevation = degree * (M_PI  / 180.0);
+            }
+            outchans = 9;
+            do_peri = 1;
+            break;
+        case 'w':
+            write_wav = 0;
+            break;
+		default:
+			fprintf(stderr,"\nabfpan: error: illegal flag option %s",argv[1]);
+			return 1;
+		}
+		argc--; argv++;
+	}
+
+	if(argc < ARG_NARGS ){
+		usage();
+		return 1;
+	}
+
+	sfname = argv[ARG_INFILE];
+	startpos = atof(argv[ARG_STARTPOS]);
+	if(startpos < 0.0 || startpos > 1.0){
+		fprintf(stderr,"abfpan2: startpos %.4lf out of range: must be between 0.0 and 1.0\n",startpos);
+		return 1;
+	}
+
+	endpos = atof(argv[ARG_ENDPOS]);
+	ifd = psf_sndOpen(sfname,&props,0);
+	if(ifd < 0){
+		fprintf(stderr,"abfpan2: unable toopen infile %s.\n",sfname);
+		return 1;
+	}
+
+    if(props.chans != 1){
+		fprintf(stderr,"abfpan2: infile must be mono.\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+
+	outsize = psf_sndSize(ifd);
+	if(outsize <= 0){
+		fprintf(stderr,"abfpan2: infile is empty!\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+    srate = props.srate;
+	props.chans = outchans;
+    if(!is_legalsize(outsize,&props)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+    
+	start_angle = - (TWOPI * startpos);	   //we think of positive as clockwise at cmdline!
+	angle_incr = TWOPI / outsize;
+	angle_incr *= (endpos - startpos);
+    
+	
+    if(write_wav == 0)
+        props.format = PSF_STDWAVE;
+    else {
+        printf("Writing B-Format file.\n");
+	    props.format = PSF_WAVE_EX;
+        props.chformat = MC_BFMT;
+    }
+    
+    peaks = (PSF_CHPEAK*)  malloc(sizeof(PSF_CHPEAK) * outchans);
+    memset(peaks,0,sizeof(PSF_CHPEAK) * outchans);
+    
+    
+	ofd = psf_sndCreate(argv[ARG_OUTFILE],&props,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		fprintf(stderr,"abfpan2: can't create outfile %s.\n",sfname);	
+		return 1;
+	}
+
+	half_sec = srate / 2;
+	total_frames = 0;
+	d_srate = (double)srate;
+	
+	//should make one 360deg rotate over duration
+	//TODO: make lookup_sin and lookup_cos ugens, taking angle arg
+	
+    if(do_peri)
+        p_frame = abfsample;
+    else
+        p_frame = outframe;
+    
+	while((got = psf_sndReadFloatFrames(ifd,&this_samp,1))==1){
+		double x,y,z,xx,yy,zz;
+        x = cos(start_angle);
+        y = sin(start_angle);
+        if(elevation ==0.0)
+            z = 0.0;
+        else
+            z = sin(elevation);
+        xx = x * x;
+        yy = y * y;
+        zz = z * z;
+        this_samp *= gain;
+        if(do_peri) {
+            abfsample[W] = (float) (this_samp * 0.7071);
+            abfsample[X] = (float) (this_samp * x);
+            abfsample[Y] = (float) (this_samp * y);
+            abfsample[Z] = (float) (this_samp * z);
+            abfsample[R] = (float) (this_samp * (1.5 * zz - 0.5));  /// ????
+            abfsample[S] = (float) (this_samp * (2.0 * z * x));
+            abfsample[T] = (float) (this_samp * (2.0 * y * z));
+            abfsample[U] = (float) (this_samp * (xx-yy));
+            abfsample[V] = (float) (this_samp * (2.0 * x * y));
+        }
+        else{
+            outframe[0]  = (float) (this_samp * 0.7071);
+            outframe[1]  = (float) (this_samp * x);
+            outframe[2]  = (float) (this_samp * y);
+            outframe[3] = (float) (this_samp * (xx-yy));
+            outframe[4] = (float) (this_samp * (2.0 * x * y));
+        }
+            
+		if(0 > psf_sndWriteFloatFrames(ofd, p_frame,1)){
+			fprintf(stderr,"abfpan2: error writing frame %ld\n",total_frames);
+			return 1;
+		}
+		start_angle -= angle_incr;
+		total_frames++;		
+		if(total_frames % half_sec ==0) {
+			printf("%.2lf secs\r",(double)total_frames / d_srate);
+            fflush(stdout);
+        }
+	}
+	if(got != 0){
+		fprintf(stderr,"abfpan2: warning: not all data was read.\n");
+	}
+	printf("%.4lf secs\nWritten %ld frames to %s.\n",(double)total_frames / d_srate,total_frames, argv[ARG_OUTFILE]);
+	if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+		printf("PEAK values:\n");
+		for(i=0; i < outchans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                       val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                       val,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate); 
+            }
+        }
+        
+	}
+
+	psf_sndClose(ifd);
+	psf_sndClose(ofd);
+	psf_finish();
+	return 0;
+}

+ 302 - 0
dev/externals/mctools/channel.c

@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+/* channelx.c */
+//RWD MCTOOLS, portsf version Oct 2009
+// nothing special, but use PEAK chunk (reading it where available)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <memory.h>
+#include <ctype.h>
+#include <portsf.h>
+
+#ifndef _MAX_PATH
+#define _MAX_PATH (1024)
+#endif
+#define MAXFILES 16
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+
+void usage(void);
+//RWD.7.99 extended to get file extension from base name
+// so can be used without CDP_SOUND_EXT
+static void getbody(char*,char*,char *);
+
+void
+usage(void)
+{
+    fprintf(stderr,"\nCDP MCTOOLS: CHANNELX V1.6 (c) RWD, CDP 2010\n");
+    fprintf(stderr,
+            "Extract all channels from m/c file\n"
+            "usage: channelx [-oBASENAME] infile chan_no [chan_no...]\n"
+            //"\nusage:  channelx [-oBASENAME] infile"
+            "  -oBASENAME = base name (with extension) of outfiles (appended *_cN for ch N)\n"
+            "(NB: Channels of WAVE-EX files are written as standard soundfiles)\n"
+            );
+}
+
+#if defined(unix)
+#define SEP     '/'
+#else
+#define SEP     '\\'
+#endif
+
+void getbody(char *filename,char *body,char *ext)
+{
+    char *fn, *sl;
+
+    if((sl = strrchr(filename, SEP)) != 0)
+        sl++;
+    else
+        sl = filename;
+
+    for(fn = sl; *fn != '.' && *fn != '\0'; ) {
+        *body++ = *fn++;
+    }
+    *body++ = '\0';
+    //save extension
+    while(*fn != '\0')
+        *ext++ = *fn++;
+    *ext = '\0';
+}
+
+
+
+
+
+int main(int argc,char *argv[])
+{
+
+    int ifd = 0;                            /* Infile descriptor */
+    int ofd[MAXFILES];                      /* Outfile descriptors */
+    int i;                      /* Assorted indexes */
+    int inchans=1;                          /* Infile channels */
+    char filename[_MAX_PATH];       /* Infilename */
+    int select[MAXFILES];           /* Flags for each channel to MAXFILES */
+    int lastchan = 0;                       /* Maximum Requested channels */
+    int numchans = 0;                       /* number of channels extracted */
+    char body[100];                         /* Infile name prefix */
+    char ext[8];
+    char outext[8];                         /* output extension: to change .amb to .wav */
+    char *p_dot;
+    char ofile[_MAX_PATH];          /* Raw outfile name */
+    PSF_PROPS inprops;
+    int nframes =0, total_frames = 0;
+    float *frame = NULL;
+    int writefiles = 0;                     /* try to keep working even if one file fails...*/
+
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.6.\n");
+        return 0;
+    }
+    if(argc<2) {
+        usage();
+        return 1;
+    }
+
+    if(psf_init()) {
+        fprintf(stderr,"Startup failure.\n");
+        return 1;
+    }
+
+    body[0] = '\0';
+    ext[0] = '\0';
+
+    while(argv[1] != 0 && argv[1][0] == '-') {
+        if(strncmp(argv[1], "-o", 2) == 0) {
+            if(body[0] != '\0')
+                usage();
+            strcpy(body, &argv[1][2]);
+        }
+        argv++;
+        argc--;
+    }
+    if(argc < 2) {
+        usage();
+        return 1;
+    }
+
+    if(argv[1] != 0) {
+        sprintf(filename,"%s",argv[1]);
+        if((ifd = psf_sndOpen(filename,&inprops,0)) < 0) {
+            fprintf(stderr,"Channelx: Cannot open input file %s\n",argv[1]);
+            return 1;
+        }
+    }
+    argv++;
+
+    if(inprops.chans > MAXFILES){
+        fprintf(stderr,"Channelx: infile has too many channels!\n");
+        psf_sndClose(ifd);
+        return 1;
+    }
+    inchans = inprops.chans;
+    nframes = psf_sndSize(ifd);
+    if(nframes < 0){
+        fprintf(stderr,"Channelx: error reading infile size\n");
+        psf_sndClose(ifd);
+        return 1;
+    }
+    if(nframes==0){
+        fprintf(stderr,"Channelx: infile contains no data!\n");
+        psf_sndClose(ifd);
+        return 1;
+    }
+    nframes /= inchans;
+    //always create a standard file!
+    if(inprops.format == PSF_WAVE_EX) {
+        inprops.format = PSF_STDWAVE;
+        inprops.chformat = STDWAVE;
+    }
+    //that's all we need to know, now set one output channel
+    inprops.chans = 1;
+
+    frame = calloc(inchans,sizeof(float));
+    if(frame == NULL){
+        puts("Channelx: no memory for internal sample data\n");
+        psf_sndClose(ifd);
+        return 1;
+    }
+
+    for(i=0;i<MAXFILES;i++){
+        select[i] = 0;
+        ofd[i] = -1;
+    }
+
+
+    while(argv[1] != 0) {
+        int chn;
+
+        if(sscanf(argv[1],"%d",&chn) < 1){
+            fprintf(stderr,
+                    "Channelx: illegal channel argument %s\n\n",argv[1]);
+            goto cleanup;
+        }
+
+        if((chn > inchans) || (chn < 1)){
+            fprintf(stderr,"Channelx: channel argument out of range\n\n");
+            goto cleanup;
+        }
+        //check for duplicates
+        if(select[chn-1]==0){
+            select[chn-1] = 1;
+            if(chn > lastchan)
+                lastchan = chn;
+            numchans++;
+        }
+        else
+            fprintf(stderr,"Channelx: WARNING: duplicate channel number %d\n",chn);
+
+        argv++;
+        argc--;
+    }
+    if(numchans < 1) {              //RWD
+        fprintf(stderr, "channelx: must extract at least one channel\n");
+        goto cleanup;
+    }
+
+    writefiles = numchans;
+
+    if(body[0] == '\0')
+        getbody(filename,body,ext);
+
+    else if((p_dot = strrchr(body,'.')) != NULL){
+        //extension set in body arg
+        *p_dot++ = '\0';
+        sprintf(ext,".%s",p_dot);
+    }
+    //trap ambisonic file; single channels must be wav
+    strcpy(outext,ext);
+    if(stricmp(outext,".amb")==0)
+        strcpy(outext,".wav");
+    for(i=0;i<inchans;i++){
+        if(select[i]){
+            sprintf(ofile, "%s_c%d%s", body, i+1,outext);
+
+
+            if((ofd[i] = psf_sndCreate(ofile,&inprops,0,0,PSF_CREATE_RDWR)) < 0) {
+                fprintf(stderr,"Channelx: Failed to create %s\n\n",ofile);
+                goto cleanup;
+            }
+        }
+    }
+
+    printf("Extracting %d channels from '%s':\n%d sample frames\n",numchans,filename,nframes);
+
+    //stopwatch(1);
+    for(i=0;i < nframes; i++){
+        int got;
+        if(writefiles==0)
+            goto cleanup;
+
+        if((got = psf_sndReadFloatFrames(ifd,frame,1)) < 1){
+            if(got < 0)
+                fprintf(stderr,"error reading data from infile, after %d frames\n",i);
+            //sndunlink all outfiles?
+            else
+                break;
+            //goto cleanup;
+        }
+
+        for(i=1;i<lastchan+1;i++){
+            if(select[i-1]){
+                if((psf_sndWriteFloatFrames(ofd[i-1],frame +(i-1),1))<0){
+                    fprintf(stderr,"Channelx: Write [channel %d] failed.\n\n",i);
+                    //RWD
+                    //sndunlink(ofd[i-1]);
+                    writefiles--;
+                    if(writefiles==0)
+                        break;
+                    else
+                        continue;       //may be other files we can still write to...
+                }
+            }
+        }
+        total_frames++;
+        if((total_frames % (inprops.srate/2)) == 0){
+            printf("%.2lf secs\r",(double)total_frames / (double)inprops.srate);
+            fflush(stdout);
+        }
+    }
+    printf("%.4lf secs\n",(double)total_frames / (double)inprops.srate);
+    printf("%d files created.\n",writefiles);
+
+ cleanup:
+    psf_sndClose(ifd);
+
+    for(i=0;i < MAXFILES; i++){
+        if(ofd[i] >= 0)
+            psf_sndClose(ofd[i]);
+    }
+
+
+    free(frame);
+    psf_finish();
+    return 0;
+}

+ 297 - 0
dev/externals/mctools/chorder.c

@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* chorder :  reorder channels in m/c file */
+/* Jan 2010: corrected behaviour with out of range order chars */
+/* July 2010 removed spurious premature call to usage() */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <portsf.h>
+
+/* set size of multi-channel frame-buffer */
+#define NFRAMES (1024)
+
+/* TODO define program argument list, excluding flags */
+enum {ARG_PROGNAME,ARG_INFILE,ARG_OUTFILE,ARG_ORDERSTRING,ARG_NARGS};
+#define N_BFORMATS (10)
+static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+
+void usage(void)
+{
+    fprintf(stderr,"\nCDP MCTOOLS: CHORDER V1.2 (c) RWD,CDP 2009,2010\n");	
+    fprintf(stderr,
+            "Reorder soundfile channels.\n"
+            "Usage:  chorder infile outfile orderstring\n"
+            "  orderstring = any combination of characters a-z inclusive.\n"
+            "  Infile channels are mapped in order as a=1,b=2...z=26\n"
+            "  (For example: channels in a 4-channel file are represented by the\n"
+            "    characters abcd; any other character is an error).\n"
+            "  Characters must be lower case, and may be used more than once.\n"
+            "  Duplicate characters duplicate the corresponding input channel.\n"
+            "  The zero character (0) may be used to set a silent channel.\n"
+            "  A maximum of 26 channels is supported for both input and output.\n"
+            "  NB: infile (WAVEX) speaker positions are discarded.\n"
+            "  The .amb extension is supported for the outfile.\n\n"
+            );
+}
+
+
+int main(int argc, char* argv[])
+{
+	PSF_PROPS inprops,outprops;									
+	long framesread;	
+	/* init all dynamic resources to default states */
+	unsigned int i;
+    int halfsec;
+	unsigned int framepos;
+    long outsize;
+    int ifd = -1,ofd = -1;
+	int error = 0;
+	PSF_CHPEAK* peaks = NULL;	
+	psf_format outformat =  PSF_FMT_UNKNOWN;
+	unsigned long nframes = 1;
+	float* inframe = NULL;
+	float* outframe = NULL;
+    float* orderptr[26];
+    char* argstring = NULL;
+    unsigned int rootchar = 'a';
+    unsigned int maxchar = 'z';
+    unsigned int  nchars,nzeros = 0;
+    unsigned int max_inchar;
+    MYLONG peaktime;
+	
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.2.\n");
+        return 0;
+    }
+
+	/* process any optional flags: remove this block if none used! */
+	if(argc > 1){
+		char flag;
+		while(argv[1][0] == '-') {
+			flag = argv[1][1];
+			switch(flag){
+			/*TODO: handle any  flag arguments here */
+			case('\0'):
+				printf("Error: missing flag name\n");
+				return 1;
+			default:
+				break;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	if(argc < ARG_NARGS){
+		printf("insufficient arguments.\n");
+		usage();
+		return 1;
+	}
+    
+    /* initial check of charstring */
+    argstring = argv[ARG_ORDERSTRING];
+    nchars = strlen(argstring);
+    if(nchars > 26) {
+        printf("error: order string too long.\n");
+        return 1;
+    }
+    
+	/*  always startup portsf */
+	if(psf_init()){
+		printf("unable to start portsf\n");
+		return 1;
+	}
+																							
+	ifd = psf_sndOpen(argv[ARG_INFILE],&inprops,0);															  
+	if(ifd < 0){
+		printf("Error: unable to open infile %s\n",argv[ARG_INFILE]);
+		error++;
+		goto exit;
+	}
+    outsize = psf_sndSize(ifd);
+	if(outsize <= 0){
+		fprintf(stderr,"chorder: infile is empty!\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+    inframe = (float*) malloc(nframes * inprops.chans * sizeof(float));
+	if(inframe==NULL){
+		puts("No memory!\n");
+		error++;
+		goto exit;
+	}
+    /* final validate and parse of charstring */
+    max_inchar = rootchar;
+    for(i=0;i < nchars;i++){
+        unsigned int thischar = argstring[i];
+//        printf("reading char %c (%d)\n",thischar,thischar);
+        unsigned int chindex;
+        if(thischar != '0' && (thischar < rootchar || thischar > maxchar)){
+            printf("illegal character in order string: %c\n",thischar);
+            goto exit;
+        }
+        if(thischar =='0'){
+//            printf("setting channel %d to zero.\n",i);
+            orderptr[i] = NULL;
+            nzeros++;
+        }
+        else{
+            if(thischar > max_inchar)
+                max_inchar = thischar;
+            chindex = thischar - rootchar;
+            orderptr[i] = &inframe[chindex];
+        }
+    }
+    if(nzeros==nchars)
+        printf("Warning: order string is all zeros - a silent file will be made!\n");
+    else{    
+        /* count inclusively! */
+        if(inprops.chans < (max_inchar - rootchar + 1)){
+            printf("File has %d channels; order string defines non-existent channels.\n",inprops.chans);
+            printf("For this file, maximum character is %c\n",rootchar+inprops.chans-1);
+            goto exit;
+        }
+    }
+	/* check file extension of outfile name, so we use correct output file format*/
+	outformat = psf_getFormatExt(argv[ARG_OUTFILE]);
+	if(outformat == PSF_FMT_UNKNOWN){
+		printf("outfile name %s has unsupported extension.\n"
+			"Use any of .wav, .aiff, .aif, .afc, .aifc, .amb\n",argv[ARG_OUTFILE]);
+		error++;
+		goto exit;
+	}
+	inprops.format = outformat;
+	outprops = inprops;
+	outprops.chans = nchars;
+    if(!is_legalsize(outsize,&outprops)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+    /* any speaker assignment etc invalidated! */
+    if(outformat == PSF_STDWAVE) {
+        outprops.chformat = MC_STD;
+        outprops.format =  PSF_WAVE_EX;
+    }
+    
+    if(outformat==PSF_WAVE_EX){
+        int matched = 0;
+        for(i=0;i < N_BFORMATS;i++)	{
+            if(inprops.chans == bformats[i]){
+                matched = 1;
+                break;
+            }
+        }
+        if(!matched){
+            printf("WARNING: No B-format definition for %d-channel file.\n",outprops.chans);
+        }
+        outprops.chformat = MC_BFMT;
+        outprops.format =  PSF_WAVE_EX;
+    }
+    
+    outframe = malloc(sizeof(float) * nchars);
+    
+	peaks  =  (PSF_CHPEAK*) malloc(outprops.chans * sizeof(PSF_CHPEAK));
+	if(peaks == NULL){
+		puts("No memory!\n");
+		error++;
+		goto exit;
+	}
+	ofd = psf_sndCreate(argv[ARG_OUTFILE],&outprops,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		printf("Error: unable to create outfile %s\n",argv[ARG_OUTFILE]);
+		error++;
+		goto exit;
+	}
+	
+    halfsec = inprops.srate / 2;
+	framepos = 0;
+	printf("processing....\n");									
+	
+	while ((framesread = psf_sndReadFloatFrames(ifd,inframe,1)) > 0){
+        float val;
+        for(i=0;i < nchars;i++){
+            if(orderptr[i] == NULL)
+                val = 0.0f;
+            else
+                val = *orderptr[i];
+            outframe[i] = val;
+        }
+		
+
+		if(psf_sndWriteFloatFrames(ofd,outframe,1) != 1){
+			printf("Error writing to outfile\n");
+			error++;
+			break;
+		}
+        if((framepos % halfsec) == 0){
+			printf("%.2lf secs\r",(double) framepos / (double) outprops.srate);
+            fflush(stdout);
+        }
+		framepos++;
+	}
+
+	if(framesread < 0)	{
+		printf("Error reading infile. Outfile is incomplete.\n");
+		error++;
+	}
+	printf("\n%.4lf secs\nWritten %d frames to %s\n",(double)framepos / (double) outprops.srate,framepos,argv[ARG_OUTFILE]);
+		
+	if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+		printf("PEAK values:\n");
+		for(i=0; i < outprops.chans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                       val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) outprops.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                       val,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) outprops.srate); 
+            }
+        }
+	}
+	printf("\n");
+exit:	 	
+	if(ifd >= 0)
+		psf_sndClose(ifd);
+	if(ofd >= 0)
+		psf_sndClose(ofd);
+	if(inframe)
+		free(inframe);
+    if(outframe)
+        free(outframe);
+	if(peaks)
+		free(peaks);
+
+	psf_finish();
+	return error;
+}

+ 760 - 0
dev/externals/mctools/chxformat.c

@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* chxformat.c */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <limits.h>
+#include <portsf.h>
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+enum {ARG_PROGNAME, ARG_INFILE,ARG_NARGS};
+enum {GUID_PCM,GUID_IEEE,GUID_AMB_PCM,GUID_AMB_IEEE};
+
+char* guidnames[] = {"PCM","PCM FLOAT","AMB PCM","AMB FLOAT"};
+
+
+#define REVDWBYTES(t)	( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
+#define REVWBYTES(t)	( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
+#define TAG(a,b,c,d)	( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
+
+#ifdef linux
+#define POS64(x) (x.__pos)
+#else
+#define POS64(x) (x)
+#endif
+
+#define WAVE_FORMAT_PCM			(0x0001)
+#define sizeof_WFMTEX  (40)
+typedef struct _GUID 
+{ 
+    unsigned int        Data1; 
+    unsigned short       Data2; 
+    unsigned short       Data3; 
+    unsigned char        Data4[8]; 
+} GUID; 
+
+
+typedef struct  {
+	WORD  wFormatTag; 
+    WORD  nChannels; 
+    DWORD nSamplesPerSec; 
+    DWORD nAvgBytesPerSec; 
+    WORD  nBlockAlign; 
+    WORD  wBitsPerSample; 
+
+} WAVEFORMAT;
+
+
+typedef struct { 
+    WORD  wFormatTag; 
+    WORD  nChannels; 
+    DWORD nSamplesPerSec; 
+    DWORD nAvgBytesPerSec; 
+    WORD  nBlockAlign; 
+    WORD  wBitsPerSample; 
+    WORD  cbSize; 
+} WAVEFORMATEX; 
+
+typedef struct {
+    WAVEFORMATEX    Format;				/* 18 bytes */
+    union {
+        WORD wValidBitsPerSample;       /* bits of precision  */
+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
+        WORD wReserved;                 /* If neither applies, set to */
+                                        /* zero. */
+    } Samples;
+    DWORD    dwChannelMask;				/* which channels are */
+                                        /* present in stream  */
+    GUID     SubFormat;
+} WAVEFORMATEXTENSIBLE;
+
+static const GUID  KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,
+								{0x80,
+								0x00,
+								0x00,
+								0xaa,
+								0x00,
+								0x38,
+								0x9b,
+								0x71}};
+
+static const GUID  KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003,0x0000,0x0010,
+								{0x80,
+								0x00,
+								0x00,
+								0xaa,
+								0x00,
+								0x38,
+								0x9b,
+								0x71}};
+
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_PCM = { 0x00000001, 0x0721, 0x11d3, 
+												{ 0x86,
+												0x44,
+												0xc8,
+												0xc1,
+												0xca,
+												0x0,
+												0x0,
+												0x0 } };
+
+
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = { 0x00000003, 0x0721, 0x11d3, 
+												{ 0x86,
+												0x44,
+												0xc8,
+												0xc1,
+												0xca,
+												0x0,
+												0x0,
+												0x0 } };
+
+
+#define WAVE_FORMAT_IEEE_FLOAT	(0x0003)
+#define WAVE_FORMAT_EXTENSIBLE	(0xfffe)
+
+static int compare_guids(const GUID *gleft, const GUID *gright)
+{
+	const char *left = (const char *) gleft, *right = (const char *) gright;
+	return !memcmp(left,right,sizeof(GUID));
+}
+
+
+static int wavDoRead(FILE* fp, void* buf, DWORD nBytes)
+{
+	DWORD got = 0;	
+	if((got = fread(buf,sizeof(char),nBytes,fp)) != nBytes) {
+        return 1;
+    }
+	return 0;
+}
+
+static int byte_order()					
+{						    
+  int   one = 1;
+  char* endptr = (char *) &one;
+  return (*endptr);
+}
+
+static void fmtSwapBytes(WAVEFORMATEX  *pfmt)
+{	
+	pfmt->wFormatTag	= (WORD) REVWBYTES(pfmt->wFormatTag);
+	pfmt->nChannels		= (WORD) REVWBYTES(pfmt->nChannels);
+	pfmt->nSamplesPerSec	= REVDWBYTES(pfmt->nSamplesPerSec);
+	pfmt->nAvgBytesPerSec	= REVDWBYTES(pfmt->nAvgBytesPerSec);
+	pfmt->nBlockAlign	= (WORD) REVWBYTES(pfmt->nBlockAlign);
+	pfmt->wBitsPerSample	= (WORD) REVWBYTES(pfmt->wBitsPerSample);	
+}
+
+
+
+void printmaskinfo()
+{
+    printf("WAVEX Speaker positions:\n");
+
+    
+printf(
+"FRONT LEFT             0x1\n"
+"FRONT RIGHT            0x2\n"
+"FRONT CENTER           0x4\n"
+"LOW FREQUENCY          0x8\n"
+"BACK LEFT              0x10\n"
+"BACK RIGHT             0x20\n"
+"FRONT LEFT OF CENTER   0x40\n"
+"FRONT RIGHT OF CENTER  0x80\n"
+"BACK CENTER            0x100\n"
+"SIDE LEFT              0x200\n"
+"SIDE RIGHT             0x400\n"
+"TOP CENTER             0x800\n"
+"TOP FRONT LEFT         0x1000\n"
+"TOP FRONT CENTER       0x2000\n"
+"TOP FRONT RIGHT        0x4000\n"
+"TOP BACK LEFT          0x8000\n"
+"TOP BACK CENTER        0x10000\n"
+"TOP BACK RIGHT         0x20000\n"
+       );
+printf("The value 0x80000000 is reserved.\n");
+    
+printf("Standard layouts:\n"
+
+"Mono       0x40        (64)\n"
+"Stereo     0x03         (3)\n"
+"Quad       0x33        (51)\n"
+"LCRS       0x107      (263)\n"
+"5.0        0x37        (55)\n"
+"5.1        0x3f        (63)\n"
+"7.1        0xff       (255)\n"
+"Cube       0x2d033 (184371)\n"
+       ); 
+}
+
+void usage(void)
+{
+    printf("CDP MCTOOLS: CHXFORMAT v1.0.1beta (c) RWD,CDP 2009\n");
+    printf("change GUID and/or speaker mask in WAVEX header of infile.\n");
+    printf("usage: chxformat [-m | [[-t] [-gGUID] [-sMASK]] filename\n");
+    printf(
+           " -gGUID : change GUID type between PCM and AMB.\n"
+           "          Plain WAVEX: GUID = 1\n"
+           "          AMB:         GUID = 2\n"
+           " -sMASK : change speaker position mask.\n"
+           "          MASK = 0:  unset channel mask\n"
+           "             else set to MASK\n"
+           "          MASK may be given in decimal or hex (prefix '0x').\n"
+           " -m     : (not in combination with other options)\n"
+           " NOTE: speaker positions are only supported for WAVEX PCM files.\n"
+           "       If GUID is set to 2, the -s option should not be used. Any existing\n"
+           "       speaker positions will be set to zero.\n"
+           " Type chxformat -m to see list of WAVEX mask values.\n"
+           " -t     : Test only: do not modify file.\n"
+           "  If only infile given, program prints current GUID type and speaker mask.\n"
+           "  In test mode, program checks file, prints current channel mask as binary,\n"
+           "   and new mask in binary, if -s option set.\n"
+           "  Otherwise: program modifies infile (\"destructively\") - use with care!\n"
+           );
+    
+}
+
+void binprintf(int val,int skipzeroes)
+{
+    unsigned int nbits = sizeof(int) * CHAR_BIT;
+    unsigned int i = 0;
+    unsigned int mask = 1 << (nbits-1); /* i.e. 0x80000000; */
+    // skip leading zeroes
+    if(skipzeroes){
+        for(;i < nbits;i++){
+            if(val&mask)
+                break;
+            mask>>=1;
+        }
+    }
+    for(;i < nbits;i++){
+        if(i > 0 && i%8 == 0)
+            printf(" ");
+        printf("%d",(val & mask) ? 1 : 0);
+        mask >>= 1;
+    }
+}
+
+int countbits(int val)
+{
+    unsigned int nbits = sizeof(int) * CHAR_BIT;
+    unsigned int i = 0;
+    int count = 0;
+    unsigned int mask = 1 << (nbits-1); /* i.e. 0x80000000; */
+    for(;i< nbits;i++){
+        if(val & mask)
+            count++;
+        mask >>= 1;
+    }
+    return count;
+}
+
+int isdec(int ch){
+    if(ch >= '0' && ch <= '9')
+        return 1;
+//    printf("isdec fail: %d\n",ch);
+    return 0;
+}
+
+int ishex(int ch){
+    
+    if((ch >='A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
+        return 1;
+//     printf("ishex fail: %c\n",ch);
+    return 0;
+}
+
+int validate_mask(const char* mask)
+{
+    int is_dec = 1,is_hex = 1;
+    size_t i,len;
+    char* pch;
+    if(mask==NULL)
+        return 0;
+    len = strlen(mask);
+    if(len==0)
+        return 0;
+    
+    pch = (char*) mask;
+    /*try hex */
+    if(len > 2 && *pch =='0'){
+        pch++;
+        if(*pch=='x' || *pch =='X'){ // prefix found
+            pch++;
+//            printf("got hex prefix\n");
+            for(i = 0;i < len-2;i++){
+//                printf("testing %d \n",pch[i]);
+                if(!(isdec(pch[i]) || ishex(pch[i]))){
+                    is_hex = 0;
+                    break;
+                }
+            }
+            if(is_hex)
+                return 1;
+        }
+        // not hex, maybe decimal
+    }
+    pch = (char*) mask;
+    for(i=0;i < len;i++){
+        if(!isdec(pch[i])){
+            is_dec = 0;
+            break;
+        }
+    }
+    return is_dec;
+}
+
+
+
+int main (int argc, char**argv)
+{
+    int do_changeguid = 0;
+    int in_guidtype =0;
+    int guidtoset = 0;
+    int do_changemask = 0;
+    unsigned int speakermask = 0;
+    int fmtfound = 0;
+    int test = 0;
+    int src_is_amb = 0;
+    FILE* fp  = NULL;
+    char* maskstring = NULL;
+    psf_format outformat;
+    psf_format new_outtype;
+    DWORD tag;
+    DWORD size;
+    fpos_t bytepos;
+    DWORD fmtoffset = 0;
+    DWORD guidoffset = 0;
+    DWORD maskoffset = 0;
+	WORD cbSize;
+    WORD validbits;
+    DWORD chmask;
+    WAVEFORMATEXTENSIBLE fmt;
+    int is_little_endian = byte_order();
+    
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.0.1b\n");
+        return 0;
+    }
+    
+    if(argc<2){
+        usage();
+        return 1;
+    }
+    while(argv[1][0]=='-'){
+		switch(argv[1][1]){
+            case 'm':
+                if(argc > 2){
+                    printf("cannot use -m with other options\n");
+                    return 1;
+                }
+                else{
+                    printmaskinfo();
+                    return 0;
+                }
+                break;
+            case 'g':
+                if(do_changeguid){
+                    printf("Cannot use -g option more than once!\n");
+                    return 1;
+                }
+                guidtoset = atoi(&argv[1][2]);
+                if(guidtoset < 1 || guidtoset > 2) {
+                    printf("bad value for -g option - use 1 or 2 only.\n");
+                    return 1;
+                }
+                do_changeguid = 1;
+                break;
+            case 's':
+                if(do_changemask){
+                    printf("Cannot use -s option more than once!\n");
+                    return 1;
+                }
+                maskstring =  &argv[1][2];
+                if(validate_mask(maskstring)==0){
+                    printf("Bad format for mask argument.\n"
+                           "Value must be decimal, or hex with 0x prefix.\n");
+                    return 1;
+                }
+                    
+                do_changemask = 1;
+                break;
+            case 't':
+                if(test==1){
+                    printf("You only need to set -t once!\n");
+                }
+                else
+                    printf("Test mode: no changes will be made.\n");
+                test = 1;
+                
+                break;
+            default:
+                printf("illegal option %s\n",argv[1]);
+                return 1;
+        }
+        argc--;
+        argv++;
+    }
+    if(argc != 2){
+        printf("infile argument required.\n");
+        usage();
+        return 1;
+    }
+    psf_init();
+    /* nitty-gritty to read header */
+    outformat = psf_getFormatExt(argv[ARG_INFILE]);
+	if(!(outformat == PSF_STDWAVE|| outformat==PSF_WAVE_EX)){
+		printf("file mustbe WAVEX format with .wav or .amb extension.\n");
+        return 1;
+	}
+    
+    fp = fopen(argv[ARG_INFILE],"rb+");
+    if(fp==NULL){
+        printf("unable to open infile %s\n",argv[ARG_INFILE]);
+        return 1;
+    }
+    if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))
+		|| wavDoRead(fp,(char *) &size,sizeof(DWORD))) {
+		printf("read error 1\n");
+        return 1;
+    }
+	if(!is_little_endian)
+		size = REVDWBYTES(size);
+	else
+		tag = REVDWBYTES(tag);
+	if(tag != TAG('R','I','F','F')){
+		printf("not a RIFF file\n");
+        return 1;
+    }
+	if(size < (sizeof(WAVEFORMAT) + 3 * sizeof(WORD))){
+		printf("file has bad header.\n");
+        return 1;
+    }
+
+	if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))) {
+		printf("read error 2\n");
+        return 1;
+    }
+	if(is_little_endian)
+		tag = REVDWBYTES(tag);
+	if(tag != TAG('W','A','V','E')){
+		printf("Not a WAVE file.\n");
+        return 1;    
+    }
+	for(;;){
+        if(fmtfound) 
+            break;
+		if(wavDoRead(fp,(char *)&tag,sizeof(DWORD))
+				|| wavDoRead(fp,(char *) &size,sizeof(DWORD))) {
+			printf("read error 3\n");
+            return 1;
+        }
+		if(!is_little_endian)
+			size = REVDWBYTES(size);
+		else
+			tag = REVDWBYTES(tag);
+		switch(tag){
+		case(TAG('f','m','t',' ')):
+			if( size < sizeof(WAVEFORMAT)){
+				printf("file has bad format.\n");
+                return 1;
+            }
+            
+			if(size > sizeof_WFMTEX) {
+				printf("file has unsupported WAVE format.\n");
+                return 1;
+            }
+			if(fgetpos(fp,&bytepos)) {
+			    printf("seek error\n");
+                return 1;
+            }
+			fmtoffset = (DWORD) POS64(bytepos);
+			if(wavDoRead(fp,(char *) &fmt.Format,sizeof(WAVEFORMAT))){				
+				printf("read error 4\n");
+                return 1;
+			}
+			if(!is_little_endian)		
+				fmtSwapBytes(&fmt.Format);	
+			/* calling function decides if format is supported*/
+			if(size > sizeof(WAVEFORMAT)) {
+				if(wavDoRead(fp,(char*)&cbSize,sizeof(WORD))){
+					printf("read error 5\n");
+                    return 1;
+                }
+				if(!is_little_endian)		
+					cbSize = (WORD) REVWBYTES(cbSize);
+                if(cbSize==0){
+                    printf("file is plain WAVE format.\n");
+                    return 1;
+                }
+				if(fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE){
+					if(cbSize != 22) {
+						printf("not a recognized WAVEX file.\n");
+                        return 1;
+					}
+				}				
+				if(wavDoRead(fp,(char *) &validbits,sizeof(WORD))){
+					printf("read error 6\n");
+                    return 1;
+                }
+                
+				if(!is_little_endian)
+                    validbits = REVWBYTES(validbits);
+                fmt.Samples.wValidBitsPerSample = (WORD) validbits;
+				if(wavDoRead(fp,(char *) &chmask,sizeof(DWORD))) {
+					printf("read error 7\n");
+                    return 1;
+                }
+				if(!is_little_endian)
+                    chmask = REVDWBYTES(chmask);
+                fmt.dwChannelMask = chmask;
+				if(wavDoRead(fp,(char *) &(fmt.SubFormat),sizeof(GUID))) {
+					printf("read error 8 \n");
+                    return 1;
+                }
+				if(!is_little_endian){
+					fmt.SubFormat.Data1 = REVDWBYTES(fmt.SubFormat.Data1);
+					fmt.SubFormat.Data2 = (WORD) REVWBYTES(fmt.SubFormat.Data2);
+					fmt.SubFormat.Data3 = (WORD) REVWBYTES(fmt.SubFormat.Data3);	
+				}
+				/* if we get a good GUID, we are ready to make changes! */
+                
+				if(compare_guids(&(fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_PCM))) {
+                    in_guidtype = GUID_PCM;
+                    if(test)
+                        printf("Current GUID: KSDATAFORMAT_SUBTYPE_PCM.\n");
+                }
+                else if(compare_guids(&(fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) {
+                    in_guidtype = GUID_IEEE;
+                    if(test)
+                        printf("Current GUID: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.\n");
+                }
+                else if(compare_guids(&(fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_PCM))) {
+                    in_guidtype = GUID_AMB_PCM;
+                    src_is_amb = 1;
+                    if(test)
+                        printf("Current GUID: SUBTYPE_AMBISONIC_B_FORMAT_PCM.\n");
+                }
+                else if(compare_guids(&(fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT))){
+                    in_guidtype = GUID_AMB_IEEE;
+                    src_is_amb = 1;
+                    if(test)
+                        printf("Current GUID: SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT.\n");
+                }
+                else {
+                    printf("unrecognized WAVE_EX GUID.\n");
+                    return 1;
+                }				
+			}
+            else {
+                printf("WAVEX format required.\n"
+                       "Use copysfx to convert to WAVEX format.\n");
+                return 1;
+            }
+            fmtfound = 1;			
+			break;
+		case TAG('d','a','t','a'):
+            if(!fmtfound){
+                printf("bad WAVE file: no fmt chunk found!\n");
+                return 1;
+            }                 
+        default:
+			/* unknown chunk - skip */
+            if(!fmtfound) {
+                if(fseek(fp,size,SEEK_CUR)){
+                    printf("seek error\n");
+                    return 1;
+                }
+            }
+			break;
+		}
+	}
+    if(!fmtfound){
+         printf("no fmt chunk found!\n");
+         return 1;
+    }
+    
+    maskoffset = fmtoffset + sizeof(WAVEFORMAT) + sizeof(WORD)*2;
+    guidoffset = maskoffset + sizeof(DWORD);
+    
+    if(!(do_changeguid || do_changemask)){
+        /* display what we know */
+        printf("Wordsize     : %d\n",validbits);
+        printf("Channels     : %d\n",fmt.Format.nChannels);
+        printf("GUID         : %s\n",guidnames[in_guidtype]);
+        if(chmask==0)
+            printf("Channel mask : 0\n");
+        else
+            printf("Channel mask : 0x%06x (%d)\n",(unsigned int) chmask,(int) chmask);
+        if(chmask){
+            printf("Channel mask (binary): ");
+            binprintf(chmask,1);
+            printf("There are %d speaker positions set.\n",countbits(chmask));
+            
+        }
+        printf("\n");
+    }
+    else {
+        int bits,is_same = 0;
+
+        /* check all settings before making any changes */
+        if(do_changemask){
+            // be strict!
+            if(src_is_amb && guidtoset != 1){
+                printf("-s flag only supported for PCM files (AMB speaker mask must be zero).\n"
+                       "Exiting.\n");
+                return 1;
+            }
+            if(strncmp(maskstring,"0x",2)==0)
+                sscanf(maskstring,"%x", &speakermask);
+            else
+                sscanf(maskstring,"%u", &speakermask);
+            bits = countbits(speakermask);
+            /* can't do much if a silly huge value given */
+            if(speakermask < 0 || speakermask >= 0x40000){
+                printf("Mask value out of range!\n");
+                return 1;
+            }
+            if(bits > fmt.Format.nChannels){
+                printf("Error: %d positions requested for %d channels: \n",bits,fmt.Format.nChannels);
+                binprintf(speakermask,1);
+                printf("\n");
+                return 1;
+            }
+            if(bits && bits < fmt.Format.nChannels){
+                printf("Warning: %d requested positions less than channels in file.\n",bits);
+            }
+        }
+                
+        if(do_changeguid){
+            GUID nuguid;
+            
+            // set float or pcm GUID as required.
+            // no copy to same: only convert if types are different
+            switch(guidtoset){
+            case 1:       // plain wavex - the easy iption
+                if(in_guidtype == GUID_AMB_PCM){
+                    nuguid = KSDATAFORMAT_SUBTYPE_PCM;
+                }
+                else if(in_guidtype == GUID_AMB_IEEE) {
+                    nuguid = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+                }
+                else
+                    is_same = 1;
+                new_outtype = PSF_STDWAVE;
+                break;
+            case 2:  // convert to amb - must zero speaker flags
+                if(in_guidtype == GUID_PCM)
+                    nuguid = SUBTYPE_AMBISONIC_B_FORMAT_PCM;
+                else if(in_guidtype == GUID_IEEE)
+                    nuguid = SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT;
+                else
+                    is_same = 1;
+                // set mask to zero if setting AMB format
+                if(is_same==0) {
+                    do_changemask = 1;
+                    maskstring = "0";
+                    speakermask = 0;
+                }
+                new_outtype = PSF_WAVE_EX;    
+                break;
+            }
+            if(is_same){
+                printf("new GUID identical - no change made.\n");
+            }
+            else {
+                int err;
+                //make the change!
+                if(!is_little_endian){
+                    nuguid.Data1 = REVDWBYTES(nuguid.Data1);
+					nuguid.Data2 = (WORD) REVWBYTES(nuguid.Data2);
+					nuguid.Data3 = (WORD) REVWBYTES(nuguid.Data3);
+                }
+                if(!test){
+                    if(fseek(fp,guidoffset,SEEK_SET) !=0){
+                        printf("seek error updating channelmask. exiting.\n");
+                        return 1;
+                    }
+                    err = fwrite((char*) &nuguid,1,sizeof(GUID),fp);
+                    if(err != sizeof(GUID)){
+                        printf("Error updating GUID. File may have inconsistent header.\n");
+                        return 1;
+                    }
+                    printf("new GUID set.\n");
+                    
+                }
+            }
+            
+        }
+        if(do_changemask){
+            /* read user mask value */
+            /* TODO: full parse, trap bad characters */
+            if(chmask==speakermask){
+                if(speakermask > 0)
+                    printf("Requested mask is already set. Data not modified.\n");
+            }
+            else {
+                DWORD writemask;
+                int err;
+                
+                if(speakermask > 0){
+                    printf("new mask = %d (",speakermask);
+                    binprintf(speakermask,1);
+                    printf(")\n");
+                }
+                if(fseek(fp,maskoffset,SEEK_SET) !=0){
+                    printf("seek error updating channelmask. exiting.\n");
+                    return 1;
+                }
+                writemask = speakermask;
+                if(!is_little_endian)
+                    writemask = REVDWBYTES(speakermask);
+                if(!test){
+                    err = fwrite((char*) &writemask,1,sizeof(DWORD),fp);
+                    if(err != sizeof(DWORD)){
+                        printf("Error updating mask. File may have inconsistent header.\n");
+                        return 1;
+                    }
+                    printf("New mask set.\n");
+                }
+            }
+                
+        }
+        if(outformat == PSF_STDWAVE && new_outtype==PSF_WAVE_EX)
+            printf("File extension should be changed to amb.\n");
+        else if(outformat == PSF_WAVE_EX && new_outtype == PSF_STDWAVE)
+            printf("File extension should be changed to wav.\n");
+    }
+    fseek(fp,0,SEEK_END);
+    fclose(fp);
+    return 0;
+}
+
+
+    

+ 604 - 0
dev/externals/mctools/copysf.c

@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/*
+ *	copysfx: version of copysfx using portsf
+ */
+/* Oct 2009: added cube */
+/* Dec 2009 1.9.1. fixed horrible bug failing to convert to WAVE_EX! */
+/* March 2012 corrected wave type arg parsing  */
+/* August 2012 updated portsf to correct SPKRS_MONO */
+/* Nov 2013 added MC_SURR_6_1 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#ifdef _WIN32
+#include <malloc.h>			//RWD.6.5.99
+#endif
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <portsf.h>
+
+#define VERSION "Revision: 2.1.1 2020 "
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+enum {OPT_STD_WAVE,OPT_WAVEX_GENERIC,OPT_WAVEX,OPT_WAVEX_LCRS,OPT_WAVEX_SURROUND,OPT_WAVEX_BFORMAT,OPT_WAVEX_5_0,OPT_WAVEX_7_1,OPT_WAVEX_CUBE,OPT_WAVEX_6_1,};
+/*
+The number of channels defines the order of the soundfield:
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly
+called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+11 channel = ffh = 3rd order horizontal + 2nd order height
+16 channel = fff = 3rd order 3-D
+*/
+#define N_BFORMATS (10)
+static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+
+int get_sampsize(psf_stype type)
+{
+    int size = 0;
+    switch(type){
+		case PSF_SAMP_8:
+			size = 1;
+			break;
+		case PSF_SAMP_16:
+			size = 2;
+			break;
+		case PSF_SAMP_24:
+			size = 3;
+			break;
+		case PSF_SAMP_32:
+		case PSF_SAMP_IEEE_FLOAT:
+			size = 4;
+			break;
+		default:
+			break;
+    }
+    return size;
+}
+
+
+
+void
+usage()
+{
+	fprintf(stderr,"\n\nCDP MCTOOLS: COPYSFX  (c) RWD,CDP %s\n",VERSION);
+	fprintf(stderr,"\tcopy/convert soundfiles\n");
+	fprintf(stderr,"usage: copysfx [-d][-h][-sN] [-tN] infile outfile\n");
+	fprintf(stderr,"-d   : apply TPDF dither to 16bit outfile.\n"
+                   "-s   : force output sample type to type N.\n");
+	fprintf(stderr,"      Available sample types:\n"
+                   "      1  : 16bit integers (shorts)\n"
+                   "      2  : 32bit integer  (longs)\n"
+                   "      3  : 32bit floating-point\n"
+                   "      4  : 24bit integer 'packed' \n"
+                   "      Default: format of infile.\n");
+				   
+	fprintf(stderr,"-h   : write mimimum header (no extra data).\n"
+				   "       Default: include PEAK data.\n"
+            );
+/*	fprintf(stderr,"-i   : interpret 32bit files as floats (use with care!)\n"); */
+	fprintf(stderr,"-tN  : write outfile format as type N\n");
+	fprintf(stderr,"Possible formats:\n"
+					"0   : standard soundfile (.wav, .aif, .afc, .aifc)\n"
+					"1   : generic WAVE_EX (no speaker assignments)\n"
+					"2   : WAVE_EX mono/stereo/quad(LF,RF,LR,RR) - infile nchans must match\n"
+					"3   : WAVE_EX quad surround (L,C,R,S)       - infile must be quad\n"
+					"4   : WAVE_EX 5.1 format surround           - infile must be 6-channel\n"
+					"5   : WAVE_EX Ambisonic B-format (W,X,Y,Z...) - file extension .amb supported \n"
+                    "6   : WAVE_EX 5.0 Surround                  - infile must be 5-channel\n"
+                    "7   : WAVE_EX 7.1 Surround                  - infile must be 8-channel\n"
+                    "8   : WAVE_EX Cube Surround                 - infile must be 8-channel\n"
+                    "9   : WAVE_EX 6.1 Surround  (new in v2.1.0) - infile must be 7-channel\n"
+					"default in all cases: outfile has format of infile\n"
+					"NB: types 1 to 9 are for WAV format only\n"
+                    "See also CHXFORMAT: destructively change WAVE_EX GUID and/or speaker mask.\n"
+					);	
+}
+
+
+
+int
+main(int argc, char *argv[])
+{
+	unsigned long size,i;
+	unsigned long channels,srate;
+	//RWD.6.5.99
+	PSF_PROPS  inprops,outprops; 
+	int force_wave = 0,rc = 0;
+    psf_stype force_stype = 0;
+	int opt_wave_type = -1;
+	int min_header = 0;
+	int interpret_floats = 0;
+	int res;	
+	char *create_msg = NULL;
+    int do_dither = 0;
+    psf_format  outformat = PSF_FMT_UNKNOWN;
+	unsigned long update_size;
+	PSF_CHPEAK  *peaks = NULL;
+	MYLONG peaktime;
+	char* ext = NULL;
+	int have_amb_ext = 0;
+    int ifd;		/* the input file */
+    int ofd;		/* the output file */
+    float *sampleframe = NULL;
+	double outsize_bytes;
+	double maxsize = pow(2.0,32.0) - 200.0;  /* some safety margin! */
+	
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("2.0.3\n");
+        return 0;
+    }
+
+	if(psf_init()) {		
+		printf("failed to init psfsys\n");
+		exit(1);
+	}
+
+	if(argc < 3){
+		usage();
+		exit(1);
+	}
+
+	printf("\nCDP MCTOOLS: COPYSFX v2.0.3 (c) RWD,CDP 2011\n");
+
+	while(argv[1][0]=='-'){
+		int sflag = 0;
+		switch(argv[1][1]){
+        case 'd':
+            do_dither = 1;
+            break;
+		case('t'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"-t flag requires parameter\n");
+				usage();
+				exit(1);
+			}
+			opt_wave_type = atoi(&(argv[1][2]));
+			if((opt_wave_type < 0) || opt_wave_type > OPT_WAVEX_CUBE){
+				fprintf(stderr,"wave type out of range\n");
+				usage();
+				exit(1);
+			}
+			force_wave = 1;
+			break;		
+		case('s'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"-s flag requires parameter\n");
+				usage();
+				exit(1);
+			}
+			if(force_stype >0){
+				fprintf(stderr,"copysfx: error: -s flag may only be used once!\n");
+				usage();
+				exit(1);
+			}
+			sflag = atoi(&(argv[1][2]));
+			if(sflag < 1 || sflag > 6){
+				fprintf(stderr,"-s parameter out of range\n");
+				usage();
+				exit(1);
+			}
+			switch(sflag){
+			case(1):
+				force_stype = PSF_SAMP_16;
+				break;
+			case(2):
+				force_stype = PSF_SAMP_32;
+				break;
+			case(3):
+				force_stype = PSF_SAMP_IEEE_FLOAT;
+				break;
+			case(4):
+				force_stype = PSF_SAMP_24;
+				break;			
+			default:
+				fprintf(stderr,"internal error: unmatched sampletype parameter!\n");
+				break;
+			}
+			break;
+		case('h'):
+            min_header = 1;    
+			break;
+#ifdef NOTDEF
+		case('i'):
+			interpret_floats = 1;
+			break;
+#endif
+		default:
+			fprintf(stderr,"unknown flag option %s\n",argv[1]);
+			usage();
+			exit(1);
+			
+		}
+		argc--; argv++;
+	}
+
+	if(argc < 3){
+		usage();
+		exit(1);
+	}
+
+	if((ifd = psf_sndOpen(argv[1],&inprops,0)) < 0) {
+		fprintf(stderr, "copysfx: can't open input SFfile %s:\n\t",
+				argv[1]);
+		exit(1);
+	}
+		
+	size = psf_sndSize(ifd);
+	channels = inprops.chans;
+	sampleframe = (float *) malloc(channels * sizeof(float));
+	if(sampleframe==NULL){
+		psf_sndClose(ifd);
+		puts("no memory for sample buffer\n");
+		exit(1);
+	}
+	
+	srate = inprops.srate;
+	update_size = (unsigned long)((double)srate * 0.5);
+	outprops = inprops;
+	/* if -i flag used, infile is floats, therefore we write outfile as floats,
+	 * UNLESS we also have outtype coercion!
+	 */
+	if(interpret_floats)
+		outprops.samptype = PSF_SAMP_IEEE_FLOAT;
+
+	if(inprops.format== PSF_WAVE_EX){		
+		printf("opened WAVE-EX file:\n");
+		switch(inprops.chformat){
+		
+		case(MC_STD):
+			printf("\tno speaker assignments defined\n");		
+			break;
+		case(MC_MONO):
+			printf("\tMONO\n");
+			break;
+		case(MC_STEREO):
+			printf("\tSTEREO\n");
+			break;		
+		case(MC_QUAD):
+			printf("\tGeneric quad format: LF-RF-LR-RR\n");			
+			break;
+		case(MC_LCRS):
+			printf("\tQuad Surround: L-C-R-S format\n");			
+			break;
+		case(MC_BFMT):
+			printf("\tAmbisonic B-Format\n");
+			break;
+		case(MC_DOLBY_5_1):
+			printf("\t5.1 surround\n");
+			break;
+        case(MC_SURR_5_0):
+			printf("\t5.0 surround\n");
+			break;
+		case(MC_SURR_6_1):
+            printf("\t6.1 Surround\n");
+            break;
+        case(MC_SURR_7_1):
+            printf("\t7.1 Surround\n");
+            break;
+        case(MC_CUBE):
+            printf("\tCube Surround\n");
+            break;
+		default:
+			printf("\tunrecognized speaker positions\n");
+			break;						
+		}
+		if(min_header==1)
+			printf("WARNING: minimum header is not recommended for WAVE_EX files\n");
+	}
+
+	peaks = (PSF_CHPEAK *) calloc(channels,sizeof(PSF_CHPEAK));
+	if(peaks==NULL){
+		puts("no memory for fpeak data buffer\n");
+		psf_sndClose(ifd);
+		exit(1);
+	}
+
+	//read infile peak data, if it exists, and report to user	
+	res = psf_sndReadPeaks(ifd,peaks,  &peaktime);
+	if(res ==0)	{
+		printf("no peak data in this infile\n");
+	}
+	else if(res < 0){
+		fprintf(stderr,"error reading infile peak data\n");
+		psf_sndClose(ifd);
+		exit(1);
+	}
+	else {
+		time_t t_peaktime = (time_t) peaktime;
+		printf("Infile PEAK data:\n\tcreation time: %s\n", ctime(&t_peaktime));
+		for(i=0;i < channels; i++)
+#ifdef CPLONG64
+			printf("CH %ld: %.4lf at frame %u:\t%.4lf secs\n",
+#else
+            printf("(32) CH %ld: %.4lf at frame %lu:\t%.4lf secs\n",
+#endif
+				i,peaks[i].val,peaks[i].pos,(double) (peaks[i].pos) / (double)srate);
+	}
+	
+	if(force_stype > PSF_SAMP_UNKNOWN)		
+		outprops.samptype = force_stype;
+
+	outsize_bytes = (double) size * outprops.chans * get_sampsize(outprops.samptype);
+    if(outsize_bytes > maxsize){
+        printf("output file size %.0f MB exceeds 4GB: cannot proceed.\n",outsize_bytes / 1048576.0);
+        exit(1);
+    }
+	
+	if(force_wave){
+		int i,matched;
+		//check file extension...
+		switch(opt_wave_type){
+		case(OPT_STD_WAVE):
+			outprops.chformat = STDWAVE;
+			outprops.format = PSF_STDWAVE;
+			create_msg = "creating standard WAVE file\n";
+			break;
+		case(OPT_WAVEX_GENERIC):
+			inprops.chformat = MC_STD;
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating STD WAVE_EX file\n";
+			break;
+		case(OPT_WAVEX):
+			switch(inprops.chans){
+			case(1):
+				outprops.chformat = MC_MONO;				
+				outprops.format = PSF_WAVE_EX;
+				create_msg = "creating MONO WAVE_EX file\n";
+				break;
+			case(2):
+				outprops.chformat = MC_STEREO;				
+				outprops.format = PSF_WAVE_EX;
+				create_msg = "creating STEREO WAVE_EX file\n";
+				break;
+			case(4):
+				outprops.chformat = MC_QUAD;			
+				outprops.format = PSF_WAVE_EX;
+				create_msg = "creating QUAD WAVE_EX file\n";
+				break;
+			default:
+				fprintf(stderr,"infile nchans incompatible with requested WAVE-EX format\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);				
+			}
+			break;
+		case(OPT_WAVEX_LCRS):
+			if(inprops.chans != 4){
+				fprintf(stderr,"infile must have four channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_LCRS;
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating LCRS-surround WAVE_EX file\n";
+			break;
+		case(OPT_WAVEX_SURROUND):
+			if(inprops.chans != 6){
+				fprintf(stderr,"infile must have six channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_DOLBY_5_1;			
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating 5.1 surround WAVE_EX file\n";
+			break;			
+		case(OPT_WAVEX_BFORMAT):
+			matched = 0;
+			for(i=0;i < N_BFORMATS;i++)	{
+				if(inprops.chans == bformats[i]){
+					matched = 1;
+					break;
+				}
+			}
+			if(!matched){
+				printf("WARNING: No Bformat definition for %d-channel file.\n",inprops.chans);
+			}
+			outprops.chformat = MC_BFMT;			
+			outprops.format = inprops.format = PSF_WAVE_EX;
+			create_msg = "creating AMBISONIC B-FORMAT WAVE_EX file\n";
+			break;
+        case(OPT_WAVEX_5_0):
+            if(inprops.chans != 5){
+				fprintf(stderr,"infile must have five channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_SURR_5_0;			
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating 5.0 surround WAVE_EX file\n";
+			break;
+				
+        case(OPT_WAVEX_7_1):
+            if(inprops.chans != 8){
+				fprintf(stderr,"infile must have eight channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_SURR_7_1;			
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating 7.1 surround WAVE_EX file\n";
+			break;
+        case OPT_WAVEX_CUBE:
+            if(inprops.chans != 8){
+				fprintf(stderr,"infile must have eight channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_CUBE;			
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating 5.0 surround WAVE_EX file\n";
+			break;			
+        case(OPT_WAVEX_6_1):
+            if(inprops.chans != 7){
+				fprintf(stderr,"infile must have seven channels\n");
+				usage();
+				psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);
+				exit(1);
+			}
+			outprops.chformat = MC_SURR_6_1;			
+			outprops.format = PSF_WAVE_EX;
+			create_msg = "creating 6.1 surround WAVE_EX file\n";
+			break;	    
+		default:
+			 printf("copysfx: Program error: impossible wave_ex type\n");
+			 psf_sndClose(ifd);
+				if(peaks)
+					free(peaks);				
+			 exit(1);
+			 
+		}
+	}
+	//ignore all that if user wants aiff!
+	//informat = inprops.format;
+	ext = strrchr(argv[2],'.');
+	if(ext && stricmp(ext,".amb")==0)
+		have_amb_ext = 1;
+
+	if(have_amb_ext){
+		if(!(outprops.format == PSF_WAVE_EX && outprops.chformat == MC_BFMT)){
+			fprintf(stderr,"Error: .amb extension only allowed for WAVE_EX B-Format file.\n");
+			exit(1);			
+		} 
+	}
+    outformat  = psf_getFormatExt(argv[2]);
+
+	if((ofd = psf_sndCreate(argv[2],&outprops,0,min_header,PSF_CREATE_RDWR)) < 0){	
+		fprintf(stderr, "copysfx: can't create output file %s:\n\t",argv[2]);		
+		psf_sndClose(ifd);
+		fprintf(stderr,"\n");
+		if(peaks)
+			free(peaks);				
+		exit(1);
+	}
+    if(do_dither)
+        psf_sndSetDither(ofd,PSF_DITHER_TPDF);
+
+	if(force_wave){
+		if(outprops.format==PSF_WAVE_EX){
+			if(outformat > PSF_WAVE_EX)
+                printf("WARNING: extended formats require .wav file format:\n\t - creating standard file\n");		
+			else
+			    printf("%s\n",create_msg);	
+		}
+	}	
+	printf("copying...\n");    
+    for(i=0;i < size;i++){
+        /* salve to CEP users: need interpret_floats somewhere...? */
+        if((psf_sndReadFloatFrames(ifd, sampleframe, 1)) != 1){
+            
+            fprintf(stderr,"copysfx: error reading from infile\n");
+            psf_sndClose(ifd);
+            psf_sndClose(ofd);
+            free(sampleframe);
+            if(peaks)
+                free(peaks);
+            exit(1);
+        }
+        if(psf_sndWriteFloatFrames(ofd, sampleframe,1)!=1){
+            fprintf(stderr,"copysfx: error writing to outfile\n");
+            psf_sndClose(ifd);
+            psf_sndClose(ofd);
+            free(sampleframe);
+            if(peaks)
+                free(peaks);
+            exit(1);			
+        }
+        if((i / channels) % update_size == 0) {	 
+            printf("%.2lf secs\r",(double) (i / channels) / (double) srate);
+            fflush(stdout);
+        }
+    }
+    printf("%.4lf secs\r",(double) (i / channels) / (double) srate);
+    	
+	if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+		printf("Outfile PEAK values:\n");
+		for(i=0; i < (unsigned long) inprops.chans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+#ifdef CPLONG64
+                printf("CH %ld: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+#else
+                printf("CH %ld: %.6f (%.2lfdB) at frame %lu:\t%.4f secs\n",i,
+#endif
+                       val,dbval,peaks[i].pos,(double)peaks[i].pos / (double) inprops.srate);
+            }
+            else{
+#ifdef CPLONG64
+                printf("CH %ld: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+#else        
+                printf("CH %ld: %.6f (-infdB) at frame %lu:\t%.4f secs\n",i,
+#endif
+                       val,peaks[i].pos,(double)peaks[i].pos / (double) inprops.srate); 
+            }
+        }
+	}
+	if(psf_sndClose(ifd) < 0) {		
+		rc++;
+	}
+
+	if(psf_sndClose(ofd) < 0) {		
+		rc++;
+	}
+    if(sampleframe)
+		free(sampleframe);
+	if(peaks)
+		free(peaks);	
+	psf_finish();
+	return rc;
+}
+
+
+

+ 400 - 0
dev/externals/mctools/fmdcode.c

@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//fmdcode.c  : decode .amb file to various speaker layouts
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <memory.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <portsf.h>
+#include "fmdcode.h"
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+/*
+ Channel order is WXYZ,RSTUV,KLMNOPQ
+ 
+ The number of channels defines the order of the soundfield:
+ 2 channel = W+Y =  "Mid-Side" 
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly
+                                                            called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+ 11 channel = ffh = 3rd order horizontal + 2nd order height
+ 16 channel = fff = 3rd order 3-D
+ 
+ 
+ Horizontal   Height  Soundfield   Number of    Channels
+ order 	      order 	  type      channels 	
+ 1 	         0 	       horizontal 	  3 	    WXY
+ 1 	         1 	      full-sphere 	  4 	    WXYZ
+ 2 	         0 	       horizontal 	  5 	    WXY....UV
+ 2 	         1 	       mixed-order    6 	    WXYZ...UV
+ 2 	         2 	      full-sphere     9 	    WXYZRSTUV
+ 3           0 	       horizontal 	  7 	    WXY....UV.....PQ
+ 3 	         1         mixed-order 	  8 	    WXYZ...UV.....PQ
+ 3 	         2 	       mixed-order 	 11 	    WXYZRSTUV.....PQ
+ 3 	         3 	      full-sphere 	 16 	    WXYZRSTUVKLMNOPQ
+
+ */
+
+
+enum {ARG_PROGNAME, ARG_INFILE,ARG_OUTFILE, ARG_LAYOUT,ARG_NARGS};
+#define N_BFORMATS (10)
+enum {FM_MONO,FM_STEREO,FM_SQUARE,FM_QUAD,FM_PENT,DM_5_0,DM_5_1,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE,FM_QUADCUBE,FM_NLAYOUTS};
+
+
+//static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+static const int layout_chans[] = {1,2,4,4,5,5,6,6,8,8,8,8};
+
+void usage(void)
+{
+   printf(
+    "usage: fmdcode [-x][-w] infile outfile layout\n"
+	"       -w    :   write plain WAVE outfile format\n"
+    "                (.wav default - use generic wavex format).\n"
+    "       -x    : write std WAVEX speaker positions to header\n"
+    "               (applies to compatible layouts only; requires .wav extension).\n"
+	"   layout    : one of the choices below.\n"
+    "       Output channel order is anticlockwise from centre front\n"
+    "            except where indicated.\n"
+    "   Layouts indicated with * are compatible with WAVEX speaker position order. \n"
+    "   Available speaker layouts:\n"
+    "   1    :  *  mono (= W signal only)\n"
+    "   2    :  *  stereo (quasi mid/side, = W +- Y)\n"
+    "   3    :     square\n"
+    "   4    :  *  quad FL,FR,RL,RR order\n"
+    "   5    :     pentagon\n"
+    "   6    :  *  5.0 surround (WAVEX order)\n"
+    "   7    :  *  5.1 surround (WAVEX order, silent LFE)\n"
+    "   8    :     hexagon\n"
+    "   9    :     octagon 1 (front pair, 45deg)\n"
+    "   10   :     octagon 2 (front centre speaker)\n"
+    "   11   :     cube (as 3, low-high interleaved. Csound-compatible.)\n"
+    "   12   :  *  cube (as 4, low quad followed by high quad).\n"
+    " NOTE: no shelf filters or NF compensation used.\n");
+}
+
+int main(int argc,char *argv[])
+{
+	int i,ifd, ofd;
+	int layout,inorder = 1;
+    int got,halfsec;
+	unsigned int framepos;
+    int inchans,outchans;
+    int outsize;
+    int write_speakerpositions = 0;
+    MYLONG peaktime;
+    psf_channelformat chformat = MC_STD;
+    psf_format  outformat;
+	char *sfname;
+    float *frame = NULL;
+    fmhcopyfunc copyfunc;
+    fmhdecodefunc decodefunc = NULL;
+	int write_wavex = 1;
+	ABFSAMPLE abfsample;
+	PSF_PROPS props;
+	PSF_CHPEAK *peaks = NULL;
+	float abfframe[16];
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.0b\n");
+        return 0;
+    }
+    
+	if(argc < 3){
+		printf("\nCDP MCTOOLS: FMDCODE v 1.0beta: RWD,CDP 2009\n"
+				"Plain multi-layout decoder for .amb files.\n"
+                "Regular layouts use standard Furse-Malham in-phase coefficients.\n" 
+                "5.x surround coefficients (maxre) from David Moore.\n");
+        usage();
+		return 1;
+	}
+	while(argv[1][0] =='-'){		
+		switch(argv[1][1]){
+		case('w'):
+			write_wavex = 0;
+			break;
+        case 'x':
+            write_speakerpositions = 1;
+            break;
+		default:
+			fprintf(stderr,"fmdcode: error: illegal flag option %s\n",argv[1]);
+			return 1;
+		}
+		argc--; argv++;
+	}
+	if(argc < ARG_NARGS){
+        
+		usage();
+		return 1;
+	}
+	if(psf_init()) {		
+		printf("failed to init psfsys\n");
+		exit(1);
+	}
+	
+	sfname = argv[ARG_OUTFILE];
+	layout = atoi(argv[ARG_LAYOUT]);
+    if(layout < 1 || layout > FM_NLAYOUTS+1){
+        printf("Unsupported layout type.\n");
+        return 1;
+    }
+    
+    
+	ifd = psf_sndOpen(argv[ARG_INFILE],&props,0);
+	if(ifd < 0){
+		fprintf(stderr,"unable toopen infile %s\n",argv[ARG_INFILE]);
+		return 1;
+	}
+    inchans = props.chans;
+    if(inchans > 4) {
+        inorder = 2;
+        printf("%d-channel input: performing 2nd-order decode.\n",inchans);
+    }
+    outsize = psf_sndSize(ifd);
+	if(outsize <= 0){
+		fprintf(stderr,"fmdcode: infile is empty!\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+    
+    switch(inchans){
+	case 3:
+        copyfunc = fmhcopy_3;
+        break;
+    case 4:
+        copyfunc = fmhcopy_4;       
+        break;
+    case 5:
+        copyfunc = fmhcopy_5;
+        break;
+    case 6:
+        copyfunc = fmhcopy_6;
+        break;
+    case 7:
+        copyfunc = fmhcopy_7;
+        break;
+    case 8:
+        copyfunc = fmhcopy_8;
+        break;
+    case 9:
+        copyfunc = fmhcopy_9;
+        break;
+    case 11:
+        copyfunc = fmhcopy_11;
+        break;
+    case 16:
+        copyfunc = fmhcopy_16;
+        break;
+    default:
+        printf("file has unsupported number of channels (%d)\n",inchans);
+        psf_sndClose(ifd);
+        return 1;
+	}
+    //FM_MONO,FM_STEREO,FM_SQUARE,FM_PENT,FM_SURR,FM_SURR6,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE
+    switch(layout-1){
+        case FM_MONO:
+            printf("Decoding to Mono\n");
+            decodefunc = fm_i1_mono;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_MONO;
+            break;
+        case FM_STEREO:
+            printf("Decoding to Stereo\n");
+            decodefunc = fm_i1_stereo;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_STEREO;
+            break;
+        case FM_SQUARE:
+            printf("Decoding to Square\n");
+            if(inorder == 1)
+               decodefunc = fm_i1_square;
+            else
+               decodefunc = fm_i2_square; 
+            break;
+        case FM_QUAD:
+            printf("Decoding to quad surround (WAVEX order)\n");
+            if(inorder == 1)
+                decodefunc = fm_i1_quad;
+            else
+                decodefunc = fm_i2_quad;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_QUAD;
+            break;
+        case FM_PENT:
+            printf("Decoding to pentagon\n");
+            if(inorder==1)
+                decodefunc = fm_i1_pent;
+            else
+                decodefunc = fm_i2_pent;
+            break;
+
+        case DM_5_0:
+            printf("Decoding to 5.0 surround (David Moore)\n");
+            if(inorder==1)
+                decodefunc = dm_i1_surr;
+            else
+                decodefunc = dm_i2_surr;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_SURR_5_0;
+            break;
+        case DM_5_1:
+            printf("Decoding to  5.1 surround (David Moore)\n");
+            if(inorder==1)
+                decodefunc = dm_i1_surr6;
+            else
+                decodefunc = dm_i2_surr6;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_DOLBY_5_1;
+            break;
+        case FM_HEX:
+            printf("Decoding to Hexagon\n");
+            if(inorder==1)
+                decodefunc = fm_i1_hex;
+            else
+                decodefunc = fm_i2_hex;
+            break;
+        case FM_OCT1:
+            printf("Decoding to Octagon 1\n");
+            if(inorder==1)
+                decodefunc = fm_i1_oct1;
+            else
+                decodefunc = fm_i2_oct1;
+            break;
+        case FM_OCT2:
+            printf("Decoding to Octagon 2\n");
+            if(inorder==1)
+                decodefunc = fm_i1_oct2;
+            else
+                decodefunc = fm_i2_oct2;
+            break;
+        case FM_CUBE:
+            printf("Decoding to Cube (FM interleaved)\n");
+            if(inorder==1)
+                decodefunc = fm_i1_cube;
+            else
+                decodefunc = fm_i2_cube;
+            break; 
+        case FM_QUADCUBE:
+            printf("Decoding to Octagon 1 (WAVEX order)\n");
+            if(inorder==1)
+                decodefunc = fm_i1_cubex;
+            else
+                decodefunc = fm_i2_cubex;
+            if(write_wavex && write_speakerpositions)
+                chformat = MC_CUBE;
+            break;
+    }
+    outformat = psf_getFormatExt(sfname);
+    if(outformat >= PSF_AIFF){
+        if(write_speakerpositions)
+            printf("Warning: -x requires .wav format\n");
+    }
+    outchans = layout_chans[layout-1];
+    frame = malloc(sizeof(float) * outchans);
+    if(frame==NULL){
+        puts("No Memory!\n");
+        return 1;
+    }
+	props.chformat = STDWAVE;
+	props.chans = outchans;
+    if(!is_legalsize(outsize,&props)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+    
+    /*TODO: set speaker pos when we can */
+	if(write_wavex){
+		props.chformat = chformat;
+		props.format = PSF_WAVE_EX;
+	}
+
+	ofd = psf_sndCreate(sfname,&props,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		fprintf(stderr,"can't create outfile %s\n",sfname);
+		psf_sndClose(ifd);
+		return 1;
+	}
+    peaks = (PSF_CHPEAK*)  malloc(sizeof(PSF_CHPEAK) * outchans);
+    memset(peaks,0,sizeof(PSF_CHPEAK) * outchans);
+
+	halfsec = props.srate / 2;
+	framepos = 0;
+	printf("\ndecoding:\n");
+	while((got = psf_sndReadFloatFrames(ifd,abfframe,1))==1){
+        memset(&abfsample,0,sizeof(ABFSAMPLE));
+        copyfunc(&abfsample,abfframe);
+        decodefunc(&abfsample,frame,1);            
+		if(0 > psf_sndWriteFloatFrames(ofd,frame,1)){
+			fprintf(stderr,"error writing to outfile\n");
+			psf_sndClose(ifd);
+			psf_sndClose(ofd);
+			return 1;
+		}
+
+		if((framepos % halfsec) == 0){
+			printf("%.2lf secs\r",(double) framepos / (double) props.srate);
+            fflush(stdout);
+        }
+		framepos++;
+	}
+
+	if(got != 0){
+		fprintf(stderr,"warning: not all data was read\n");
+	}
+	printf("\n%.4lf secs\nWritten %d frames to %s\n",(double)framepos / (double) props.srate,framepos,sfname);
+	
+    if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+        printf("PEAK values:\n");
+        for(i=0; i < outchans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+            
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                       val,dbval,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                       val,(unsigned int)peaks[i].pos,(double)peaks[i].pos / (double) props.srate); 
+            }
+        }
+    }
+    printf("\n");
+	psf_sndClose(ifd);
+	psf_sndClose(ofd);
+    if(peaks)
+        free(peaks);
+	psf_finish();
+	return 0;
+}

+ 106 - 0
dev/externals/mctools/fmdcode.h

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmdcode.h */
+
+/*
+ Channel order is WXYZ,RSTUV,KLMNOPQ
+ 
+ The number of channels defines the order of the soundfield:
+ 2 channel = UHJ 
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly
+                                                            called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+ 11 channel = ffh = 3rd order horizontal + 2nd order height
+ 16 channel = fff = 3rd order 3-D
+ 
+ 
+ Horizontal   Height  Soundfield   Number of    Channels
+ order 	      order 	  type      channels 	
+ 1 	         0 	       horizontal 	  3 	    WXY
+ 1 	         1 	      full-sphere 	  4 	    WXYZ
+ 2 	         0 	       horizontal 	  5 	    WXY....UV
+ 2 	         1 	       mixed-order    6 	    WXYZ...UV
+ 2 	         2 	      full-sphere     9 	    WXYZRSTUV
+ 3           0 	       horizontal 	  7 	    WXY....UV.....PQ
+ 3 	         1         mixed-order 	  8 	    WXYZ...UV.....PQ
+ 3 	         2 	       mixed-order 	 11 	    WXYZRSTUV.....PQ
+ 3 	         3 	      full-sphere 	 16 	    WXYZRSTUVKLMNOPQ
+ */
+
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;
+    float R;
+    float S;
+    float T;
+	float U;
+    float V;
+} ABFSAMPLE;
+
+typedef void (*fmhcopyfunc)(ABFSAMPLE*,const float*);
+
+typedef void (*fmhdecodefunc)(const ABFSAMPLE*, float*,unsigned int);
+//void bfdcode4(float *inbuf,long numframes);
+void fmhcopy_3(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_4(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf);
+
+
+// i1 = inphase 1st order, etc
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_surr(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);

+ 32 - 0
dev/externals/mctools/fmhcube1.txt

@@ -0,0 +1,32 @@
+f left low	<0.5774,0.5774,-0.5774>		0.1768	0.1140	0.1140	-0.1140	0.0000	-0.0369	-0.0369	0.0000	0.0369
+f right	low 	<0.5774,-0.5774,-0.5774>	0.1768	0.1140	-0.1140	-0.1140	0.0000	-0.0369	0.0369	0.0000	-0.0369
+r right low	<-0.5774,-0.5774,-0.5774>	0.1768	-0.1140	-0.1140	-0.1140	0.0000	0.0369	0.0369	0.0000	0.0369
+r left low	<-0.5774,0.5774,-0.5774>	0.1768	-0.1140	0.1140	-0.1140	0.0000	0.0369	-0.0369	0.0000	-0.0369
+f left high	<0.5774,0.5774,0.5774>		0.1768	0.1140	0.1140	0.1140	0.0000	0.0369	0.0369	0.0000	0.0369
+f right high	<0.5774,-0.5774,0.5774>		0.1768	0.1140	-0.1140	0.1140	0.0000	0.0369	-0.0369	0.0000	-0.0369
+r right high	<-0.5774,-0.5774,0.5774>	0.1768	-0.1140	-0.1140	0.1140	0.0000	-0.0369	-0.0369	0.0000	0.0369
+r left high	<-0.5774,0.5774,0.5774>		0.1768	-0.1140	0.1140	0.1140	0.0000	-0.0369	0.0369	0.0000	-0.0369
+
+
+Csound order					W	  X	  Y       Z      R         S      T        U       V
+f left low	<0.5774,0.5774,-0.5774>		0.1768	 0.1140	 0.1140	-0.1140	0.0000	-0.0369	-0.0369	0.0000	 0.0369
+f left high	<0.5774,0.5774,0.5774>		0.1768	 0.1140	 0.1140	 0.1140	0.0000	 0.0369	 0.0369	0.0000	 0.0369
+r left low	<-0.5774,0.5774,-0.5774>	0.1768	-0.1140	 0.1140	-0.1140	0.0000	 0.0369	-0.0369	0.0000	-0.0369
+r left high	<-0.5774,0.5774,0.5774>		0.1768	-0.1140	 0.1140	 0.1140	0.0000	-0.0369	 0.0369	0.0000	-0.0369
+r right low	<-0.5774,-0.5774,-0.5774>	0.1768	-0.1140	-0.1140	-0.1140	0.0000	 0.0369	 0.0369	0.0000	 0.0369
+r right high	<-0.5774,-0.5774,0.5774>	0.1768	-0.1140	-0.1140	 0.1140	0.0000	-0.0369	-0.0369	0.0000	 0.0369
+f right	low 	<0.5774,-0.5774,-0.5774>	0.1768	 0.1140	-0.1140	-0.1140	0.0000	-0.0369	 0.0369	0.0000	-0.0369
+f right high	<0.5774,-0.5774,0.5774>		0.1768	 0.1140	-0.1140	 0.1140	0.0000	 0.0369	-0.0369	0.0000	-0.0369
+
+WAVEX ORDER, LOW + HIGH
+
+f left low	<0.5774,0.5774,-0.5774>		0.1768	 0.1140	 0.1140	-0.1140	0.0000	-0.0369	-0.0369	0.0000	 0.0369
+f right	low 	<0.5774,-0.5774,-0.5774>	0.1768	 0.1140	-0.1140	-0.1140	0.0000	-0.0369	 0.0369	0.0000	-0.0369
+r left low	<-0.5774,0.5774,-0.5774>	0.1768	-0.1140	 0.1140	-0.1140	0.0000	 0.0369	-0.0369	0.0000	-0.0369
+r right low	<-0.5774,-0.5774,-0.5774>	0.1768	-0.1140	-0.1140	-0.1140	0.0000	 0.0369	 0.0369	0.0000	 0.0369
+
+f left high	<0.5774,0.5774,0.5774>		0.1768	 0.1140	 0.1140	 0.1140	0.0000	 0.0369	 0.0369	0.0000	 0.0369
+f right high	<0.5774,-0.5774,0.5774>		0.1768	 0.1140	-0.1140	 0.1140	0.0000	 0.0369	-0.0369	0.0000	-0.0369
+r left high	<-0.5774,0.5774,0.5774>		0.1768	-0.1140	 0.1140	 0.1140	0.0000	-0.0369	 0.0369	0.0000	-0.0369
+r right high	<-0.5774,-0.5774,0.5774>	0.1768	-0.1140	-0.1140	 0.1140	0.0000	-0.0369	-0.0369	0.0000	 0.0369
+

+ 712 - 0
dev/externals/mctools/fmhfuncs.c

@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmhfuncs.c */
+#include "fmdcode.h"
+
+/* TODO: expand to handle numframes frames? */
+
+void fmhcopy_3(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf;
+}
+
+void fmhcopy_4(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf;
+}
+
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// following discard 3rd order chans
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// these identical for 2nd order horiz max, but may be expanded later!
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+/********** DECODE FUNCS *************/
+/* TODO: complete support for numframes > 1 */
+
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    int i;	
+	float *p_out = outbuf;
+	double aw;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		*p_out++ = (float) aw;  		  
+	}
+}
+
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    int i;	
+	float *p_out = outbuf;
+	double aw,ay;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		ay = (double) inbuf->Y * 0.5;		
+		
+		*p_out++ = (float) (aw +  ay);  
+		*p_out++ = (float) (aw  - ay);  
+	}
+}
+
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out++ = (float) (aw - ax - ay);  //RR
+		*p_out++ = (float) (aw + ax - ay);  //FR
+	}
+}
+void fm_i2_square(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out++ = (float) (aw - ax - ay + av);  //RR
+		*p_out++ = (float) (aw + ax - ay - av);  //FR
+	}
+}
+/* ditto, RLRL layout for WAVEX */
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+        *p_out++ = (float) (aw + ax - ay);  //FR
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out++ = (float) (aw - ax - ay);  //RR
+		
+	}
+}
+void fm_i2_quad(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+        *p_out++ = (float) (aw + ax - ay - av);  //FR
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out++ = (float) (aw - ax - ay + av);  //RR
+		
+	}
+}
+
+
+//front pair angle 72deg
+void fm_i1_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax*0.1618) + (ay*0.1176));  //FL
+		*p_out++ = (float) (aw - (ax*0.0618) + (ay*0.1902));  
+		*p_out++ = (float) (aw - (ax*0.2));  
+		*p_out++ = (float) (aw - (ax*0.0618) - (ay*0.1902));  
+        *p_out++ = (float) (aw + (ax*0.1618) - (ay*0.1176)); //FR
+	}
+}
+
+void fm_i2_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;	
+        au = (double) inbuf->U;
+        av = (double) inbuf->V;
+		
+		*p_out++ = (float) (aw + (ax*0.2227) + (ay*0.1618) + (au*0.0238) + (av * 0.0733));  
+		*p_out++ = (float) (aw - (ax*0.0851) + (ay*0.2619) - (au*0.0624) - (av * 0.0453));  
+		*p_out++ = (float) (aw - (ax*0.2753)               + (au * 0.0771)              );  
+		*p_out++ = (float) (aw - (ax*0.0851) - (ay*0.2619) - (au*0.0624) + (av * 0.0453));  
+        *p_out++ = (float) (aw + (ax*0.2227) - (ay*0.1618) + (au*0.0238) - (av * 0.0733));
+	}
+}
+
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		/* TODO: fix this order! */
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C    ///???
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R    ///????
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+/* 5.1 versions - silent LFE */
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R
+        *p_out++ = 0.0f;                                                    //LFE
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+        *p_out++ = 0.0f;                                                                              //LFE
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+// 1st order 5.0
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+//1st order 5.1
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		*p_out++ = 0.0f;                                                     //LFE
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+// 2nd order 5.0
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+// 2nd order 5.1
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = 0.0f;                                                                                  //LFE
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+
+
+void fm_i1_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1443;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.0833));  //FL
+		*p_out++ = (float) (aw      + (ay * 0.1667));  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.0833));  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.0833));  //RR
+        *p_out++ = (float) (aw      - (ay * 0.1667));  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.0833));  //FR
+	}
+}
+void fm_i2_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1987;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U;
+        av = (double) inbuf->V * 0.0556;
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.1147) + (au * 0.0321) + av);  //FL
+		*p_out++ = (float) (aw      + (ay * 0.2294) - (au * 0.0643)     );  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.1147) + (au * 0.0321) - av);  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.1147) + (au * 0.0321) + av);  //RR
+        *p_out++ = (float) (aw      - (ay * 0.2294) - (au * 0.0643)     );  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.1147) + (au * 0.0321) - av);  //FR
+	}
+}
+
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.1155) + (ay * 0.0478));  
+		*p_out++ = (float) (aw + (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.1155) + (ay * 0.0478));  
+        *p_out++ = (float) (aw - (ax * 0.231)  - (ay * 0.0957));  
+        *p_out++ = (float) (aw - (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++ = (float) (aw + (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++   = (float) (aw + (ax * 0.1155) - (ay * 0.0478));  
+	}
+}
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.03417;
+		av = (double) inbuf->V * 0.03417;
+        
+		*p_out++ = (float) (aw + (ax * 0.15906) + (ay * 0.06588) + au + av);  
+		*p_out++ = (float) (aw + (ax * 0.06588) + (ay * 0.15906) - au + av);  
+		*p_out++ = (float) (aw - (ax * 0.06588) + (ay * 0.15906) - au - av);  
+		*p_out++ = (float) (aw - (ax * 0.15906) + (ay * 0.06588) + au - av);  
+        *p_out++ = (float) (aw - (ax * 0.15906) - (ay * 0.06588) + au + av);  
+        *p_out++ = (float) (aw - (ax * 0.06588) - (ay * 0.15906) - au + av);  
+        *p_out++ = (float) (aw + (ax * 0.06588) - (ay * 0.15906) - au - av);  
+        *p_out++ = (float) (aw + (ax * 0.15906) - (ay * 0.06588) + au - av);  
+	}
+}
+
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.125)                 );  
+		*p_out++ = (float) (aw + (ax * 0.0884) + (ay * 0.0884));  
+		*p_out++ = (float) (aw                 + (ay * 0.125) );  
+		*p_out++ = (float) (aw - (ax * 0.0884) + (ay * 0.0884));  
+        *p_out++ = (float) (aw - (ax * 0.125)                 );  
+        *p_out++ = (float) (aw - (ax * 0.0884) - (ay * 0.0884));  
+        *p_out++ = (float) (aw                 - (ay * 0.125) );  
+        *p_out++ = (float) (aw + (ax * 0.0884) - (ay * 0.0884));  
+	}
+}
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.0482;
+		av = (double) inbuf->V * 0.0482;
+        
+		*p_out++ = (float) (aw + (ax * 0.1721)                 + au     );  
+		*p_out++ = (float) (aw + (ax * 0.1217) + (ay * 0.1217)      + av);  
+		*p_out++ = (float) (aw                 + (ay * 0.1721) - au     );  
+		*p_out++ = (float) (aw - (ax * 0.1217) + (ay * 0.1217)      - av);  
+        *p_out++ = (float) (aw - (ax * 0.1721)                 + au     );  
+        *p_out++ = (float) (aw - (ax * 0.1217) - (ay * 0.1217)      + av);  
+        *p_out++ = (float) (aw                 - (ay * 0.1721) - au     );  
+        *p_out++ = (float) (aw + (ax * 0.1217) - (ay * 0.1217)      - av);  
+	}
+}
+
+/* csound order; low/high anti-clockwise. 
+FMH page order, 4 low folowed by 4 high , clockwise! */
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        
+		*p_out++ = (float) (aw - ax + ay - az);  // RL low
+		*p_out++ = (float) (aw - ax + ay + az);  //    hi
+        
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        *p_out++ = (float) (aw - ax - ay + az);  //   hi
+        
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az);  //    hi
+	}
+}
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av); //FL low 
+		*p_out++ = (float) (aw + ax + ay + az + as + at + av); //   hi 
+		
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av); //RL low
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  
+        
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av); // RR low
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  
+        
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az + as - at - av);   
+	}
+}
+/* ditto, wavex order */
+/* Front L, front R, Back L, Back R; top Front L, Top Fr R, Top Back L, Top back R */
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        *p_out++ = (float) (aw + ax - ay + az);  // FR hi
+		*p_out++ = (float) (aw - ax + ay + az);  // RL hi
+        *p_out++ = (float) (aw - ax - ay + az);  // RR hi
+	}
+}
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av);  // RR low
+		
+        *p_out++ = (float) (aw + ax + ay + az + as + at + av);  // FL  hi 
+		*p_out++ = (float) (aw + ax - ay + az + as - at - av);  // FR  hi
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  // RL  hi 
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  // RR  hi 
+	}
+}
+
+
+#ifdef NOTDEF
+void bfdcode4(float *inbuf,long numframes)
+{
+	int i;	
+	float *p_buf = inbuf;	
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double)(p_buf[0]) ;
+		ax = (double)(p_buf[1]) * 0.707;
+		ay = (double)(p_buf[2]) * 0.707;		
+		//decode frame
+		*p_buf++ = (float)(0.3333 * (aw + ax + ay)); //FL
+		*p_buf++ = (float)(0.3333 * (aw + ax - ay)); //FR
+		*p_buf++ = (float)(0.3333 * (aw - ax + ay)); //RL
+		*p_buf++ = (float)(0.3333 * (aw - ax - ay));  //RR
+	}
+}
+
+
+/* handle 3ch in to 4ch out! */
+
+void bfdcode324(float *inbuf,float*outbuf,long numframes)
+{
+	int i;	
+	float *p_buf = inbuf;
+	float * p_outbuf = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		int j;
+		aw = (double)(*p_buf++) ;
+		ax = (double)(*p_buf++) * 0.707;
+		ay = (double)(*p_buf++) * 0.707;
+        
+		//decode frame
+		*p_outbuf++ = (float)(0.3333 * (aw + ax + ay));
+		*p_outbuf++ = (float)(0.3333 * (aw + ax - ay));
+		*p_outbuf++ = (float)(0.3333 * (aw - ax + ay));
+		*p_outbuf++ = (float)(0.3333 * (aw - ax - ay));
+	}
+}
+#endif

+ 504 - 0
dev/externals/mctools/interlx.c

@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/*TODO: fix bug writing header when using -x with .amb extension (omits cbSize setting) */
+
+/*interlx.c */
+/* v1.3. nov 2005; support placeholder arg for silent channel */
+/* v 1.7 beta ; added surr 5.0; updated sfsys with bit-correct 24bit copy */
+/* v 1.8 March 2009 updated sfsys  for AIFC int24 suport */
+
+/* OCT 2009 portsf(64) version. int24 aifc supported for reading. */
+/* v2.0.1 Jan 2010: Corrected usage message to refer to outfile.  */
+/* Nov 2013 added MC_SURR_6_1 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include <string.h>
+#include <ctype.h>
+#include "portsf.h"
+
+/*
+The number of channels defines the order of the soundfield:
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+11 channel = ffh = 3rd order horizontal + 2nd order height
+16 channel = fff = 3rd order 3-D
+*/
+
+#define N_BFORMATS (10)
+static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+#define MAX_INFILES (16)	//should keep most people happy! Won't get much more on a cmdline anyway
+
+enum {OPT_STD_WAVE,OPT_WAVEX_GENERIC,OPT_WAVEX,OPT_WAVEX_LCRS,OPT_WAVEX_SURROUND,OPT_WAVEX_BFORMAT,
+   OPT_SURR_5_0,OPT_WAVEX_7_1,OPT_WAVEX_CUBE,OPT_WAVEX_6_1, OPT_MAXOPTS};
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+#ifndef max
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+void
+usage()
+{
+	fprintf(stderr,"\nCDP MCTOOLS: INTERLX v2.1.0 (c) RWD,CDP 2009,2013\n"
+			"Interleave mono or stereo files into a multi-channel file\n"
+			"Usage: interlx [-tN] outfile infile1 infile2 [infile3...]\n"
+			
+			"       Up to %d files may be interleaved.\n"						
+			"       Output format is taken from infile1.\n"
+			"       Files must match sample rate and number of channels,\n"
+			"          but can have different sample types.\n"
+			"       To create a silent channel, for infile2 onwards,\n"
+			"          use 0 (zero) as filename. Infile1 must be a soundfile.\n"
+			"       NB: Speaker-positions in WAVE_EX infiles are ignored\n"
+			"       Note that the same infile can be listed multiple times,\n"
+			"          for example, to write a mono file as stereo, quad, etc.\n"
+			"       The .amb B-Format extension is supported: \n"
+			"          the program warns if channel count is anomalous.\n"
+			"       recognised Bformat channel counts: 3,4,5,6,7,8,9,11,16.\n"
+            "   -tN  : write outfile format as type N\n"
+	        "    Available formats:\n"
+			"    0   : (default) standard soundfile (.wav, .aif, .afc, .aifc)\n"
+			"    1   : generic WAVE_EX (no speaker assignments)\n"
+			"    2   : WAVE_EX mono/stereo/quad(LF,RF,LR,RR)   - total chans must match.\n"
+			"    3   : WAVE_EX quad surround (L,C,R,S)         - total chans must be 4.\n"
+			"    4   : WAVE_EX 5.1 format surround             - total chans must be 6.\n"
+			"    5   : WAVE_EX Ambisonic B-format (W,X,Y,Z...) - extension .amb recommended.\n"
+            "    6   : WAVE_EX 5.0 surround                    - total chans must be 5.\n"
+            "    7   : WAVE_EX 7.1 Surround                    - total chans must be 8.\n"
+            "    8   : WAVE_EX Cube Surround                   - total chans must be 8.\n"
+            "    9   : WAVE_EX 6.1 surround (new in v 2.1.0)   - total chans must be 7.\n"
+			"          in all cases: outfile has sample format of infile1\n"
+			"          NB: types 1 to %d are for WAV format only\n"			
+			,MAX_INFILES,OPT_MAXOPTS-1);
+	
+}
+	
+
+void cleanup(int *sflist)
+{
+
+	int i;
+	for(i=0;i < MAX_INFILES; i++)
+		if(sflist[i] >= 0)
+			psf_sndClose(sflist[i]);
+	psf_finish();
+}
+
+int main(int argc, char *argv[])
+{
+
+	long outframesize,thissize;
+	int i,ofd;
+	int ifdlist[MAX_INFILES];
+	//int force_stype = -1,out_stype;
+    int force_wave = 0;
+	int infilearg,inchans;
+	int num_infiles = 0;
+	int halfsec;
+    MYLONG peaktime;
+	float *outframe	= NULL,*inframe = NULL;
+	PSF_PROPS firstinprops,inprops;
+	PSF_CHPEAK *fpeaks = NULL;
+    int wave_type = -1;
+    char *create_msg = NULL;
+    psf_format informat = PSF_STDWAVE;
+	char* p_dot = NULL;		/* to find extension of outfile */
+	
+	for(i=0;i < MAX_INFILES;i++)
+		ifdlist[i] = -1;
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("2.0.1.\n");
+        return 0;
+    }
+    
+	if(argc < 4) {
+		fprintf(stderr,"interlx: insufficient arguments\n");
+		usage();
+		exit(1);
+	}
+
+	while(argv[1][0] =='-'){
+		
+		switch(argv[1][1]){
+        case('t'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"-t flag requires parameter\n");
+				usage();
+				exit(1);
+			}
+			wave_type = atoi(&(argv[1][2]));
+			if((wave_type < 0) || wave_type >= OPT_MAXOPTS){
+				fprintf(stderr,"wave type out of range\n");
+				usage();
+				exit(1);
+			}
+			force_wave = 1;
+			break;
+		default:
+			fprintf(stderr,"\nabfpan: error: illegal flag option %s",argv[1]);
+			exit(1);
+		}
+
+		argc--; argv++;
+	}
+
+	if(argc < 4){
+		fprintf(stderr,"interlx error: at least two infiles required!\n");
+		usage();
+		exit(1);
+	}
+
+	if(psf_init()){
+		printf("Startup failure.\n");
+		return  1;
+	}
+	
+	//open first infile and get properties
+	ifdlist[0] = psf_sndOpen(argv[2],&firstinprops,0);
+	if(ifdlist[0] < 0){
+		fprintf(stderr,"unable to open infile %s\n",argv[2]);
+		cleanup(ifdlist);
+		return 1;
+	}
+
+/* we don't know how to deal with speaker positions yet, so disregard these
+	if(firstinprops.chformat > MC_STD){
+		printf(stderr,"Warning,interlx: ignoring source file speaker positions\n");
+	}
+*/
+	outframesize = psf_sndSize(ifdlist[0]);
+	if(outframesize < 0){
+		fprintf(stderr,"unable to read size of infile %s\n",argv[2]);
+		cleanup(ifdlist);
+		return 1;
+	}
+	inchans = firstinprops.chans;
+	/*we can always allow more channels if someone really needs it! */
+	if(!(inchans==1 || inchans==2)){
+		fprintf(stderr,"interlx: error: infile %s has %d channels\n"
+				"\t(only mono and stereo files can be used)\n",argv[2],inchans);
+		cleanup(ifdlist);
+		return 1;
+	}
+	
+	num_infiles = 1;
+	printf("interleaving %d-channel files,sample rate = %d\n",inchans,firstinprops.srate);
+
+	infilearg = 3;
+	while(argv[infilearg] != NULL){
+		if(strcmp(argv[infilearg],"0")==0){
+			ifdlist[num_infiles] = -1;           // mark silent channel
+		}
+		else{
+
+			if((ifdlist[num_infiles] = psf_sndOpen(argv[infilearg],&inprops,0)) < 0){
+				fprintf(stderr,"cannot open infile %s\n",argv[infilearg]);
+				cleanup(ifdlist);
+				return 1;
+			}
+			if(inprops.chans != firstinprops.chans){
+				fprintf(stderr,"interlx: error: channel mismatch from infile %s\n",argv[infilearg]);
+				cleanup(ifdlist);
+				return 1;
+			}
+
+			if(inprops.srate != firstinprops.srate){
+			   fprintf(stderr,"interlx: error: sample rate mismatch from infile %s\n",argv[infilearg]);
+				cleanup(ifdlist);
+				return 1;
+			}
+
+			thissize = psf_sndSize(ifdlist[num_infiles]);
+			if(thissize < 0){
+				fprintf(stderr,"unable to read size of infile %s\n",argv[infilearg]);
+				cleanup(ifdlist);
+				return 1;
+			}			
+			outframesize = max(outframesize,thissize);
+		}
+		
+		infilearg++;
+		num_infiles++;
+		if(num_infiles > MAX_INFILES){
+			fprintf(stderr,"Sorry! too many infiles. Maximum accepted is %d.\n",MAX_INFILES);
+			cleanup(ifdlist);
+			exit(1);
+		}
+
+	}
+
+	
+	inframe = malloc(inchans * sizeof(float));
+	if(inframe==NULL){
+		puts("interlx: error: no memory for input buffer!\n");
+		cleanup(ifdlist);
+		return 1;
+	}
+
+	firstinprops.chans *= num_infiles;
+	outframe = (float *) malloc(firstinprops.chans * sizeof(float));
+	if(outframe==NULL){
+		puts("\ninterlx: error: no memory for output buffer!\n");
+		cleanup(ifdlist);
+		return 1;
+	}
+
+	fpeaks = (PSF_CHPEAK *) calloc(firstinprops.chans,sizeof(PSF_CHPEAK));
+	if(fpeaks==NULL){
+		puts("interlx: error: no memory for internal PEAK buffer\n");
+		cleanup(ifdlist);
+		return 1;
+	}
+    if(force_wave){
+		int i,matched;
+		
+		switch(wave_type){
+        case(OPT_WAVEX_GENERIC):
+			inprops.chformat = MC_STD;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating STD WAVE_EX file";
+			break;
+		case(OPT_WAVEX):
+			switch(firstinprops.chans){
+			case(1):
+				firstinprops.chformat = MC_MONO;
+				informat = PSF_WAVE_EX;
+				create_msg = "creating MONO WAVE_EX file";
+				break;
+			case(2):
+				firstinprops.chformat = MC_STEREO;
+				informat = PSF_WAVE_EX;
+				create_msg = "creating STEREO WAVE_EX file";
+				break;
+			case(4):
+				firstinprops.chformat = MC_QUAD;
+				informat = PSF_WAVE_EX;
+				create_msg = "creating QUAD WAVE_EX file";
+				break;
+			default:
+				fprintf(stderr,"infile nchans incompatible with requested WAVE-EX format\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;				
+			}
+			break;
+		case(OPT_WAVEX_LCRS):
+			if(firstinprops.chans != 4){
+				fprintf(stderr,"result must have four channels\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;
+			}
+			firstinprops.chformat = MC_LCRS;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating LCRS-surround WAVE_EX file";
+			break;
+		case(OPT_WAVEX_SURROUND):
+			if(firstinprops.chans != 6){
+				fprintf(stderr,"result must have six channels\n");
+				usage();
+				cleanup(ifdlist);
+				exit(1);
+			}
+			firstinprops.chformat = MC_DOLBY_5_1;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating 5.1 surround WAVE_EX file";
+			break;
+            
+        case(OPT_SURR_5_0):
+			if(firstinprops.chans != 5){
+				fprintf(stderr,"result must have five channels.\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;
+			}
+			firstinprops.chformat = MC_SURR_5_0;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating 5.0 surround WAVE_EX file";
+			break;
+
+		case(OPT_WAVEX_BFORMAT):
+			matched = 0;
+			for(i=0;i < N_BFORMATS;i++)	{
+				if(firstinprops.chans == bformats[i]){
+					matched = 1;
+					break;
+				}
+			}
+			if(!matched){
+				printf("WARNING: No Bformat definition for %d-channel file.\n",inprops.chans);
+			}
+			firstinprops.chformat = MC_BFMT;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating AMBISONIC B-FORMAT WAVE_EX file";
+			break;
+        case OPT_WAVEX_7_1:
+            if(firstinprops.chans != 8){
+				fprintf(stderr,"result must have  channels\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;
+			}
+			firstinprops.chformat = MC_SURR_7_1;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating 7.1 surround WAVE_EX file";
+			break;
+        case OPT_WAVEX_CUBE:
+            if(firstinprops.chans != 8){
+				fprintf(stderr,"result must have  channels\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;
+			}
+			firstinprops.chformat = MC_CUBE;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating cube surround WAVE_EX file";
+			break;
+		case OPT_WAVEX_6_1:
+            if(firstinprops.chans != 7){
+				fprintf(stderr,"result must have  channels\n");
+				usage();
+				cleanup(ifdlist);
+				return 1;
+			}
+			firstinprops.chformat = MC_SURR_6_1;
+			informat = PSF_WAVE_EX;
+			create_msg = "creating 6.1 surround WAVE_EX file";
+			break;
+		default:
+            inprops.chformat = STDWAVE;
+			informat = PSF_STDWAVE;
+			create_msg = "creating plain sound file";
+			break;
+		}		
+	}
+	
+	/*  want to avoid WAVE_EX if just plain WAVE possible */
+	firstinprops.format = informat;
+		/*firstinprops.chformat = MC_STD;*/		/* RWD April 2006 do this here? */
+	
+	p_dot = strrchr(argv[1],'.');
+	if(stricmp(++p_dot,"amb")==0) {
+		int i;
+		int matched = 0;
+		firstinprops.chformat = MC_BFMT;
+		for(i=0;i < N_BFORMATS;i++)	{
+			if(firstinprops.chans == bformats[i]){
+				matched = 1;
+				break;
+			}
+		}
+		if(!matched)
+			printf("\nWARNING: channel count %d unknown for BFormat.\n",firstinprops.chans);
+
+	}
+    if(!is_legalsize(outframesize,&firstinprops)){
+        fprintf(stderr,"error: outfile size %ld exceeds capacity of format.\n",outframesize);
+        return 1;
+    }
+    printf("\n%s: %d channels, %ld frames.\n",create_msg,firstinprops.chans,outframesize);
+	ofd = psf_sndCreate(argv[1],&firstinprops,0,0,PSF_CREATE_RDWR);
+	
+	
+	if(ofd < 0){
+		fprintf(stderr,"interlx: error: unable to create outfile %s.\n",argv[1]);
+		cleanup(ifdlist);
+		return 1;
+	}
+
+	halfsec = firstinprops.srate / 2;
+	for(i=0;i < outframesize; i++){		// frame loop
+		float *p_framesamp,*p_filesamp;
+		int j;
+		
+		p_framesamp = outframe;
+		memset((char *)outframe,0,firstinprops.chans * sizeof(float));
+
+		for(j=0;j < num_infiles;j++) {	//file loop
+			int k,got;
+            
+			memset((char *)inframe,0,inchans * sizeof(float));
+			if(ifdlist[j] < 0){
+				got = inchans; // placeholder - write silent channel
+			}
+			else{
+				if((got = psf_sndReadFloatFrames(ifdlist[j],inframe,1)) < 0){
+					fprintf(stderr,"interlx: error reading from infile %s\n",argv[2+j]);
+					psf_sndClose(ofd);					
+					cleanup(ifdlist);
+					return 1;
+				}
+			}
+			if(got==1){
+				p_filesamp = inframe;
+				for(k=0;k < inchans;k++)	//channel loop
+					*p_framesamp++	= *p_filesamp++;
+			}
+			else
+				p_framesamp += inchans;
+		}
+		
+		if(psf_sndWriteFloatFrames(ofd,outframe,1) < 0){
+			fprintf(stderr,"interlx: error writing to outfile\n");
+			psf_sndClose(ofd);            
+            cleanup(ifdlist);
+            return 1;
+		}
+        if(i % halfsec==0) {
+			printf("%.2lf secs\r",(double)i / (double) firstinprops.srate);
+            fflush(stdout);
+        }
+	}
+	printf("%.4lf secs\nWritten %ld sample frames to %s\n",(double)outframesize / (double)firstinprops.srate,outframesize,argv[1]);
+	if(psf_sndReadPeaks( ofd,fpeaks,&peaktime)){
+        printf("PEAK data:\n");
+        for(i = 0; i < firstinprops.chans; i++) {
+            double val, dbval;
+            val = (double) fpeaks[i].val;
+            dbval = 20.0 * log10(val);
+            printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                   val,dbval,(unsigned int) fpeaks[i].pos,(double)fpeaks[i].pos / (double) firstinprops.srate);
+        }
+    }
+	
+	printf("\n");
+	psf_sndClose(ofd);
+	free(inframe);
+	free(outframe);
+    if(fpeaks)
+        free(fpeaks);
+	cleanup(ifdlist);
+    
+	return 0;
+}

+ 698 - 0
dev/externals/mctools/njoin.c

@@ -0,0 +1,698 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* njoin.c: concantenate files with optional spacing */
+/* 12 Dec 2006 v 0.7: fixed bug in read_filelist: trap leading spaces on line */
+/* OCT 2009: v1.0 support tilde-prefixed path under unix */
+/* Jan 2010 v1.0.1 fixed bug processing lots of files and running out of portsf slots */
+/* Nov 2013 recognise MC_SURR_6_1 */
+/* Mar 2021  fix small file format type mismatch error (line 543 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h>
+#ifdef unix
+#include <glob.h>
+#endif
+#include "portsf.h"
+
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+#define F_MAXLEN (1024) 
+/* For CERN will need to increase this a lot! */
+#define MAXFILES (512)
+
+
+enum {ARG_PROGNAME, ARG_FLIST, ARG_NARGS};
+enum {ARG_OUTFILE = ARG_NARGS};
+
+int read_filelist(FILE* ifp, char* p_flist[]);
+void cleanup(int ifd, int ofd,char* flist[], int nfiles);
+
+#ifdef unix
+char* CreatePathByExpandingTildePath(char* path)
+{
+    glob_t globbuf;
+    char **v;
+    char *expandedPath = NULL, *result = NULL;
+    
+    assert(path != NULL);
+    
+    if (glob(path, GLOB_TILDE, NULL, &globbuf) == 0) //success
+    {
+        v = globbuf.gl_pathv; //list of matched pathnames
+        expandedPath = v[0]; //number of matched pathnames, gl_pathc == 1
+        
+        result = (char*)calloc( strlen(expandedPath) + 1,sizeof(char)); //the extra char is for the null-termination
+        if(result)
+            strncpy(result, expandedPath, strlen(expandedPath) + 1); //copy the null-termination as well
+        
+        globfree(&globbuf);
+    }
+    
+    return result;
+}
+#endif
+
+void
+usage()
+{
+	fprintf(stderr,"\nCDP MCTOOLS: NJOIN v1.1.1 (c) RWD,CDP 2006,2010,2013,2021\n"
+			"concatenate multiple files into a single file\n"
+			"Usage: njoin [-sSECS | -SSECS][-cCUEFILE][-x] filelist.txt [outfile] \n"						
+			"       filelist.txt: text file containing list of sfiles\n"
+			"                     in order. One file per line. \n"
+			"                     Channel spec (if present) must be the same,\n"
+			"                     but files with no spec assumed compatible.\n"
+			"       -cCUEFILE   : if outfile used, generate cue textfile as CUEFILE.\n"
+			"       -sSECS      : separate files with silence of SECS seconds\n"
+			"       -SSECS      : as above, but no silence before first file.\n"
+			"                     Default: files are joined with no gap.\n"
+            "      -x           : strict: allow only CD-compatible files:\n"
+            "                     Must use sr=44100, minimum duration 4 secs.\n"
+			"       NB: Files must match sample rate and number of channels,\n"
+			"        but can have different sample types.\n"
+			"       Output sample format taken from file with highest precision.\n"
+			"       If no outfile given: program scans files and prints report.\n"
+#ifdef unix
+            "       Unix systems:  ~/ notation for home dir supported for file paths.\n"
+#endif
+			);	
+}
+
+void cleanup(int ifd, int ofd,char* flist[], int nfiles)
+{
+	int i;
+	for(i=0;i < nfiles; i++) {		
+		if(flist[i])
+			free(flist[i]);
+	}
+	if(ifd >=0)
+		psf_sndClose(ifd);
+	if(ofd >=0)
+		psf_sndClose(ofd);	
+}
+/*SHORT8,SHORT16,FLOAT32,INT_32,INT2424,INT2432,INT2024,INT_MASKED*/
+static int wordsize[] = {1,2,4,4,3,4,3};
+
+/*return 0 for props2 same or less, 1 for props2 higher*/
+int compare_precision(const PSF_PROPS* props1,const PSF_PROPS* props2)
+{
+	int retval = 0;
+	switch(props1->samptype){
+	case(PSF_SAMP_8):
+		if(props2->samptype != props1->samptype)
+			retval = 1;
+		break;
+	case(PSF_SAMP_16):
+		if(props2->samptype > props1->samptype)
+			retval = 1;
+		break;
+	case(PSF_SAMP_IEEE_FLOAT):
+		/* only higher prec is 32bit int */
+		if(props2->samptype== PSF_SAMP_32)
+			retval = 1;
+		break;
+	case(PSF_SAMP_32):
+		/* nothing higher than this!*/
+		break;
+	case(PSF_SAMP_24):
+	//case(INT2432): // NB illegal for almost all formats!
+	//case(INT2024):
+		if(props2->samptype == PSF_SAMP_IEEE_FLOAT || props2->samptype == PSF_SAMP_32)
+			retval = 1;
+		break;
+	default:  
+		break;
+	}
+	return retval;
+}
+
+const char* stype_as_string(const PSF_PROPS* props)
+{
+	const char* msg;
+	switch(props->samptype){
+	case(PSF_SAMP_8):
+		msg = "8-bit";
+		break;
+	case(PSF_SAMP_16):
+		msg = "16-bit";
+		break;
+	case(PSF_SAMP_IEEE_FLOAT):
+		msg = "32-bit floats";
+		break;
+	case(PSF_SAMP_32):
+		msg = "32-bit integer";
+		break;
+	case(PSF_SAMP_24):
+		msg = "24-bit";
+		break;
+	default:
+		msg = "unknown WAVE_EX sample size!";
+		break;
+	}
+	return msg;
+}
+//STDWAVE,MC_STD,MC_MONO,MC_STEREO,MC_QUAD,MC_LCRS,MC_BFMT,MC_DOLBY_5_1,MC_WAVE_EX 
+const char* chformat_as_string(const PSF_PROPS* props)
+{
+	const char* msg;
+	switch(props->chformat){
+	case MC_STD:
+		msg = "Generic WAVE-EX";
+		break;
+	case MC_MONO:
+		msg = "WAVE_EX Mono";
+		break;
+	case MC_STEREO:
+		msg = "WAVE_EX Stereo";
+		break;
+	case MC_QUAD:
+		msg = "WAVE_EX Quad";
+		break;
+	case MC_LCRS:
+		msg = "WAVE_EX LCRS Surround";
+		break;
+	case MC_BFMT:
+		msg = "WAVE_EX B-Format";
+		break;
+	case MC_DOLBY_5_1:
+		msg = "WAVE_EX Dolby 5.1";
+		break;
+	case MC_SURR_6_1:
+	    msg =  "6.1 surround";
+	    break;
+    case MC_SURR_5_0:
+        msg = "5.0 surround";
+        break;
+    case MC_SURR_7_1:
+        msg = "7.1 Surround";
+        break;
+    case MC_CUBE:
+        msg = "Cube Surround";
+        break;
+	case MC_WAVE_EX:
+		msg = "WAVE-EX Custom Multi-Channel";
+		break;
+    default:  // STDWAVE
+        msg = "Standard soundfile";
+        break;
+	}
+
+	return msg;
+}
+
+int read_filelist(FILE* ifp, char* p_flist[])
+{
+	char buf[F_MAXLEN];
+	long len;
+	int nfiles = 0;
+#ifdef _DEBUG
+	assert(ifp);
+	assert(p_flist);
+#endif
+	if(ifp==NULL || p_flist == NULL)
+		return -1;
+	while (fgets(buf,F_MAXLEN-1,ifp)){
+		char* pbuf = buf;
+		while(*pbuf == ' ')
+			pbuf++;
+		len = strlen(pbuf);
+		if(len > 1){	// line has at least a eol byte
+			p_flist[nfiles] = malloc(len+1);
+			strcpy(p_flist[nfiles],pbuf);
+			if(p_flist[nfiles][len-1] == 0x0A)
+				p_flist[nfiles][len-1] = '\0';
+			nfiles++;
+		}
+		if(feof(ifp))
+			break;
+		if(ferror(ifp))
+			return -1;
+	}	
+	return nfiles;
+}
+ 
+int strict_check(PSF_PROPS* props,unsigned long dur)
+{
+    int ret = 0;
+    unsigned long mindur = 4 * 44100;
+    
+    if(props->srate==44100 && dur >= mindur && props->chans==2)
+        ret = 1;
+    return ret;
+}
+
+int main(int argc, char* argv[])
+{
+	int i = 0,j,ofd = -1;
+	int ifd = -1;
+	char* flist[MAXFILES];
+	char* cuefilename = NULL;
+	FILE* cuefp = NULL;
+	int num_infiles = 0;
+	float *inframe = NULL;
+    float*space_frame = NULL;
+	PSF_PROPS inprops, thisinprops,outprops;
+	PSF_CHPEAK *fpeaks = NULL;
+	double space_secs = 0.0;
+	long space_frames = 0;
+    long thisdur = 0;
+	double totaldur = 0.0;
+	FILE* fp = NULL;
+	int formatsrc = 0;
+	unsigned int max_datachunk = 0xFFFFFFFFU - 1024U;   /* check Ok for PEAK chunk */
+	double maxdur;
+	double blockdur = 0.25;  /* match buffersize to srate, so we get tidy upodate msgs */
+	long buflen,block_frames;
+	unsigned long written;
+	int error = 0;
+	int do_process = 1;
+	int have_s = 0, have_S = 0;
+    int strict  =0;
+    int strict_failures = 0;
+    char* fname;
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.1.0\n");
+        return 0;
+    }
+    
+	if(argc < ARG_NARGS){
+		fprintf(stderr,"njoin: insufficient arguemnts\n");
+		usage();
+		return 1;
+	}
+	while(argv[1][0] =='-'){	
+		switch(argv[1][1]){
+		case('c'):
+			if(argv[1][2]== '\0'){
+				fprintf(stderr,"-c flag needs filename.\n");
+				return 1;
+			}
+			cuefilename = &(argv[1][2]);
+			break;
+		case('s'):
+			if(have_S){
+				fprintf(stderr,"njoin: cannot have both -s and -S.\n");
+				return 1;
+			}
+			space_secs = atof(&argv[1][2]);
+			if(space_secs < 0.0){
+				fprintf(stderr,"njoin: -tSECS cannot be negative!\n");
+				return 1;
+			}
+			have_s = 1;
+			break;
+		case('S'):
+			if(have_s){
+				fprintf(stderr,"njoin: cannot have both -s and -S.\n");
+				return 1;
+			}
+			space_secs = atof(&argv[1][2]);
+			if(space_secs < 0.0){
+				fprintf(stderr,"njoin: -tSECS cannot be negative!\n");
+				return 1;
+			}
+			have_S = 1;
+			break;
+        case 'x':
+            strict = 1;
+            break;
+		default:
+			fprintf(stderr,"\nnjoin: illegal flag option %s",argv[1]);
+			exit(1);
+		}
+		argc--; argv++;
+	}
+
+	if(argc < ARG_NARGS){
+		fprintf(stderr,"njoin: insufficient arguemnts\n");
+		usage();
+		return 1;
+	}
+	
+	if(argc==ARG_NARGS)
+		do_process = 0;
+	/********** READ filelist *********/
+
+	fp = fopen(argv[ARG_FLIST],"r");
+	if(fp==NULL){
+		fprintf(stderr,"njoin: Unable to open input file %s\n",argv[ARG_FLIST]);
+		return 1;
+	}
+	
+	memset(flist,0,MAXFILES * sizeof(char*));
+	num_infiles = read_filelist(fp, flist);
+	if(num_infiles < 0){
+		fprintf(stderr,"njoin: file error reading filelist %s\n",argv[ARG_FLIST]);
+		fclose(fp);
+		return 1;
+	}
+	if(num_infiles ==0){
+		fprintf(stderr,"njoin: filelist is empty!\n");
+		fclose(fp);
+		return 1;
+	}
+	if(num_infiles ==1){
+		fprintf(stderr,"njoin: only one file listed - nothing to do!\n");
+		fclose(fp);
+		return 1;
+	}
+	fclose(fp); fp = NULL;
+
+#ifdef _DEBUG
+	fprintf(stderr, "file list contains %d files: \n",num_infiles);	
+	for(i=0;i < num_infiles; i++)
+		fprintf(stderr,"%s\n",flist[i]);
+#endif
+
+	/********* open and check all soundfiles ***********/
+	fprintf(stderr,"checking files...\n");
+	
+	if(psf_init()){
+		fprintf(stderr,"njoin: startup failure.\n");
+		return 1;
+	}
+    i = 0;
+#ifdef unix
+    fname = CreatePathByExpandingTildePath(flist[i]);
+    /* must free pointer later */
+#else
+    fname = flist[i];
+#endif
+	//open first infile and get properties
+	ifd = psf_sndOpen(fname,&inprops,0);
+	if(ifd < 0){
+		fprintf(stderr,"unable to open infile %s.\n",fname);
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 1;
+	}
+	thisdur = psf_sndSize(ifd);
+    if(strict){
+        if(!strict_check(&inprops,thisdur)){
+            fprintf(stderr,"Strict: file %s is not CD-compatible.\n",fname);
+            strict_failures++;
+        }
+    }
+	if(thisdur==0){
+		fprintf(stderr,"WARNING: file 1 empty: %s\n",fname);
+	}
+	else  {	
+		totaldur += (double) thisdur / inprops.srate;
+	}
+	psf_sndClose(ifd);
+#ifdef unix
+    free(fname);
+#endif
+	ifd = -1;
+	/* scan firther files, find max precision */
+	/* drop out if channel formats different */
+	for(i=1; i <	num_infiles; i++){
+#ifdef unix
+        fname = CreatePathByExpandingTildePath(flist[i]);
+        /* must free pointer later */
+#else
+        fname = flist[i];
+#endif
+        
+        
+		ifd = psf_sndOpen(fname,&thisinprops,0);
+		if(ifd < 0){
+			fprintf(stderr,"unable to open infile %s.\n",fname);
+			cleanup(ifd,ofd,flist,num_infiles);
+			exit(1);
+		}
+        thisdur = psf_sndSize(ifd);
+        if(strict){
+            if(!strict_check(&thisinprops,thisdur)){
+                fprintf(stderr,"Strict: file %s is not CD-compatible.\n",fname);
+                strict_failures++;
+            }
+        }
+		if(inprops.chans != thisinprops.chans){
+			fprintf(stderr,"njoin: channel mismatch in file %s",fname);
+			cleanup(ifd,ofd,flist,num_infiles);
+#ifdef unix
+            free(fname);
+#endif
+			return 1;
+		}
+		if(inprops.srate != thisinprops.srate){
+			fprintf(stderr,"njoin: sample rate mismatch in file %s",fname);
+			cleanup(ifd,ofd,flist,num_infiles);
+#ifdef unix
+            free(fname);
+#endif
+			return 1;
+		}
+		/* allow old multichannel files to be compatible with everything! */
+		if(! (inprops.chformat==(psf_channelformat)PSF_STDWAVE || thisinprops.chformat==(psf_channelformat)PSF_STDWAVE)){
+			if(inprops.chformat != thisinprops.chformat){
+				fprintf(stderr,"njoin: channel format mismatch in file %s",fname);
+				cleanup(ifd,ofd,flist,num_infiles);
+#ifdef unix
+                free(fname);
+#endif
+				return 1;
+			}
+		}
+		else {
+			/* one file is generic: promote format if possible*/
+			if(thisinprops.chformat > inprops.chformat)
+				inprops.chformat = thisinprops.chformat;
+		}
+
+		/* compare wordlength precision */
+		if(compare_precision(&inprops,&thisinprops)) {
+			inprops = thisinprops;
+			formatsrc = i;
+		}
+		thisdur = psf_sndSize(ifd);
+		if(thisdur==0){
+			fprintf(stderr,"WARNING: file %d empty: %s\n",i+1,fname);
+		}
+		else  {	
+			totaldur += (double) thisdur / thisinprops.srate;
+		}
+		psf_sndClose(ifd);
+		ifd = -1;
+#ifdef unix
+        free(fname);
+#endif
+	}
+    if(strict_failures){ 
+        fprintf(stderr,"Strict: %d files are CD-incompatible. Exiting.\n",strict_failures);
+        return 1;
+    }
+    
+	fprintf(stderr, "output format taken from file %d:\n\t%s\n",formatsrc+1,flist[formatsrc]);
+	fprintf(stderr, "sample type: %s\n",stype_as_string(&inprops));  
+	fprintf(stderr,"channel format: %s\n",chformat_as_string(&inprops));
+
+	maxdur = (double)(max_datachunk/ inprops.chans / wordsize[inprops.samptype]) / inprops.srate;
+	/*TODO: make sure we allow for size of PEAK chunk */
+	fprintf(stderr, "Max duration available for this format: %f secs.\n",maxdur);
+	if(have_S)
+		fprintf(stderr,"Total outfile length including spacing: %f secs\n", totaldur + (num_infiles-1) * space_secs);
+	else
+		fprintf(stderr,"Total outfile length including spacing: %f secs\n", totaldur + (num_infiles) * space_secs);
+
+	if(do_process == 0){
+		if(totaldur > maxdur)
+			fprintf(stderr, "Error: total duration exceeds capacity of file format.\n");
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 0;
+	}
+
+	if(totaldur > maxdur){
+		fprintf(stderr, "Sorry! Total duration exceeds capacity of file format.\nProcess aborted.\n");
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 1;
+	}
+    
+	/* if here, OK! We can make the file */
+    printf("processing files...\n");
+	outprops = inprops;
+	/* try to make a legal wave file! */
+	if((outprops.chans > 2 || outprops.samptype > PSF_SAMP_IEEE_FLOAT)
+		 && (outprops.format==PSF_STDWAVE))
+        outprops.chformat = /* PSF_WAVE_EX */ MC_STD;       //RWD 10:03:21
+    block_frames = (long)(blockdur * outprops.srate);
+	buflen = block_frames * outprops.chans;
+	inframe = malloc(buflen * sizeof(float));
+	if(inframe==NULL){
+		puts("No memory!\n");
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 1;
+	}
+	//setup PEAK data
+	fpeaks = (PSF_CHPEAK *) calloc(outprops.chans,sizeof(PSF_CHPEAK));
+	if(fpeaks==NULL){
+		puts("njoin: error: no memory for internal PEAK buffer\n");
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 1;
+	}
+
+	if(cuefilename){
+		cuefp = fopen(cuefilename, "w");
+		if(cuefp == NULL){
+			fprintf(stderr, "WARNING: unable to create cue file %s.\n",cuefilename);
+			cuefilename = NULL;
+		}
+	}
+
+    space_frame = calloc(sizeof(float),outprops.chans);
+    
+	ofd = psf_sndCreate(argv[ARG_OUTFILE],&outprops,0,0,PSF_CREATE_RDWR);
+	if(ofd < 0){
+		fprintf(stderr,"njoin: Cannot create outfile %s\n",argv[ARG_OUTFILE]);
+		cleanup(ifd,ofd,flist,num_infiles);
+		return 1;
+	}
+	fprintf(stderr, "generating outfile...\n");
+	written = 0;
+	if(cuefp) {
+		//fprintf(cuefp,"FILE %s WAVE\n",snd_getfilename(ofd));
+        fprintf(cuefp,"FILE %s WAVE\n",argv[ARG_OUTFILE]);
+		fprintf(cuefp,"\tTRACK 01 AUDIO\n");
+				fprintf(cuefp,"\t\tINDEX 01 00:00:00\n");
+	}
+	space_frames = (long) (space_secs * outprops.srate + 0.5);
+	/* add leading space ? */
+	if(have_s && space_frames > 0){
+		for(j=0; j < space_frames; j++) {
+			if(psf_sndWriteFloatFrames(ofd,space_frame,1) < 0){
+				fprintf(stderr,"njoin: error writing outfile\n");
+				cleanup(ifd,ofd,flist,num_infiles);
+				free(inframe);
+				free(fpeaks);
+				return 1;
+			}
+		}
+		written += space_frames;
+	}
+	for(i=0; i < num_infiles; i++){
+		long got, put;
+        PSF_PROPS fprops; // dummy - not needed 
+#ifdef unix
+        fname = CreatePathByExpandingTildePath(flist[i]);
+        /* must free pointer later */
+        /*RWD TODO: may fail with null return if file does not exist */
+#else
+        
+        fname = flist[i];
+#endif        
+		ifd = psf_sndOpen(fname,&fprops,0);
+		if(ifd < 0){
+			fprintf(stderr,"unable to open infile %s.\n",fname);
+			error++;
+			break;
+		} 
+        
+		do {			
+			got = psf_sndReadFloatFrames(ifd,inframe,block_frames);
+			if(got < 0){
+				fprintf(stderr,"njoin: error reading file %s\n",fname);
+				error++;
+				break;
+			}
+			
+			put = psf_sndWriteFloatFrames(ofd,inframe,got);
+			if(put != got){
+				fprintf(stderr,"njoin: error writing outfile\n");
+				error++;
+				break;
+			}
+			written += got;
+			fprintf(stderr,"%.2f\r",(double) written / outprops.srate);
+		} while (got > 0);
+		if(error)
+			break;
+		
+		/* add space */
+		if(i < num_infiles - 1){
+			/* update cue file */
+			if(cuefp){
+				double pos = (double)(psf_sndTell(ofd)) / outprops.srate;
+				int mins = (int)(pos / 60.0);
+				int secs = (int)(pos - mins);
+				int frames = (int) ((pos - (60.0 * mins + secs)) * 75.0);
+
+				fprintf(cuefp,"\tTRACK %.2d AUDIO\n",i+2);
+				fprintf(cuefp,"\t\tINDEX 01 %.2d:%.2d:%.2d\n",mins,secs,frames); 
+			}
+			
+			for(j=0; j < space_frames ; j++) {
+				if(psf_sndWriteFloatFrames(ofd,space_frame,1) < 0){
+					fprintf(stderr,"njoin: error writing outfile\n");
+					error++;
+					break;
+				}
+			}
+		}
+		if(error)
+			break;
+		written += space_frames;
+#ifdef unix
+        free(fname);
+#endif
+        /*RWD Jan 2010 MUST close the file or we may run out of portsf slots! */
+        psf_sndClose(ifd);
+        ifd = -1;
+	}
+
+	if(error){
+		fprintf(stderr,"Error: Outfile incomplete.\n");
+//		sndunlink(ofd);
+	}
+	else {
+		fprintf(stderr, "Done.\nWritten %ld frames to outfile\n",written);
+		if(psf_sndReadPeaks(ofd,fpeaks,NULL) > 0){
+            long i;
+            double peaktime;
+            printf("PEAK information:\n");	
+			for(i=0;i < inprops.chans;i++){
+                double val = fpeaks[i].val;
+				peaktime = (double) fpeaks[i].pos / (double) inprops.srate;
+                if(val==0.0)
+                    printf("CH %ld:\t%.4f           at %.4f secs\n", i+1, val,peaktime);
+                else
+                    printf("CH %ld:\t%.4f (%.2fdB) at %.4f secs\n", i+1, val, 20.0*log10(val),peaktime);
+			}
+        }
+	}
+	if(inframe)
+		free(inframe);
+	if(fpeaks)
+		free(fpeaks);
+	if(cuefp)
+		fclose(cuefp);
+	cleanup(ifd,ofd,flist,num_infiles);
+	psf_finish();
+	return 0;
+}

+ 333 - 0
dev/externals/mctools/nmix.c

@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+//nmix.c
+//Oct 2009 updated to use portsf
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <memory.h>
+#include <sys/timeb.h>
+#include "portsf.h"
+
+#ifdef unix
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+//usage: nmix [-oOFFSET]  [-f] infile1 infile2 outfile
+void usage() {
+    printf("\nCDP MCTOOLS: NMIX V2.0.1 (c) RWD,CDP 1999,2009\n"
+           "mix two multi-channel files\n"
+           "usage:\n"
+           "nmix [-d][-f][-oOFFSET] infile1 infile2 outfile\n"
+           "     -d      : apply TPDF dither (16bit format output only).\n"
+           "     -f      : set output sample type to floats.\n"
+           "                Default: outfile type is that of infile 1\n"
+           "     -oOFFSET:  start infile2 at OFFSET seconds.\n"
+           "Files must have the same channel count.\n"
+           "WAVE-EX files must have the same speaker layout\n");
+
+    exit(0);
+}
+
+#define BUFLEN (1024)
+
+int main(int argc, char *argv[])
+{
+    int i,ifd1 = -1,ifd2=-1,ofd=-1;
+    int force_floats = 0;
+    int do_dither = 0;
+    float ampfac = 0.0;
+    long len_in1, len_in2,outlen;
+    double offset = 0.0;
+    long offsetframes = 0,framecount = 0;
+    long chans;
+    long sr;
+    long in1_sofar,in2_sofar;
+    int halfsec;
+    MYLONG peaktime;
+    PSF_PROPS props_in1,props_in2;
+    float *frame_in1 = NULL, *frame_in2 = NULL, *outframe = NULL;
+    PSF_CHPEAK *peaks = NULL;
+
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("2.0.1\n");
+        return 0;
+    }
+
+    if(argc < 4)
+        usage();
+
+    while(argv[1][0]=='-'){
+        char *valstr;
+        double val;
+        switch(argv[1][1]){
+        case 'd':
+            do_dither = 1;
+            break;
+        case('f'):
+            force_floats = 1;
+            break;
+        case('o'):
+            if(argv[1][2]=='\0'){
+                fprintf(stderr,"-o flag needs a number\n");
+                exit(1);
+            }
+            valstr = &argv[1][2];
+            val=atof(valstr);
+            if(val < 0.0){
+
+                fprintf(stderr,"offset must be >= 0\n");
+                exit(1);
+            }
+            if(val > 0.0)
+                offset = val;
+            break;
+
+        default:
+            fprintf(stderr,"incorrect flag option %c\n",argv[1][1]);
+            exit(1);
+        }
+        argc--; argv++;
+    }
+
+    if(argc < 4)
+        usage();
+    if(force_floats && do_dither)
+        fprintf(stderr,"Warning: dither option ignored for floats format.\n");
+
+    psf_init();
+
+    //open soundfiles: no auto-rescaling
+    if((ifd1 = psf_sndOpen(argv[1],&props_in1,0)) < 0){
+        fprintf(stderr,"Cannot open soundfile %s\n",argv[1]);
+        goto cleanup;
+    }
+
+    if((ifd2 = psf_sndOpen(argv[2],&props_in2,0)) < 0){
+        fprintf(stderr,"Cannot open soundfile %s \n",argv[2]);
+        goto cleanup;
+    }
+
+    if(props_in1.chans != props_in2.chans){
+        fprintf(stderr,"files do not have the same number of channels\n");
+        goto cleanup;
+    }
+
+    if(props_in1.srate != props_in2.srate){
+        fprintf(stderr,"files do not have the same sample rate\n");
+        goto cleanup;
+
+    }
+
+    if(props_in1.chformat != props_in2.chformat){
+        fprintf(stderr,"files do not have the same channel format\n");
+        goto cleanup;
+    }
+
+
+    sr = props_in1.srate;
+    chans  = props_in1.chans;
+    ampfac = 0.5f;                                  //just mixing 2 files...
+    if(offset > 0.0){
+        offsetframes = (long) (offset * (double) sr);
+    }
+    /* now we can set up frame arrays */
+
+    frame_in1 = (float *) calloc(chans, sizeof(float));
+    frame_in2 = (float *) calloc(chans, sizeof(float));
+    outframe = (float *) calloc(chans, sizeof(float));
+
+    if(frame_in1==NULL || frame_in2==NULL || outframe==NULL ){
+        puts("\nno memory for frame buffers");
+        goto cleanup;
+    }
+
+    len_in1 = psf_sndSize(ifd1);
+    if(len_in1==0){
+        fprintf(stderr,"infile %s is empty!\n",argv[1]);
+        goto cleanup;
+    }
+    len_in2 = psf_sndSize(ifd2);
+    if(len_in2==0){
+        fprintf(stderr,"infile %s is empty!\n",argv[2]);
+        goto cleanup;
+    }
+    if(len_in1 < 0){
+        fprintf(stderr,"system problem: cannot read size of infile %s\n",argv[1]);
+        goto cleanup;
+    }
+
+    if(len_in2 < 0){
+        fprintf(stderr,"system problem: cannot read size of infile %s\n",argv[2]);
+        goto cleanup;
+    }
+
+
+
+    outlen = len_in2 + offsetframes;
+    if(len_in1 > outlen)
+        outlen = len_in1;
+
+#ifdef _DEBUG
+    printf("DEBUG: outfile size expected to be %d frames\n",outlen);
+#endif
+    //setup PEAK data
+    peaks = (PSF_CHPEAK *) calloc(props_in1.chans,sizeof(PSF_CHPEAK));
+    if(peaks==NULL){
+        puts("nmix: error: no memory for internal PEAK buffer\n");
+        goto cleanup;
+    }
+    if(force_floats)
+        props_in1.samptype = PSF_SAMP_IEEE_FLOAT;
+
+
+    if(!is_legalsize(outlen,&props_in1)){
+        fprintf(stderr,"error: outfile size exceeds capacity of format.\n");
+        return 1;
+    }
+
+
+    if((ofd = psf_sndCreate(argv[3],&props_in1,0,0,PSF_CREATE_RDWR)) < 0){
+        fprintf(stderr,"unable to create outfile %s\n",argv[3]);
+        goto cleanup;
+    }
+    if(do_dither)
+        psf_sndSetDither(ofd,PSF_DITHER_TPDF);
+    halfsec = sr / 2;
+    //OK now we can do it....
+    printf("\nmixing....\n");
+
+    in1_sofar = in2_sofar = 0;
+    if(offsetframes > 0){
+        for(i=0;i < offsetframes; i++){
+            int got,j;
+            got = psf_sndReadFloatFrames(ifd1,frame_in1,1);
+            if(got != 1){
+                fprintf(stderr,"error reading from infile 1\n");
+                goto cleanup;
+            }
+            for(j=0;j < chans; j++) {
+                frame_in1[j] *= ampfac;
+            }
+            if((psf_sndWriteFloatFrames(ofd,frame_in1,1)) !=1){
+                fprintf(stderr,"\nerror writing to outfile\n");
+                goto cleanup;
+            }
+            if(framecount % halfsec==0)
+                printf("%.2lf secs\r",(double)framecount / (double) sr);
+
+            framecount++;
+        }
+    }
+    in1_sofar = offsetframes;
+    //now we are mixing two files...
+    for(i= offsetframes; i < outlen; i++){
+        int got, j;
+        //clear frame blocks
+        memset(frame_in1,0, chans * sizeof(float));
+        memset(frame_in2,0, chans * sizeof(float));
+        //if we  have data, fill frame and scale
+        if(in1_sofar < len_in1){
+            got = psf_sndReadFloatFrames(ifd1,frame_in1,1);
+            if(got != 1){
+                fprintf(stderr,"\nerror reading from infile 1\n");
+                goto cleanup;
+            }
+            in1_sofar++;
+            for(j=0;j < chans; j++)
+                frame_in1[j] *= ampfac;
+        }
+
+
+        if(in2_sofar < len_in2){
+            got = psf_sndReadFloatFrames(ifd2,frame_in2,1);
+            if(got != 1){
+                fprintf(stderr,"\nerror reading from infile 1\n");
+                goto cleanup;
+            }
+
+            in2_sofar++;
+
+            for(j=0;j < chans; j++)
+                frame_in2[j] *= ampfac;
+
+        }
+
+        //mix and write
+        for(j=0;j < chans; j++) {
+            outframe[j] = frame_in1[j] + frame_in2[j];
+
+        }
+        if(psf_sndWriteFloatFrames(ofd,outframe,1) < 0){
+            fprintf(stderr,"\nerror writing to outfile\n");
+            goto cleanup;
+        }
+        if(framecount % halfsec==0) {
+            printf("%.2lf secs\r",(double)framecount / (double) sr);
+            fflush(stdout);
+        }
+        framecount++;
+    }
+    printf("%.4lf secs\nWritten %ld sample frames to %s\n",(double)framecount / (double)sr,framecount,argv[3]);
+    if(psf_sndReadPeaks( ofd,peaks,&peaktime)){
+        printf("PEAK values:\n");
+        for(i=0; i < chans; i++){
+            double val, dbval;
+            val = (double) peaks[i].val;
+
+            if(val > 0.0){
+                dbval = 20.0 * log10(val);
+                printf("CH %d: %.6f (%.2lfdB) at frame %u:\t%.4f secs\n",i,
+                       val,dbval,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props_in1.srate);
+            }
+            else{
+                printf("CH %d: %.6f (-infdB) at frame %u:\t%.4f secs\n",i,
+                       val,(unsigned int) peaks[i].pos,(double)peaks[i].pos / (double) props_in1.srate);
+            }
+        }
+    }
+
+
+
+ cleanup:
+    if(ifd1 >=0)
+        psf_sndClose(ifd1);
+    if(ifd2 >=0)
+        psf_sndClose(ifd2);
+    if(ofd >=0)
+        psf_sndClose(ofd);
+    if(peaks)
+        free(peaks);
+    if(frame_in1 != NULL)
+        free(frame_in1);
+
+    if(frame_in2 != NULL)
+        free(frame_in2);
+    if(outframe != NULL)
+        free(outframe);
+
+
+    psf_finish();
+    return 0;
+}

+ 320 - 0
dev/externals/mctools/rmsinfo.cpp

@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+// rmsinfo
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <portsf.h>
+#include <math.h>
+#include <signal.h>
+#ifdef WIN32
+# if _MSC_VER  && _MSC_VER <= 1200
+# include <new.h>
+# endif
+#endif
+
+
+#ifdef unix
+/* in portsf.lib */
+extern "C" {
+    int stricmp(const char *a, const char *b);
+}
+#endif
+
+enum {ARG_NAME, ARG_INFILE,ARG_NARGS};
+
+enum {CH_RMS_POWER,CH_AMPSUM,CH_BISUM,CH_NORM_RMS,CH_NORM_AMPSUM};
+
+typedef struct {
+    double rmspower;
+    double abssum;
+    double bisum;
+    double norm_rms;
+    double norm_abssum;
+} CH_RMSINFO;
+
+#define BUFLEN (1024)
+
+static int scanning(1);
+
+void runhandler(int sig)
+{
+	if(sig == SIGINT){
+		scanning = 0;
+    }
+}
+
+void usage(){
+    printf("\nCDP MCTOOLS: RMSINFO v1.0.1 (c) RWD, CDP 2009\n");
+    printf("Scans infile and reports rms and average loudness (power) of infile,\n"
+           " relative to digital peak (0dBFS)\n"
+           );
+    printf("Usage: rmsinfo [-n] infile [startpos [endpos]]\n"
+           "Standard output shows:\n"
+           "     RMS level\n"
+           "     Average level\n"
+           "     DC level (bipolar average)\n"
+           "-n : Include equivalent 0dBFS-normalised RMS and AVG levels.\n"
+           );
+    printf("Optional arguments:\n"
+           "startpos  :  start file scan from <startpos> seconds.\n"
+           "endpos    :  finish file scan at <endpos> seconds.\n"
+           );
+    printf("To stop a scan early, use CTRL-C.\n"
+           "Program will report levels up to that point.\n\n"
+           );
+}
+#ifdef WIN32
+#if _MSC_VER  && _MSC_VER <= 1200
+
+ int newhandler(size_t size);
+ class bad_alloc{};
+ int newhandler(size_t size)
+ {
+     throw bad_alloc();
+ }
+#endif
+#endif
+
+
+
+int main(int argc, char**argv)  {
+    long inframes,framesread=0,total_framesread;
+    double maxsamp = 0.0,startpos = 0.0,endpos;
+    long startframe = 0,endframe;
+    long halfsecframes = 0;
+    int i,j,ifd = -1;
+    int chans;
+    int do_norm = 0;
+	PSF_PROPS inprops;
+    CH_RMSINFO* rmsinfo = NULL;
+    double* rmsfac  = 0;
+    double* ampsum  = 0;
+    double* ampsumb = 0;
+    double* inbuf   = 0;
+    double* nrmsfac = 0;
+    double* nampsum = 0;
+    
+    
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.0.1\n");
+        return 0;
+    }
+#ifdef WIN32    
+# if _MSC_VER  && _MSC_VER <= 1200
+     _set_new_handler( newhandler );
+# endif
+#endif
+    if(psf_init()){
+		puts("unable to start portsf\n");
+		return 1;
+	}
+    if(argc < 2){		 
+		usage();
+		return(1);
+	}
+    while(argv[1][0]=='-'){
+		switch(argv[1][1]){
+        case 'n':
+            do_norm = 1;
+            break;
+        default:
+            fprintf(stderr, "Unrecognised flag option %s\n",argv[1]);
+            return 1;
+        }
+        argc--; argv++;
+    }
+    if(argc < 2){		 
+		usage();
+		return(1);
+	}
+    if((ifd = psf_sndOpen(argv[ARG_INFILE],&inprops, 0)) < 0){
+		fprintf(stderr,"\nUnable to open input soundfile %s",argv[ARG_INFILE]);
+		return(1);
+	}
+	inframes = psf_sndSize(ifd);  // m/c frames
+    endframe = inframes;
+    
+	if(inframes <= 0)
+        return 0;
+	if(argc >= 3) {
+        long lpos;
+        startpos = atof(argv[ARG_INFILE+1]);
+        if(startpos < 0.0){
+            fprintf(stderr,"Error: startpos must be positive\n");
+            return 1;
+        }
+        lpos = (long)( startpos * inprops.srate);
+        if(lpos > inframes){
+            fprintf(stderr,"Error: startpos value beyond end of file.\n");
+            return 1;
+        }
+        startframe = lpos;
+    }
+    if(argc >= 4) {
+        long lpos;
+        endpos = atof(argv[ARG_INFILE+2]);
+        
+        lpos = (long)(endpos * inprops.srate);
+        if(lpos > inframes){
+            fprintf(stderr,"Warning: endpos value too large - reset to end of file.\n");
+            return 1;
+        }
+        endframe = lpos;
+        if(!(endframe > startframe)){
+            fprintf(stderr,"Error: endpos must be beyond startpos.\n");
+            return 1;
+        }
+    }
+    if(startframe)
+        printf("Starting at frame %ld, ending at frame %ld\n",startframe, endframe);
+    
+    chans   = inprops.chans;
+    try {
+        inbuf   = new double[BUFLEN * chans];
+        rmsinfo = new CH_RMSINFO[chans];
+        rmsfac  = new double[chans];
+        ampsum  = new double[chans];
+        ampsumb = new double[chans];
+        nrmsfac = new double[chans];
+        nampsum = new double[chans];
+    }
+    catch(...){
+        fputs("no memory!\n",stderr);
+        return 1;
+    }
+    for(i=0; i < chans; i++){
+        rmsfac[i]  = 0.0;
+        ampsum[i]  = 0.0;
+        ampsumb[i] = 0.0;
+        nrmsfac[i] = 0.0;
+        nampsum[i] = 0.0;
+    }
+    halfsecframes = inprops.srate / 2; 
+    signal(SIGINT,runhandler);
+    long wanted = endframe - startframe;
+    printf("Scanning %ld frames (%.3lf secs):\n",wanted, (double)wanted / inprops.srate);
+    total_framesread = 0;
+    if(startframe) {
+        if(psf_sndSeek(ifd,startframe,PSF_SEEK_SET)){
+            fprintf(stderr,"File Seek error.\n");
+            return 1;
+        }
+    }
+        
+	while((framesread = psf_sndReadDoubleFrames(ifd,inbuf,BUFLEN)) > 0){
+        double fval;
+        for(i = 0;i < framesread;i++) {
+            for(j = 0; j < chans; j++){
+                double val = inbuf[i*chans + j];
+                fval = fabs(val);
+                maxsamp = fval >  maxsamp ? fval : maxsamp;
+                ampsum[j]   += fval;
+                rmsfac[j]   += val*val; 
+                ampsumb[j]  += val;
+            }
+            total_framesread++;
+            if(scanning==0)
+                break;
+            if(total_framesread == wanted) 
+                break;
+            if((total_framesread % halfsecframes) == 0){
+                printf("%.2lf\r",total_framesread / (double) inprops.srate);
+                fflush(stdout);
+            }
+        }
+        if(total_framesread == wanted) {
+            break;
+        }
+    }
+    if(framesread < 0){
+        fprintf(stderr,"Error reading file.\n");
+        return 1;
+    }
+    for(i=0;i < chans;i++){
+        rmsfac[i]  /= total_framesread;
+        rmsfac[i]   = sqrt(rmsfac[i]);
+        ampsum[i]  /= total_framesread;
+        ampsumb[i] /= total_framesread;
+    }
+
+    double normfac = 1.0 / maxsamp;
+    if(scanning==0)
+        printf("\nScan stopped.\n");
+    if(total_framesread < inframes){
+        printf("Scanned %ld frames (%.2lf secs).\n",total_framesread,total_framesread / (double)inprops.srate);
+    }
+    
+    printf("Maximum sample = %lf (%.2lfdB)\n",maxsamp,20.0 * log10(maxsamp));
+    printf("Maximum normalisation factor = %.4f\n",normfac);
+    
+    for(i=0;i < chans;i++){
+        rmsinfo[i].rmspower    = rmsfac[i];
+        rmsinfo[i].abssum      = ampsum[i];
+        rmsinfo[i].bisum       = ampsumb[i];
+        rmsinfo[i].norm_rms    = normfac * rmsfac[i];
+        rmsinfo[i].norm_abssum = normfac * ampsum[i];
+    }
+    if(do_norm){
+        printf("\t   RMS LEVEL\t    AVG  \t   NET DC\t   NORM RMS\t  NORM AVG\n");
+        printf("CH\t AMP\t DB\t AMP\t DB\t AMP\t DB\t AMP\t DB\t AMP\t DB     \n");
+    }
+    else{
+        printf("\t   RMS LEVEL\t    AVG  \t   NET DC\n");
+        printf("CH\t AMP\t DB\t AMP\t DB\t AMP\t DB\n"); 
+    }
+    for(i=0;i < chans;i++){
+        double d1,d2,d3,d4,d5;
+        
+        d1 = 20*log10(rmsfac[i]);
+        d2 = 20*log10(ampsum[i]);
+        d3 = 20*log10(fabs(ampsumb[i]));
+        d4 = 20*log10(normfac * rmsfac[i]);
+        d5 = 20*log10(normfac * ampsum[i]);
+        if(do_norm){
+            printf("%d\t%.5lf\t%.2lf\t%.5lf\t%.2lf\t%+.4lf\t%.2lf\t%.5lf\t%.2lf\t%.5lf\t%.2lf\n",i+1,
+               rmsinfo[i].rmspower,d1,
+               rmsinfo[i].abssum,d2,
+               rmsinfo[i].bisum,d3,
+               rmsinfo[i].norm_rms,d4,
+               rmsinfo[i].norm_abssum,d5
+            );
+        }
+        else {
+            printf("%d\t%.5lf\t%.2lf\t%.5lf\t%.2lf\t%+.4lf\t%.2lf\n",i+1,
+                   rmsinfo[i].rmspower,d1,
+                   rmsinfo[i].abssum,d2,
+                   rmsinfo[i].bisum,d3
+            );
+        }
+    }
+        
+    delete [] inbuf;
+    delete [] rmsfac;
+    delete [] ampsum;
+    delete [] ampsumb;
+    delete [] rmsinfo;
+    psf_sndClose(ifd);
+    psf_finish();
+
+	return 0;
+}

+ 378 - 0
dev/externals/mctools/sfprops.c

@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* sfprops.c: display primary soundfile properties 
+ * uses sfsysEx.lib: recognizes WAVE-EX formats
+ * part of the CDP MCTOOLS suite
+ */
+//RWD.6.99 use new funcs to report 24bit formats, etc
+//last update: 22.6.99 enumerate PEAK chans from 1, link with latest sfsysEx
+/* Nov28 2001: rebuild with sfsysEx recognizing stupid QuickTime AIFC floats with 16bit size! */
+/*Dec 2005 support .amb extension */
+/*April 2006: build with updated sfsys to read PEAK chunk after data chunk (Thanks to Sony!)*/
+/* OCt 2009 TODO: sort out 64bit platform issues (peaktime etc) */
+/* FEB 2010: decl filesize etc as unsigned to read huge file sizes! */
+/* Nov 2013 added MC_SURR_6_1 */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <time.h>
+#include <sfsys.h>
+#include <chanmask.h>
+
+#ifdef unix
+
+int stricmp(const char *a, const char *b)
+{
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+        
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+        
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+#endif
+
+typedef struct wave_ex_speaker {
+    int mask;
+    char name[28];
+} WAVE_EX_SPEAKER;
+
+
+static const WAVE_EX_SPEAKER speakers[NUM_SPEAKER_POSITIONS] = {
+    {SPEAKER_FRONT_LEFT,"Front Left"},
+    {SPEAKER_FRONT_RIGHT,"Front Right"},
+    {SPEAKER_FRONT_CENTER,"Front Centre"},
+    {SPEAKER_LOW_FREQUENCY,"Low Frequency"},
+    {SPEAKER_BACK_LEFT,"Back Left"},
+    {SPEAKER_BACK_RIGHT,"Back Right"},
+    {SPEAKER_FRONT_LEFT_OF_CENTER,"Front Centre-Left"},
+    {SPEAKER_FRONT_RIGHT_OF_CENTER,"Front Centre-Right"},
+    {SPEAKER_BACK_CENTER,"Back Centre"},
+    {SPEAKER_SIDE_LEFT,"Side Left"},
+    {SPEAKER_SIDE_RIGHT,"Side Right"},
+    {SPEAKER_TOP_CENTER,"Top Centre"},
+    {SPEAKER_TOP_FRONT_LEFT,"Front Top Left"},
+    {SPEAKER_TOP_FRONT_CENTER,"Front Top Centre"},
+    {SPEAKER_TOP_FRONT_RIGHT,"Front Top Right"},
+    {SPEAKER_TOP_BACK_LEFT,"Back Top Left"},
+    {SPEAKER_TOP_BACK_CENTER,"Back Top Centre"},
+    {SPEAKER_TOP_BACK_RIGHT,"Back Top Right"}
+};
+
+void usage(){
+    fprintf(stderr,"CDP MCTOOLS: SFPROPS v2.2.0 (c) RWD,CDP,1999,2009,2010,2013\n"
+                    "Display soundfile details, with WAVE-EX speaker positions\n"
+                    "\n\tusage: sfprops infile\n");
+}
+
+ 
+
+int main(int argc, char **argv)
+{
+
+    int i,ifd;
+    unsigned int nframes,filesize; /* FEB 2010 */
+    double srate;
+    SFPROPS props = {0};
+    const char *name = NULL;
+    CHPEAK *peaks = NULL;
+    int res,peaktime,speakermask = 0;
+    float fmaxamp;
+    int lmaxamp;
+    
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("2.2.0.\n");
+        return 0;
+    }
+    
+    if(argc< 2){
+        usage();
+        exit(1);
+    }
+    if(sflinit("sfprops")){
+        fprintf(stderr,"SFPROPS: unable to initialize CDP Sound Filing System\n");
+        exit(1);
+    }
+    if((ifd = sndopenEx(argv[1],0,CDP_OPEN_RDONLY)) < 0){
+        fprintf(stderr,"SFPROPS: unable to open infile %s: %s\n",argv[1], sferrstr());
+        exit(1);
+    }
+
+    if(!snd_headread(ifd,&props)){
+        fprintf(stderr,"SFPROPS: unable to read infile header\n");
+        exit(1);
+    }
+
+    srate = (double) props.srate;
+    filesize = sndsizeEx(ifd);
+    nframes = filesize/ props.chans;        /*assume soundfile for now */
+    name = snd_getfilename(ifd);
+    printf("Properties of %s:\n",name);
+    printf("File type:  ");
+    switch(props.type){
+    case(wt_wave):
+        printf("soundfile\nFormat         :  ");
+
+        switch(props.format){
+        case(WAVE):
+            printf("Standard WAVE format\n");
+            break;
+        case(WAVE_EX):
+            printf("MS WAVE-FORMAT-EXTENSIBLE\n");
+            printf("SPEAKER CONFIGURATION:\t");
+
+            switch(props.chformat){
+            //case(MC_STD):
+            //  printf("unassigned (generic format)");
+            //  break;
+            case(MC_MONO):
+                printf("Mono\n");
+                break;
+            case(MC_STEREO):
+                printf("Stereo\n");
+                break;
+            case(MC_QUAD):
+                printf("Rectangular Quad\n");
+                break;
+            case(MC_LCRS):
+                printf("Quad Surround\n");
+                break;
+            case(MC_BFMT):
+                printf("Ambisonic B-Format\n");
+                break;
+            case(MC_DOLBY_5_1):
+                printf("5.1 (Dolby) Surround\n");
+                break;
+            case(MC_SURR_6_1):
+                printf("6.1 Surround\n");
+                break;
+            case(MC_SURR_7_1):
+                printf("7.1 Surround\n");
+                break;
+            case(MC_CUBE):
+                printf("Cube Surround\n");
+                break;
+            default:
+                printf("Special speaker assignments\n");
+                break;
+            }
+            speakermask = sndgetchanmask(ifd);
+            if(speakermask < 0)
+                fprintf(stderr,"Unable to read speakermask from WAVE_EX header\n");
+            else {
+                //try to figure out the mask
+                int assigned = 0;
+                int shift = 0;
+                int this_channel = 1;
+                printf("Speaker Mask = %d (0x%x)\n",speakermask,(unsigned int) speakermask);
+                while(assigned < props.chans){
+                    if(speakers[shift].mask & speakermask){
+                        printf("Channel %d: %s\n",this_channel++,speakers[shift].name);
+                        assigned++;
+                    }
+                    if(++shift == NUM_SPEAKER_POSITIONS)
+                        break;
+                }
+                if(assigned < props.chans)
+                    printf("Remaining channels not assigned speaker locations.\n");
+
+            }
+            break;
+                
+        case(AIFF):
+            printf("AIFF format\n");
+            break;
+        case(AIFC):
+            printf("AIFC format\n");
+            break;
+        default:
+            printf("unknown format\n");
+            break;
+        }
+        printf("Sample Rate    :  %d\n",props.srate);
+        printf("Channels       :  %d\n",props.chans);
+        printf("Sample Frames  :  %d\n",nframes);
+        printf("sample type:   :  ");
+        switch(props.samptype){
+        case(SHORT8):
+            printf("8-bit\n");
+            break;
+        case(SHORT16):
+            printf("16-bit\n");
+            break;
+        case(FLOAT32):
+            printf("32bit floats\n");
+            break;
+        case(INT_32):
+            printf("32bit (integer)\n");
+            break;
+        case(INT2424):
+            printf("24bit (packed)\n");
+            break;
+        case(INT2432):
+            printf("24bit in 32bit words\n");
+            break;
+        case(INT2024):
+            printf("20bit in 24bit words\n");
+            break;
+        case(INT_MASKED):
+            printf("non-standard WAVE_EX format\n");
+            //fet more info....
+            break;
+        default:
+            printf("sorry: don't recognize word format!\n");
+            break;
+
+        }
+        printf("duration       :  %.4lf secs\n",(double)nframes / srate);
+        //this is the problem: we never decided what type maxamp is, officially!
+        if(props.samptype==FLOAT32){
+            if(sndgetprop(ifd,"maxamp",(char *)&fmaxamp,sizeof(float)) == sizeof(float)){
+                printf("CDP maxamp     :  %.4lf\n",fmaxamp);    
+            }
+        }
+        else{
+            if(sndgetprop(ifd,"maxamp",(char *)&lmaxamp,sizeof(int)) == sizeof(int)){
+                printf("CDP maxamp     :  %d\n",lmaxamp);   
+            }
+        }
+        peaks = (CHPEAK *) calloc(props.chans,sizeof(CHPEAK));
+        if(peaks==NULL){
+            puts("sfprops: no memory for fpeak data buffer\n");
+            exit(1);
+        }
+        else{
+            time_t thistime;
+            res = sndreadpeaks(ifd,props.chans,peaks, (int *) &peaktime);
+            thistime = (time_t) peaktime;
+            if(res ==0)
+                printf("no peak data in this infile\n");
+            else if(res < 0){
+                fprintf(stderr,"sfprops: WARNING: error reading infile peak data\n");
+                
+            }
+            else {      
+                printf("PEAK data:\n\tcreation time: %s\n", ctime(&thistime));
+                //for(i=0;i < props.chans; i++)
+                //  printf("CH %d: %.4lf at frame %d:\t%.4lf secs\n",
+                //  i+1,peaks[i].value,peaks[i].position,(double) (peaks[i].position) / srate);
+                for(i=0; i < props.chans; i++){
+                    double val, dbval;
+                    val = (double) peaks[i].value;
+                    
+                    if(val > 0.0){
+                        dbval = 20.0 * log10(val);
+                        printf("CH %d: %.6f (%.2lfdB) at frame %9d:\t%.4f secs\n",i,
+                               val,dbval,peaks[i].position,(double)(peaks[i].position / (double) srate));
+                    }
+                    else{
+                        printf("CH %d: %.6f (-infdB)  \t\t:\t%.4f secs\n",
+                               i,
+                               val, (double)(peaks[i].position / (double) srate)); 
+                    }
+                }
+            }
+        }
+        
+        break;
+    case(wt_analysis):
+        printf("CDP pvoc analysis file.\n");
+        printf("Channel Format:       Amplitude,Frequency\n");
+        printf("Orig rate:            %d\n",props.origrate);
+        printf("Analysis Window Size: %d\n",props.winlen);
+        printf("Analysis channels:    %d\n",props.chans/2);
+        printf("Window Overlap:       %d\n",props.decfac);
+        printf("Analysis rate:        %.4f\n",props.arate);
+        printf("Frame count:          %d\n",filesize / (props.chans));
+        printf("Data size (floats):   %d\n",filesize);
+        printf("Duration (secs):      %.3lf\n",
+            ((filesize / props.chans) * props.decfac) / (double) props.origrate);
+        break;
+    case(wt_formant):
+        printf("CDP formant data\n");
+        printf("Specenvcnt:           %d\n",props.specenvcnt);
+        printf("Analysis Window Size: %d\n",props.winlen);
+        printf("Channels:             %d\n",props.chans);
+        printf("Frame count:          %d\n",filesize);
+        printf("Orig rate:            %d\n",props.origrate);        
+        printf("Window Overlap:       %d\n",props.decfac);
+        printf("Analysis rate:        %.4f\n",props.arate);
+        printf("Orig Chans:          %d\n",props.origchans);        
+        printf("Duration (secs):      %.3lf\n",(filesize /props.specenvcnt)/ props.arate );
+        break;
+    case(wt_transposition):
+        printf("CDP transposition data\n");
+        printf("Orig sample rate:     %d\n",props.origrate);
+        printf("Analysis Window Size: %d\n",props.winlen);
+        printf("Channels:             %d\n",props.chans);
+        printf("Window Overlap:       %d\n",props.decfac);
+        printf("Analysis rate:        %.4f\n",props.arate);
+        printf("Orig Chans:          %d\n",props.origchans);
+        printf("Data size (floats):   %d\n",filesize);
+        printf("Duration (secs):      %.3lf\n",
+            ((filesize / props.chans) * props.decfac) / (double) props.origrate);
+        break;
+    case(wt_pitch):
+        printf("CDP pitch data\n");
+        printf("Orig sample rate:     %d\n",props.origrate);
+        printf("Analysis Window Size: %d\n",props.winlen);
+        printf("Channels:             %d\n",props.chans);
+        printf("Window Overlap:       %d\n",props.decfac);
+        printf("Analysis rate:        %.4f\n",props.arate);
+        printf("Orig Chans:          %d\n",props.origchans);
+        printf("Data size (floats):   %d\n",filesize);
+        printf("Duration (secs):      %.3lf\n",
+            ((filesize / props.chans) * props.decfac) / (double) props.origrate);
+        break;
+    case(wt_binenv):
+        printf("CDP binary envelope data\n");
+        printf("envelope window size: %f msecs\n",props.window_size);
+        printf("sample rate:          %d\n",props.srate);
+        printf("Channels:             %d\n",props.chans);
+        printf("Points:               %d\n",filesize);
+        printf("Duration (secs):      %.3f\n",filesize * (props.window_size * 0.001));
+        break;
+    default:
+        printf("internal error! unlisted soundfile type\n");
+        break;
+    }
+
+    sndcloseEx(ifd);
+//  sffinish();
+    return 0;
+}
+
+
+

+ 5 - 0
dev/externals/paprogs/CMakeLists.txt

@@ -0,0 +1,5 @@
+
+add_subdirectory(listaudevs)
+add_subdirectory(paplay)
+add_subdirectory(pvplay)
+add_subdirectory(recsf)

BIN
dev/externals/paprogs/dx9mgw.zip


+ 26 - 0
dev/externals/paprogs/listaudevs/CMakeLists.txt

@@ -0,0 +1,26 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -Dunix -fomit-frame-pointer -funroll-loops")
+  include_directories ( /Developer/Headers/FlatCarbon )
+  find_library(COREAUDIOLIB CoreAudio)
+  find_library(AUDIOTOOLBOX AudioToolbox)
+  find_library(AULIB AudioUnit)
+  find_library(CARBONLIB Carbon)
+  set(EXTRA_LIBRARIES1 ${COREAUDIOLIB} ${AUDIOTOOLBOX} ${AULIB} ${CARBONLIB} ${EXTRA_LIBRARIES})
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -fomit-frame-pointer  -funroll-loops")
+    set(EXTRA_LIBRARIES1 winmm dsound winspool ${EXTRA_LIBRARIES})
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 jack asound portsf pthread ${EXTRA_LIBRARIES})
+  endif()
+endif()
+
+link_directories(../../include ../portaudio/lib/.libs)
+
+include_directories(../../../include ../include ../portaudio/include ../portaudio/src/common )
+
+add_executable(listaudevs devs.c)
+target_link_libraries(listaudevs portaudio.a  ${EXTRA_LIBRARIES1})
+
+my_install(listaudevs)

+ 69 - 0
dev/externals/paprogs/listaudevs/devs.c

@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+
+/* devs.c : display list of installed audio devices */  
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+# include <windows.h>
+#endif
+#include <portaudio.h>
+
+int show_devices(void);
+
+int main(void)
+{
+    return show_devices();
+}
+
+int show_devices(void)
+{
+        PaDeviceIndex numDevices,p;
+        const    PaDeviceInfo *pdi;
+        PaError  err;
+        int nOutputDevices = 0;
+
+        Pa_Initialize();
+        numDevices =  Pa_GetDeviceCount();
+        if( numDevices < 0 )
+        {
+            printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+            err = numDevices;
+            return err;
+        }
+        //printf("Number of devices = %d\n", numDevices );
+        printf("Device\tInput\tOutput\tName\n");
+        
+        for( p=0; p<numDevices; p++ )
+        {
+            pdi = Pa_GetDeviceInfo( p );    
+            nOutputDevices++;
+            if( p == Pa_GetDefaultOutputDevice() ) 
+                printf("*");
+            else
+                printf(" ");
+            printf("%d\t%d\t%d\t%s\n",p,
+                pdi->maxInputChannels,
+                pdi->maxOutputChannels,
+                pdi->name);         
+        }
+        Pa_Terminate();
+        return 0;
+}

+ 32 - 0
dev/externals/paprogs/listaudevs/orig_Makefile

@@ -0,0 +1,32 @@
+# OSX makefile for listaudevs © Richard Dobson Feb 1 2014
+
+CC = gcc
+OPT = -O3
+#OPT = -g
+# change this path as needed
+PADIR = ../mctools/portaudio
+PAINC = $(PADIR)/include
+PACOM = $(PADIR)/src/common
+
+PALIB = $(PADIR)/lib/libportaudio.a
+CFLAGS = $(OPT)  -g -mmacosx-version-min=10.5 -arch i386 -Dunix -DMAC  -DPA_USE_COREAUDIO -I$(PAINC)
+IOLINK =  -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
+PROG=listaudevs  
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+	 
+$(PROG):	devs.o 
+	$(CC) $(CFLAGS) devs.o -o $(PROG) $(IOLINK)  $(PALIB)
+
+
+clean: 
+	rm  devs.o
+veryclean:	clean
+	rm $(PROG)
+
+
+
+
+

BIN
dev/externals/paprogs/pa_stable_v19_20140130.tgz


+ 37 - 0
dev/externals/paprogs/palinuxbuild.txt

@@ -0,0 +1,37 @@
+palinuxbuild.txt:
+
+Portaudio is required by the audio programs (paplay, pvplay, recsf, listaudevs).
+If the required libportaudio.a is not detected, the top level build script 
+(makeprograms.sh)  will skip this stage.
+
+The audio program makefiles assume availability and use of both alsa and jack.
+
+The supplied source archive is the latest v19 stable release 2014.
+
+To build, unpack pa--tgz in situ; this will create a portaudio directory. 
+Run configure with these options:
+
+./configure --with-alsa --with-jack
+
+Then run make as usual to build, 
+
+Do not perform run 'make install' unless you are happy for it to replace your current system portaudio libs.
+
+The CDP makefiles look for the local lib/.libs folder for libportaudio.a
+
+To build the audio programs, either:
+
+run the Makefile in each program directory:
+
+make install -f Makefile.linux
+
+or run the top level makeprograms.sh script.
+
+NB "install" above merely copies the binaries into the local Release directory under dev/.
+
+
+
+
+
+
+

+ 44 - 0
dev/externals/paprogs/pamacbuild.txt

@@ -0,0 +1,44 @@
+pamacbuild.txt:
+
+Use this combination command to configure portaudio to build as a (single architecture) 
+static library on OS X, required by the audio programs (paplay, pvplay, recsf, listaudevs).
+
+The CDP programs build only for the 32bit architecture (-arch i386), 
+as they have not yet been fully tested for correctness with a 64bit architecture.
+
+Portaudio tends to assume all architectures are available, but this is not always the case 
+from Xcode 4.x onwards (e.g. no ppc support).
+
+You may need to edit the Makefile (or configure.in) by hand to add or exclude architectures. 
+
+You will likely need to type the commands below directly, in Terminal. Cutting and pasting from a text
+ editor (possibly using Unicode) may result in a config error.
+ 
+On a 32bit OS type:
+
+CFLAGS=$CFLAGS -mmacosx-version-min=10.4  ./configure --enable-shared=no --enable-mac-universal=no 
+
+If building on a 64bit machine, omit --enable-mac-universal=no and  make sure to add -arch i386 
+to the CFLAGS if not present, removing any which are not supported or needed, e.g:
+
+CFLAGS="$CFLAGS -arch i386 -arch x86_64"
+
+(the quotation marks are required if the CFLAGS string contains multiple options separated by spaces). 
+
+Then run make as usual to build. 
+
+The CDP makefiles look for the local lib/.libs folder for libportaudio.a
+There is no need to run sudo make install
+
+This is to avoid a couple of warnings when compiling pa_mac-core.c.
+The macosx version setting is to prevent warnings  about deprecated APIs.
+The configure script specifies -Werror ("treat warnings as errors"), 
+which means that any such warning stops the build.
+
+The command --enable-shared=no also seems to be needed, otherwise (for some reason) 
+the linker fails to find the Pa_Util functions.
+
+Newer builds of portaudio can of course be used; they may obviate some or all of the above issues; but may introduce new ones too!
+
+
+

+ 74 - 0
dev/externals/paprogs/pamingwbuild.txt

@@ -0,0 +1,74 @@
+How to buld Portaudio static library for Windows, using MinGW. 
+
+This is required by the audio programs (paplay, pvplay, recsf, listaudevs)
+
+This assumes MinGW is already installed. This has been developed using the standard MinGW distribution; it has not been tested with the 
+Either or both ASIO and Directx support is needed for building the programs.
+
+An unpacked portaudio folder is provided, based on the latest release
+(see http://www.portaudio.com). It has a modified configure.in file to plug gaps in the default 
+portaudio configure setup.
+
+
+1. If ASIO support is desired, obtain the SDK from the Steinberg site (we are not allowed to distribute it ourselves).
+See: http://www.steinberg.net/en/company/developer.html
+
+It is convenient to put the unpacked directory ASIOSDK2 inside the portaudio folder, for ease of reference,
+and especially if it is shared with other projects, but other locations are possible, including /usr/local. 
+See the web page referenced at the bottom of this document.
+
+2. DirectSound support
+
+Unzip dx9mgw.zip into (e.g.) your MinGW home directory. This is the location assumed in the following instructions.
+
+Source of this sdk: http://alleg.sourceforge.net/files/dx9mgw.zip
+(see https://www.allegro.cc/forums/thread/610763)
+
+These examples assume the user name is "Richard". of course, replace this as needed with your MinGW user name
+ 
+Portaudio configure has to be told which APIs to activate, and the paths to the SDKs for those APIs.
+
+cd to the portaudio directory (inside dev/externals/paprogs)
+
+ASIO only configure (NB long lines here):
+
+$ ./configure --with-host_os=mingw --with-winapi=asio --without-jack --enable-shared=no --enable-debug-output=no --with-asiodir=./ASIOSDK2
+
+
+dsound only configure:
+
+$ ./configure --with-host_os=mingw --with-winapi=directx --without-jack --enable-shared=no --enable-debug-output=no --with-dxdir=/home/Richard/dx9mgw 
+
+
+Both (recommended):
+
+$ ./configure --with-host_os=mingw --with-winapi=directx,asio --without-jack --enable-shared=no --enable-debug-output=no --with-dxdir=/home/Richard/dx9mgw --with-asiodir=./ASIOSDK2
+
+Thjis generates the Makefile which we will use to build portaudio
+
+3. Modify the Makefile
+
+Add the define -DPAWIN_USE_WDMKS_DEVICE_INFO to CFLAGS and CXXFLAGS
+
+Add the object file src/os/win/pa_win_wdmks_utils.o  to the OTHER_OJBJS list
+
+
+The standard configure does not set up the exact combination we need, for multichannel device support.
+
+
+
+And finally:
+
+$ make
+
+Note: only the static library libportaudio.a is built.
+The makefiles for the audio programs look for this in the local hidden directory (lib/.libs).
+There is no need to run [sudo] make install, which would overwrite any existing copy.
+
+
+See also here, for an extended discussion of how to build portaudio with MinGW:
+
+https://www.assembla.com/spaces/portaudio/wiki/Notes_about_building_PortAudio_with_MinGW
+
+Richard Dobson Feb 2014
+

+ 30 - 0
dev/externals/paprogs/paplay/CMakeLists.txt

@@ -0,0 +1,30 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -Dunix -fomit-frame-pointer -funroll-loops")
+  include_directories ( /Developer/Headers/FlatCarbon )
+  find_library(COREAUDIOLIB CoreAudio)
+  find_library(AUDIOTOOLBOX AudioToolbox)
+  find_library(AULIB AudioUnit)
+  find_library(CARBONLIB Carbon)
+  find_library(AAIOLIB names libaaio.a paths /usr/local/lib)
+  set(EXTRA_LIBRARIES1 portsf pthread ${AAIOLIB} ${COREAUDIOLIB} ${AUDIOTOOLBOX} ${AULIB} ${CARBONLIB} ${EXTRA_LIBRARIES})
+  
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -DUSE_ASIO -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 winmm dsound winspool ${EXTRA_LIBRARIES})
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 jack asound portsf pthread ${EXTRA_LIBRARIES})
+  endif()
+endif()
+
+
+
+link_directories (../../lib ../portaudio/lib/.libs  /usr/local/lib)
+
+include_directories(../../include ../include ../portaudio/include ../portaudio/src/common /usr/local/include)
+
+add_executable(paplay paplay.c fmhfuncs.c)
+target_link_libraries(paplay portaudio.a  ${EXTRA_LIBRARIES1})
+
+my_install(paplay)

+ 106 - 0
dev/externals/paprogs/paplay/fmdcode.h

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmdcode.h */
+
+/*
+ Channel order is WXYZ,RSTUV,KLMNOPQ
+ 
+ The number of channels defines the order of the soundfield:
+ 2 channel = UHJ 
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly
+                                                            called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+ 11 channel = ffh = 3rd order horizontal + 2nd order height
+ 16 channel = fff = 3rd order 3-D
+ 
+ 
+ Horizontal   Height  Soundfield   Number of    Channels
+ order 	      order 	  type      channels 	
+ 1 	         0 	       horizontal 	  3 	    WXY
+ 1 	         1 	      full-sphere 	  4 	    WXYZ
+ 2 	         0 	       horizontal 	  5 	    WXY....UV
+ 2 	         1 	       mixed-order    6 	    WXYZ...UV
+ 2 	         2 	      full-sphere     9 	    WXYZRSTUV
+ 3           0 	       horizontal 	  7 	    WXY....UV.....PQ
+ 3 	         1         mixed-order 	  8 	    WXYZ...UV.....PQ
+ 3 	         2 	       mixed-order 	 11 	    WXYZRSTUV.....PQ
+ 3 	         3 	      full-sphere 	 16 	    WXYZRSTUVKLMNOPQ
+ */
+
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;
+    float R;
+    float S;
+    float T;
+	float U;
+    float V;
+} ABFSAMPLE;
+
+typedef void (*fmhcopyfunc)(ABFSAMPLE*,const float*);
+
+typedef void (*fmhdecodefunc)(const ABFSAMPLE*, float*,unsigned int);
+//void bfdcode4(float *inbuf,long numframes);
+void fmhcopy_3(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_4(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf);
+
+
+// i1 = inphase 1st order, etc
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_surr(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);

+ 712 - 0
dev/externals/paprogs/paplay/fmhfuncs.c

@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmhfuncs.c */
+#include "fmdcode.h"
+
+/* TODO: expand to handle numframes frames? */
+
+void fmhcopy_3(ABFSAMPLE* abf,const float *buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf;
+}
+
+void fmhcopy_4(ABFSAMPLE* abf,const float *buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf;
+}
+
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// following discard 3rd order chans
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// these identical for 2nd order horiz max, but may be expanded later!
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+/********** DECODE FUNCS *************/
+/* TODO: complete support for numframes > 1 */
+
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    unsigned int i;	
+	float *p_out = outbuf;
+	double aw;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		*p_out++ = (float) aw;  		  
+	}
+}
+
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ay;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		ay = (double) inbuf->Y * 0.5;		
+		
+		*p_out++ = (float) (aw +  ay);  
+		*p_out++ = (float) (aw  - ay);  
+	}
+}
+
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out++ = (float) (aw - ax - ay);  //RR
+		*p_out++ = (float) (aw + ax - ay);  //FR
+	}
+}
+void fm_i2_square(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out++ = (float) (aw - ax - ay + av);  //RR
+		*p_out++ = (float) (aw + ax - ay - av);  //FR
+	}
+}
+/* ditto, RLRL layout for WAVEX */
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+        *p_out++ = (float) (aw + ax - ay);  //FR
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out++ = (float) (aw - ax - ay);  //RR
+		
+	}
+}
+void fm_i2_quad(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+        *p_out++ = (float) (aw + ax - ay - av);  //FR
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out++ = (float) (aw - ax - ay + av);  //RR
+		
+	}
+}
+
+
+//front pair angle 72deg
+void fm_i1_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax*0.1618) + (ay*0.1176));  //FL
+		*p_out++ = (float) (aw - (ax*0.0618) + (ay*0.1902));  
+		*p_out++ = (float) (aw - (ax*0.2));  
+		*p_out++ = (float) (aw - (ax*0.0618) - (ay*0.1902));  
+        *p_out++ = (float) (aw + (ax*0.1618) - (ay*0.1176)); //FR
+	}
+}
+
+void fm_i2_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;	
+        au = (double) inbuf->U;
+        av = (double) inbuf->V;
+		
+		*p_out++ = (float) (aw + (ax*0.2227) + (ay*0.1618) + (au*0.0238) + (av * 0.0733));  
+		*p_out++ = (float) (aw - (ax*0.0851) + (ay*0.2619) - (au*0.0624) - (av * 0.0453));  
+		*p_out++ = (float) (aw - (ax*0.2753)               + (au * 0.0771)              );  
+		*p_out++ = (float) (aw - (ax*0.0851) - (ay*0.2619) - (au*0.0624) + (av * 0.0453));  
+        *p_out++ = (float) (aw + (ax*0.2227) - (ay*0.1618) + (au*0.0238) - (av * 0.0733));
+	}
+}
+
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		/* TODO: fix this order! */
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C    ///???
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R    ///????
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+/* 5.1 versions - silent LFE */
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R
+        *p_out++ = 0.0f;                                                    //LFE
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+        *p_out++ = 0.0f;                                                                              //LFE
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+// 1st order 5.0
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+//1st order 5.1
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		*p_out++ = 0.0f;                                                     //LFE
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+// 2nd order 5.0
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+// 2nd order 5.1
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = 0.0f;                                                                                  //LFE
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+
+
+void fm_i1_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1443;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.0833));  //FL
+		*p_out++ = (float) (aw      + (ay * 0.1667));  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.0833));  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.0833));  //RR
+        *p_out++ = (float) (aw      - (ay * 0.1667));  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.0833));  //FR
+	}
+}
+void fm_i2_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1987;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U;
+        av = (double) inbuf->V * 0.0556;
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.1147) + (au * 0.0321) + av);  //FL
+		*p_out++ = (float) (aw      + (ay * 0.2294) - (au * 0.0643)     );  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.1147) + (au * 0.0321) - av);  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.1147) + (au * 0.0321) + av);  //RR
+        *p_out++ = (float) (aw      - (ay * 0.2294) - (au * 0.0643)     );  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.1147) + (au * 0.0321) - av);  //FR
+	}
+}
+
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.1155) + (ay * 0.0478));  
+		*p_out++ = (float) (aw + (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.1155) + (ay * 0.0478));  
+        *p_out++ = (float) (aw - (ax * 0.231)  - (ay * 0.0957));  
+        *p_out++ = (float) (aw - (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++ = (float) (aw + (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++   = (float) (aw + (ax * 0.1155) - (ay * 0.0478));  
+	}
+}
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.03417;
+		av = (double) inbuf->V * 0.03417;
+        
+		*p_out++ = (float) (aw + (ax * 0.15906) + (ay * 0.06588) + au + av);  
+		*p_out++ = (float) (aw + (ax * 0.06588) + (ay * 0.15906) - au + av);  
+		*p_out++ = (float) (aw - (ax * 0.06588) + (ay * 0.15906) - au - av);  
+		*p_out++ = (float) (aw - (ax * 0.15906) + (ay * 0.06588) + au - av);  
+        *p_out++ = (float) (aw - (ax * 0.15906) - (ay * 0.06588) + au + av);  
+        *p_out++ = (float) (aw - (ax * 0.06588) - (ay * 0.15906) - au + av);  
+        *p_out++ = (float) (aw + (ax * 0.06588) - (ay * 0.15906) - au - av);  
+        *p_out++ = (float) (aw + (ax * 0.15906) - (ay * 0.06588) + au - av);  
+	}
+}
+
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.125)                 );  
+		*p_out++ = (float) (aw + (ax * 0.0884) + (ay * 0.0884));  
+		*p_out++ = (float) (aw                 + (ay * 0.125) );  
+		*p_out++ = (float) (aw - (ax * 0.0884) + (ay * 0.0884));  
+        *p_out++ = (float) (aw - (ax * 0.125)                 );  
+        *p_out++ = (float) (aw - (ax * 0.0884) - (ay * 0.0884));  
+        *p_out++ = (float) (aw                 - (ay * 0.125) );  
+        *p_out++ = (float) (aw + (ax * 0.0884) - (ay * 0.0884));  
+	}
+}
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.0482;
+		av = (double) inbuf->V * 0.0482;
+        
+		*p_out++ = (float) (aw + (ax * 0.1721)                 + au     );  
+		*p_out++ = (float) (aw + (ax * 0.1217) + (ay * 0.1217)      + av);  
+		*p_out++ = (float) (aw                 + (ay * 0.1721) - au     );  
+		*p_out++ = (float) (aw - (ax * 0.1217) + (ay * 0.1217)      - av);  
+        *p_out++ = (float) (aw - (ax * 0.1721)                 + au     );  
+        *p_out++ = (float) (aw - (ax * 0.1217) - (ay * 0.1217)      + av);  
+        *p_out++ = (float) (aw                 - (ay * 0.1721) - au     );  
+        *p_out++ = (float) (aw + (ax * 0.1217) - (ay * 0.1217)      - av);  
+	}
+}
+
+/* csound order; low/high anti-clockwise. 
+FMH page order, 4 low folowed by 4 high , clockwise! */
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        
+		*p_out++ = (float) (aw - ax + ay - az);  // RL low
+		*p_out++ = (float) (aw - ax + ay + az);  //    hi
+        
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        *p_out++ = (float) (aw - ax - ay + az);  //   hi
+        
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az);  //    hi
+	}
+}
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av); //FL low 
+		*p_out++ = (float) (aw + ax + ay + az + as + at + av); //   hi 
+		
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av); //RL low
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  
+        
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av); // RR low
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  
+        
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az + as - at - av);   
+	}
+}
+/* ditto, wavex order */
+/* Front L, front R, Back L, Back R; top Front L, Top Fr R, Top Back L, Top back R */
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        *p_out++ = (float) (aw + ax - ay + az);  // FR hi
+		*p_out++ = (float) (aw - ax + ay + az);  // RL hi
+        *p_out++ = (float) (aw - ax - ay + az);  // RR hi
+	}
+}
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av);  // RR low
+		
+        *p_out++ = (float) (aw + ax + ay + az + as + at + av);  // FL  hi 
+		*p_out++ = (float) (aw + ax - ay + az + as - at - av);  // FR  hi
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  // RL  hi 
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  // RR  hi 
+	}
+}
+
+
+#ifdef NOTDEF
+void bfdcode4(float *inbuf,long numframes)
+{
+	int i;	
+	float *p_buf = inbuf;	
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double)(p_buf[0]) ;
+		ax = (double)(p_buf[1]) * 0.707;
+		ay = (double)(p_buf[2]) * 0.707;		
+		//decode frame
+		*p_buf++ = (float)(0.3333 * (aw + ax + ay)); //FL
+		*p_buf++ = (float)(0.3333 * (aw + ax - ay)); //FR
+		*p_buf++ = (float)(0.3333 * (aw - ax + ay)); //RL
+		*p_buf++ = (float)(0.3333 * (aw - ax - ay));  //RR
+	}
+}
+
+
+/* handle 3ch in to 4ch out! */
+
+void bfdcode324(float *inbuf,float*outbuf,long numframes)
+{
+	int i;	
+	float *p_buf = inbuf;
+	float * p_outbuf = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		int j;
+		aw = (double)(*p_buf++) ;
+		ax = (double)(*p_buf++) * 0.707;
+		ay = (double)(*p_buf++) * 0.707;
+        
+		//decode frame
+		*p_outbuf++ = (float)(0.3333 * (aw + ax + ay));
+		*p_outbuf++ = (float)(0.3333 * (aw + ax - ay));
+		*p_outbuf++ = (float)(0.3333 * (aw - ax + ay));
+		*p_outbuf++ = (float)(0.3333 * (aw - ax - ay));
+	}
+}
+#endif

+ 1593 - 0
dev/externals/paprogs/paplay/paplay.c

@@ -0,0 +1,1593 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+
+/* paplay.c
+ * Play a soundfile using the Portable Audio api for Max OS X using Core Audio.
+ *
+ • new version using threads and ring buffer, with thanks to Robert Bielik
+*/
+
+/* Dec 2005 support 3ch B-Format, b-format files via portsf */
+/* OCT 2009 much revised, extended b-format decoding options */
+/*     set ch mask to zero on Windows mme/ds.
+       TODO: decide whether/how to enable user to pass chmask of file to driver!
+*/
+/* Apr 2011 build with new portaudio stable v 19 */
+/* July 2012 added "to" option, memory playback mode */
+/* August 2012  restored -x flag */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <assert.h>
+#include <time.h>
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <conio.h>
+#include <windows.h>
+#include <process.h>
+// NB: need to have PAWIN_USE_WDMKS_DEVICE_INFO defined
+#include "pa_win_ds.h"
+#endif
+
+#ifdef unix
+
+#include <sys/types.h>
+#include <sys/timeb.h>
+#endif
+
+#include <signal.h>
+
+#ifdef unix
+#include <aaio.h>
+#endif
+
+#ifdef MAC
+#include <libkern/OSAtomic.h>
+#endif
+
+#ifdef unix
+#include <sys/time.h>
+#include <pthread.h>
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+#include "portaudio.h"
+#include "pa_ringbuffer.h"
+#include "pa_util.h"
+#include "portsf.h"
+#include "fmdcode.h"
+
+
+
+#ifndef min
+#define min(x,y)  ((x) < (y) ? (x) : (y))
+#endif
+
+// TODO: scale by sample rate
+
+
+#define FRAMES_PER_BUFFER (4096)
+#define RINGBUF_NFRAMES    (32768)
+#define NUM_WRITES_PER_BUFFER   (4)
+
+
+enum {FM_MONO,FM_STEREO,FM_SQUARE,FM_QUAD,FM_PENT,DM_5_0,DM_5_1,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE,FM_QUADCUBE,FM_NLAYOUTS};
+
+#ifdef _WIN32
+HANDLE ghEvent;
+#endif
+
+
+typedef struct {
+    PaUtilRingBuffer ringbuf;
+    PaStream *stream;
+    PaTime startTime;
+    PaTime lastTime;
+    float *ringbufData;
+    float *inbuf;   //for decoding and other tx; used as temp working buffer for read thread func.
+    /* for memory read func */
+    float *membuf;
+    float** orderptr; // for chan mapping;
+#ifdef _WIN32
+    void *hThread;
+    HANDLE hTimer;
+    HANDLE hTimerCount;
+#else
+#ifdef unix
+    pthread_t hThread;
+#endif
+#endif
+    fmhdecodefunc decodefunc;
+    fmhcopyfunc copyfunc;
+    double gain;
+    unsigned long frames_played;
+    unsigned long current_frame;
+    unsigned long from_frame;
+    unsigned long to_frame;
+    unsigned long memFramelength;
+    unsigned long memFramePos;
+    unsigned long inbuflen;
+    int srate;
+    int inchans;
+    int outchans;
+    int flag;
+    int ifd;
+#ifdef DEBUG
+    int ofd;
+#endif
+    int do_decode;
+    int do_mapping;
+    int play_looped;
+    int finished;
+} psfdata;
+
+static int file_playing;
+static psfdata *g_pdata = NULL;  // for timer interrupt routine
+
+void playhandler(int sig)
+{
+    if(sig == SIGINT)
+        file_playing = 0;
+}
+
+
+#ifdef unix
+void alarm_wakeup (int i)
+{
+    struct itimerval tout_val;
+
+    signal(SIGALRM,alarm_wakeup);
+
+
+    if(file_playing && g_pdata->stream) {
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        g_pdata->lastTime = Pa_GetStreamTime(g_pdata->stream ) - g_pdata->startTime;
+        printf("%.2f secs\r", g_pdata->lastTime);
+        fflush(stdout);
+    }
+    tout_val.it_interval.tv_sec = 0;
+    tout_val.it_interval.tv_usec = 0;
+    tout_val.it_value.tv_sec = 0;
+    tout_val.it_value.tv_usec = 200000;
+
+    setitimer(ITIMER_REAL, &tout_val,0);
+
+}
+#endif
+
+
+void finishedCallback(void *userData)
+{
+    psfdata *pdata = (psfdata*) userData;
+    //    printf("stream finished!\n");
+    pdata->finished = 1;
+    file_playing = 0;
+
+
+}
+
+
+#ifdef _WIN32
+
+VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
+{
+    psfdata *pdata = (psfdata*) lpParam;
+
+    if(file_playing && pdata->stream) {
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        pdata->lastTime = Pa_GetStreamTime(pdata->stream ) - pdata->startTime;
+        printf("%.2f secs\r", pdata->lastTime);
+        fflush(stdout);
+    }
+
+    SetEvent(ghEvent);
+}
+
+#endif
+/* thread func for file playback */
+/* writes decoded data to ring buffer */
+/* size of element is full m/c frame */
+
+// TODO: unix thread should return void*
+#ifdef unix
+static  int  threadFunctionReadFromRawFile(void* ptr)
+#else
+    static unsigned int __stdcall threadFunctionReadFromRawFile(void* ptr)
+#endif
+{
+    psfdata* pdata = (psfdata*)ptr;
+
+    /* Mark thread started */
+    pdata->flag = 0;
+
+    while (1) {
+        ring_buffer_size_t nElementsProcessed = 0;
+        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
+
+        //memset(pdata->inbuf,0,pdata->inbuflen * sizeof(float) * pdata->inchans);
+
+        if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
+            void* ptr[2] = {NULL};
+            ring_buffer_size_t sizes[2] = {0};
+
+            /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
+            PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
+
+            if (file_playing)  {
+                ring_buffer_size_t itemsReadFromFile;
+                int i,j;
+                int framesread = 0;
+                // we work with frames, = constant across inchans and outchans
+                itemsReadFromFile = 0;
+                for(i = 0; i < 2 && (ptr[i] != NULL); i++) {
+                    // NB ringbuf is sized by m/c frames
+                    int frameswanted = sizes[i];
+
+                    pdata->current_frame = psf_sndTell(pdata->ifd);
+                    // read up to end frame if requested
+                    if(pdata->to_frame < pdata->current_frame + frameswanted)
+                        frameswanted = pdata->to_frame - pdata->current_frame;
+
+                    if(frameswanted > 0){
+                        framesread = psf_sndReadFloatFrames(pdata->ifd,pdata->inbuf,frameswanted);
+                        if(framesread < 0){ // read error!
+                            printf("Error reading soundfile\n");
+                            pdata->flag = 1;
+                            file_playing = 0;
+                            break;  // just out of for loop - need to return instead?
+                        }
+                    }
+                    if(framesread == 0){
+                        /* EOF. EITHER: finish, or rewind if looping playback*/
+                        if(pdata->play_looped){
+                            if(psf_sndSeek(pdata->ifd,pdata->from_frame,PSF_SEEK_SET)){
+                                printf("Error looping soundfile\n");
+                                pdata->flag = 1;
+                                file_playing = 0;
+                                break;
+                            }
+                            // sizes[1] especially may well = 0
+                            if(frameswanted==0)
+                                break;
+                            framesread = psf_sndReadFloatFrames(pdata->ifd,pdata->inbuf,frameswanted);
+                            if(framesread < 0){ // read error!
+                                printf("Error reading soundfile\n");
+                                pdata->flag = 1;
+                                file_playing = 0;
+                                break;
+                            }
+                        }
+                        else {
+                            // we must watch the ring buffer to make sure all data has been rendered,
+                            // over several callback blocks
+                            //printf("End of data. playing = %d:\n", file_playing);
+                            //printf("\telements remaining = %d\n",elementsInBuffer);
+                            if(elementsInBuffer == pdata->ringbuf.bufferSize)  {
+                                pdata->flag = 1;
+                                break;
+                                //return 0;
+                            }
+                        }
+                    }
+                    else {
+                        // ringbuf calcs always relative to outchans
+                        itemsReadFromFile += framesread;
+
+                        // now ready to apply decoding or other processing
+
+                        if(pdata->gain != 1.0){
+                            for(j = 0; j < framesread * pdata->inchans; j++)
+                                pdata->inbuf[j]  = (float)(pdata->inbuf[j] * pdata->gain);
+                        }
+                        if(pdata->do_mapping) {
+                            int k;
+                            float val;
+                            float *buf = ptr[i];
+                            float *pchan;
+                            for(j=0; j < framesread; j++){
+                                for(k=0; k < pdata->outchans; k++){
+                                    pchan = pdata->orderptr[k];
+                                    if(pchan == NULL)
+                                        val = 0.0f;
+                                    else
+                                        val = *(pchan + j * pdata->inchans);
+                                    buf[j*pdata->outchans + k] = val;
+                                }
+                            }
+                        }
+                        else if(pdata->do_decode) {
+                            ABFSAMPLE abfsamp;
+
+                            for(j=0; j < framesread; j++){
+                                // single frame only
+                                pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
+                                /* BIG TODO: update funcs to process large frame buffer! */
+                                /* NB: ring buffer is effectively defined as raw bytes */
+#ifdef unix
+                                pdata->decodefunc(&abfsamp,ptr[i] + (j * pdata->outchans * sizeof(float)), 1);
+#else
+                                // suppress VC++ complaint of unknown size
+                                pdata->decodefunc(&abfsamp, (float*)(ptr[i]) + (j * pdata->outchans), 1);
+
+#endif
+                                nElementsProcessed++;
+                            }
+                        }
+                        else {  // inchans = outchans
+                            memcpy(ptr[i],pdata->inbuf,framesread * sizeof(float) * pdata->inchans);
+                            nElementsProcessed += framesread;
+                        }
+                    }
+                }
+                //                assert(nElementsProcessed == itemsReadFromFile);
+                if(framesread)
+                    PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, itemsReadFromFile);
+
+
+            }
+            else {
+                // this code is activated on Ctrl-C. Can do immediate finish by setting flag
+                //               printf("file done\n");
+                pdata->flag = 1;
+                break;
+            }
+        }
+        //  else {
+        //      printf("ringbuf size = %d, elements remaining = %d, playing = %d\n",pdata->ringbuf.bufferSize,elementsInBuffer,file_playing);
+        //  }
+
+        /* Sleep a little while... */
+        Pa_Sleep(10);
+    }
+    return 0;
+}
+
+
+
+#ifdef unix
+// TODO: unix return type should be void*
+typedef int (*threadfunc)(void*);
+#endif
+#ifdef _WIN32
+typedef unsigned int (__stdcall *threadfunc)(void*);
+#endif
+
+
+/* Start up a new thread for given function */
+static PaError startThread( psfdata* pdata, threadfunc fn )
+{
+    pdata->flag = 1;
+#ifdef _WIN32
+    pdata->hThread = (void*)_beginthreadex(NULL, 0, fn, pdata, 0, NULL);
+    if (pdata->hThread == NULL)
+        return paUnanticipatedHostError;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    }
+    /* Set file thread to a little higher prio than normal */
+    SetThreadPriority(pdata->hThread, THREAD_PRIORITY_ABOVE_NORMAL);
+#else
+
+#if defined(__APPLE__) || defined(__GNUC__)
+    if(pthread_create(&pdata->hThread,NULL,(void*) fn,pdata))
+        return -1;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    }
+#endif
+#endif
+
+    return paNoError;
+}
+
+#if 0
+static int stopThread( psfdata* pdata )
+{
+    // RWD: just called when all data played; must be called before StopStream
+    //   pdata->flag = 1;
+    /* Wait for thread to stop */
+    //   while (pdata->flag) {
+    //       Pa_Sleep(10);
+    //   }
+#ifdef _WIN32
+    CloseHandle(pdata->hThread);
+    pdata->hThread = 0;
+#else
+
+#if defined(__APPLE__) || defined(__GNUC__)
+    pthread_cancel(pdata->hThread);
+#endif
+#endif
+    return paNoError;
+}
+#endif
+
+static int paplayCallback(const void *inputBuffer, void *outputBuffer,
+                          unsigned long framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags statusFlags,
+                          void *userData )
+{
+    psfdata *data = (psfdata *)userData;
+    ring_buffer_size_t sampsAvailable = PaUtil_GetRingBufferReadAvailable(&data->ringbuf) * data->outchans;
+    ring_buffer_size_t sampsToPlay = min(sampsAvailable, (ring_buffer_size_t)(framesPerBuffer * data->outchans));
+    float *out = (float*) outputBuffer;
+    int framesToPlay = sampsToPlay/data->outchans;
+    int played;
+    /* outbuf may be NULL on initial startup */
+    if(out == NULL)
+        return paContinue;
+
+    // if framestoplay < framesPerBuffer, need to clean up the remainder
+    memset(out,0,framesPerBuffer * data->outchans * sizeof(float));
+    played = PaUtil_ReadRingBuffer(&data->ringbuf, out, framesToPlay);
+
+    data->frames_played += played;
+    if(data->flag) {
+        //printf("callback - complete\n");
+        return paComplete;
+    }
+    return paContinue;
+}
+
+
+
+static int MemCallback(const    void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       const PaStreamCallbackTimeInfo* timeInfo,
+                       PaStreamCallbackFlags statusFlags,
+                       void *userData )
+{
+    psfdata *pdata = (psfdata *)userData;
+
+    float *out = (float*) outputBuffer;
+    unsigned long inSamps,outSamps, inSampPos;
+    unsigned long i;
+    unsigned long framesToPlay = framesPerBuffer;
+
+    /* outbuf may be NULL on initial startup */
+    if(out == NULL)
+        return paContinue;
+
+    // if framesToPlay < framesPerBuffer, need to clean up the remainder
+    memset(out,0,framesPerBuffer * pdata->outchans * sizeof(float));
+    inSamps = pdata->memFramelength * pdata->inchans;
+    outSamps = framesPerBuffer * pdata->inchans;
+    inSampPos = pdata->memFramePos * pdata->inchans;
+
+    if(pdata->play_looped){
+        for(i=0;i < outSamps; i++){
+            pdata->inbuf[i] = pdata->membuf[inSampPos++] * pdata->gain;
+            if(inSampPos == inSamps)
+                inSampPos = 0;
+        }
+    }
+    else {
+        for(i=0;i < outSamps; i++){
+            pdata->inbuf[i] = pdata->membuf[inSampPos++] * pdata->gain;
+            if(inSampPos == inSamps)
+                break;
+        }
+        framesToPlay = i / pdata->inchans;
+        for(; i < outSamps;i++){
+            pdata->inbuf[i] = 0.0;
+        }
+
+    }
+    // write to output
+    // everything now in inbuf
+    if(pdata->do_decode) {
+        ABFSAMPLE abfsamp;
+        int j;
+        for(j=0; j < framesToPlay; j++){
+            pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
+            pdata->decodefunc(&abfsamp,out + (j * pdata->outchans), 1);
+        }
+    }
+    else {  // inchans = outchans
+        memcpy(out,pdata->inbuf,framesToPlay * sizeof(float) * pdata->inchans);
+    }
+    pdata->memFramePos = inSampPos / pdata->inchans;
+    pdata->frames_played += framesToPlay;
+    if(framesToPlay < framesPerBuffer)
+        pdata->flag = 1;           // end of stream
+    if(pdata->flag) {
+        //printf("\nnewcallback - complete\n");
+        //fflush(stdout);
+        return paComplete;
+    }
+    return paContinue;
+}
+
+
+
+
+static unsigned NextPowerOf2(unsigned val)
+{
+    val--;
+    val = (val >> 1) | val;
+    val = (val >> 2) | val;
+    val = (val >> 4) | val;
+    val = (val >> 8) | val;
+    val = (val >> 16) | val;
+    return ++val;
+}
+
+
+int show_devices(void);
+
+#define N_BFORMATS (10)
+//static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+//static const int layout_chans[] = {1,2,4,4,5,5,6,6,8,8,8,8};
+
+enum {FLAG_B = 0, FLAG_BM, FLAG_D, FLAG_G, FLAG_H, FLAG_I, FLAG_L, FLAG_M, FLAG_NFLAGS};
+
+
+void usage(void){
+#ifdef WIN32
+    printf("usage:\n  paplay [-BN][-dN][-gN]-hN][-i][-l][-b[N]|-m[S]][-u][-x] infile [from] [to] \n"
+#else
+           printf("usage:\n  paplay [-BN][-dN][-gN]-hN][-i][-l][-b[N]|-m[S]][-u] infile [from] [to]\n"
+#endif
+                  "       -dN  : use output Device N.\n"
+                  "       -gN  : apply gain factor N to input.\n"
+                  "       -BN  : set memory buffer size to N frames (default: %d).\n"
+                  "                N must be a power of 2 (e.g 4096, 8192, 16384 etc).\n"
+                  "       -hN  : set hardware blocksize to N frames (32 < N <= BN/4).\n"
+                  "               (N recommended to be a power of two size. Default: from soundcard).\n"
+                  "               Where set, buffer sizes are doubled internally for sample rates > 48KHz.\n"
+                  "       -i   : play immediately (do not wait for keypress).\n"
+                  "       -l   : loop file continuously, from start-time to end-time.\n"
+                  "       -m[S]: render using channel map string S.\n"
+                  "                Use -m without parameter for usage.\n"
+                  "       -u   : suppress elapsed time updates\n"
+#ifdef WIN32
+                  "       -x   : Apply WAVE_EX infile channel mask to DS audio stream\n"
+                  "              (ignored if -m or -b used)\n"
+#endif
+                  "       from : set start time (secs) for playback and looping. Default: 0.\n"
+                  "       to   : set end time (secs) for playback and looping. Default: EOF.\n"
+                  "       -b[N]: apply 1st-order B-Format decoding to standard soundfile.\n"
+                  "              (file must have at least 3 channels)\n"
+                  "               B-Format files (.amb) will be decoded automatically.\n"
+                  "               N sets speaker layout to one of the following:\n"
+                  "               1    :  *  mono (= W signal only)\n"
+                  "               2    :  *  stereo (quasi mid/side, = W +- Y)\n"
+                  "               3    :     square\n"
+                  "               4    :  *  quad FL,FR,RL,RR order   (default)\n"
+                  "               5    :     pentagon\n"
+                  "               6    :  *  5.0 surround (WAVEX order)\n"
+                  "               7    :  *  5.1 surround (WAVEX order, silent LFE)\n"
+                  "               8    :     hexagon\n"
+                  "               9    :     octagon 1 (front pair, 45deg)\n"
+                  "              10    :     octagon 2 (front centre speaker)\n"
+                  "              11    :     cube (as 3, low-high interleaved. Csound-compatible.)\n"
+                  "              12    :  *  cube (as 4, low quad followed by high quad).\n"
+                  "              Default decode layout is 4 (quad).\n"
+                  "              NB: no decoding is performed if -m flag used.\n"
+                  "paplay reads PEAK chunk if present to rescale over-range floatsam files.\n\n"
+                  ,RINGBUF_NFRAMES);
+           }
+
+        void mapusage(void)
+        {
+            printf( "  PAPLAY channel map mode -mS: Map arbitrary infile channels to output channels\n\n"
+                    "  Order string S = any combination of characters a-z inclusive.\n"
+                    "  Infile channels are mapped in order as a=1,b=2...z=26\n"
+                    "  (For example: channels in a 4-channel file are represented by the\n"
+                    "    characters abcd; any other character is an error).\n"
+                    "  Characters must be lower case, and may be used more than once.\n"
+                    "  Duplicate characters duplicate the corresponding input channel.\n"
+                    "  The zero character (0) may be used to set a silent channel.\n"
+                    "  A maximum of 26 channels is supported for both input and output.\n"
+                    "  The number of characters in S sets the number of output channels,\n"
+                    "    which must be supported by the selected device.\n"
+                    "  NB: -m cannot be combined with -b (B-Format decoding).\n"
+                    "  If channel mapping is used, a B-format file (AMB) \n"
+                    "    will be rendered without decoding.\n"
+                    );
+        }
+
+    /*******************************************************************/
+    int main(int argc,char **argv)
+    {
+        PaStreamParameters outputParameters;
+#ifdef _WIN32
+        /* portaudio sets default channel masks we can't use; we must do all this to set default mask = 0! */
+        PaWinDirectSoundStreamInfo directSoundStreamInfo;
+        //    PaWinMmeStreamInfo winmmeStreamInfo;
+
+#endif
+        PaDeviceInfo *devinfo = NULL;
+        PaStream *stream = NULL;
+        PaStreamCallback *playcallback = paplayCallback;
+        PaError err = paNoError;
+        psfdata sfdata;
+        PSF_CHPEAK *fpeaks = NULL;
+        PSF_PROPS props;
+        // ABFSAMPLE abfsample;
+        // ABFSAMPLE *abf_frame = NULL;
+        int res,inorder = 1;
+        time_t in_peaktime = 0;
+        MYLONG lpeaktime;
+        int waitkey = 1;
+        //int play_looped = 0;
+        double fromdur = 0.0;
+        double totime  = 0.0;
+        int ifd = -1,from_frame = 0, to_frame = 0;
+        unsigned int i=0;
+        PaDeviceIndex device;
+        long filesize;
+        //long framesize = 0;   // Apr 2010 to be scaled by sample rate
+        unsigned long ringframelen = RINGBUF_NFRAMES;  // length of ring buffer in m/c frames
+        unsigned long frames_per_buffer = 0;
+        unsigned long nFramesToPlay = 0;
+        double gain = 1.0;
+        unsigned int inchans,outchans;
+        int layout = 4; // default, quad decode */
+        fmhcopyfunc copyfunc = NULL;
+        fmhdecodefunc decodefunc = NULL;
+        // vars for chorder facilit
+        char* argstring = NULL;
+        unsigned int rootchar = 'a';
+        unsigned int maxchar = 'z';
+        unsigned int  nchars=0,nzeros = 0;
+        unsigned int max_inchar = 0;
+        unsigned int flags[FLAG_NFLAGS] = {0};
+#ifdef WIN32
+        int do_speakermask = 0;
+        int speakermask = 0;
+#endif
+        int do_updatemessages = 1;
+#ifdef unix
+        struct itimerval tout_val;
+        tout_val.it_interval.tv_sec = 0;
+        tout_val.it_interval.tv_usec = 0;
+        tout_val.it_value.tv_sec = 0;
+        tout_val.it_value.tv_usec = 200000;
+#endif
+
+#ifdef _WIN32
+        HANDLE hTimerQueue;
+        ghEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+        if(ghEvent==NULL){
+            printf("Failed to start internal timer (1).\n");
+            return 1;
+        }
+        hTimerQueue = CreateTimerQueue();
+        if(hTimerQueue == NULL){
+            printf("Failed to start internal timer (2).\n");
+            return 1;
+        }
+#endif
+        /* CDP version number */
+        if(argc==2 && (stricmp(argv[1],"--version")==0)){
+            printf("3.0.1\n");
+            return 0;
+        }
+
+        signal(SIGINT,playhandler);
+
+        sfdata.inbuf = NULL;
+        sfdata.membuf = NULL;
+        sfdata.memFramelength = 0;
+        sfdata.memFramePos = 0;
+        sfdata.ringbufData = NULL;
+        sfdata.orderptr = NULL;
+        sfdata.do_mapping = 0;
+
+        printf("PAPLAY: play multi-channel soundfile. V3.0.1 (c)  RWD,CDP 2012, 2013\n");
+        file_playing = 0;
+        err = Pa_Initialize();
+        if( err != paNoError ) {
+            printf("Failed to initialize Portaudio.\n");
+            return 1;
+        }
+        device =  Pa_GetDefaultOutputDevice();
+
+        if(argc < 2){
+            usage();
+            show_devices();
+            Pa_Terminate();
+            return 1;
+        }
+
+        while(argv[1][0]=='-'){
+            unsigned long userbuflen;
+            switch(argv[1][1]){
+            case('b'):
+                if(flags[FLAG_B]){
+                    printf("error: multiple -b flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2] != '\0'){
+                    layout = atoi(&argv[1][2]);
+                    if(layout < 1 || layout > 12){
+                        fprintf(stderr,"value for -b flag out of range.\n");
+                        Pa_Terminate();
+                        return 1;
+                    }
+                }
+                flags[FLAG_B] = 1;
+                break;
+            case('d'):
+                if(flags[FLAG_D]){
+                    printf("error: multiple -d flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-d flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                device = atoi(&(argv[1][2]));
+                flags[FLAG_D] = 1;
+                break;
+            case('g'):
+                if(flags[FLAG_G]){
+                    printf("error: multiple -g flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-g flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                gain = atof(&(argv[1][2]));
+                if(gain <= 0.0){
+                    fprintf(stderr,"gain value must be positive.\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_G] = 1;
+                break;
+            case 'h':
+                if(flags[FLAG_H]){
+                    printf("error: multiple -h flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-h flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                frames_per_buffer = atoi(&(argv[1][2]));
+                if((frames_per_buffer > 0) && (frames_per_buffer < 32)){
+                    printf("-h value too small: must be at least 32.\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_H] = 1;
+                break;
+            case('i'):
+                if(flags[FLAG_I]){
+                    printf("error: multiple -i flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_I] = 1;
+                waitkey = 0;
+                break;
+            case ('l'):
+                flags[FLAG_L] = 1;
+                break;
+            case 'm':
+                if(flags[FLAG_M]){
+                    printf("error: multiple -m flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    mapusage();
+                    Pa_Terminate();
+                    return 1;
+                }
+                argstring = &argv[1][2];
+                nchars = strlen(argstring);
+                if(nchars > 26) {
+                    printf("error: -m order list too long.\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(nchars < 1){
+                    printf("error: -m must specify at least one channel.\n");
+                    Pa_Terminate();
+                    return 1;
+
+                }
+                flags[FLAG_M] = 1;
+                break;
+
+                // RWD Feb 2011: need to support dummy -u flag for compatibility with pvplay
+                // will we actually need to implement it?
+            case 'u':
+                do_updatemessages = 0;
+                break;
+#ifdef WIN32
+            case 'x':
+                do_speakermask = 1;
+                break;
+#endif
+            case 'B':
+                if(flags[FLAG_BM]){
+                    printf("error: multiple -B flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-B flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+
+                ringframelen = atoi(&(argv[1][2]));
+                if(ringframelen <= 1024){
+                    printf("-B: framesize value must be >=1024!n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                userbuflen = NextPowerOf2(ringframelen);
+                if(userbuflen != ringframelen){
+                    printf("-B: framesize value must be power of 2 size\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_BM] = 1;
+                break;
+            default:
+                printf("unrecognised flag option\n");
+                Pa_Terminate();
+                return 1;
+            }
+            argv++;  argc--;
+        }
+        // this just cheks flag usage. We render amb files undecoded, if using channel mapping
+        if(flags[FLAG_B] && flags[FLAG_M]){
+            printf("Sorry: cannot use -m with -b\n");
+            Pa_Terminate();
+            return 1;
+        }
+        if(frames_per_buffer > ringframelen/4){
+            printf(" hardware (-h) framesize %lu too large for file buffer (-B) size %lu\n",
+                   frames_per_buffer,ringframelen);
+            Pa_Terminate();
+            return 1;
+        }
+
+        if(argc < 2 || argc > 4){
+            usage();
+            show_devices();
+            Pa_Terminate();
+            return 1;
+        }
+
+        if(argc>=3){
+            fromdur = atof(argv[2]);
+            if(fromdur < 0.0){
+                printf("Error: start position must be positive\n");
+                Pa_Terminate();
+                return 1;
+            }
+        }
+        if(argc==4){
+            totime = atof(argv[3]);
+            if(totime <= fromdur){
+                printf("Error: end time must be after from time\n");
+                Pa_Terminate();
+                return 1;
+            }
+
+
+        }
+        if(psf_init()){
+            printf("Error initializing psfsys\n");
+            goto error;
+        }
+        /* allow auto rescaling of overrange floats via PEAK chunk */
+        ifd = psf_sndOpen(argv[1],&props,1);
+        if(ifd < 0){
+            printf("unable to open soundfile %s\n",argv[1]);
+            goto error;
+        }
+        filesize = psf_sndSize(ifd);
+        if(filesize ==0){
+            printf("Soundfile is empty!\n");
+            goto error;
+        }
+
+#if WIN32
+        if(props.format == PSF_WAVE_EX){
+            if(do_speakermask){
+                if(flags[FLAG_B] || flags[FLAG_M]){
+                    printf("-x flag ignored if -B or -m used\n");
+                }
+                else {
+                    int mask = psf_speakermask(ifd);
+                    if(mask < 0){
+                        printf("could not read speaker mask. Using 0\n");
+                        speakermask = 0;
+                    }
+                    else
+                        speakermask = mask;
+                }
+            }
+        }
+#endif
+        from_frame = (long)(fromdur * props.srate);
+        if(from_frame >= filesize){
+            printf("Error: start is beyond end of file\n");
+            goto error;
+        }
+
+        nFramesToPlay = filesize - from_frame;
+        to_frame = filesize -1;
+        if(totime > 0.0){
+            to_frame = (long)(totime * props.srate);
+            if(to_frame >= filesize){
+                printf("Error: end time is beyond end of file\n");
+                goto error;
+            }
+            //printf("fromframe = %d, toframe = %d\n",(int)from_frame,(int) to_frame);
+            if(to_frame <= from_frame){
+                printf("end time must be later than from time\n");
+                goto error;
+            }
+            nFramesToPlay = to_frame - from_frame;
+            //printf("playing %d frames\n",(int) nFramesToPlay);
+        }
+
+        outchans = inchans = props.chans;
+        /* if using order string, outchans may change arbitrarily;
+           later on we set inchans to outchans,
+           to emulate a plain outchans-sized file
+        */
+        if(flags[FLAG_M]){
+            outchans = nchars;
+        }
+
+        if(props.chformat==MC_BFMT){
+            if(flags[FLAG_M]){
+                printf("-m specified: not decoding B-Format file.\n");
+                flags[FLAG_B] = 0;
+            }
+            else
+                flags[FLAG_B] = 1;
+        }
+
+        // we will not have both do_channel_mapping and do_decode toigether
+        if(flags[FLAG_B] == 1){
+            if(inchans > 4) {
+                inorder = 2;
+                printf("%u-channel input: performing 2nd-order decode.\n",inchans);
+            }
+            switch(inchans){
+            case 3:
+                copyfunc = fmhcopy_3;
+                break;
+            case 4:
+                copyfunc = fmhcopy_4;
+                break;
+            case 5:
+                copyfunc = fmhcopy_5;
+                break;
+            case 6:
+                copyfunc = fmhcopy_6;
+                break;
+            case 9:
+                copyfunc = fmhcopy_9;
+                break;
+            case 11:
+                copyfunc = fmhcopy_11;
+                break;
+            case 16:
+                copyfunc = fmhcopy_16;
+                break;
+            default:
+                fprintf(stderr,"unsupported channel count (%u) for B-format file.\n",inchans);
+                return 1;
+            }
+
+            switch(layout-1){
+            case FM_MONO:
+                printf("Decoding to Mono\n");
+                decodefunc = fm_i1_mono;
+                outchans = 1;
+                break;
+            case FM_STEREO:
+                printf("Decoding to Stereo\n");
+                decodefunc = fm_i1_stereo;
+                outchans = 2;
+                break;
+            case FM_SQUARE:
+                printf("Decoding to Square\n");
+                if(inorder == 1)
+                    decodefunc = fm_i1_square;
+                else
+                    decodefunc = fm_i2_square;
+                outchans = 4;
+                break;
+            case FM_PENT:
+                printf("Decoding to pentagon\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_pent;
+                else
+                    decodefunc = fm_i2_pent;
+                outchans = 5;
+                break;
+            case DM_5_0:
+                printf("Decoding to 5.0 surround (David Moore)\n");
+                if(inorder==1)
+                    decodefunc = dm_i1_surr;
+                else
+                    decodefunc = dm_i2_surr;
+                outchans = 5;
+                break;
+            case DM_5_1:
+                printf("Decoding to  5.1 surround (David Moore)\n");
+                if(inorder==1)
+                    decodefunc = dm_i1_surr6;
+                else
+                    decodefunc = dm_i2_surr6;
+                outchans = 6;
+                break;
+            case FM_HEX:
+                printf("Decoding to Hexagon\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_hex;
+                else
+                    decodefunc = fm_i2_hex;
+                outchans = 6;
+                break;
+            case FM_OCT1:
+                printf("Decoding to Octagon 1\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_oct1;
+                else
+                    decodefunc = fm_i2_oct1;
+                outchans = 8;
+                break;
+            case FM_OCT2:
+                printf("Decoding to Octagon 2\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_oct2;
+                else
+                    decodefunc = fm_i2_oct2;
+                outchans = 8;
+                break;
+            case FM_CUBE:
+                printf("Decoding to Cube (FM interleaved)\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_cube;
+                else
+                    decodefunc = fm_i2_cube;
+                outchans = 8;
+                break;
+            case FM_QUADCUBE:
+                printf("Decoding to Octagon 1 (WAVEX order)\n");
+                if(inorder==1)
+                    decodefunc = fm_i1_cubex;
+                else
+                    decodefunc = fm_i2_cubex;
+                outchans = 8;
+                break;
+            default:
+                //quad
+                printf("Decoding to quad surround (WAVEX order)\n");
+                if(inorder == 1)
+                    decodefunc = fm_i1_quad;
+                else
+                    decodefunc = fm_i2_quad;
+                outchans = 4;
+                break;
+            }
+        }
+
+
+
+        printf("opened %s: %ld frames, %u channels\n",argv[1],filesize,inchans);
+        fpeaks = (PSF_CHPEAK *) calloc(inchans,sizeof(PSF_CHPEAK));
+        if(fpeaks==NULL){
+            puts("no memory for PEAK data\n");
+            goto error;
+        }
+        //lpeaktime = (int) in_peaktime;
+        res = psf_sndReadPeaks(ifd,fpeaks,(MYLONG *) &lpeaktime);
+        if(res==0) {
+            printf("no PEAK data in this soundfile\n");
+        }
+        else if(res < 0){
+            printf("Error reading PEAK data\n");
+            goto error;
+        }
+        else{
+            in_peaktime = (time_t) lpeaktime;
+            printf("PEAK data:\ncreation time: %s",ctime(&in_peaktime));
+
+            for(i=0;i < inchans;i++){
+                printf("CH %d: %.4f at frame %lu: \t%.4f secs\n",
+                       i,fpeaks[i].val,fpeaks[i].pos,(double)(fpeaks[i].pos / (double) props.srate));
+            }
+        }
+        // if doing channel map,  precompute into mem buffer
+        if(nFramesToPlay <= ringframelen){
+            sfdata.membuf =  (float *) PaUtil_AllocateMemory(nFramesToPlay * sizeof(float) * /*inchans*/ outchans);
+            if( sfdata.membuf == NULL )   {
+                puts("Could not allocate memory play buffer.\n");
+                goto error;
+            }
+            sfdata.memFramelength = nFramesToPlay;
+            sfdata.memFramePos = 0;
+            playcallback = MemCallback;
+            printf("RAM block size =  %lu\n",nFramesToPlay);
+
+        }
+        else {
+            // set up ring buffer  NB must be power of 2 size
+            if(props.srate > 48000)
+                ringframelen <<= 1;
+
+            // NB ring buffer sized for decoded data, hence outchans here; otherwise inchans = outchans
+            sfdata.ringbufData = (float *) PaUtil_AllocateMemory( ringframelen * sizeof(float) * outchans); /* From now on, recordedSamples is initialised. */
+            if( sfdata.ringbufData == NULL )   {
+                puts("Could not allocate play buffer.\n");
+                goto error;
+            }
+
+            // number of elements has to be a power of two, so each element has to be a full m/c frame
+            if (PaUtil_InitializeRingBuffer(&sfdata.ringbuf, sizeof(float) * outchans, ringframelen , sfdata.ringbufData) < 0) {
+                puts("Could not initialise play buffer.\n");
+                goto error;
+            }
+            printf("File buffer size = %ld\n",ringframelen);
+
+        }
+
+        // worst case, ring buffer is empty! So need enough space
+        // NB inchans may well be > outchans
+        sfdata.inbuf = (float *) PaUtil_AllocateMemory(ringframelen * sizeof(float) * inchans);
+        if(sfdata.inbuf==NULL){
+            puts("No memory for read buffer\n");
+            goto error;
+        }
+        sfdata.inbuflen = ringframelen;
+
+        if(flags[FLAG_M]) {
+            max_inchar = rootchar;
+
+            sfdata.orderptr = malloc(outchans * sizeof(float*));
+            for(i=0;i < nchars;i++){
+                unsigned int thischar = argstring[i];
+                //          printf("reading char %c (%d)\n",thischar,thischar);
+                unsigned int chindex;
+                if(thischar != '0' && (thischar < rootchar || thischar > maxchar)){
+                    printf("illegal character in order string: %c\n",thischar);
+                    goto error;
+                }
+                if(thischar =='0'){
+                    //            printf("setting channel %d to zero.\n",i);
+                    sfdata.orderptr[i] = NULL;
+                    nzeros++;
+                }
+                else{
+                    if(thischar > max_inchar)
+                        max_inchar = thischar;
+                    chindex = thischar - rootchar;
+                    sfdata.orderptr[i] = sfdata.inbuf + chindex;
+                }
+            }
+            if(nzeros==nchars)
+                printf("Warning: -m order string is all zeros - a silent file will be made!\n");
+            else{
+                /* count inclusively! */
+                if(props.chans < (int)(max_inchar - rootchar + 1)){
+                    printf("File has %d channels; order string defines non-existent channels.\n",props.chans);
+                    printf("For this file, maximum character is %c\n",rootchar + props.chans -1);
+                    goto error;
+                }
+            }
+            outchans = nchars;
+            sfdata.do_mapping = 1;
+
+            // final alloc when infile is read, below
+        }
+
+        if(props.srate > 48000) {
+            frames_per_buffer *= 2;
+            if(frames_per_buffer)
+                printf("Audio buffer size = %lu\n",frames_per_buffer);
+        }
+        sfdata.ifd          = ifd;
+        sfdata.inchans      = inchans;
+        sfdata.outchans     = outchans;
+        sfdata.frames_played  = 0;
+        sfdata.gain         = gain;
+        sfdata.copyfunc     = copyfunc;
+        sfdata.decodefunc   = decodefunc;
+        sfdata.do_decode    = flags[FLAG_B];
+        sfdata.from_frame   = from_frame;
+        if(to_frame > 0)
+            sfdata.to_frame = to_frame;
+        else {
+            sfdata.to_frame = 0;
+        }
+        sfdata.current_frame = 0;
+        sfdata.play_looped  = flags[FLAG_L];
+
+        sfdata.srate = props.srate;
+
+        sfdata.finished = 0;
+        g_pdata = &sfdata;
+
+#ifdef DEBUG
+        {
+            PSF_PROPS outprops = props;
+            outprops.chans = outchans;
+            sfdata.ofd = psf_sndCreate("debug.wav",&outprops,0,0,PSF_CREATE_RDWR);
+            if(sfdata.ofd == -1){
+                printf("Sorry - unable to create debug outfile\n");
+            }
+        }
+#endif
+
+        outputParameters.device = device;   /*Pa_GetDefaultOutputDevice(); */ /* default output device */
+        outputParameters.channelCount = outchans;
+        outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output */
+        outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+        outputParameters.hostApiSpecificStreamInfo = NULL;
+
+        devinfo = (PaDeviceInfo *) Pa_GetDeviceInfo(device);
+#if defined MAC || defined unix
+        if(devinfo){
+            printf("Using device %d: %s\n",device,devinfo->name);
+        }
+#endif
+
+#ifdef _WIN32
+
+#ifdef NOTDEF
+        if(devinfo){
+            if(apiinfo->type  == paMME ){
+                winmmeStreamInfo.size = sizeof(winmmeStreamInfo);
+                winmmeStreamInfo.hostApiType = paMME;
+                winmmeStreamInfo.version = 1;
+                winmmeStreamInfo.flags = paWinMmeUseChannelMask;
+                winmmeStreamInfo.channelMask = 0;
+                outputParameters.hostApiSpecificStreamInfo = &winmmeStreamInfo;
+# ifdef _DEBUG
+                printf("WIN DEBUG: WinMME device channel mask set to 0.\n");
+# endif
+            }
+        }
+#endif
+
+        if(devinfo) {
+            int apitype = devinfo->hostApi;
+            const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(apitype);
+            //  if(do_speakermask)
+            //      printf("Using file channel format %d\n",speakermask);
+            printf("Using device %d: %s ",device,devinfo->name);
+
+            if(apiinfo->type  == paDirectSound ){
+                printf("(DS)\n");
+                /* set this IF we are using Dsound device. */
+                directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
+                directSoundStreamInfo.hostApiType = paDirectSound;
+                directSoundStreamInfo.version = 2;
+                directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
+                directSoundStreamInfo.channelMask = speakermask;
+                outputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
+            }
+            else if(apiinfo->type == paASIO)
+                printf("(ASIO)\n");
+            // else
+            //    printf("API unknown!);
+            else
+                printf("\n");
+        }
+#endif
+
+        err = Pa_OpenStream(
+                            &stream,
+                            NULL,  /* No input */
+                            &outputParameters, /* As above. */
+                            props.srate,
+                            frames_per_buffer,
+                            paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+                            playcallback,
+                            &sfdata );
+
+
+
+        if( err != paNoError ) {
+            printf("Unable to open output device for %u-channel file.\n",outchans);
+            goto error;
+        }
+        err =  Pa_SetStreamFinishedCallback( stream, finishedCallback );
+        if( err != paNoError ) {
+            printf("Unable to set finish callback\n");
+            goto error;
+        }
+        sfdata.stream = stream;
+
+        file_playing = 1; // need this for c/l test below!
+
+        if(waitkey){
+            printf("Press any key to start:\n");
+            while (!kbhit()){
+                if(!file_playing)        //check for instant CTRL-C
+                    goto error;
+            };
+#ifdef _WIN32
+            if(kbhit())
+                _getch();                        //prevent display of char
+#endif
+        }
+
+        // should this go in read thread func too?
+        if(from_frame > 0){
+            if(psf_sndSeek(sfdata.ifd,from_frame,PSF_SEEK_SET))     {
+                printf("Error setting start position\n");
+                goto error;
+            }
+            sfdata.current_frame = from_frame;
+        }
+
+        /* if small block, read it all into memory
+         * if doing channel map, do it here rather in callback; we must set inchans to outchans */
+        if(sfdata.memFramelength > 0){
+            if(flags[FLAG_M]) {
+                unsigned int i,j;
+                int k;
+
+                float **orderptr = NULL;
+                float val;
+                float *tempframebuf = malloc(sizeof(float) * sfdata.memFramelength * props.chans);
+                nzeros = 0;
+                if(tempframebuf==NULL){
+                    puts("no memory for audio data\n");
+                    goto error;
+                }
+                orderptr = malloc(outchans * sizeof(float*));
+                for(i=0;i < nchars;i++){
+                    unsigned int thischar = argstring[i];
+                    unsigned int chindex;
+                    if(thischar != '0' && (thischar < rootchar || thischar > maxchar)){
+                        printf("illegal character in order string: %c\n",thischar);
+                        goto error;
+                    }
+                    if(thischar =='0'){
+                        orderptr[i] = NULL;
+                        nzeros++;
+                    }
+                    else{
+                        if(thischar > max_inchar)
+                            max_inchar = thischar;
+                        chindex = thischar - rootchar;
+                        orderptr[i] = tempframebuf + chindex;
+                    }
+                }
+                if(nzeros==nchars)
+                    printf("Warning: -m order string is all zeros - a silent file will be made!\n");
+                else {
+                    /* count inclusively! */
+                    if(props.chans < (int)(max_inchar - rootchar + 1)){
+                        printf("File has %d channels; order string defines non-existent channels.\n",props.chans);
+                        printf("For this file, maximum character is %c\n",rootchar + props.chans -1);
+                        goto error;
+                    }
+                }
+                if(psf_sndReadFloatFrames(sfdata.ifd,tempframebuf,sfdata.memFramelength) != sfdata.memFramelength) {
+                    printf("Error reading soundfile into memory\n");
+                    goto error;
+                }
+
+
+                for(j=0;j < sfdata.memFramelength; j++) {
+                    for(k = 0; k < sfdata.outchans; k++) {
+                        // find requested input chan
+                        float *pchan = orderptr[k];
+                        if(pchan == NULL)
+                            val = 0.0f;
+                        else
+                            val = *(pchan + j * props.chans);
+                        // and write to stream memory buffer
+                        sfdata.membuf[j * sfdata.outchans + k] = val;
+                    }
+                }
+                // done with these now
+                free(tempframebuf);
+                free(orderptr);
+                sfdata.inchans = sfdata.outchans;
+            }
+            else {  // no fancy stuff, just read straight into mem buffer
+                if(psf_sndReadFloatFrames(sfdata.ifd,sfdata.membuf,sfdata.memFramelength)
+                   != sfdata.memFramelength) {
+                    printf("Error reading soundfile into memory\n");
+                    goto error;
+                }
+            }
+        }
+
+        // set up timer
+#ifdef unix
+        if(do_updatemessages) {
+            setitimer(ITIMER_REAL, &tout_val,0);
+            signal(SIGALRM,alarm_wakeup); /* set the Alarm signal capture */
+        }
+#endif
+
+#ifdef _WIN32
+        // not sure of the best position for this
+        //if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
+        //    (WAITORTIMERCALLBACK) TimerCallback, &sfdata,250,250,0)) {
+        //    printf("failed to start timer (3).\n");
+        //    return 1;
+        // }
+#endif
+        /* Start the file reading thread */
+        sfdata.startTime = Pa_GetStreamTime(stream );
+
+        if(sfdata.memFramelength == 0){
+
+            err = startThread(&sfdata, threadFunctionReadFromRawFile);
+            if( err != paNoError ) goto error;
+        }
+        else {
+            sfdata.flag = 0;
+        }
+
+
+#ifdef _WIN32
+        if(do_updatemessages){
+            if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
+                                      (WAITORTIMERCALLBACK) TimerCallback, &sfdata,200,200,0)) {
+                printf("failed to start timer (3).\n");
+                return 1;
+            }
+        }
+#endif
+        err = Pa_StartStream( stream );
+        if( err != paNoError )
+            goto error;
+
+        printf("Hit CTRL-C to stop.\n");
+        while(!sfdata.finished && file_playing){
+            // nothing to do!
+            Pa_Sleep(10);
+        }
+        // note to programmer: any bug in audio buffer arithmetic will likely cause crash here!
+        err = Pa_StopStream( stream );
+        if( err != paNoError ) {
+            printf("Error stopping stream\n");
+            goto error;
+        }
+
+        /*  read thread should exit always, no need to Stop it here */
+
+
+        err = Pa_CloseStream( stream );
+        if( err != paNoError ) {
+            printf("Error closing stream\n");
+            goto error;
+        }
+        printf("\r%.2f secs\n",(float)(sfdata.lastTime));
+        fflush(stdout);
+        printf("Playback finished.\n");
+    error:
+        Pa_Terminate();
+
+#ifdef _WIN32
+        CloseHandle(ghEvent);
+        DeleteTimerQueue(hTimerQueue);
+#endif
+        if( sfdata.ringbufData )
+            PaUtil_FreeMemory(sfdata.ringbufData);
+        if(sfdata.inbuf)
+            PaUtil_FreeMemory(sfdata.inbuf);
+        if(sfdata.membuf)
+            PaUtil_FreeMemory(sfdata.membuf);
+        if(sfdata.orderptr)
+            free(sfdata.orderptr);
+        if(ifd >=0)
+            psf_sndClose(ifd);
+        if(fpeaks)
+            free(fpeaks);
+#ifdef DEBUG
+        if(sfdata.ofd >=0)
+            psf_sndClose(sfdata.ofd);
+#endif
+        psf_finish();
+
+        return 0;
+    }
+
+    int show_devices(void)
+    {
+        PaDeviceIndex numDevices,p;
+        const    PaDeviceInfo *pdi;
+#ifdef _WIN32
+        const PaHostApiInfo* api_info;
+        const char *apiname;
+#endif
+        PaError  err;
+        int nOutputDevices = 0;
+
+#ifdef USE_ASIO
+        printf("For ASIO multi-channel, you may need to select the highest device no.\n");
+#endif
+        /*Pa_Initialize();*/
+        numDevices =  Pa_GetDeviceCount();
+        if( numDevices < 0 )
+            {
+                printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+                err = numDevices;
+                return err;
+            }
+#ifdef WIN32
+        printf("Driver\tDevice\tInput\tOutput\tName\n");
+#else
+        printf("Device\tInput\tOutput\tName\n");
+#endif
+        //printf("Number of devices = %d\n", numDevices );
+        for( p = 0; p < numDevices; p++ )
+            {
+                pdi = Pa_GetDeviceInfo( p );
+
+                //#ifdef _WIN32
+                //                      /*RWD: skip, info on inputs */
+                //                      if(pdi->maxOutputChannels == 0)
+                //                              continue;
+                //#endif
+                nOutputDevices++;
+
+                if( p == Pa_GetDefaultOutputDevice() )
+                    printf("*");
+                else
+                    printf(" ");
+#ifdef WIN32
+                api_info = Pa_GetHostApiInfo(pdi->hostApi);
+                apiname = api_info->name;
+                if(strcmp(apiname,"Windows DirectSound")==0)
+                    apiname = "DS ";
+                printf("(%s)\t%d\t%d\t%d\t%s\n",apiname,p,
+                       pdi->maxInputChannels,
+                       pdi->maxOutputChannels,
+                       pdi->name);
+#else
+                printf("%d\t%d\t%d\t%s\n",p,
+                       pdi->maxInputChannels,
+                       pdi->maxOutputChannels,
+                       pdi->name);
+
+
+#endif
+            }
+        /*Pa_Terminate();*/
+        return 0;
+    }

+ 248 - 0
dev/externals/paprogs/pvplay/.gdb_history

@@ -0,0 +1,248 @@
+break pvplay2.cpp:467
+run sh.ana
+ p a  pv
+ p /a  pv
+pv
+ p /h  pv
+ p /x  pv
+n
+print props.origrate
+print pv
+print anal_framesize
+print props.winlen
+print props.decfac
+print winlen
+c
+q
+break pvplay2.cpp:443
+run sh.ana
+q
+break pvplay2.cpp:443
+run sh.ana
+n
+n
+n
+print props.chans
+print props.origrate
+q
+break pvplay2.cpp:443
+run sh.ana
+ni
+ni
+ni
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+s
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+q
+break snd.c:448
+run sh.ana
+q
+break snd.c:448
+run sh.ana
+break pvplay2.cpp:443
+run sh.ana
+s
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+n
+s
+break 367
+c
+n
+n
+p origsize
+print origsize
+print origrate
+n
+print origrate
+q

+ 30 - 0
dev/externals/paprogs/pvplay/CMakeLists.txt

@@ -0,0 +1,30 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.8 -Dunix  -fomit-frame-pointer -funroll-loops")
+  set(CMAKE_CXX_FLAGS "-O2 -Wall -mmacosx-version-min=10.8  -Dunix -fomit-frame-pointer -funroll-loops -std=c++11 -stdlib=libc++")
+  include_directories ( /Developer/Headers/FlatCarbon )
+  find_library(COREAUDIOLIB CoreAudio)
+  find_library(AUDIOTOOLBOX AudioToolbox)
+  find_library(AULIB AudioUnit)
+  find_library(CARBONLIB Carbon)
+  #RWD only need this for APPLE?
+  find_library(AAIOLIB names libaaio.a paths /usr/local/lib)
+  set(EXTRA_LIBRARIES1 pthread ${AAIOLIB} ${COREAUDIOLIB} ${AUDIOTOOLBOX} ${AULIB} ${CARBONLIB} ${EXTRA_LIBRARIES})
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -DUSE_ASIO -fomit-frame-pointer -funroll-loops")
+    set(CMAKE_CXX_FLAGS "-O3 -DWIN32 -D_WIN32 -DUSE_ASIO -fomit-frame-pointer -funroll-loops -static-libgcc -static-libstdc++")
+    set(EXTRA_LIBRARIES1 ${EXTRA_LIBRARIES})
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 asound jack pthread ${EXTRA_LIBRARIES})
+  endif()
+endif()
+
+link_directories(../../lib ../portaudio/lib/.libs ../../../sfsys)
+
+include_directories(../../../include ../include ../portaudio/include ../portaudio/src/common /usr/local/include)
+
+add_executable(pvplay pvplay.cpp pvoc2.cpp fmhfuncs.c pvthreads.cpp pvfileio.c mxfft.c)
+target_link_libraries(pvplay portaudio.a sfsys ${EXTRA_LIBRARIES1})
+
+my_install(pvplay)

BIN
dev/externals/paprogs/pvplay/Toolkit.pdf


+ 106 - 0
dev/externals/paprogs/pvplay/fmdcode.h

@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmdcode.h FMH decode functions */
+
+/*
+ Channel order is WXYZ,RSTUV,KLMNOPQ
+ 
+ The number of channels defines the order of the soundfield:
+ 2 channel = UHJ 
+ 3 channel = h   = 1st order horizontal
+ 4 channel = f   = 1st order 3-D
+ 5 channel = hh  = 2nd order horizontal
+ 6 channel = fh  = 2nd order horizontal + 1st order height (formerly
+                                                            called 2.5 order)
+ 7 channel = hhh = 3rd order horizontal
+ 8 channel = fhh = 3rd order horizontal + 1st order height
+ 9 channel = ff  = 2nd order 3-D
+ 11 channel = ffh = 3rd order horizontal + 2nd order height
+ 16 channel = fff = 3rd order 3-D
+ 
+ 
+ Horizontal   Height  Soundfield   Number of    Channels
+ order 	      order 	  type      channels 	
+ 1 	         0 	       horizontal 	  3 	    WXY
+ 1 	         1 	      full-sphere 	  4 	    WXYZ
+ 2 	         0 	       horizontal 	  5 	    WXY....UV
+ 2 	         1 	       mixed-order    6 	    WXYZ...UV
+ 2 	         2 	      full-sphere     9 	    WXYZRSTUV
+ 3           0 	       horizontal 	  7 	    WXY....UV.....PQ
+ 3 	         1         mixed-order 	  8 	    WXYZ...UV.....PQ
+ 3 	         2 	       mixed-order 	 11 	    WXYZRSTUV.....PQ
+ 3 	         3 	      full-sphere 	 16 	    WXYZRSTUVKLMNOPQ
+ */
+
+typedef struct abf_samp {
+	float W;
+	float X;
+	float Y;
+	float Z;
+    float R;
+    float S;
+    float T;
+	float U;
+    float V;
+} ABFSAMPLE;
+
+typedef void (*fmhcopyfunc)(ABFSAMPLE*,const float*);
+
+typedef void (*fmhdecodefunc)(const ABFSAMPLE*, float*,unsigned int);
+//void bfdcode4(float *inbuf,long numframes);
+void fmhcopy_3(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_4(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf);
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf);
+
+
+// i1 = inphase 1st order, etc
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_pent(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_surr(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes);
+void fm_i1_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_hex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes);

+ 667 - 0
dev/externals/paprogs/pvplay/fmhfuncs.c

@@ -0,0 +1,667 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* fmhfuncs.c : FMH decode funcs */
+#include "fmdcode.h"
+
+/* TODO: expand to handle numframes frames? */
+
+void fmhcopy_3(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf;
+}
+
+void fmhcopy_4(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf;
+}
+
+void fmhcopy_5(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_6(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// following discard 3rd order chans
+void fmhcopy_7(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_8(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+// these identical for 2nd order horiz max, but may be expanded later!
+void fmhcopy_9(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+void fmhcopy_11(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+void fmhcopy_16(ABFSAMPLE* abf,const float*buf)
+{
+    abf->W = *buf++;
+    abf->X = *buf++;
+    abf->Y = *buf++;
+    abf->Z = *buf++;
+    abf->R = *buf++;
+    abf->S = *buf++;
+    abf->T = *buf++;
+    abf->U = *buf++;
+    abf->V = *buf;
+}
+
+/********** DECODE FUNCS *************/
+/* TODO: complete support for numframes > 1 */
+
+void fm_i1_mono(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    unsigned int i;	
+	float *p_out = outbuf;
+	double aw;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		*p_out++ = (float) aw;  		  
+	}
+}
+
+void fm_i1_stereo(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+    unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ay;
+    for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.7071;
+		ay = (double) inbuf->Y * 0.5;		
+		
+		*p_out++ = (float) (aw +  ay);  
+		*p_out++ = (float) (aw  - ay);  
+	}
+}
+
+void fm_i1_square(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out++ = (float) (aw - ax - ay);  //RR
+		*p_out++ = (float) (aw + ax - ay);  //FR
+	}
+}
+void fm_i2_square(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out++ = (float) (aw - ax - ay + av);  //RR
+		*p_out++ = (float) (aw + ax - ay - av);  //FR
+	}
+}
+/* ditto, RLRL layout for WAVEX */
+void fm_i1_quad(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.35355;
+		ax = (double) inbuf->X * 0.17677;
+		ay = (double) inbuf->Y * 0.17677;		
+		
+		*p_out++ = (float) (aw + ax + ay);  //FL
+        *p_out++ = (float) (aw + ax - ay);  //FR
+		*p_out++ = (float) (aw - ax + ay);  //RL
+		*p_out = (float) (aw - ax - ay);  //RR
+		
+	}
+}
+void fm_i2_quad(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;		
+	float *p_out = outbuf;	
+	double aw,ax,ay,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.3536;
+		ax = (double) inbuf->X * 0.2434;
+		ay = (double) inbuf->Y * 0.2434;		
+		av = (double) inbuf->V * 0.0964;
+		*p_out++ = (float) (aw + ax + ay + av);  //FL
+        *p_out++ = (float) (aw + ax - ay - av);  //FR
+		*p_out++ = (float) (aw - ax + ay - av ); //RL
+		*p_out = (float) (aw - ax - ay + av);  //RR
+		
+	}
+}
+
+
+//front pair angle 72deg
+void fm_i1_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax*0.1618) + (ay*0.1176));  //FL
+		*p_out++ = (float) (aw - (ax*0.0618) + (ay*0.1902));  
+		*p_out++ = (float) (aw - (ax*0.2));  
+		*p_out++ = (float) (aw - (ax*0.0618) - (ay*0.1902));  
+        *p_out++ = (float) (aw + (ax*0.1618) - (ay*0.1176)); //FR
+	}
+}
+
+void fm_i2_pent(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2828;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;	
+        au = (double) inbuf->U;
+        av = (double) inbuf->V;
+		
+		*p_out++ = (float) (aw + (ax*0.2227) + (ay*0.1618) + (au*0.0238) + (av * 0.0733));  
+		*p_out++ = (float) (aw - (ax*0.0851) + (ay*0.2619) - (au*0.0624) - (av * 0.0453));  
+		*p_out++ = (float) (aw - (ax*0.2753)               + (au * 0.0771)              );  
+		*p_out++ = (float) (aw - (ax*0.0851) - (ay*0.2619) - (au*0.0624) + (av * 0.0453));  
+        *p_out++ = (float) (aw + (ax*0.2227) - (ay*0.1618) + (au*0.0238) - (av * 0.0733));
+	}
+}
+
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		/* TODO: fix this order! */
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C    ///???
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R    ///????
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+/* 5.1 versions - silent LFE */
+/* FMH only defines 1st order decode */ 
+void fm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) ((aw * 0.169)  + (ax*0.0797) + (ay * 0.0891));   //L
+		*p_out++ = (float) ((aw * 0.1635) + (ax*0.0923));                   //C
+		*p_out++ = (float) ((aw * 0.169)  - (ax*0.0797) - (ay * 0.0891));   //R
+        *p_out++ = 0.0f;                                                    //LFE
+		*p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) + (ay * 0.1543));   //LS
+        *p_out++ = (float) ((aw * 0.4563) - (ax*0.1259) - (ay * 0.1543));   //RS
+	}
+}
+/* from Bruce Wiggins via Csound */
+void fm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.405) + (ax*0.32) + (ay * 0.31)  + (au * 0.085) + (av * 0.125));   //L
+        *p_out++ = (float) ((aw * 0.405) + (ax*0.32) - (ay * 0.31)  + (au * 0.085) - (av * 0.125));   //R
+		*p_out++ = (float) ((aw * 0.085) + (ax*0.04)                + (au * 0.045)               );   //C
+        *p_out++ = 0.0f;                                                                              //LFE
+		*p_out++ = (float) ((aw * 0.635) - (ax*0.335) + (ay * 0.28) - (au * 0.08)  + (av * 0.08));    //LS
+        *p_out++ = (float) ((aw * 0.635) - (ax*0.335) - (ay * 0.28) - (au * 0.08)  - (av * 0.08));    //RS
+	}
+}
+
+// 1st order 5.0
+void dm_i1_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+//1st order 5.1
+void dm_i1_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+        
+		*p_out++ = (float) ((aw * 0.4597)  + (ax*0.4536) + (ay * 0.3591));   //L
+        *p_out++ = (float)  ((aw * 0.4597)  + (ax*0.4536) - (ay * 0.3591));  //R 
+		*p_out++ = 0.0f;                                                     //C
+		*p_out++ = 0.0f;                                                     //LFE
+		*p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) + (ay * 0.4606));    //LS
+        *p_out++ = (float) ((aw * 0.5662) - (ax*0.3681) - (ay * 0.4606));    //RS
+    }
+}
+// 2nd order 5.0
+void dm_i2_surr(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+// 2nd order 5.1
+void dm_i2_surr6(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		au = (double) inbuf->U;
+        av = (double) inbuf->V;
+        
+		*p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) + (ay * 0.3487) + (au * 0.0828) + (av*0.1489));  //L
+        *p_out++ = (float) ((aw * 0.3314)  + (ax*0.4097) - (ay * 0.3487) + (au * 0.0828) - (av*0.1489));  //R 
+		*p_out++ = (float) ((aw * 0.0804)  + (ax * 0.1327));                                              //C
+		*p_out++ = 0.0f;                                                                                  //LFE
+		*p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) + (ay * 0.4089) - (au * 0.0567));                 //LS
+        *p_out++ = (float) ((aw * 0.6025) - (ax*0.3627) - (ay * 0.4089) - (au * 0.0567));                 //RS
+    }
+}
+
+
+void fm_i1_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1443;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.0833));  //FL
+		*p_out++ = (float) (aw      + (ay * 0.1667));  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.0833));  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.0833));  //RR
+        *p_out++ = (float) (aw      - (ay * 0.1667));  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.0833));  //FR
+	}
+}
+void fm_i2_hex(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.2357;
+		ax = (double) inbuf->X * 0.1987;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U;
+        av = (double) inbuf->V * 0.0556;
+		
+		*p_out++ = (float) (aw + ax + (ay * 0.1147) + (au * 0.0321) + av);  //FL
+		*p_out++ = (float) (aw      + (ay * 0.2294) - (au * 0.0643)     );  //SL
+		*p_out++ = (float) (aw - ax + (ay * 0.1147) + (au * 0.0321) - av);  //RL
+		*p_out++ = (float) (aw - ax - (ay * 0.1147) + (au * 0.0321) + av);  //RR
+        *p_out++ = (float) (aw      - (ay * 0.2294) - (au * 0.0643)     );  //SR
+        *p_out++ = (float) (aw + ax - (ay * 0.1147) + (au * 0.0321) - av);  //FR
+	}
+}
+
+void fm_i1_oct1(const ABFSAMPLE *inbuf,float*outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.1155) + (ay * 0.0478));  
+		*p_out++ = (float) (aw + (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.0478) + (ay * 0.1155));  
+		*p_out++ = (float) (aw - (ax * 0.1155) + (ay * 0.0478));  
+        *p_out++ = (float) (aw - (ax * 0.231)  - (ay * 0.0957));  
+        *p_out++ = (float) (aw - (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++ = (float) (aw + (ax * 0.0478) - (ay * 0.1155));  
+        *p_out++   = (float) (aw + (ax * 0.1155) - (ay * 0.0478));  
+	}
+}
+void fm_i2_oct1(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.03417;
+		av = (double) inbuf->V * 0.03417;
+        
+		*p_out++ = (float) (aw + (ax * 0.15906) + (ay * 0.06588) + au + av);  
+		*p_out++ = (float) (aw + (ax * 0.06588) + (ay * 0.15906) - au + av);  
+		*p_out++ = (float) (aw - (ax * 0.06588) + (ay * 0.15906) - au - av);  
+		*p_out++ = (float) (aw - (ax * 0.15906) + (ay * 0.06588) + au - av);  
+        *p_out++ = (float) (aw - (ax * 0.15906) - (ay * 0.06588) + au + av);  
+        *p_out++ = (float) (aw - (ax * 0.06588) - (ay * 0.15906) - au + av);  
+        *p_out++ = (float) (aw + (ax * 0.06588) - (ay * 0.15906) - au - av);  
+        *p_out++ = (float) (aw + (ax * 0.15906) - (ay * 0.06588) + au - av);  
+	}
+}
+
+void fm_i1_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;		
+		
+		*p_out++ = (float) (aw + (ax * 0.125)                 );  
+		*p_out++ = (float) (aw + (ax * 0.0884) + (ay * 0.0884));  
+		*p_out++ = (float) (aw                 + (ay * 0.125) );  
+		*p_out++ = (float) (aw - (ax * 0.0884) + (ay * 0.0884));  
+        *p_out++ = (float) (aw - (ax * 0.125)                 );  
+        *p_out++ = (float) (aw - (ax * 0.0884) - (ay * 0.0884));  
+        *p_out++ = (float) (aw                 - (ay * 0.125) );  
+        *p_out++ = (float) (aw + (ax * 0.0884) - (ay * 0.0884));  
+	}
+}
+void fm_i2_oct2(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,au,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X;
+		ay = (double) inbuf->Y;
+		au = (double) inbuf->U * 0.0482;
+		av = (double) inbuf->V * 0.0482;
+        
+		*p_out++ = (float) (aw + (ax * 0.1721)                 + au     );  
+		*p_out++ = (float) (aw + (ax * 0.1217) + (ay * 0.1217)      + av);  
+		*p_out++ = (float) (aw                 + (ay * 0.1721) - au     );  
+		*p_out++ = (float) (aw - (ax * 0.1217) + (ay * 0.1217)      - av);  
+        *p_out++ = (float) (aw - (ax * 0.1721)                 + au     );  
+        *p_out++ = (float) (aw - (ax * 0.1217) - (ay * 0.1217)      + av);  
+        *p_out++ = (float) (aw                 - (ay * 0.1721) - au     );  
+        *p_out++ = (float) (aw + (ax * 0.1217) - (ay * 0.1217)      - av);  
+	}
+}
+
+/* csound order; low/high anti-clockwise. 
+FMH page order, 4 low folowed by 4 high , clockwise! */
+void fm_i1_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        
+		*p_out++ = (float) (aw - ax + ay - az);  // RL low
+		*p_out++ = (float) (aw - ax + ay + az);  //    hi
+        
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        *p_out++ = (float) (aw - ax - ay + az);  //   hi
+        
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az);  //    hi
+	}
+}
+void fm_i2_cube(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av); //FL low 
+		*p_out++ = (float) (aw + ax + ay + az + as + at + av); //   hi 
+		
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av); //RL low
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  
+        
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av); // RR low
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  
+        
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw + ax - ay + az + as - at - av);   
+	}
+}
+/* ditto, wavex order */
+/* Front L, front R, Back L, Back R; top Front L, Top Fr R, Top Back L, Top back R */
+void fm_i1_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;
+	double aw,ax,ay,az;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.17677;
+		ax = (double) inbuf->X * 0.07216;
+		ay = (double) inbuf->Y * 0.07216;
+		az = (double) inbuf->Z * 0.07216;
+		
+		*p_out++ = (float) (aw + ax + ay - az);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az);  // RR low
+        
+		*p_out++ = (float) (aw + ax + ay + az);  // FL hi
+        *p_out++ = (float) (aw + ax - ay + az);  // FR hi
+		*p_out++ = (float) (aw - ax + ay + az);  // RL hi
+        *p_out++ = (float) (aw - ax - ay + az);  // RR hi
+	}
+}
+void fm_i2_cubex(const ABFSAMPLE *inbuf,float *outbuf,unsigned int numframes)
+{
+	unsigned int i;	
+	float *p_out = outbuf;	
+	double aw,ax,ay,az,as,at,av;
+	
+	for(i=0;i < numframes; i++){
+		aw = (double) inbuf->W * 0.1768;
+		ax = (double) inbuf->X * 0.114;
+		ay = (double) inbuf->Y * 0.114;
+        az = (double) inbuf->Z * 0.114;
+        as = (double) inbuf->S * 0.0369;
+        at = (double) inbuf->T * 0.0369;
+		av = (double) inbuf->V * 0.0369;
+        
+		*p_out++ = (float) (aw + ax + ay - az - as - at + av);  // FL low
+        *p_out++ = (float) (aw + ax - ay - az - as + at - av);  // FR low
+        *p_out++ = (float) (aw - ax + ay - az + as - at - av);  // RL low
+        *p_out++ = (float) (aw - ax - ay - az + as + at + av);  // RR low
+		
+        *p_out++ = (float) (aw + ax + ay + az + as + at + av);  // FL  hi 
+		*p_out++ = (float) (aw + ax - ay + az + as - at - av);  // FR  hi
+		*p_out++ = (float) (aw - ax + ay + az - as + at - av);  // RL  hi 
+        *p_out++ = (float) (aw - ax - ay + az - as - at + av);  // RR  hi 
+	}
+}

+ 831 - 0
dev/externals/paprogs/pvplay/mxfft.c

@@ -0,0 +1,831 @@
+/*
+ * 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
+ *
+ */
+ 
+/* This program converted from the FORTRAN routines by Singleton in
+ * Section 1.4 of  "Programs for Digital Signal Processing", IEEE Press, 1979.
+ *  Conversion by Trevor Wishart and Keith Henderson, York Univ.
+ */
+
+static char *rcsid = "$Id: mxfft.c%v 3.4 1994/10/31 17:37:28 martin Exp $";
+/*
+ *	$Log: mxfft.c%v $
+ * Revision 3.4  1994/10/31  17:37:28  martin
+ * Starting with rcs
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pvoc.h"
+
+void fft_(),fftmx(),reals_();
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  fft
+ * multivariate complex fourier transform, computed in place
+ * using mixed-radix fast fourier transform algorithm.
+ *-----------------------------------------------------------------------
+ *
+ *	this is the call from C:
+ *		fft_(anal,banal,&one,&N2,&one,&mtwo);
+ *	CHANGED TO:-
+ *		fft_(anal,banal,one,N2,one,mtwo);
+ */
+
+void
+fft_(a, b, nseg, n, nspn, isn)
+float 	*a,	/*  pointer to array 'anal'  */
+	*b;	/*  pointer to array 'banal' */
+
+int	nseg,
+	n,
+	nspn,
+	isn;
+      
+{	int nfac[16];		/*  These are one bigger than needed   */
+				/*  because wish to use Fortran array  */
+				/* index which runs 1 to n, not 0 to n */
+
+	int 	m = 0,
+		nf,
+		k,
+		kt,
+		ntot,
+		j, 
+		jj,
+		maxf, maxp;
+
+/* work space pointers */
+	float	*at, *ck, *bt, *sk;
+	int	*np;
+
+
+/* reduce the pointers to input arrays - by doing this, FFT uses FORTRAN
+   indexing but retains compatibility with C arrays */
+	a--;	b--;
+
+/*	
+ * determine the factors of n
+ */
+	k=nf=abs(n);
+      	if(nf==1) 
+		return;
+
+	nspn=abs(nf*nspn);
+	ntot=abs(nspn*nseg);
+
+	if(isn*ntot == 0){
+		fprintf(stderr,"\nerror - zero in fft parameters %d %d %d %d",
+			nseg, n, nspn, isn);
+		return;
+	}
+	for (m=0; !(k%16); nfac[++m]=4,k/=16);
+	for (j=3,jj=9; jj<=k; j+=2,jj=j*j)
+		for (; !(k%jj); nfac[++m]=j,k/=jj);
+
+      	if (k<=4){
+	      	kt = m;
+	      	nfac[m+1] = k;
+	      	if(k != 1) 
+			m++;
+	}
+	else{
+		if(k%4==0){
+		nfac[++m]=2;
+		k/=4;
+		}
+
+	 	kt = m;
+	       	maxp = max((kt+kt+2),(k-1));
+		for(j=2; j<=k; j=1+((j+1)/2)*2)
+			if(k%j==0){
+				nfac[++m]=j;
+				k/=j;
+			}
+	}
+	if(m <= kt+1) 
+		maxp = m + kt + 1;
+      	if(m+kt > 15) {
+		fprintf(stderr,
+		"\nerror - fft parameter n has more than 15 factors : %d", n);
+	    	return;
+	}
+      	if(kt!=0){
+	      	j = kt;
+		while(j)
+			nfac[++m]=nfac[j--];
+	}
+	maxf = nfac[m-kt];
+      	if(kt > 0) 
+		maxf = max(nfac[kt],maxf);
+
+/*  allocate workspace - assume no errors! */
+	at = (float *) calloc(maxf,sizeof(float));
+	ck = (float *) calloc(maxf,sizeof(float));
+	bt = (float *) calloc(maxf,sizeof(float));
+	sk = (float *) calloc(maxf,sizeof(float));
+	np = (int *) calloc(maxp,sizeof(int));
+
+/* decrement pointers to allow FORTRAN type usage in fftmx */
+	at--;	bt--;	ck--;	sk--;	np--;
+
+/* call fft driver */
+
+	fftmx(a,b,ntot,nf,nspn,isn,m,&kt,at,ck,bt,sk,np,nfac);
+
+/* restore pointers before releasing */
+	at++;	bt++;	ck++;	sk++;	np++;
+
+/* release working storage before returning - assume no problems */
+	(void) free(at);
+	(void) free(sk);
+	(void) free(bt);
+	(void) free(ck);
+	(void) free(np);
+      	return;
+}
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  fftmx
+ * called by subroutine 'fft' to compute mixed-radix fourier transform
+ *-----------------------------------------------------------------------
+ */
+void
+fftmx(a,b,ntot,n,nspan,isn,m,kt,at,ck,bt,sk,np,nfac)
+float	*a,*b,*at,*ck,*bt,*sk;
+int	*np;
+int ntot, n, nspan, isn, m;
+int *kt;
+int nfac[];
+{	
+	int	i,inc,
+		j,jc,jf, jj,
+		k, k1, k2, k3, k4,
+		kk,klim,ks,kspan, kspnn,
+		lim,
+		maxf,mm,
+		nn,nt;
+	double  aa, aj, ajm, ajp, ak, akm, akp,
+		bb, bj, bjm, bjp, bk, bkm, bkp,
+		c1, c2, c3, c72, cd,
+		dr,
+		rad, 
+		sd, s1, s2, s3, s72, s120;
+
+	double	xx;	/****** ADDED APRIL 1991 *********/
+	inc=abs(isn);
+	nt = inc*ntot;
+      	ks = inc*nspan;
+/******************* REPLACED MARCH 29: ***********************
+					rad = atan((double)1.0);
+**************************************************************/
+	rad = 0.785398163397448278900;
+/******************* REPLACED MARCH 29: ***********************
+			      		s72 = rad/0.625;
+			      		c72 = cos(s72);
+				      	s72 = sin(s72);
+**************************************************************/
+	c72 = 0.309016994374947451270;
+	s72 = 0.951056516295153531190;
+/******************* REPLACED MARCH 29: ***********************
+			      		s120 = sqrt((double)0.75);
+**************************************************************/
+      	s120 = 0.866025403784438707600;
+
+/* scale by 1/n for isn > 0 ( reverse transform ) */
+
+      	if (isn < 0){
+	      	s72 = -s72;
+	      	s120 = -s120;
+	      	rad = -rad;}
+	else{	ak = 1.0/(double)n;
+		for(j=1; j<=nt;j += inc){
+	        	a[j] *= ak;
+	        	b[j] *= ak;
+		}
+	}
+	kspan = ks;
+      	nn = nt - inc;
+      	jc = ks/n;
+
+/* sin, cos values are re-initialized each lim steps  */
+
+      	lim = 32;
+      	klim = lim * jc;
+      	i = 0;
+      	jf = 0;
+      	maxf = m - (*kt);
+      	maxf = nfac[maxf];
+      	if((*kt) > 0) 
+		maxf = max(nfac[*kt],maxf);
+
+/*
+ * compute fourier transform
+ */
+
+lbl40:
+	dr = (8.0 * (double)jc)/((double)kspan);
+/*************************** APRIL 1991 POW & POW2 not WORKING.. REPLACE *******
+	  	    cd = 2.0 * (pow2 ( sin((double)0.5 * dr * rad)) );
+*******************************************************************************/
+	xx =  sin((double)0.5 * dr * rad);
+      	cd = 2.0 * xx * xx;
+      	sd = sin(dr * rad);
+      	kk = 1;
+      	if(nfac[++i]!=2) goto lbl110;
+/*
+ * transform for factor of 2 (including rotation factor)
+ */
+      	kspan /= 2;
+      	k1 = kspan + 2;
+lbl50:	do{	do{	k2 = kk + kspan;
+		      	ak = a[k2];
+		      	bk = b[k2];
+		      	a[k2] = (a[kk]) - ak;
+		      	b[k2] = (b[kk]) - bk;
+		      	a[kk] = (a[kk]) + ak;
+		      	b[kk] = (b[kk]) + bk;
+		      	kk = k2 + kspan;
+		} while(kk <= nn);
+	      	kk -= nn;
+	}while(kk <= jc);
+      	if(kk > kspan) goto lbl350;
+lbl60:  c1 = 1.0 - cd;
+      	s1 = sd;
+      	mm = min((k1/2),klim);
+      	goto lbl80;
+lbl70:	ak = c1 - ((cd*c1)+(sd*s1));
+      	s1 = ((sd*c1)-(cd*s1)) + s1;
+      	c1 = ak;
+lbl80:	do{	do{	k2 = kk + kspan;
+		      	ak = a[kk] - a[k2];
+		      	bk = b[kk] - b[k2];
+		      	a[kk] = a[kk] + a[k2];
+		      	b[kk] = b[kk] + b[k2];
+		      	a[k2] = (c1 * ak) - (s1 * bk);
+		      	b[k2] = (s1 * ak) + (c1 * bk);
+		      	kk = k2 + kspan;
+		}while(kk < nt);
+	      	k2 = kk - nt;
+	      	c1 = -c1;
+	      	kk = k1 - k2;
+	}while(kk > k2);
+      	kk += jc;
+      	if(kk <= mm) goto lbl70;
+      	if(kk < k2)  goto lbl90;
+      	k1 += (inc + inc);
+      	kk = ((k1-kspan)/2) + jc;
+      	if(kk <= (jc+jc)) goto lbl60;
+      	goto lbl40;
+lbl90: 	s1 = ((double)((kk-1)/jc)) * dr * rad;
+      	c1 = cos(s1);
+      	s1 = sin(s1);
+      	mm = min( k1/2, mm+klim);
+      	goto lbl80;
+/*
+ * transform for factor of 3 (optional code)
+ */
+
+
+lbl100:	k1 = kk + kspan;
+	k2 = k1 + kspan;
+	ak = a[kk];
+	bk = b[kk];
+      	aj = a[k1] + a[k2];
+      	bj = b[k1] + b[k2];
+      	a[kk] = ak + aj;
+      	b[kk] = bk + bj;
+      	ak += (-0.5 * aj);
+      	bk += (-0.5 * bj);
+      	aj = (a[k1] - a[k2]) * s120;
+      	bj = (b[k1] - b[k2]) * s120;
+      	a[k1] = ak - bj;
+      	b[k1] = bk + aj;
+      	a[k2] = ak + bj;
+      	b[k2] = bk - aj;
+      	kk = k2 + kspan;
+      	if(kk < nn)     goto lbl100;
+      	kk -= nn;
+      	if(kk <= kspan) goto lbl100;
+      	goto lbl290;
+
+/*
+ * transform for factor of 4
+ */
+
+lbl110:	if(nfac[i] != 4) goto lbl230;
+      	kspnn = kspan;
+      	kspan = kspan/4;
+lbl120:	c1 = 1.0;
+      	s1 = 0;
+      	mm = min( kspan, klim);
+      	goto lbl150;
+lbl130:	c2 = c1 - ((cd*c1)+(sd*s1));
+      	s1 = ((sd*c1)-(cd*s1)) + s1;
+/*
+ * the following three statements compensate for truncation
+ * error.  if rounded arithmetic is used, substitute
+ * c1=c2
+ *
+ * c1 = (0.5/(pow2(c2)+pow2(s1))) + 0.5;
+ * s1 = c1*s1;
+ * c1 = c1*c2;
+ */
+      	c1 = c2;
+lbl140:	c2 = (c1 * c1) - (s1 * s1);
+      	s2 = c1 * s1 * 2.0;
+      	c3 = (c2 * c1) - (s2 * s1);
+      	s3 = (c2 * s1) + (s2 * c1);
+lbl150:	k1 = kk + kspan;
+      	k2 = k1 + kspan;
+      	k3 = k2 + kspan;
+      	akp = a[kk] + a[k2];
+      	akm = a[kk] - a[k2];
+      	ajp = a[k1] + a[k3];
+      	ajm = a[k1] - a[k3];
+      	a[kk] = akp + ajp;
+       	ajp = akp - ajp;
+      	bkp = b[kk] + b[k2];
+      	bkm = b[kk] - b[k2];
+      	bjp = b[k1] + b[k3];
+      	bjm = b[k1] - b[k3];
+      	b[kk] = bkp + bjp;
+      	bjp = bkp - bjp;
+      	if(isn < 0) goto lbl180;
+      	akp = akm - bjm;
+      	akm = akm + bjm;
+      	bkp = bkm + ajm;
+      	bkm = bkm - ajm;
+      	if(s1 == 0.0) goto lbl190;
+lbl160:	a[k1] = (akp*c1) - (bkp*s1);
+      	b[k1] = (akp*s1) + (bkp*c1);
+      	a[k2] = (ajp*c2) - (bjp*s2);
+      	b[k2] = (ajp*s2) + (bjp*c2);
+      	a[k3] = (akm*c3) - (bkm*s3);
+      	b[k3] = (akm*s3) + (bkm*c3);
+      	kk = k3 + kspan;
+      	if(kk <= nt)   goto lbl150;
+lbl170: kk -= (nt - jc);
+      	if(kk <= mm)   goto lbl130;
+      	if(kk < kspan) goto lbl200;
+      	kk -= (kspan - inc);
+      	if(kk <= jc)   goto lbl120;
+      	if(kspan==jc)  goto lbl350;
+      	goto lbl40;
+lbl180:	akp = akm + bjm;
+      	akm = akm - bjm;
+      	bkp = bkm - ajm;
+      	bkm = bkm + ajm;
+      	if(s1 != 0.0)  goto lbl160;
+lbl190:	a[k1] = akp;
+      	b[k1] = bkp;
+      	a[k2] = ajp;
+      	b[k2] = bjp;
+      	a[k3] = akm;
+      	b[k3] = bkm;
+      	kk = k3 + kspan;
+      	if(kk <= nt) goto lbl150;
+      	goto lbl170;
+lbl200: s1 = ((double)((kk-1)/jc)) * dr * rad;
+      	c1 = cos(s1);
+      	s1 = sin(s1);
+      	mm = min( kspan, mm+klim);
+      	goto lbl140;
+
+/*
+ * transform for factor of 5 (optional code)
+ */
+
+lbl210:	c2 = (c72*c72) - (s72*s72);
+      	s2 = 2.0 * c72 * s72;
+lbl220:	k1 = kk + kspan;
+      	k2 = k1 + kspan;
+      	k3 = k2 + kspan;
+      	k4 = k3 + kspan;
+      	akp = a[k1] + a[k4];
+      	akm = a[k1] - a[k4];
+      	bkp = b[k1] + b[k4];
+      	bkm = b[k1] - b[k4];
+      	ajp = a[k2] + a[k3];
+      	ajm = a[k2] - a[k3];
+      	bjp = b[k2] + b[k3];
+      	bjm = b[k2] - b[k3];
+      	aa = a[kk];
+      	bb = b[kk];
+      	a[kk] = aa + akp + ajp;
+      	b[kk] = bb + bkp + bjp;
+      	ak = (akp*c72) + (ajp*c2) + aa;
+      	bk = (bkp*c72) + (bjp*c2) + bb;
+      	aj = (akm*s72) + (ajm*s2);
+      	bj = (bkm*s72) + (bjm*s2);
+      	a[k1] = ak - bj;
+      	a[k4] = ak + bj;
+      	b[k1] = bk + aj;
+      	b[k4] = bk - aj;
+      	ak = (akp*c2) + (ajp*c72) + aa;
+      	bk = (bkp*c2) + (bjp*c72) + bb;
+      	aj = (akm*s2) - (ajm*s72);
+	bj = (bkm*s2) - (bjm*s72);
+	a[k2] = ak - bj;
+      	a[k3] = ak + bj;
+      	b[k2] = bk + aj;
+      	b[k3] = bk - aj;
+      	kk = k4 + kspan;
+      	if(kk < nn)     goto lbl220;
+      	kk -= nn;
+      	if(kk <= kspan) goto lbl220;
+      	goto lbl290;
+
+/*
+ * transform for odd factors
+ */
+
+lbl230:	k = nfac[i];
+	kspnn = kspan;
+	kspan /= k;
+	if(k==3)   goto lbl100;
+	if(k==5)   goto lbl210;
+	if(k==jf)  goto lbl250;
+      	jf = k;
+      	s1 = rad/(((double)(k))/8.0);
+      	c1 = cos(s1);
+      	s1 = sin(s1);
+      	ck[jf] = 1.0;
+	sk[jf] = 0.0;
+	for(j=1; j<k ; j++){
+		ck[j] = (ck[k])*c1 + (sk[k])*s1;
+	      	sk[j] = (ck[k])*s1 - (sk[k])*c1;
+	      	k--;
+	      	ck[k] = ck[j];
+	      	sk[k] = -(sk[j]);
+	}
+lbl250:	k1 = kk;
+      	k2 = kk + kspnn;
+	aa = a[kk];
+	bb = b[kk];
+      	ak = aa;
+      	bk = bb;
+      	j = 1;
+      	k1 += kspan;
+lbl260:	do{	k2 -= kspan;
+	      	j++;
+	      	at[j] = a[k1] + a[k2];
+	      	ak = at[j] + ak;	
+	      	bt[j] = b[k1] + b[k2];
+	      	bk = bt[j] + bk;	
+	      	j++;
+	      	at[j] = a[k1] - a[k2];
+	      	bt[j] = b[k1] - b[k2];
+	      	k1 += kspan;
+	}while(k1 < k2);
+      	a[kk] = ak;
+      	b[kk] = bk;
+      	k1 = kk;
+      	k2 = kk + kspnn;
+      	j = 1;
+lbl270:	k1 += kspan;
+      	k2 -= kspan;
+      	jj = j;
+      	ak = aa;
+      	bk = bb;
+      	aj = 0.0;
+      	bj = 0.0;
+      	k = 1;
+lbl280:	do{	k++;
+	      	ak = (at[k] * ck[jj]) + ak;
+	      	bk = (bt[k] * ck[jj]) + bk;	
+	      	k++;
+	      	aj = (at[k] * sk[jj]) + aj;
+	      	bj = (bt[k] * sk[jj]) + bj;
+	      	jj += j;
+	      	if (jj > jf) 
+			jj -= jf;
+	}while(k < jf);
+      	k = jf - j;
+      	a[k1] = ak - bj;
+      	b[k1] = bk + aj;
+      	a[k2] = ak + bj;
+      	b[k2] = bk - aj;
+      	j++;
+      	if(j < k)     goto lbl270;
+      	kk += kspnn;
+      	if(kk <= nn)  goto lbl250;
+      	kk -= nn;
+      	if(kk<=kspan) goto lbl250;
+
+/*
+ * multiply by rotation factor (except for factors of 2 and 4)
+ */
+
+lbl290:	if(i==m) goto lbl350;
+      	kk = jc + 1;
+lbl300:	c2 = 1.0 - cd;
+      	s1 = sd;
+      	mm = min( kspan, klim);
+      	goto lbl320;
+lbl310:	c2 = c1 - ((cd*c1) + (sd*s1));
+      	s1 = s1 + ((sd*c1) - (cd*s1));
+lbl320:	c1 = c2;
+      	s2 = s1;
+      	kk += kspan;
+lbl330:	ak = a[kk];
+      	a[kk] = (c2*ak) - (s2 * b[kk]);
+      	b[kk] = (s2*ak) + (c2 * b[kk]);
+      	kk += kspnn;
+      	if(kk <= nt) goto lbl330;
+      	ak = s1*s2;
+      	s2 = (s1*c2) + (c1*s2);
+      	c2 = (c1*c2) - ak;
+      	kk -= (nt - kspan);
+     	if(kk <= kspnn) goto lbl330;
+      	kk -= (kspnn - jc);
+      	if(kk <= mm)   goto lbl310;
+      	if(kk < kspan) goto lbl340;
+      	kk -= (kspan - jc - inc);
+      	if(kk <= (jc+jc)) goto lbl300;
+      	goto lbl40;
+lbl340:	s1 = ((double)((kk-1)/jc)) * dr * rad;
+      	c2 = cos(s1);
+      	s1 = sin(s1);
+     	mm = min( kspan, mm+klim);
+      	goto lbl320;
+
+/*
+ * permute the results to normal order---done in two stages
+ * permutation for square factors of n
+ */
+
+lbl350:	np[1] = ks;
+      	if (!(*kt)) goto lbl440;
+      	k = *kt + *kt + 1;
+      	if(m < k) 
+		k--;
+	np[k+1] = jc;
+lbl360:	for(j=1; j < k; j++,k--){
+		np[j+1] = np[j] / nfac[j];
+	      	np[k] = np[k+1] * nfac[j];
+	}
+      	k3 = np[k+1];
+      	kspan = np[2];
+      	kk = jc + 1;
+      	k2 = kspan + 1;
+      	j = 1;
+      	if(n != ntot) goto lbl400;
+/*
+ * permutation for single-variate transform (optional code)
+ */
+lbl370:	do{	ak = a[kk];
+	      	a[kk] = a[k2];
+	      	a[k2] = ak;
+	      	bk = b[kk];
+	      	b[kk] = b[k2];
+	      	b[k2] = bk;
+	      	kk += inc;
+	      	k2 += kspan;
+	}while(k2 < ks);
+lbl380:	do{	k2 -= np[j++];
+	      	k2 += np[j+1];
+	}while(k2 > np[j]);
+      	j = 1;
+lbl390:	if(kk < k2){
+		goto lbl370;
+	}
+      	kk += inc;
+      	k2 += kspan;
+      	if(k2 < ks) goto lbl390;
+      	if(kk < ks) goto lbl380;
+      	jc = k3;
+      	goto lbl440;
+/*
+ * permutation for multivariate transform
+ */
+lbl400:	do{	do{	k = kk + jc;
+lbl410:			do{	ak = a[kk];
+			      	a[kk] = a[k2];
+			      	a[k2] = ak;
+			      	bk = b[kk];
+			      	b[kk] = b[k2];
+			      	b[k2] = bk;
+			      	kk += inc;
+			      	k2 += inc;
+			}while(kk < k);
+		      	kk += (ks - jc);
+		      	k2 += (ks - jc);
+		}while(kk < nt);
+	      	k2 -= (nt - kspan);
+	      	kk -= (nt - jc);
+	}while(k2 < ks);
+lbl420:	do{	k2 -= np[j++];
+	      	k2 += np[j+1];
+	}while(k2 > np[j]);
+      	j = 1;
+lbl430:	if(kk < k2) 	 goto lbl400;
+      	kk += jc;
+      	k2 += kspan;
+      	if(k2 < ks)      goto lbl430;
+      	if(kk < ks)      goto lbl420;
+      	jc = k3;
+lbl440:	if((2*(*kt))+1 >= m)
+		return;
+
+      	kspnn = *(np + *(kt) + 1);
+      	j = m - *kt;		
+      	nfac[j+1] = 1;
+lbl450:	nfac[j] = nfac[j] * nfac[j+1];
+      	j--;
+      	if(j != *kt) goto lbl450;
+      	*kt = *(kt) + 1;
+      	nn = nfac[*kt] - 1;
+      	jj = 0;
+      	j = 0;
+      	goto lbl480;
+lbl460:	jj -= k2;
+      	k2 = kk;
+      	kk = nfac[++k];
+lbl470:	jj += kk;
+      	if(jj >= k2) goto lbl460;
+      	np[j] = jj;
+lbl480:	k2 = nfac[*kt];
+      	k = *kt + 1;	
+      	kk = nfac[k];
+      	j++;
+      	if(j <= nn) goto lbl470;
+/* Determine permutation cycles of length greater than 1 */
+      	j = 0;
+      	goto lbl500;
+lbl490:	k = kk;
+     	kk = np[k];	
+      	np[k] = -kk;	
+      	if(kk != j) goto lbl490;
+      	k3 = kk;
+lbl500:	kk = np[++j];	
+      	if(kk < 0)  goto lbl500;
+      	if(kk != j) goto lbl490;
+      	np[j] = -j;
+      	if(j != nn) goto lbl500;
+      	maxf *= inc;
+/* Perform reordering following permutation cycles */
+      	goto lbl570;
+lbl510:	j--;
+      	if (np[j] < 0) goto lbl510;
+      	jj = jc;
+lbl520:	kspan = jj;
+      	if(jj > maxf) 
+		kspan = maxf;
+      	jj -= kspan;
+      	k = np[j];	
+      	kk = (jc*k) + i + jj;
+      	k1 = kk + kspan;
+      	k2 = 0;
+lbl530:	k2++;
+      	at[k2] = a[k1];
+      	bt[k2] = b[k1];
+      	k1 -= inc;
+      	if(k1 != kk) goto lbl530;
+lbl540:	k1 = kk + kspan;
+      	k2 = k1 - (jc * (k + np[k]));
+      	k = -(np[k]);
+lbl550:	a[k1] = a[k2];
+      	b[k1] = b[k2];
+      	k1 -= inc;
+      	k2 -= inc;
+      	if(k1 != kk) goto lbl550;
+      	kk = k2;
+      	if(k != j)   goto lbl540;
+      	k1 = kk + kspan;
+      	k2 = 0;
+lbl560:	k2++;
+      	a[k1] = at[k2];
+      	b[k1] = bt[k2];
+      	k1 -= inc;
+      	if(k1 != kk) goto lbl560;
+      	if(jj)       goto lbl520;
+      	if(j  != 1)  goto lbl510;
+lbl570:	j = k3 + 1;
+      	nt -= kspnn;
+      	i = nt - inc + 1;
+      	if(nt >= 0)  goto lbl510;
+      	return; 
+}
+
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  reals
+ * used with 'fft' to compute fourier transform or inverse for real data
+ *-----------------------------------------------------------------------
+ *	this is the call from C:
+ *		reals_(anal,banal,N2,mtwo);
+ *	which has been changed from CARL call
+ *		reals_(anal,banal,&N2,&mtwo);
+ */
+
+void
+reals_(a, b, n, isn)
+
+float 	*a,	/* a refers to an array of floats 'anal'   */
+	*b;	/* b refers to an array of floats 'banal'  */
+int	n,
+	isn;
+
+/* See IEEE book for a long comment here on usage */
+
+{	int	inc,
+		j,
+		k,
+		lim,
+		mm,ml,
+		nf,nk,nh;
+ 
+	double	aa,ab,
+		ba,bb,
+		cd,cn,
+		dr,
+		em,
+		rad,re,
+		sd,sn;
+	double	xx;	/******* ADDED APRIL 1991 ******/
+/* adjust  input array pointers (called from C) */
+	a--;	b--;
+	inc=abs(isn);
+	nf=abs(n);
+      	if(nf*isn==0){
+		fprintf(stderr,"\nerror - zero in reals parameters : %d : %d ",n,isn);
+	       	return;
+	}
+lbl10: 	nk = (nf*inc) + 2;
+      	nh = nk/2;
+/*****************************
+    	rad  = atan((double)1.0);
+******************************/
+	rad = 0.785398163397448278900;
+      	dr = -4.0/(double)(nf);
+/********************************** POW2 REMOVED APRIL 1991 *****************
+			      	cd = 2.0 * (pow2(sin((double)0.5 * dr * rad)));
+*****************************************************************************/
+	xx = sin((double)0.5 * dr * rad);
+      	cd = 2.0 * xx * xx;
+      	sd = sin(dr * rad);
+/*
+ * sin,cos values are re-initialized each lim steps
+ */
+      	lim = 32;
+      	mm = lim;
+      	ml = 0;
+      	sn = 0.0;
+	if(isn<0){
+		cn = 1.0;
+		a[nk-1] = a[1];
+		b[nk-1] = b[1]; }
+	else {
+		cn = -1.0;
+		sd = -sd;
+	}
+lbl20: 	for(j=1;j<=nh;j+=inc)	{
+        	k = nk - j;
+        	aa = a[j] + a[k];
+        	ab = a[j] - a[k];
+        	ba = b[j] + b[k];
+        	bb = b[j] - b[k];
+        	re = (cn*ba) + (sn*ab);
+        	em = (sn*ba) - (cn*ab);
+        	b[k] = (em-bb)*0.5;
+        	b[j] = (em+bb)*0.5;
+        	a[k] = (aa-re)*0.5;
+		a[j] = (aa+re)*0.5;
+        	ml++;
+		if(ml!=mm){
+			aa = cn - ((cd*cn)+(sd*sn));
+			sn = ((sd*cn) - (cd*sn)) + sn;
+			cn = aa;}
+		else {
+			mm +=lim;
+			sn = ((float)ml) * dr * rad;
+			cn = cos(sn);
+			if(isn>0)
+				cn = -cn;
+			sn = sin(sn);
+		}
+	}
+	return;
+}
+
+

+ 43 - 0
dev/externals/paprogs/pvplay/pvdefs.h

@@ -0,0 +1,43 @@
+/* pvdefs.h */
+#ifndef __PVDEFS_H_INCLUDED
+#define __PVDEFS_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define VERY_TINY_VAL (1e-20)
+
+#define ODD(x)	  			((x)&1)
+#define EVEN(x)	  			(!ODD(x))
+#define CHAN_SRCHRANGE_F	(4)
+
+
+#ifndef max
+#define max(a, b)  (((a) > (b)) ? (a) : (b)) 
+#endif
+#ifndef min
+#define min(a, b)  (((a) < (b)) ? (a) : (b)) 
+#endif
+
+
+/* for future reference: IEEE_DOUBLE not implemented yet for PVOCEX */
+enum pvoc_wordformat { PVOC_IEEE_FLOAT, PVOC_IEEE_DOUBLE};
+typedef enum pvoc_mode {  PVPP_NOT_SET,PVPP_OFFLINE,PVPP_STREAMING} pvocmode;
+/* the old CARL pvoc flags */
+typedef enum pvoc_wfac {PVOC_O_W0,PVOC_O_W1,PVOC_O_W2,PVOC_O_W3,PVOC_O_DEFAULT} pvoc_overlapfac;
+typedef enum pvoc_scaletype {PVOC_S_TIME,PVOC_S_PITCH,PVOC_S_NONE} pv_scaletype;
+
+typedef enum pvoc_frametype { PVOC_AMP_FREQ,PVOC_AMP_PHASE,PVOC_COMPLEX } pv_frametype;
+typedef enum pvoc_windowtype {PVOC_DEFAULT,
+								PVOC_HAMMING,
+								PVOC_HANN,
+								PVOC_KAISER,
+								PVOC_RECT,
+								PVOC_CUSTOM} pv_wtype;
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 1300 - 0
dev/externals/paprogs/pvplay/pvfileio.c

@@ -0,0 +1,1300 @@
+/* pvfileio.c */
+/* pvocex format test routines*/
+/* Initial version RWD May 2000.
+ * All rights reserved: work in progress!
+ * Manifestly not a complete API yet!
+ * In particular, error returns are kept very simplistic at the moment.
+ * (and are not even very consistent yet...)
+ * In time, a full set of error values and messages will be developed.
+ *
+ * NB: the RIFF<WAVE> functions only look for, and accept, a PVOCEX format file.
+ * NB also: if windows.h is included anywhere (should be no need in this file,
+ *          or in pvfileio.h),
+ *          the WAVE_FORMAT~ symbols may need to be #ifndef-ed.
+ */
+
+/*   very simple CUSTOM window chunk:
+ *
+ *  <PVXW><size><data>
+ *
+ *  where size as usual gives the size of the data in bytes.
+ *  the size in samples much match dwWinlen (which may not be the same as N (fft length)
+ *  the sample type must be the same as that of the pvoc data itself
+ *  (only floatsams supported so far)
+ *  values must be normalized to peak of 1.0
+ */
+//#ifdef WINDOWS
+//#include "stdafx.h"
+//#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <io.h>
+#endif
+#ifdef unix
+#include <unistd.h>
+#define O_BINARY (0)
+#define _S_IWRITE S_IWRITE
+#define _S_IREAD  S_IREAD
+#endif
+
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include "pvdefs.h"
+#include "pvfileio.h"
+
+/* NB probably no good on 64bit platforms */
+#define REVDWBYTES(t)   ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
+#define REVWBYTES(t)    ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
+#define TAG(a,b,c,d)    ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
+#define WAVE_FORMAT_EXTENSIBLE (0xFFFE)
+#ifndef WAVE_FORMAT_PCM
+#define WAVE_FORMAT_PCM (0x0001)
+#endif
+#define WAVE_FORMAT_IEEE_FLOAT (0x0003)
+
+
+const GUID KSDATAFORMAT_SUBTYPE_PVOC = {
+                    0x8312b9c2,
+                    0x2e6e,
+                    0x11d4,
+                    { 0xa8, 0x24, 0xde, 0x5b, 0x96, 0xc3, 0xab, 0x21 }
+};
+
+
+static  char *pv_errstr = "";
+
+#define MAXFILES (16)
+/* or any desired larger number: will be dynamically allocated one day */
+
+
+typedef struct pvoc_file {
+    WAVEFORMATEX fmtdata;
+    PVOCDATA pvdata;
+    long datachunkoffset;
+    long nFrames;   /* no of frames in file */
+    long FramePos;  /* where we are in file */
+    long curpos;
+    int fd;
+    int to_delete;
+    int readonly;
+    int do_byte_reverse;
+    char *name;
+    float *customWindow;
+} PVOCFILE;
+
+static PVOCFILE *files[MAXFILES];
+
+static int pvoc_writeheader(int ofd);
+static int pvoc_readheader(int ifd,WAVEFORMATPVOCEX *pWfpx);
+
+
+static int write_guid(int fd,int byterev,const GUID *pGuid)
+{
+    long written;
+#ifdef _DEBUG
+    assert(fd >= 0);
+    assert(pGuid);
+#endif
+
+    if(byterev){
+        GUID guid;
+        guid.Data1 = REVDWBYTES(pGuid->Data1);
+        guid.Data2 = REVWBYTES(pGuid->Data2);
+        guid.Data3 = REVWBYTES(pGuid->Data3);
+        memcpy((char *) (guid.Data4),(char *) (pGuid->Data4),8);
+        written = write(fd,(char *) &guid,sizeof(GUID));
+    }
+    else
+        written = write(fd,(char *) pGuid,sizeof(GUID));
+
+    return written;
+
+}
+
+static int compare_guids(const GUID *gleft, const GUID *gright)
+{
+    const char *left = (const char *) gleft, *right = (const char *) gright;
+    return !memcmp(left,right,sizeof(GUID));
+}
+
+
+static int write_pvocdata(int fd,int byterev,const PVOCDATA *pData)
+{
+    long written;
+    long dwval;
+#ifdef _DEBUG
+    assert(fd >= 0);
+    assert(pData);
+#endif
+
+
+    if(byterev){
+        PVOCDATA data;
+        data.wWordFormat = REVWBYTES(pData->wWordFormat);
+        data.wAnalFormat = REVWBYTES(pData->wAnalFormat);
+        data.wSourceFormat = REVWBYTES(pData->wSourceFormat);
+        data.wWindowType = REVWBYTES(pData->wWindowType);
+        data.nAnalysisBins = REVDWBYTES(pData->nAnalysisBins);
+        data.dwWinlen   = REVDWBYTES(pData->dwWinlen);
+        data.dwOverlap   = REVDWBYTES(pData->dwOverlap);
+        data.dwFrameAlign = REVDWBYTES(pData->dwFrameAlign);
+        
+        dwval = * (long *) &(pData->fAnalysisRate);
+        dwval = REVDWBYTES(dwval);
+        data.fAnalysisRate = * (float *) &dwval;
+
+        dwval = * (long *) &(pData->fWindowParam);
+        dwval = REVDWBYTES(dwval);
+        data.fWindowParam = * (float *) &dwval;
+
+        written = write(fd,(char *) &data,sizeof(PVOCDATA));
+    }
+    else
+        written = write(fd,(char *) pData,sizeof(PVOCDATA));
+
+    return written;
+
+}
+
+static int write_fmt(int fd, int byterev,const WAVEFORMATEX *pfmt)
+{   
+    /*NB have to write out each element, as not guaranteed alignment othewise.
+     *  Consider it documentation. */
+
+#ifdef _DEBUG
+    assert(fd >=0);
+    assert(pfmt);
+#endif
+
+    if(byterev){
+        WAVEFORMATEX fmt;
+        fmt.wFormatTag      = REVWBYTES(pfmt->wFormatTag);
+        fmt.nChannels       = REVWBYTES(pfmt->nChannels);
+        fmt.nSamplesPerSec  = REVDWBYTES(pfmt->nSamplesPerSec);
+        fmt.nAvgBytesPerSec = REVDWBYTES(pfmt->nAvgBytesPerSec);
+        fmt.nBlockAlign     = REVWBYTES(pfmt->nBlockAlign);
+        fmt.wBitsPerSample  = REVWBYTES(pfmt->wBitsPerSample);
+        fmt.cbSize          = REVWBYTES(pfmt->cbSize);
+
+        if(write(fd,(char *) &(fmt.wFormatTag),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(fmt.nChannels),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(fmt.nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+            || write(fd,(char *) &(fmt.nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+            || write(fd,(char *) &(fmt.nBlockAlign),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(fmt.wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(fmt.cbSize),sizeof(WORD)) != sizeof(WORD))
+
+        return 0;
+
+    }
+    else {
+       if(write(fd,(char *) &(pfmt->wFormatTag),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(pfmt->nChannels),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(pfmt->nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+            || write(fd,(char *) &(pfmt->nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+            || write(fd,(char *) &(pfmt->nBlockAlign),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(pfmt->wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
+            || write(fd,(char *) &(pfmt->cbSize),sizeof(WORD)) != sizeof(WORD))
+
+        return 0;
+    }
+
+    return SIZEOF_WFMTEX;
+}
+
+static int pvoc_writeWindow(int fd,int byterev,float *window,DWORD length)
+{
+    if(byterev){
+        /* don't corrupt source array! */
+        DWORD i;
+        long lval, *lp = (long *) window;
+
+        for(i=0;i < length; i++){
+            lval = *lp++;
+            lval = REVDWBYTES(lval);
+            if(write(fd,(char *)&lval,sizeof(long)) != sizeof(long))
+                return 0;
+        }
+    }
+    else{
+        if(write(fd,(char *) window,length * sizeof(float)) != (int)(length*sizeof(float)))
+            return 0;
+    }
+
+
+    return length * sizeof(float);
+}
+
+static int pvoc_readWindow(int fd, int byterev, float *window,DWORD length)
+{
+    if(byterev){
+        DWORD i;
+        long got,oval,lval, *lp = (long *) window;
+#ifdef SINGLE_FLOAT 
+        for(i=0;i < length;i++){
+            if(read(fd,(char *)&lval,sizeof(long)) != sizeof(long))
+                return 0;
+            oval = REVDWBYTES(lval);
+            *lp++ = oval;
+        }
+#else
+        /* read whole block then swap...should be faster */
+        got = read(fd,(char *) window,length * sizeof(float));
+        if(got != (int)(length * sizeof(float)))
+            return 0;
+        /* then byterev */
+        for(i=0;i < length;i++){
+            lval = *lp;
+            oval = REVDWBYTES(lval);
+            *lp++ = oval;
+        }
+
+#endif
+    }
+    else{
+        if(read(fd,(char *) window,length * sizeof(float)) != (int)(length * sizeof(float)))
+            return 0;
+    }
+
+    return length * sizeof(float);
+
+}
+
+
+
+const char *pvoc_errorstr()
+{
+    return (const char *) pv_errstr;
+}
+
+
+
+/* thanks to the SNDAN programmers for this! */
+/* return 0 for big-endian machine, 1 for little-endian machine*/
+/* probably no good for 16bit swapping though */
+static int byte_order()
+{
+  int   one = 1;
+  char* endptr = (char *) &one;
+  return (*endptr);
+}
+
+/***** loosely modelled on CDP sfsys ******
+ *  This is a static array, but coul be made dynamic in an OOP sort of way.
+ *  The idea is that all low-level operations and data 
+ *  are completely hidden from the user, so that internal format changes can be made
+ * with little or no disruption to the public functions.
+ * But avoiding the full monty of a C++ implementation.
+
+ *******************************************/
+
+int init_pvsys(void)
+{
+    int i;
+
+    if(files[0] != NULL) {
+        pv_errstr = "\npvsys: already initialized";
+        return 0;
+    }
+    for(i = 0;i < MAXFILES;i++)
+        files[i] = NULL;
+
+    return 1;
+}
+
+static void prepare_pvfmt(WAVEFORMATEX *pfmt,unsigned long chans, unsigned long srate, 
+                          pv_stype stype)
+{
+
+#ifdef _DEBUG
+    assert(pfmt);
+#endif
+
+
+    pfmt->wFormatTag        = WAVE_FORMAT_EXTENSIBLE;
+    pfmt->nChannels         = (WORD) chans;
+    pfmt->nSamplesPerSec    = srate;
+    switch(stype){
+    case(STYPE_16):
+        pfmt->wBitsPerSample = (WORD)16;
+        pfmt->nBlockAlign    = (WORD)(chans * 2 *  sizeof(char));
+        
+        break;
+    case(STYPE_24):
+        pfmt->wBitsPerSample = (WORD) 24;
+        pfmt->nBlockAlign    = (WORD)(chans *  3 * sizeof(char));
+        break;
+    case(STYPE_32):
+    case(STYPE_IEEE_FLOAT):
+        pfmt->wBitsPerSample = (WORD) 32;
+        pfmt->nBlockAlign    = (WORD)(chans *  4 * sizeof(char));
+        break;
+    default:
+#ifdef _DEBUG
+        assert(0);
+#endif
+        break;
+    }
+
+    pfmt->nAvgBytesPerSec   = pfmt->nBlockAlign * srate;
+    /* if we have extended WindowParam fields, or something ,will need to adjust this */
+    pfmt->cbSize            = 62;
+
+}
+
+
+/* lots of different ways of doing this! */
+/* we will need  one in the form:
+ * in pvoc_fmtcreate(const char *fname,PVOCDATA *p_pvfmt, WAVEFORMATEX *p_wvfmt);
+ */
+
+/* a simple minimalist function to begin with!*/
+/*set D to 0, and/or dwWinlen to 0, to use internal default */
+/* fWindow points to userdef window, or is NULL */
+/* NB currently this does not enforce a soundfile extension; probably it should... */
+int  pvoc_createfile(const char *filename, 
+                     DWORD fftlen,DWORD overlap,DWORD chans,
+                     DWORD format,long srate, 
+                     pv_stype stype,pv_wtype wtype,
+                     float wparam,float *fWindow,DWORD dwWinlen)
+{
+    
+    int i;
+    long N,D;
+    char *pname;
+    PVOCFILE *pfile = NULL;
+    float winparam = 0.0f;
+
+    N = fftlen;                   /* keep the CARL varnames for now */
+    D = overlap;
+
+    if(N == 0 || chans <=0 || filename==NULL || D > N) {
+        pv_errstr = "\npvsys: bad arguments";
+        return -1;
+    }
+    if(/*format < PVOC_AMP_FREQ ||*/ format > PVOC_COMPLEX) {    /* RWD unsigned, so nothing < 0 possible */
+        pv_errstr = "\npvsys: bad format parameter";
+        return -1;
+    }
+
+    if(!(wtype >= PVOC_DEFAULT && wtype <= PVOC_CUSTOM)){
+        pv_errstr = "\npvsys: bad window type";
+        return -1;
+    }
+
+    /* load it, but ca't write until we have a PVXW chunk definition...*/
+    if(wtype==PVOC_CUSTOM){
+
+    }
+
+    if(wtype==PVOC_DEFAULT)
+        wtype = PVOC_HAMMING;
+
+    if(wtype==PVOC_KAISER)
+        if(wparam != 0.0f)
+            winparam = wparam;
+    /*will need an internal default for window parameters...*/
+
+    for(i=0;i < MAXFILES;i++)
+        if(files[i]==NULL)
+            break;
+    if(i==MAXFILES) {
+        pv_errstr = "\npvsys: too many files open";
+        return -1;
+    }
+    pfile = (PVOCFILE *) malloc(sizeof(PVOCFILE));
+
+    if(pfile==NULL){
+        pv_errstr = "\npvsys: no memory";
+        return -1;
+    }
+    pname = (char *) malloc(strlen(filename)+1);
+    if(pname == NULL){
+        free(pfile);
+        pv_errstr = "\npvsys: no memory";
+        return -1;
+    }
+    pfile->customWindow = NULL;
+    /* setup rendering inforamtion */
+    prepare_pvfmt(&pfile->fmtdata,chans,srate,stype);
+
+    strcpy(pname,filename);
+
+    pfile->pvdata.wWordFormat = PVOC_IEEE_FLOAT;
+    pfile->pvdata.wAnalFormat = (WORD) format;
+    pfile->pvdata.wSourceFormat =  (stype == STYPE_IEEE_FLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
+    pfile->pvdata.wWindowType = wtype;
+    pfile->pvdata.nAnalysisBins = (N>>1) + 1;
+    if(dwWinlen==0)
+        pfile->pvdata.dwWinlen      = N;
+    else
+        pfile->pvdata.dwWinlen  = dwWinlen;
+    if(D==0)
+        pfile->pvdata.dwOverlap = N/8;
+    else
+        pfile->pvdata.dwOverlap = D;
+    pfile->pvdata.dwFrameAlign = pfile->pvdata.nAnalysisBins * 2 * sizeof(float);
+    pfile->pvdata.fAnalysisRate = (float)srate / (float) pfile->pvdata.dwOverlap;
+    pfile->pvdata.fWindowParam = winparam;
+    pfile->to_delete = 0;
+    pfile->readonly = 0;
+    if(fWindow!= NULL){
+        pfile->customWindow = malloc(dwWinlen * sizeof(float));
+        if(pfile->customWindow==NULL){
+            pv_errstr = "\npvsys: no memory for custom window";
+            return -1;
+        }
+        memcpy((char *)(pfile->customWindow),(char *)fWindow,dwWinlen * sizeof(float));
+    }
+
+
+    pfile->fd = open(filename,O_BINARY | O_CREAT | O_RDWR | O_TRUNC,_S_IWRITE | _S_IREAD);
+    if(pfile->fd < 0){
+        free(pname);        
+        if(pfile->customWindow)
+            free(pfile->customWindow);
+        free(pfile);
+
+        pv_errstr = "\npvsys: unable to create file";
+        return -1;
+    }
+
+    pfile->datachunkoffset = 0;
+    pfile->nFrames = 0;
+    pfile->FramePos = 0;
+    pfile->curpos = 0;
+    pfile->name = pname;
+    pfile->do_byte_reverse = !byte_order(); 
+    files[i] = pfile;
+
+    if(!pvoc_writeheader(i)) {
+        close(pfile->fd);
+        remove(pfile->name);
+        free(pfile->name);
+        if(pfile->customWindow)
+            free(pfile->customWindow);
+        free(pfile);
+        files[i] = NULL;
+        return -1;
+    }
+
+    return i;
+}
+
+int pvoc_openfile(const char *filename,PVOCDATA *data,WAVEFORMATEX *fmt)
+{
+    int i;
+    WAVEFORMATPVOCEX wfpx;
+    char *pname;
+    PVOCFILE *pfile = NULL;
+//  long size = sizeof(WAVEFORMATPVOCEX);
+    
+    if(data==NULL || fmt==NULL){
+        pv_errstr = "\npvsys: Internal error: NULL data arrays";
+        return -1;
+    }
+
+    for(i=0;i < MAXFILES;i++)
+        if(files[i]==NULL)
+            break;
+    if(i==MAXFILES){
+        pv_errstr = "\npvsys: too many files open";
+        return -1;
+    }
+
+    pfile = (PVOCFILE *) malloc(sizeof(PVOCFILE));
+    if(pfile==NULL){
+        pv_errstr = "\npvsys: no memory for file data";
+        return -1;
+    }
+    pfile->customWindow = NULL;
+    pname = (char *) malloc(strlen(filename)+1);
+    if(pname == NULL){
+        free(pfile);
+        pv_errstr = "\npvsys: no memory";
+        return -1;
+    }
+    pfile->fd = open(filename,O_BINARY | O_RDONLY,_S_IREAD);
+    if(pfile->fd < 0){
+        free(pname);
+        free(pfile);
+        pv_errstr = "\npvsys: unable to create file";
+        return -1;
+    }
+    strcpy(pname,filename);
+    pfile->datachunkoffset = 0;
+    pfile->nFrames = 0;
+    pfile->curpos = 0;
+    pfile->FramePos = 0;
+    pfile->name = pname;
+    pfile->do_byte_reverse = !byte_order(); 
+    pfile->readonly = 1;
+    files[i] = pfile;
+
+    if(!pvoc_readheader(i,&wfpx)){
+        close(pfile->fd);
+        free(pfile->name);
+        if(pfile->customWindow)
+            free(pfile->customWindow);
+        free(pfile);
+        files[i] = NULL;
+        return -1;
+    }
+
+    memcpy((char *)data, (char *)&(wfpx.data),sizeof(PVOCDATA));
+    memcpy((char *)fmt,(char *)&(wfpx.wxFormat.Format),SIZEOF_WFMTEX);
+
+    files[i] = pfile;
+
+    return i;
+
+}
+/*RWD TODO: add byterev stuff*/
+static int pvoc_readfmt(int fd,int byterev,WAVEFORMATPVOCEX *pWfpx)
+{
+    unsigned long dword;
+    unsigned short word;
+
+#ifdef _DEBUG
+    assert(fd >= 0);
+    assert(pWfpx);
+#endif
+
+    if(read(fd,(char *) &(pWfpx->wxFormat.Format.wFormatTag),sizeof(WORD)) != sizeof(WORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.nChannels),sizeof(WORD)) != sizeof(WORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.nSamplesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.nAvgBytesPerSec),sizeof(DWORD)) != sizeof(DWORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.nBlockAlign),sizeof(WORD)) != sizeof(WORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.wBitsPerSample),sizeof(WORD)) != sizeof(WORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.Format.cbSize),sizeof(WORD)) != sizeof(WORD)){
+        pv_errstr = "\npvsys: error reading Source format data";
+        return 0;
+    }
+
+    if(byterev){
+        word = pWfpx->wxFormat.Format.wFormatTag;
+        pWfpx->wxFormat.Format.wFormatTag= REVWBYTES(word);
+        word = pWfpx->wxFormat.Format.nChannels;
+        pWfpx->wxFormat.Format.nChannels = REVWBYTES(word);
+        dword = pWfpx->wxFormat.Format.nSamplesPerSec;
+        pWfpx->wxFormat.Format.nSamplesPerSec = REVDWBYTES(dword);
+        dword = pWfpx->wxFormat.Format.nAvgBytesPerSec;
+        pWfpx->wxFormat.Format.nAvgBytesPerSec = REVDWBYTES(dword);
+        word = pWfpx->wxFormat.Format.nBlockAlign;
+        pWfpx->wxFormat.Format.nBlockAlign = REVWBYTES(word);
+        word = pWfpx->wxFormat.Format.wBitsPerSample;
+        pWfpx->wxFormat.Format.wBitsPerSample = REVWBYTES(word);
+        word = pWfpx->wxFormat.Format.cbSize;
+        pWfpx->wxFormat.Format.cbSize = REVWBYTES(word);
+
+    }
+
+    /* the first clues this is pvx format...*/
+    if(pWfpx->wxFormat.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE){
+        pv_errstr = "\npvsys: not a WAVE_EX file";
+        return 0;
+    }
+
+    if(pWfpx->wxFormat.Format.cbSize != 62){
+        pv_errstr = "\npvsys: bad size for fmt chunk";
+        return 0;
+    }
+
+    if(read(fd,(char *) &(pWfpx->wxFormat.Samples.wValidBitsPerSample),sizeof(WORD)) != sizeof(WORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.dwChannelMask),sizeof(DWORD)) != sizeof(DWORD)
+        || read(fd,(char *) &(pWfpx->wxFormat.SubFormat),sizeof(GUID)) != sizeof(GUID)){
+        pv_errstr = "\npvsys: error reading Extended format data";
+        return 0;
+    }
+
+    if(byterev){
+        word = pWfpx->wxFormat.Samples.wValidBitsPerSample;
+        pWfpx->wxFormat.Samples.wValidBitsPerSample = REVWBYTES(word);
+        dword = pWfpx->wxFormat.dwChannelMask;
+        pWfpx->wxFormat.dwChannelMask = REVDWBYTES(dword);
+
+        dword = pWfpx->wxFormat.SubFormat.Data1;
+        pWfpx->wxFormat.SubFormat.Data1 = REVDWBYTES(dword);
+        word = pWfpx->wxFormat.SubFormat.Data2;
+        pWfpx->wxFormat.SubFormat.Data2 = REVWBYTES(word);
+        word = pWfpx->wxFormat.SubFormat.Data3;
+        pWfpx->wxFormat.SubFormat.Data3 = REVWBYTES(word);
+        /* don't need to reverse the char array */
+    }
+
+    /* ... but this is the clincher */
+    if(!compare_guids(&(pWfpx->wxFormat.SubFormat),&KSDATAFORMAT_SUBTYPE_PVOC)){
+        pv_errstr = "\npvsys: not a PVOCEX file";
+        return 0;
+    }
+
+    if(read(fd,(char *) &(pWfpx->dwVersion),sizeof(DWORD)) != sizeof(DWORD)
+        || read(fd,(char *) &(pWfpx->dwDataSize),sizeof(DWORD)) != sizeof(DWORD)
+        || read(fd,(char *) &(pWfpx->data),sizeof(PVOCDATA)) != sizeof(PVOCDATA)){
+        pv_errstr = "\npvsys: error reading Extended pvoc format data";
+        return 0;
+    }
+
+    if(byterev){
+        dword = pWfpx->dwVersion;
+        pWfpx->dwVersion = REVDWBYTES(dword);
+
+        /* check it now! */
+        if(pWfpx->dwVersion != PVX_VERSION){
+            pv_errstr = "\npvsys: unknown pvocex Version";
+            return 0;
+        }
+
+        dword = pWfpx->dwDataSize;
+        pWfpx->dwDataSize = REVDWBYTES(dword);
+
+        word = pWfpx->data.wWordFormat;
+        pWfpx->data.wWordFormat= REVWBYTES(word);
+        word = pWfpx->data.wAnalFormat;
+        pWfpx->data.wAnalFormat= REVWBYTES(word);
+        word = pWfpx->data.wSourceFormat;
+        pWfpx->data.wSourceFormat= REVWBYTES(word);
+        word = pWfpx->data.wWindowType;
+        pWfpx->data.wWindowType= REVWBYTES(word);
+
+        dword = pWfpx->data.nAnalysisBins;
+        pWfpx->data.nAnalysisBins = REVDWBYTES(dword);
+        dword = pWfpx->data.dwWinlen;
+        pWfpx->data.dwWinlen = REVDWBYTES(dword);
+        dword = pWfpx->data.dwOverlap;
+        pWfpx->data.dwOverlap = REVDWBYTES(dword);
+        dword = pWfpx->data.dwFrameAlign;
+        pWfpx->data.dwFrameAlign = REVDWBYTES(dword);
+
+        dword = * (DWORD *)&(pWfpx->data.fAnalysisRate);
+        dword = REVDWBYTES(dword);
+        pWfpx->data.fAnalysisRate = *(float *)&dword;
+        dword = * (DWORD *)&(pWfpx->data.fWindowParam);
+        dword = REVDWBYTES(dword);
+        pWfpx->data.fWindowParam = *(float *)&dword;
+
+    }
+    if(pWfpx->dwVersion != PVX_VERSION){
+        pv_errstr = "\npvsys: unknown pvocex Version";
+        return 0;
+    }
+
+    return 1;
+
+
+}
+
+
+static int pvoc_readheader(int ifd,WAVEFORMATPVOCEX *pWfpx)
+{
+    DWORD tag, size,riffsize;
+    int fmtseen = 0, /* dataseen = 0,*/ windowseen = 0;
+//  DWORD windowlength = 0;
+
+#ifdef _DEBUG
+    assert(pWfpx);
+    assert(files[ifd]);
+    assert(files[ifd]->fd >= 0);
+    size = sizeof(WAVEFORMATEXTENSIBLE);
+    size += 2 * sizeof(DWORD);
+    size += sizeof(PVOCDATA);
+#endif
+
+    if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)
+        || read(files[ifd]->fd,(char *) &size,sizeof(DWORD)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error reading header";
+        return 0;
+    }
+    if(files[ifd]->do_byte_reverse)
+        size = REVDWBYTES(size);
+    else
+        tag = REVDWBYTES(tag);
+
+    if(tag != TAG('R','I','F','F')){
+        pv_errstr = "\npvsys: not a RIFF file";
+        return 0;
+    }
+    if(size < 24 * sizeof(DWORD) + SIZEOF_FMTPVOCEX){
+        pv_errstr = "\npvsys: file too small";
+        return 0;
+    }
+    riffsize = size;
+    if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error reading header";
+        return 0;
+    }
+
+    if(!files[ifd]->do_byte_reverse)
+        tag = REVDWBYTES(tag);
+
+    if(tag != TAG('W','A','V','E')){
+        pv_errstr = "\npvsys: not a WAVE file";
+        return 0;
+    }
+    riffsize -= sizeof(DWORD);
+    /*loop for chunks */
+    while(riffsize > 0){
+        if(read(files[ifd]->fd,(char *) &tag,sizeof(DWORD)) != sizeof(DWORD)
+            || read(files[ifd]->fd,(char *) &size,sizeof(DWORD)) != sizeof(DWORD)){
+            pv_errstr = "\npvsys: error reading header";
+            return 0;
+        }
+        if(files[ifd]->do_byte_reverse)
+            size = REVDWBYTES(size);
+        else
+            tag = REVDWBYTES(tag);
+        riffsize -= 2 * sizeof(DWORD);
+        switch(tag){
+        case TAG('f','m','t',' '):
+            /* bail out if not a pvoc file: not trying to read all WAVE formats!*/
+            if(size < SIZEOF_FMTPVOCEX){
+                pv_errstr = "\npvsys:   not a PVOC-EX file";
+                return 0;
+            }
+            if(!pvoc_readfmt(files[ifd]->fd,files[ifd]->do_byte_reverse,pWfpx)){
+                pv_errstr = "\npvsys: error reading format chunk";
+                return 0;
+            }
+            riffsize -=  SIZEOF_FMTPVOCEX;
+            fmtseen = 1;
+            memcpy((char *)&(files[ifd]->fmtdata),(char *)&(pWfpx->wxFormat),SIZEOF_WFMTEX);
+            memcpy((char *)&(files[ifd]->pvdata),(char *)&(pWfpx->data),sizeof(PVOCDATA));
+            break;
+        case TAG('P','V','X','W'):
+            if(!fmtseen){
+                pv_errstr = "\npvsys: PVXW chunk found before fmt chunk.";
+                return 0;
+            }
+            if(files[ifd]->pvdata.wWindowType!=PVOC_CUSTOM){
+                /*whaddayado? can you warn the user and continue?*/
+                pv_errstr = "\npvsys: PVXW chunk found but custom window not specified";
+                return 0;
+            }
+            files[ifd]->customWindow = malloc(files[ifd]->pvdata.dwWinlen * sizeof(float));
+            if(files[ifd]->customWindow == NULL){
+                pv_errstr = "\npvsys: no memory for custom window data.";
+                return 0;
+            }
+            if(pvoc_readWindow(files[ifd]->fd,files[ifd]->do_byte_reverse,
+                files[ifd]->customWindow,files[ifd]->pvdata.dwWinlen)
+                != (int)(files[ifd]->pvdata.dwWinlen * sizeof(float))){
+                pv_errstr = "\npvsys: error reading window data.";
+                return 0;
+            }
+            windowseen = 1;
+            break;
+        case TAG('d','a','t','a'):
+            if(riffsize - size  != 0){
+                pv_errstr = "\npvsys: bad RIFF file";
+                return 0;
+            }
+            
+            if(!fmtseen){
+                pv_errstr = "\npvsys: bad format, data chunk before fmt chunk";
+                return 0;
+            }
+
+            if(files[ifd]->pvdata.wWindowType==PVOC_CUSTOM)
+                if(!windowseen){
+                    pv_errstr = "\npvsys: custom window chunk PVXW not found";
+                    return 0;
+                }
+
+            files[ifd]->datachunkoffset = lseek(files[ifd]->fd,0,SEEK_CUR);
+            files[ifd]->curpos = files[ifd]->datachunkoffset;
+            /* not m/c frames, for now */
+            files[ifd]->nFrames = size / files[ifd]->pvdata.dwFrameAlign;
+
+            return 1;
+            break;
+        default:
+            /* skip any onknown chunks */
+            riffsize -= 2 * sizeof(DWORD);
+            if(lseek(files[ifd]->fd,size,SEEK_CUR) < 0){
+                pv_errstr = "\npvsys: error skipping unknown WAVE chunk";
+                return 0;
+            }
+            riffsize -= size;
+            break;
+        }
+
+    }
+    /* if here, something very wrong!*/
+    pv_errstr = "\npvsys: bad format in RIFF file";
+    return 0;
+
+}
+
+static int pvoc_writeheader(int ofd)
+{
+    long tag,size,version;
+    WORD validbits;
+    
+    const GUID *pGuid =  &KSDATAFORMAT_SUBTYPE_PVOC;
+
+#ifdef _DEBUG
+    assert(files[ofd] != NULL);
+    assert(files[ofd]->fd >=0);
+#endif
+
+
+    tag = TAG('R','I','F','F');
+    size = 0;
+    if(files[ofd]->do_byte_reverse)
+        size = REVDWBYTES(size);
+    if(!files[ofd]->do_byte_reverse)
+        tag = REVDWBYTES(tag);
+
+    if(write(files[ofd]->fd,&tag,sizeof(long)) != sizeof(long)
+        || write(files[ofd]->fd,&size,sizeof(long)) != sizeof(long)) {
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+
+    tag = TAG('W','A','V','E');
+    if(!files[ofd]->do_byte_reverse)
+        tag = REVDWBYTES(tag);
+    if(write(files[ofd]->fd,&tag,sizeof(long)) != sizeof(long)){
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+
+    tag = TAG('f','m','t',' ');
+    size =  SIZEOF_WFMTEX + sizeof(WORD) + 
+            sizeof(DWORD) 
+            + sizeof(GUID) 
+            + 2*sizeof(DWORD)
+            + sizeof(PVOCDATA);
+    if(files[ofd]->do_byte_reverse)
+        size = REVDWBYTES(size);
+    if(!files[ofd]->do_byte_reverse)
+        tag = REVDWBYTES(tag);
+    if(write(files[ofd]->fd,(char *)&tag,sizeof(long)) != sizeof(long)
+        || write(files[ofd]->fd,(char *)&size,sizeof(long)) != sizeof(long)) {
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+    
+    if(write_fmt(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->fmtdata)) != SIZEOF_WFMTEX){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+
+    validbits = files[ofd]->fmtdata.wBitsPerSample;  /*nothing fancy here */
+    if(files[ofd]->do_byte_reverse)
+        validbits = REVWBYTES(validbits);
+
+    if(write(files[ofd]->fd,(char *) &validbits,sizeof(WORD)) != sizeof(WORD)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+    /* we will take this from a WAVE_EX file, in due course */
+    size = 0;   /*dwChannelMask*/
+    if(write(files[ofd]->fd,(char *)&size,sizeof(long)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+
+    if(write_guid(files[ofd]->fd,files[ofd]->do_byte_reverse,pGuid) != sizeof(GUID)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+    version  = 1;
+    size = sizeof(PVOCDATA);
+    if(files[ofd]->do_byte_reverse){
+        version = REVDWBYTES(version);
+        size = REVDWBYTES(size);
+    }
+
+    if(write(files[ofd]->fd,&version,sizeof(long)) != sizeof(long)
+        || write(files[ofd]->fd,&size,sizeof(long)) != sizeof(long)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+
+
+    if(write_pvocdata(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->pvdata)) != sizeof(PVOCDATA)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+
+    }
+
+    /* VERY experimental; may not even be a good idea...*/
+
+    if(files[ofd]->customWindow){
+        tag = TAG('P','V','X','W');
+        size = files[ofd]->pvdata.dwWinlen * sizeof(float);
+        if(files[ofd]->do_byte_reverse)
+            size = REVDWBYTES(size);
+        else
+            tag = REVDWBYTES(tag);
+        if(write(files[ofd]->fd,(char *)&tag,sizeof(long)) != sizeof(long)
+            || write(files[ofd]->fd,(char *)&size,sizeof(long)) != sizeof(long)) {
+            pv_errstr = "\npvsys: error writing header";
+            return 0;
+        }
+        if(pvoc_writeWindow(files[ofd]->fd,
+                            files[ofd]->do_byte_reverse,
+                            files[ofd]->customWindow,
+                            files[ofd]->pvdata.dwWinlen)!= (int)(files[ofd]->pvdata.dwWinlen * sizeof(float))){
+            pv_errstr = "\npvsys: error writing window data.";
+            return 0;
+        }
+    }
+
+    /* no other chunks to write yet! */
+    tag = TAG('d','a','t','a');
+    if(!files[ofd]->do_byte_reverse)
+        tag = REVDWBYTES(tag);
+    if(write(files[ofd]->fd,&tag,sizeof(long)) != sizeof(long)){
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+
+    /* we need to update size later on...*/
+
+    size = 0;
+    if(write(files[ofd]->fd,&size,sizeof(long)) != sizeof(long)){
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+    files[ofd]->datachunkoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
+
+    files[ofd]->curpos = files[ofd]->datachunkoffset;
+    return 1;
+}
+
+
+static int pvoc_updateheader(int ofd)
+{
+    long riffsize,datasize;
+    unsigned long pos;
+
+#ifdef _DEBUG   
+    assert(files[ofd]);
+    assert(files[ofd]->fd >= 0);
+    assert(files[ofd]->curpos == lseek(files[ofd]->fd,0,SEEK_CUR));
+#endif
+
+    datasize = files[ofd]->curpos - files[ofd]->datachunkoffset;
+    pos = lseek(files[ofd]->fd,files[ofd]->datachunkoffset-sizeof(DWORD),SEEK_SET);
+    if(pos != files[ofd]->datachunkoffset-sizeof(DWORD)){
+        pv_errstr = "\npvsys: error updating data chunk";
+        return 0;
+    }
+
+    if(files[ofd]->do_byte_reverse)
+        datasize = REVDWBYTES(datasize);
+    if(write(files[ofd]->fd,(char *) &datasize,sizeof(DWORD)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error updating data chunk";
+        return 0;
+    }
+
+
+    riffsize = files[ofd]->curpos - 2* sizeof(DWORD);
+    if(files[ofd]->do_byte_reverse)
+        riffsize = REVDWBYTES(riffsize);
+    pos = lseek(files[ofd]->fd,sizeof(DWORD),SEEK_SET);
+    if(pos != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error updating data chunk";
+        return 0;
+    }
+    if(write(files[ofd]->fd,(char *) &riffsize,sizeof(DWORD)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: error updating riff chunk";
+        return 0;
+    }
+
+    pos = lseek(files[ofd]->fd,0,SEEK_END);
+    if(pos < 0){
+        pv_errstr = "\npvsys: error seeking to end of file";
+        return 0;
+    }
+    return 1;
+}
+
+
+
+
+int pvoc_closefile(int ofd)
+{
+    if(files[ofd]==NULL){
+        pv_errstr = "\npvsys: file does not exist";
+        return 0;
+    }
+
+    if(files[ofd]->fd < 0){
+        pv_errstr = "\npvsys: file not open";
+        return 0;
+    }
+    if(!files[ofd]->readonly)
+        if(!pvoc_updateheader(ofd))
+            return 0;
+    
+    close(files[ofd]->fd);
+    if(files[ofd]->to_delete && !(files[ofd]->readonly))
+        remove(files[ofd]->name);
+
+    free(files[ofd]->name);
+    free(files[ofd]);
+    files[ofd] = NULL;
+
+    return 1;
+}
+
+/* does not directly address m/c streams, or alternative numeric formats, yet
+ * so for m/c files, write each frame in turn, for each channel.
+ * The format requires multi-channel frames to be interleaved in the usual way:
+ * if nChannels= 4, the file will contain:
+ * frame[0][0],frame[0][1],frame[0][2],frame[0][3],frme[1][0],frame[1][1].....
+ *
+ * The idea is to offer e.g. a floats version and a longs version ONLY, but
+ * independently of the underlying representation, so that the user can write a floats
+ * block, even though the underlying format might be longs or doubles. Most importantly,
+ * the user does not have to deal with byte-reversal, which would otherwise always be the case
+ * it the user had direct access to the file.
+ * 
+ * So these functions are the most likely to change over time!.
+ *
+ * return 0 for error, 1 for success. This could change....
+
+
+ */
+int pvoc_putframes(int ofd,const float *frame,long numframes)
+{
+    DWORD i;
+    DWORD towrite;  /* count in 'words' */
+    long temp,*lfp;
+    
+
+    if(files[ofd]==NULL){
+        pv_errstr = "\npvsys: bad file descriptor";
+        return 0;
+    }
+    if(files[ofd]->fd < 0){
+        pv_errstr = "\npvsys: file not open";
+        return 0;
+    }
+    /* NB doubles not supported yet */
+    
+    towrite = files[ofd]->pvdata.nAnalysisBins * 2 * numframes;
+    
+    if(files[ofd]->do_byte_reverse){
+        /* do this without overwriting source data! */
+        lfp = (long *) frame;
+        for(i=0;i < towrite; i++){
+            temp = *lfp++;
+            temp = REVDWBYTES(temp);
+            if(write(files[ofd]->fd,(char *) &temp,sizeof(long)) != sizeof(long)){
+                pv_errstr = "\npvsys: error writing data";
+                return 0;
+            }
+
+        }
+    }
+    else {
+        if(write(files[ofd]->fd,(char *) frame,towrite * sizeof(float)) != (int)(towrite * sizeof(float))){
+            pv_errstr = "\npvsys: error writing data";
+            return 0;
+        }
+    }
+
+    files[ofd]->FramePos += numframes;
+    files[ofd]->curpos += towrite * sizeof(float);
+    return 1;
+}
+
+/* Simplistic read function
+ * best practice here is to read nChannels frames *
+ * return -1 for error, 0 for EOF, else numframes read
+ */
+int pvoc_getframes(int ifd,float *frames,unsigned long nframes)
+{
+    long i;
+    long toread;
+    long oval,temp,*lfp;
+    long got;
+    int rc = -1;
+    if(files[ifd]==NULL){
+        pv_errstr = "\npvsys: bad file descriptor";
+        return rc;
+    }
+    if(files[ifd]->fd < 0){
+        pv_errstr = "\npvsys: file not open";
+        return rc;
+    }
+
+    toread = files[ifd]->pvdata.nAnalysisBins * 2 * nframes;
+
+    if(files[ifd]->do_byte_reverse){
+        lfp = (long *) frames;
+#ifdef SINGLE_FLOAT
+        for(i=0;i < toread;i++){
+            if((got=read(files[ifd]->fd,(char *) &temp,sizeof(long))) <0){
+                pv_errstr = "\npvsys: error reading data";
+                return rc;
+            }
+            if(got < sizeof(long)){
+                /* file size incorrect? */
+                return 0;           /* assume EOF */
+            }
+            temp = REVDWBYTES(temp);
+            *lfp++ = temp;
+        }
+#else
+        /* much faster on G4!!! */
+        got = read(files[ifd]->fd,(char *)frames,toread * sizeof(float));
+        if(got < 0){
+            pv_errstr = "\npvsys: error reading data";
+            return rc;
+        }
+        for(i=0;i < got / sizeof(float);i++){
+            temp = *lfp;
+            oval = REVDWBYTES(temp);
+            *lfp++ = oval;
+        }
+        if(got < (int)(toread * sizeof(float))){
+            /* some (user?) error in file size: return integral number of frames read*/
+            toread  = got / (files[ifd]->pvdata.nAnalysisBins * 2 * sizeof(float));
+            /* RWD 4:2002 need to revise this too */
+            nframes = toread;
+        }
+            
+#endif
+        rc = nframes;   /*RWD 4:2002 */
+    }
+    else{
+        if((got = read(files[ifd]->fd,(char *)frames,toread * sizeof(float))) < (int)(toread * sizeof(float))){
+            if(got < 0){
+                pv_errstr = "\npvsys: error reading data";
+                return rc;
+            }
+            else if(got < (int)(toread * sizeof(float))){
+                /* some (user?) error in file size: return integral number of frames read*/
+                toread  = got / (files[ifd]->pvdata.nAnalysisBins * 2 * sizeof(float));
+                rc = toread;
+                /* RWD 4:2002 need to revise this too */
+                nframes = toread;
+            }
+        }
+        else
+            rc = nframes;
+    }
+    /*files[ifd]->curpos += (toread * sizeof(float));*/
+    files[ifd]->curpos += got;  /* RWD 4:2002 */
+    files[ifd]->FramePos += nframes;
+
+    return rc;
+}
+
+int pvoc_rewind(int ifd,int skip_first_frame)
+{
+    int rc = -1;
+    int fd;
+    long pos;
+    long skipsize = 0;
+    long skipframes = 0;
+
+    if(files[ifd]==NULL){
+        pv_errstr = "\npvsys: bad file descriptor";
+        return rc;
+    }
+    if(files[ifd]->fd < 0){
+        pv_errstr = "\npvsys: file not open";
+        return rc;
+    }
+    skipsize =  files[ifd]->pvdata.dwFrameAlign * files[ifd]->fmtdata.nChannels;
+    skipframes = files[ifd]->fmtdata.nChannels;
+    
+    fd = files[ifd]->fd;
+    pos = files[ifd]->datachunkoffset;
+    if(skip_first_frame){
+        skipsize =  files[ifd]->pvdata.dwFrameAlign * skipframes;
+        pos += skipsize;
+    }
+    if(lseek(fd,pos,SEEK_SET) != pos ) {
+        pv_errstr = "\npvsys: error rewinding file";
+        return rc;
+    }
+    files[ifd]->curpos = files[ifd]->datachunkoffset + skipsize;
+    files[ifd]->FramePos = skipframes;
+
+    return 0;
+
+}
+
+/* may be more to do in here later on */
+int pvsys_release(void)
+{
+    int i;
+    
+
+    for(i=0;i < MAXFILES;i++){
+        if(files[i]) {
+#ifdef _DEBUG
+            fprintf(stderr,"\nDEBUG WARNING: files still open!\n");
+#endif
+            if(!pvoc_closefile(i)){             
+                pv_errstr = "\npvsys: unable to close file on termination";
+                return 0;
+            }
+        }
+    }
+    return 1;
+}
+
+/*return raw framecount:  channel-agnostic for now */
+int pvoc_framecount(int ifd)
+{
+    if(files[ifd]==NULL)
+        return -1;
+
+    return files[ifd]->nFrames;
+}
+/* RWD Jan 2014   */
+int pvoc_framepos(int ifd)
+{
+    if(files[ifd]==NULL)
+        return -1;
+        
+    return files[ifd]->FramePos;
+}
+
+int pvoc_seek_mcframe(int ifd, long offset, int mode)
+{
+    unsigned long mcframealign;
+    unsigned long newpos;
+    int rc = -1;
+    if(files[ifd]==NULL)
+        return -1;
+    mcframealign =  files[ifd]->pvdata.dwFrameAlign * files[ifd]->fmtdata.nChannels;
+    switch(mode){
+    case SEEK_SET:
+        newpos = mcframealign * offset;
+        if(offset >= files[ifd]->nFrames / files[ifd]->fmtdata.nChannels){
+            pv_errstr = "\npvsys: seek target beyond end of file";
+            break;
+        } 
+        newpos += files[ifd]->datachunkoffset;
+        if(lseek(files[ifd]->fd,newpos,SEEK_SET) != newpos ) {
+            pv_errstr = "\npvsys: seek error";
+            return -1;
+        }
+        files[ifd]->curpos = newpos;
+        files[ifd]->FramePos = offset * files[ifd]->fmtdata.nChannels;
+        rc = 0;
+        break;
+    case SEEK_END:
+    case SEEK_CUR:
+        pv_errstr = "\npvsys: seek mode not supported yet!";
+        break;
+    }
+    return rc;
+}

+ 131 - 0
dev/externals/paprogs/pvplay/pvfileio.h

@@ -0,0 +1,131 @@
+/*pvfileio.h: header file for PVOC_EX file format */
+/* Initial Version 0.1 RWD 25:5:2000 all rights reserved: work in progress! */
+/* NB a special version of this file is used in Csound - do not auto-replace! */ 
+#ifndef __PVFILEIO_H_INCLUDED
+#define __PVFILEIO_H_INCLUDED
+
+//#ifndef WORD
+//#define WORD unsigned short
+//#endif
+//#ifndef DWORD
+//#define DWORD unsigned long
+//#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef _WINDOWS
+#include <windows.h>
+#endif
+
+#ifndef _WINDOWS
+#ifndef WORD
+#define WORD unsigned short
+#endif
+#ifndef DWORD
+#define DWORD unsigned long
+#endif
+
+typedef struct _GUID 
+{ 
+    DWORD			Data1; 
+    WORD			Data2; 
+    WORD			Data3; 
+    unsigned char	Data4[8]; 
+} GUID;
+
+
+typedef struct /*waveformatex */{ 
+    WORD  wFormatTag; 
+    WORD  nChannels; 
+    DWORD nSamplesPerSec; 
+    DWORD nAvgBytesPerSec; 
+    WORD  nBlockAlign; 
+    WORD  wBitsPerSample; 
+    WORD  cbSize; 
+} WAVEFORMATEX; 
+#endif
+
+
+#include "pvdefs.h"
+
+
+/* Renderer information: source is presumed to be of this type */
+typedef enum pvoc_sampletype {STYPE_16,STYPE_24,STYPE_32,STYPE_IEEE_FLOAT} pv_stype;
+
+
+
+typedef struct pvoc_data {				/* 32 bytes*/
+	WORD	wWordFormat;				/* pvoc_wordformat */
+	WORD	wAnalFormat;				/* pvoc_frametype */
+	WORD	wSourceFormat;				/* WAVE_FORMAT_PCM or WAVE_FORMAT_IEEE_FLOAT*/
+	WORD	wWindowType;				/* pvoc_windowtype */
+	DWORD	nAnalysisBins;				/* implicit FFT size = (nAnalysisBins-1) * 2*/
+	DWORD	dwWinlen;					/* analysis winlen,samples, NB may be <> FFT size */
+	DWORD	dwOverlap;					/* samples */
+	DWORD	dwFrameAlign;				/* usually nAnalysisBins * 2 * sizeof(float) */
+	float	fAnalysisRate;
+	float	fWindowParam;				/* default 0.0f unless needed */
+} PVOCDATA;
+
+
+typedef struct {
+    WAVEFORMATEX    Format;				 /* 18 bytes:  info for renderer as well as for pvoc*/
+    union {								 /* 2 bytes*/
+        WORD wValidBitsPerSample;        /*  as per standard WAVE_EX: applies to renderer*/
+        WORD wSamplesPerBlock;          
+        WORD wReserved;                  
+                                        
+    } Samples;
+    DWORD           dwChannelMask;      /*  4 bytes : can be used as in standrad WAVE_EX */
+                                        
+    GUID            SubFormat;			/* 16 bytes*/
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
+
+
+typedef struct {
+	WAVEFORMATEXTENSIBLE wxFormat;		 /* 40 bytes*/
+	DWORD dwVersion;					 /* 4 bytes*/
+	DWORD dwDataSize;					 /* 4 bytes: sizeof PVOCDATA data block */ 
+	PVOCDATA data;						 /* 32 bytes*/
+} WAVEFORMATPVOCEX;						 /* total 80 bytes*/
+
+
+/* at least VC++ will give 84 for sizeof(WAVEFORMATPVOCEX), so we need our own version*/
+#define SIZEOF_FMTPVOCEX (80)
+/* for the same reason:*/
+#define SIZEOF_WFMTEX (18)
+#define PVX_VERSION		(1)
+/******* the all-important PVOC GUID 
+
+ {8312B9C2-2E6E-11d4-A824-DE5B96C3AB21}
+
+**************/
+
+extern 	const GUID KSDATAFORMAT_SUBTYPE_PVOC;
+
+
+/* pvoc file handling functions */
+
+const char *pvoc_errorstr();
+int init_pvsys(void);
+int  pvoc_createfile(const char *filename, 
+					 unsigned long fftlen,unsigned long overlap, unsigned long chans,
+					 unsigned long format,long srate, 
+					 pv_stype stype,pv_wtype wtype,float wparam,float *fWindow,DWORD dwWinlen);
+int pvoc_openfile(const char *filename,PVOCDATA *data,WAVEFORMATEX *fmt);	
+int pvoc_closefile(int ofd);
+int pvoc_putframes(int ofd,const float *frame,long numframes);
+int pvoc_getframes(int ifd,float *frames,unsigned long nframes);
+int pvoc_framecount(int ifd);
+int pvoc_rewind(int ifd,int skip_first_frame);		/* RWD 14:4:2001 */
+int pvoc_framepos(int ifd);							/* RWD Jan 2014 */
+int pvoc_seek_mcframe(int ifd, long offset, int mode);
+int pvsys_release(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 43 - 0
dev/externals/paprogs/pvplay/pvoc.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1983-2020  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
+ *
+ */
+ 
+/* Functions in PVOC.C */
+
+void 	logo(void);
+void	warpse(float*,float*,int,double);
+void	usage(void);
+void	hamming(float*,int,int);
+float	*float_array(int);
+void	malerr(char*,int);
+void	kaiser_(int*,float*,int*,int*,float*);
+
+
+/* Functions in MXFFT.C */
+#ifndef USE_FFTW
+void fft_(float *, float *,int,int,int,int);
+void fftmx(float *,float *,int,int,int,int,int,int *,float *,float *,float *,float *,int *,int[]);
+void reals_(float *,float *,int,int);
+#endif
+
+#ifndef min
+#define min(a,b)	(((a)<(b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b)	(((a)>(b)) ? (a) : (b))
+#endif

+ 1541 - 0
dev/externals/paprogs/pvplay/pvoc2.cpp

@@ -0,0 +1,1541 @@
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+
+
+/*pvoc.cpp*/
+/*  simple class wrapper for (modified) CARL pvoc; supports realtime streaming. (c) Richard Dobson 2000,2014 */
+
+/* includes correction to sinc function code from Dan Timis, June 2000 */
+/* RWD 12:2000: define PVOC_NORM_PHASE to use slower normalized phase calcs */
+/* NB: optional FFTW code is for v 2.1.5 */
+
+#include <math.h>
+#include <stdlib.h>
+#include <memory.h>
+extern "C" {
+#ifdef USE_FFTW
+# include <rfftw.h>
+# else
+void fft_(float *, float *,int,int,int,int);
+void fftmx(float *,float *,int,int,int,int,int,int *,float *,float *,float *,float *,int *,int[]);
+void reals_(float *,float *,int,int);
+#endif
+}
+#include "pvpp.h"
+
+#ifndef PI
+#define PI (3.141592653589793)
+#endif
+#define TWOPI (2.0 * PI)
+
+#if defined _WIN32 && defined _MSC_VER
+#pragma message ("using assembler round()") 
+__inline static int round(double fval)
+{
+    int result;
+    _asm{
+        fld     fval
+        fistp   result
+        mov     eax,result
+    }
+    return result;
+}
+
+#else
+#define round(x) lround((x))
+#endif
+
+
+phasevocoder::phasevocoder()
+{
+
+    input       =  NULL;
+    output      = NULL;
+    anal        = NULL;
+    syn         =  NULL;        /* pointer to start of synthesis buffer */
+    banal       =  NULL;        /* pointer to anal[1] (for FFT calls) */
+    bsyn        =  NULL;        /* pointer to syn[1]  (for FFT calls) */
+    nextIn      =  NULL;    /* pointer to next empty word in input */
+    nextOut     =  NULL;    /* pointer to next empty word in output */
+    analWindow  =  NULL;    /* pointer to center of analysis window */
+    synWindow   =  NULL;    /* pointer to center of synthesis window */
+    maxAmp      =  NULL;    /* pointer to start of max amp buffer */
+    avgAmp      =  NULL;    /* pointer to start of avg amp buffer */
+    avgFrq      =  NULL;    /* pointer to start of avg frq buffer */
+    env     =  NULL;        /* pointer to start of spectral envelope */
+    i0      =  NULL;        /* pointer to amplitude channels */
+    i1      =  NULL;        /* pointer to frequency channels */
+    oi      =  NULL;        /* pointer to old phase channels */
+    oldInPhase      =  NULL;    /* pointer to start of input phase buffer */
+    oldOutPhase     =  NULL;    /* pointer to start of output phase buffer */
+    m = n = 0;
+
+    N = 0;      /* number of phase vocoder channels (bands) */
+    M = 0;      /* length of analWindow impulse response */
+    L = 0;      /* length of synWindow impulse response */
+    D = 0;      /* decimation factor (default will be M/8) */
+    I = 0;      /* interpolation factor (default will be I=D)*/
+    W = -1;     /* filter overlap factor (determines M, L) */
+    //F = 0;        /* fundamental frequency (determines N) */
+    //F2 = 0;       /* F/2 */
+    /*RWD */
+    Fexact = 0.0f;
+    analWinLen = 0, /* half-length of analysis window */
+    synWinLen = 0;  /* half-length of synthesis window */
+
+    sampsize = 0;   /* sample size for output file */
+    outCount = 0;   /* number of samples written to output */
+    ibuflen= 0; /* length of input buffer */
+    obuflen= 0; /* length of output buffer */
+    nI = 0;     /* current input (analysis) sample */
+    nO= 0;      /* current output (synthesis) sample */
+    nMaxOut= 0; /* last output (synthesis) sample */
+    nMin = 0;   /* first input (analysis) sample */
+    nMax = 100000000;   /* last input sample (unless EOF) */
+/***************************** 6:2:91  OLD CODE **************
+                        long    origsize;
+*******************************NEW CODE **********************/
+    origsize = 0;   /* sample type of file analysed */
+    ch = 0;
+    ifd =  ofd = -1;
+    beta = 6.8f;    /* parameter for Kaiser window */
+    real = 0.0f;        /* real part of analysis data */
+    imag= 0.0f;     /* imaginary part of analysis data */
+    mag= 0.0f;      /* magnitude of analysis data */
+    phase= 0.0f;        /* phase of analysis data */
+    angleDif= 0.0f; /* angle difference */
+    RoverTwoPi= 0.0f;   /* R/D divided by 2*Pi */
+    TwoPioverR= 0.0f;   /* 2*Pi divided by R/I */
+    sum= 0.0f;      /* scale factor for renormalizing windows */
+    ftot = 0.0f,    /* scale factor for calculating statistics */
+    rIn= 0.0f;      /* decimated sampling rate */
+    rOut= 0.0f;     /* pre-interpolated sampling rate */
+    invR= 0.0f;     /* 1. / srate */
+    time= 0.0f;     /* nI / srate */
+    tvx0 = 0.0f;
+    tvx1 = 0.0f;
+    tvdx = 0.0f;
+    tvy0 = tvy1 = 0.0f;
+    tvdy = 0.0f;
+    frac = 0.0f;
+    warp = 0.0f;    /* spectral envelope warp factor */
+    R = 0.0f;       /* input sampling rate */
+    P = 1.0f;       /* pitch scale factor */
+    Pinv= 0.0f;     /* 1. / P */
+    T = 1.0f;       /* time scale factor ( >1 to expand)*/
+    //Mlen,
+    Mf = 0;     /* flag for even M */
+    Lf = 0;     /* flag for even L */
+    //Dfac,
+    flag = 0;       /* end-of-input flag */
+    C = 0;      /* flag for resynthesizing even or odd chans */
+    Ci = 0;     /* flag for resynthesizing chans i to j only */
+    Cj = 0;     /* flag for resynthesizing chans i to j only */
+    CC = 0;     /* flag for selected channel resynthesis */
+    X = 0;      /* flag for magnitude output */
+    E = 0;      /* flag for spectral envelope output */
+    tvflg = 0;  /* flag for time-varying time-scaling */
+    tvnxt = 0;
+    tvlen = 0;
+    timecheckf= 0.0f;
+    K = H = 0;  /* default window is Hamming */
+    Nchans = 0;
+    NO2 = 0;
+    vH = 0;                     /* RWD set to 1 to set von Hann window */
+    bin_index = 0;
+    m_mode = PVPP_NOT_SET;
+    synWindow_base = NULL;
+    analWindow_base = NULL;
+};
+
+/* use when decfac needs specifying directly: cuts out other options */
+bool phasevocoder::init(long outsrate,long fftlen,long winlen,long decfac,float scalefac,
+                        pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode)
+{       
+    N = fftlen;  
+    M = N*2;          /* RWD make double-window the default  */
+    D = decfac;
+    if(scalefac <=0.0)
+        return false;
+    
+    switch(stype){
+    case PVOC_S_TIME:
+        T = scalefac;
+        P = 1.0f;
+        break;
+    case PVOC_S_PITCH:
+        T = P = scalefac;
+        break;
+    default:
+        T = P = 1.0f;
+        break;
+    }
+    switch(wtype){
+    case PVOC_HANN:
+        H = 1;
+        break;
+    case PVOC_KAISER:
+        K = 1;
+        break;
+    default:
+        /* for now, anything else just sets Hamming window! */
+        break;
+    }
+    if(N <=0)
+        return false;
+    if(D < 0)
+        return false;
+    if(M < 0)
+        return false;
+
+    /*for now */
+    if(!(mode == PVPP_OFFLINE || mode == PVPP_STREAMING) )
+        return false;
+    m_mode  = mode;
+
+    isr = outsrate;
+    R       = srate = (float) outsrate;
+    N       = N  + N%2; /* Make N even */
+    N2      = N / 2;
+//  if (N2 > 16384){
+//      return false;
+//  }
+
+//  F       = (int)((float) R / N);
+    Fexact  = (float)R / (float)N;      /* RWD */
+//  F2      = F / 2;
+    if(winlen > 0)
+        M   = winlen;
+    Mf      = 1 - M%2;
+
+    L       =  (L != 0 ? L : M);
+    Lf      = 1 - L%2;
+    ibuflen = 4 * M;
+    obuflen = 4 * L;
+
+
+    if (W == -1)
+        W = (int)(3.322 * log10((double)(4. * N) / M));/* cosmetic */
+
+    if (Cj == 0)
+        Cj = N2;
+    if (Cj > N2)
+        Cj = N2;
+    if (Ci < 0)
+        Ci = 0;
+    D = (int)((D != 0 ? D : M/(8.0*(T > 1.0 ? T : 1.0))));
+
+    if (D == 0){
+        //fprintf(stderr,"pvoc: warning - T greater than M/8 \n");
+        D = 1;
+    }
+
+    I = (int)(I != 0 ? I : (float) T*D );
+
+    T = ((float) I / D);
+
+    if (P != 1.)
+        P = T;
+
+    NO = (int)((float) N / P);  /* synthesis transform will be NO points */
+    NO = NO + NO%2;             /* make NO even */
+
+    NO2 = NO / 2;
+
+    P = ((float) N / NO);       /* ideally, N / NO = I / D = pitch change */
+    Pinv = (float)(1.0/ P);
+
+    if (warp == -1.)
+        warp = P;
+    if ((E == 1) && (warp == 0.))
+        warp = 1.0f;
+
+
+    //if ((P != 1.) && (P != T))
+    //   fprintf(stderr,"pvoc: warning P=%f not equal to T=%f\n",P,T);
+
+    IO = (int)((float) I / P);
+
+    nMax -= nMin;
+
+    /*RWD need this to get sum setup for synth window! */
+    /* NB vonHann window also available now */
+
+    /* 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). Any low pass
+        window will work; a Hamming window is generally fine,
+        but a Kaiser is also available.  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 phase vocoder amplitude
+        estimates are properly scaled.  The maximum allowable
+        window duration is ibuflen/2. */
+
+
+    analWindow_base = new float[M+Mf];
+    analWindow = analWindow_base + (analWinLen = M/2);
+#ifdef USE_FFTW 
+    in_fftw_size = N;
+    out_fftw_size = NO;
+    forward_plan = rfftwnd_create_plan_specific(1,&in_fftw_size, 
+        FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE | FFTW_IN_PLACE,
+        (fftw_real*) analWindow_base,1,NULL,1);
+    inverse_plan = rfftwnd_create_plan(1,&out_fftw_size, FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE | FFTW_IN_PLACE);
+#endif
+
+    if (K)  
+        kaiser(analWindow_base,M+Mf,beta);          /* ??? or just M?*/
+    else if (vH)
+        vonhann(analWindow,analWinLen,Mf);
+    else
+        hamming(analWindow,analWinLen,Mf);
+
+
+    for (i = 1; i <= analWinLen; i++)
+        *(analWindow - i) = *(analWindow + i - Mf);
+
+    if (M > N) {
+        if (Mf)
+        *analWindow *=(float)( (double)N * sin((double)PI*.5/N) /(double)( PI*.5));
+        for (i = 1; i <= analWinLen; i++) 
+            *(analWindow + i) *=(float)
+            ((double)N * sin((double) (PI*(i+.5*Mf)/N)) / (PI*(i+.5*Mf)));  /* D.T. 2000*/
+        for (i = 1; i <= analWinLen; i++)
+            *(analWindow - i) = *(analWindow + i - Mf);
+    }
+
+    sum = 0.0f;
+    for (i = -analWinLen; i <= analWinLen; i++)
+        sum += *(analWindow + i);
+
+    sum = (float)(2.0 / 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. */
+
+    synWindow_base = new float[L+Lf];
+    synWindow = synWindow_base + (synWinLen = L/2);
+#ifdef USE_FFTW
+    Ninv = (float) (1.0 / N);
+#endif
+    if (M <= N){
+        
+        if(K)
+            kaiser(synWindow_base,L+Lf,beta);
+        else if(vH)
+            vonhann(synWindow,synWinLen,Lf);
+        else
+            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.0f;
+        for (i = -synWinLen; i <= synWinLen; i+=I)
+            sum += *(synWindow + i) * *(synWindow + i);
+
+        sum = (float)(1.0/ sum);
+#ifdef USE_FFTW
+        sum *= Ninv;
+#endif
+        for (i = -synWinLen; i <= synWinLen; i++)
+            *(synWindow + i) *= sum;
+    }
+    else {
+        if(K)
+            kaiser(synWindow_base,L+Lf,beta);
+        else if(vH)
+            vonhann(synWindow,synWinLen,Lf);
+        else
+            hamming(synWindow,synWinLen,Lf);
+        for (i = 1; i <= synWinLen; i++)
+            *(synWindow - i) = *(synWindow + i - Lf);
+
+        if (Lf)
+            *synWindow *= (float)((double)IO * sin((double) (PI*.5/IO)) / (double)(PI*.5));
+        for (i = 1; i <= synWinLen; i++) 
+                *(synWindow + i) *=(float)
+                ((double)IO * sin((double) (PI*(i+.5*Lf)/IO)) /(double) (PI*(i+.5*Lf)));
+        for (i = 1; i <= synWinLen; i++)
+            *(synWindow - i) = *(synWindow + i - Lf);
+
+        sum = (float)(1.0/sum);
+#ifdef USE_FFTW
+        sum *= Ninv;
+#endif
+        for (i = -synWinLen; i <= synWinLen; i++)
+            *(synWindow + i) *= sum;
+    }
+
+
+    
+    try{
+
+        /* 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. */
+
+        input = new float[ibuflen];
+
+        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.  */
+        output =    new float [obuflen];
+
+        nextOut =   output;
+
+
+        /* set up analysis buffer for (N/2 + 1) channels: The input is real,
+        so the other channels are redundant. oldInPhase is used
+        in the conversion to remember the previous phase when
+        calculating phase difference between successive samples. */
+
+        anal    =   new float[N+2];
+        banal   =   anal + 1;
+
+        oldInPhase =    new float[N2+1];
+        maxAmp =    new float[N2+1];
+        avgAmp =    new float[N2+1];
+        avgFrq =    new float[N2+1];
+        env =       new float[N2+1];
+
+        /* set up synthesis buffer for (N/2 + 1) channels: (This is included
+        only for clarity.)  oldOutPhase is used in the re-
+        conversion to accumulate angle differences (actually angle
+        difference per second). */
+
+        syn =       new float[NO+2];
+        bsyn = syn + 1;
+        oldOutPhase =   new float[NO2+1];
+    }
+    catch(...){
+        if(synWindow_base){
+            delete [] synWindow_base;
+            synWindow_base = 0;
+        }
+        if(analWindow_base){
+            delete [] analWindow_base;
+            analWindow_base = 0;
+
+        }
+        if(input) {
+            delete [] input;
+            input = 0;
+        }
+
+        if(output) {
+            delete [] output;
+            output = 0;
+        }
+        if(anal) {
+            delete [] anal;
+            anal = 0;
+        }
+        if(oldInPhase) {
+            delete [] oldInPhase;
+            oldInPhase = 0;
+        }
+        if(maxAmp){
+            delete [] maxAmp;
+            maxAmp = 0;
+        }
+        if(avgAmp) {
+            delete [] avgAmp;
+            avgAmp = 0;
+        }
+        if(avgFrq) {
+            delete [] avgFrq;
+            avgFrq = 0;
+        }
+        if(env){
+            delete [] env;
+            env= 0;
+        }
+        if(syn){
+            delete [] syn;
+            syn = 0;
+        }
+        if(oldOutPhase){
+            delete [] oldOutPhase;
+            oldOutPhase = 0;
+        }
+        return false;
+    }
+
+    outCount = 0;
+    rIn = ((float) R / D);
+    rOut = ((float) R / I);
+    invR =((float) 1. / R);
+    RoverTwoPi = (float)(rIn / TWOPI);
+    TwoPioverR = (float)(TWOPI / rOut);
+    nI = -(analWinLen / D) * D; /* input time (in samples) */
+    nO = (long)((float) T/P * nI);  /* output time (in samples) */
+    Dd = analWinLen + nI + 1;   /* number of new inputs to read */
+    Ii = 0;             /* number of new outputs to write */
+    IOi = 0;
+    flag = 1;
+
+    for(i=0;i < ibuflen;i++) {
+        input[i] = 0.0f;
+        output[i] = 0.0f;
+    }
+    for(i=0;i < NO+2;i++)
+        syn[i] = 0.0f;
+    for(i=0;i < N+2;i++)
+        anal[i] = 0.0f;
+    for(i=0;i < NO2+1;i++)
+        oldOutPhase[i] = 0.0f;
+    for(i=0;i < N2+1;i++)
+        oldInPhase[i] = maxAmp[i] = avgAmp[i] = avgFrq[i] = env[i] = 0.0f;
+    return true;
+}
+
+/* closer to PVOC command-line format; we will need a full array of settings ere long */
+/* e.g to install a custom window...*/
+bool phasevocoder::init(long outsrate,long fftlen,pvoc_overlapfac ofac,float scalefac,
+                        pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode)
+{
+    N   = fftlen;
+    D   = 0;
+    //D = N/4;      /* one problem - when M is larger, overlap is larger too */
+    switch(ofac){   
+    case PVOC_O_W0:
+        W = 0;
+        break;
+    case PVOC_O_W1:
+        W = 1;
+        break;
+    case PVOC_O_W2:
+        W = 2;
+        break;
+    case PVOC_O_W3:
+        W = 3;
+        break;
+    default:
+        W = 1;       /* double-window option */
+        break;
+    }
+    switch(wtype){
+    case PVOC_HANN:
+        H = 1;      
+        break;
+    case PVOC_KAISER:
+        K = 1;      
+        break;
+    default:
+        /* for now, anything else just sets Hamming window! */
+        break;
+    }
+
+
+    if(scalefac <=0.0)
+        return false;
+    switch(stype){
+    case PVOC_S_TIME:
+        T = scalefac;
+        P = 1.0f;
+        break;
+    case PVOC_S_PITCH:
+        T = P = scalefac;
+        break;
+    default:
+        T = P = 1.0f;
+        break;
+    }
+
+
+    if(N <=0)
+        return false;
+    if(D < 0)
+        return false;
+    /*for now */
+    if(!(mode == PVPP_OFFLINE || mode == PVPP_STREAMING) )
+        return false;
+    m_mode  = mode;
+
+    isr = outsrate;
+    R       = srate = (float) outsrate;
+    N       = N  + N%2; /* Make N even */
+    N2      = N / 2;
+//  if (N2 > 16384){
+//      return false;
+//  }
+
+//  F       = (int)((float) R / N);
+    Fexact  = (float)R / (float)N;      /* RWD */
+//  F2      = F / 2;
+    if (W != -1)
+        /* cannot be used with M; but we don't touch M anyway here */
+        switch(W){
+        case 0: M = 4*N;
+            break;
+        case 1: M = 2*N;        /* RWD: we want this one, most of the time */
+            break;
+        case 2: M = N;
+            break;
+        case 3: M = N2;
+            break;
+        default:
+            break;
+        }
+
+    M       = (M != 0 ? M : N*2);      /* RWD double-window as default */
+    Mf      = 1 - M%2;
+
+    L       =  M;
+    Lf      = 1 - L%2;
+    ibuflen = 4 * M;
+    obuflen = 4 * L;
+
+    if (W == -1)
+        W = (int)(3.322 * log10((double)(4. * N) / M));/* cosmetic */
+    
+    if (Cj == 0)
+        Cj = N2;
+    if (Cj > N2)
+        Cj = N2;
+    if (Ci < 0)
+        Ci = 0;
+    D = (int)((D != 0 ? D : M/(8.0*(T > 1.0 ? T : 1.0))));
+
+    if (D == 0){
+        //fprintf(stderr,"pvoc: warning - T greater than M/8 \n");
+        D = 1;
+    }
+
+    I = (int)(I != 0 ? I : (float) T*D );
+
+    T = ((float) I / D);
+
+    if (P != 1.)
+        P = T;
+
+    NO = (int)((float) N / P);  /* synthesis transform will be NO points */
+    NO = NO + NO%2;             /* make NO even */
+
+    NO2 = NO / 2;
+
+    P = ((float) N / NO);       /* ideally, N / NO = I / D = pitch change */
+    Pinv = (float)(1.0/ P);
+
+    if (warp == -1.)
+        warp = P;
+    if ((E == 1) && (warp == 0.))
+        warp = 1.0f;
+
+
+    //if ((P != 1.) && (P != T))
+    //   fprintf(stderr,"pvoc: warning P=%f not equal to T=%f\n",P,T);
+
+    IO = (int)((float) I / P);
+
+    nMax -= nMin;
+    /*RWD need this to get sum setup for synth window! */
+    /* NB vonHann window also available now */
+
+    /* 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). Any low pass
+        window will work; a Hamming window is generally fine,
+        but a Kaiser is also available.  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 phase vocoder amplitude
+        estimates are properly scaled.  The maximum allowable
+        window duration is ibuflen/2. */
+
+
+    analWindow_base = new float[M+Mf];
+    analWindow = analWindow_base + (analWinLen = M/2);
+
+
+    if (K)  
+        kaiser(analWindow_base,M+Mf,beta);          /* ??? or just M?*/
+    else if (H)
+        vonhann(analWindow,analWinLen,Mf);
+    else
+        hamming(analWindow,analWinLen,Mf);
+
+    for (i = 1; i <= analWinLen; i++)
+        *(analWindow - i) = *(analWindow + i - Mf);
+
+    if (M > N) {
+        if (Mf)
+        *analWindow *=(float)( (double)N * sin((double)PI*.5/N) /(double)( PI*.5));
+        for (i = 1; i <= analWinLen; i++) 
+            *(analWindow + i) *=(float)
+            ((double)N * sin((double) (PI*(i+.5*Mf)/N)) / (PI*(i+.5*Mf)));  /* D.T 2000*/
+        for (i = 1; i <= analWinLen; i++)
+            *(analWindow - i) = *(analWindow + i - Mf);
+    }
+
+    sum = 0.0f;
+    for (i = -analWinLen; i <= analWinLen; i++)
+        sum += *(analWindow + i);
+
+    sum = (float)(2.0 / 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. */
+
+    synWindow_base = new float[L+Lf];
+    synWindow = synWindow_base + (synWinLen = L/2);
+#ifdef USE_FFTW
+    Ninv = (float) (1.0 / N);
+#endif
+
+    if (M <= N){
+        if(K)
+            kaiser(synWindow_base,L+Lf,beta);
+        else if(H)
+            vonhann(synWindow,synWinLen,Lf);
+        else
+            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.0f;
+        /* RWD ??? this jumps by overlap samps thru table*/
+        for (i = -synWinLen; i <= synWinLen; i+=I)
+            sum += *(synWindow + i) * *(synWindow + i);
+
+        sum = (float)(1.0/ sum);
+#ifdef USE_FFTW
+        sum *= Ninv;
+#endif
+        for (i = -synWinLen; i <= synWinLen; i++)
+            *(synWindow + i) *= sum;
+    }
+    else {
+        if(K)
+            kaiser(synWindow_base,L+Lf,beta);
+        else if(H)
+            vonhann(synWindow,synWinLen,Lf);
+        else
+            hamming(synWindow,synWinLen,Lf);
+        for (i = 1; i <= synWinLen; i++)
+            *(synWindow - i) = *(synWindow + i - Lf);
+
+        if (Lf)
+            *synWindow *= (float)((double)IO * sin((double) (PI*.5/IO)) / (double)(PI*.5));
+        for (i = 1; i <= synWinLen; i++) 
+                *(synWindow + i) *=(float)
+                ((double)IO * sin((double) (PI*(i+.5*Lf)/IO)) /(double) (PI*(i+.5*Lf)));
+        for (i = 1; i <= synWinLen; i++)
+            *(synWindow - i) = *(synWindow + i - Lf);
+
+        sum = (float)(1.0/sum);
+#ifdef USE_FFTW
+        sum *= Ninv;
+#endif
+        for (i = -synWinLen; i <= synWinLen; i++)
+            *(synWindow + i) *= sum;
+    }
+
+#ifdef USE_FFTW 
+    in_fftw_size = N;
+    out_fftw_size = NO;
+    forward_plan = rfftwnd_create_plan_specific(1,&in_fftw_size, 
+        FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE | FFTW_IN_PLACE,
+        (fftw_real*) analWindow_base,1,NULL,1);
+    inverse_plan = rfftwnd_create_plan_specific(1,&out_fftw_size, 
+        FFTW_COMPLEX_TO_REAL, FFTW_ESTIMATE | FFTW_IN_PLACE,
+        (fftw_real*) synWindow_base,1,NULL,1);
+
+#endif
+
+    try{
+
+        /* 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. */
+
+        input = new float[ibuflen];
+
+        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.  */
+        output =    new float [obuflen];
+
+        nextOut =   output;
+
+
+        /* set up analysis buffer for (N/2 + 1) channels: The input is real,
+        so the other channels are redundant. oldInPhase is used
+        in the conversion to remember the previous phase when
+        calculating phase difference between successive samples. */
+
+        anal    =   new float[N+2];
+        banal   =   anal + 1;
+
+        oldInPhase =    new float[N2+1];
+        maxAmp =    new float[N2+1];
+        avgAmp =    new float[N2+1];
+        avgFrq =    new float[N2+1];
+        env =       new float[N2+1];
+
+        /* set up synthesis buffer for (N/2 + 1) channels: (This is included
+        only for clarity.)  oldOutPhase is used in the re-
+        conversion to accumulate angle differences (actually angle
+        difference per second). */
+
+        syn =       new float[NO+2];
+        bsyn = syn + 1;
+        oldOutPhase =   new float[NO2+1];
+    }
+    catch(...){
+        if(synWindow_base){
+            delete [] synWindow_base;
+            synWindow_base = 0;
+        }
+        if(analWindow_base){
+            delete [] analWindow_base;
+            analWindow_base = 0;
+
+        }
+        if(input) {
+            delete [] input;
+            input = 0;
+        }
+
+        if(output) {
+            delete [] output;
+            output = 0;
+        }
+        if(anal) {
+            delete [] anal;
+            anal = 0;
+        }
+        if(oldInPhase) {
+            delete [] oldInPhase;
+            oldInPhase = 0;
+        }
+        if(maxAmp){
+            delete [] maxAmp;
+            maxAmp = 0;
+        }
+        if(avgAmp) {
+            delete [] avgAmp;
+            avgAmp = 0;
+        }
+        if(avgFrq) {
+            delete [] avgFrq;
+            avgFrq = 0;
+        }
+        if(env){
+            delete [] env;
+            env= 0;
+        }
+        if(syn){
+            delete [] syn;
+            syn = 0;
+        }
+        if(oldOutPhase){
+            delete [] oldOutPhase;
+            oldOutPhase = 0;
+        }
+        return false;
+    }
+
+    outCount = 0;
+    rIn = ((float) R / D);
+    rOut = ((float) R / I);
+    invR =((float) 1. / R);
+    RoverTwoPi = (float)(rIn / TWOPI);
+    TwoPioverR = (float)(TWOPI / rOut);
+    nI = -(analWinLen / D) * D; /* input time (in samples) */
+    nO = (long)((float) T/P * nI);  /* output time (in samples) */
+    Dd = analWinLen + nI + 1;   /* number of new inputs to read */
+    Ii = 0;             /* number of new outputs to write */
+    IOi = 0;
+    flag = 1;
+
+    for(i=0;i < ibuflen;i++) {
+        input[i] = 0.0f;
+        output[i] = 0.0f;
+    }
+    for(i=0;i < NO+2;i++)
+        syn[i] = 0.0f;
+    for(i=0;i < N+2;i++)
+        anal[i] = 0.0f;
+    for(i=0;i < NO2+1;i++)
+        oldOutPhase[i] = 0.0f;
+    for(i=0;i < N2+1;i++)
+        oldInPhase[i] = maxAmp[i] = avgAmp[i] = avgFrq[i] = env[i] = 0.0f;
+    return true;
+}
+
+
+
+
+
+phasevocoder::~phasevocoder()
+{
+    if(synWindow_base)
+        delete [] synWindow_base;
+    if(analWindow_base)
+        delete [] analWindow_base;
+    if(input)
+        delete [] input;
+    if(output)
+        delete [] output;
+    if(anal)
+        delete [] anal;
+    if(oldInPhase)
+        delete [] oldInPhase;
+    if(maxAmp)
+        delete [] maxAmp;
+    if(avgAmp)
+        delete [] avgAmp;
+    if(avgFrq)
+        delete [] avgFrq;
+    if(env)
+        delete [] env;
+    if(syn)
+        delete [] syn;  
+    if(oldOutPhase)
+        delete [] oldOutPhase;
+
+#ifdef USE_FFTW
+    rfftwnd_destroy_plan(forward_plan);
+    rfftwnd_destroy_plan(inverse_plan);
+#endif
+
+}
+
+void phasevocoder::hamming(float *win,int winLen,int even)
+{
+    double Pi,ftmp;
+    int i;
+
+/***********************************************************
+                    Pi = (double)((double)4.*atan((double)1.));
+***********************************************************/
+    Pi = (double)PI;
+    ftmp = Pi/winLen;
+
+    if (even) {
+        for (i=0; i<winLen; i++)
+        *(win+i) = (float)(.54 + .46*cos(ftmp*((double)i+.5)));
+        *(win+winLen) = 0.0;}
+    else{   
+        *(win) = 1.0;
+        for (i=1; i<=winLen; i++)
+        *(win+i) = (float)(.54 + .46*cos(ftmp*(double)i));
+    }
+
+}
+
+
+double phasevocoder::besseli( double x)
+{
+    double ax, ans;
+    double y;
+
+    if (( ax = fabs( x)) < 3.75)     {
+    y = x / 3.75;
+    y *= y;
+    ans = (1.0 + y * ( 3.5156229 +
+              y * ( 3.0899424 +
+                y * ( 1.2067492 +
+                      y * ( 0.2659732 +
+                        y * ( 0.360768e-1 +
+                          y * 0.45813e-2))))));
+    }
+    else {
+    y = 3.75 / ax;
+    ans = ((exp ( ax) / sqrt(ax))
+        * (0.39894228 +
+           y * (0.1328592e-1 +
+            y * (0.225319e-2 +
+             y * (-0.157565e-2 +
+                  y * (0.916281e-2 +
+                   y * (-0.2057706e-1 +
+                    y * (0.2635537e-1 +
+                         y * (-0.1647633e-1 +
+                          y * 0.392377e-2)))))))));
+    }
+    return ans;
+}
+
+//courtesy of Csound....
+
+void phasevocoder::kaiser(float *win,int len,double Beta)
+{
+    float *ft = win;
+    double i,xarg = 1.0;    //xarg = amp scalefactor
+    for (i = -len/2.0 + .1 ; i < len/2.0 ; i++)
+        *ft++ = (float) (xarg *
+              besseli((Beta * sqrt(1.0-pow((2.0*i/(len - 1)), 2.0)))) /
+              besseli( Beta));
+   // assymetrical hack: sort out first value!
+   win[0] = win[len-1];
+}
+
+void phasevocoder::vonhann(float *win,int winLen,int even)
+{
+    float Pi,ftmp;
+    int i;
+
+    Pi = (float)PI;
+    ftmp = Pi/winLen;
+
+    if (even) {
+        for (i=0; i<winLen; i++)
+        *(win+i) = (float)(.5 + .5 *cos(ftmp*((double)i+.5)));
+        *(win+winLen) = 0.0f;
+    }
+    else{   *(win) = 1.0f;
+        for (i=1; i<=winLen; i++)
+        *(win+i) =(float)(.5 + .5 *cos(ftmp*(double)i));
+    }   
+}
+
+
+
+long phasevocoder::process_frame(float *anal,float *outbuf,pvoc_frametype frametype)
+{
+
+    /*RWD vars */
+    int n;
+    long written;
+    float *obufptr;
+
+    /* reconversion: The magnitude and angle-difference-per-second in syn
+        (assuming an intermediate sampling rate of rOut) are
+        converted to real and imaginary values and are returned in syn.
+        This automatically incorporates the proper phase scaling for
+        time modifications. */
+    
+    if (NO <= N){
+        for (i = 0; i < NO+2; i++)
+            *(syn+i) = *(anal+i);
+    }
+    else {
+        for (i = 0; i <= N+1; i++)
+            *(syn+i) = *(anal+i);
+        for (i = N+2; i < NO+2; i++)
+            *(syn+i) = 0.0f;
+    }
+
+    if(frametype==PVOC_AMP_PHASE){
+        for(i=0, i0=syn, i1=syn+1; i<= NO2; i++, i0+=2,  i1+=2){
+            mag = *i0;
+            phase = *i1;
+            *i0 = (float)((double)mag * cos((double)phase));
+            *i1 = (float)((double)mag * sin((double)phase));
+        }
+    }
+    else if(frametype == PVOC_AMP_FREQ){
+        for(i=0, i0=syn, i1=syn+1; i<= NO2; i++, i0+=2,  i1+=2){
+            mag = *i0;
+            /* keep phase wrapped within +- TWOPI */
+            /* this could possibly be spread across several frame cycles, as the problem does not
+                develop for a while */
+            double angledif, the_phase;
+            angledif = TwoPioverR * (*i1  - ((float) i * /*F*/Fexact));
+            the_phase = *(oldOutPhase + i) +angledif;
+            if(i== bin_index)
+                the_phase = (float) fmod(the_phase,TWOPI);
+            *(oldOutPhase + i) = (float) the_phase;
+            phase = (float) the_phase;
+
+
+            *i0 = (float)((double)mag * cos((double)phase));
+            *i1 = (float)((double)mag * sin((double)phase));
+        }
+    }
+#ifdef PVOC_NORM_PHASE
+    if(++bin_index == NO2+1)
+        bin_index = 0;
+#endif
+
+    /* else it must be PVOC_COMPLEX */
+    if (P != 1.)
+        for (i = 0; i < NO+2; i++)
+            *(syn+i) *= Pinv;
+    
+    /* synthesis: The synthesis subroutine uses the Weighted Overlap-Add
+            technique to reconstruct the time-domain signal.  The (N/2 + 1)
+            phase vocoder channel 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). The subroutines reals and fft
+            together perform an efficient inverse FFT.  */
+
+
+# ifdef USE_FFTW
+    rfftwnd_one_complex_to_real(inverse_plan,(fftw_complex * )syn,NULL);
+# else
+    reals_(syn,bsyn,NO2,2);
+    fft_(syn,bsyn,1,NO2,1,2);
+#endif
+    j = nO - synWinLen - 1;
+    while (j < 0)
+        j += obuflen;
+    j = j % obuflen;
+
+    k = nO - synWinLen - 1;
+    while (k < 0)
+        k += NO;
+    k = k % NO;
+
+    for (i = -synWinLen; i <= synWinLen; i++) { /*overlap-add*/
+        if (++j >= obuflen)
+            j -= obuflen;
+        if (++k >= NO)
+            k -= NO;
+        *(output + j) += *(syn + k) * *(synWindow + i);
+    }
+
+    obufptr = outbuf;   /*RWD */
+    written = 0;
+    for (i = 0; i < IOi;){  /* shift out next IOi values */
+        int j;
+        int todo = MIN(IOi-i, output+obuflen-nextOut);
+        /*outfloats(nextOut, todo, ofd);*/
+        /*copy data to external buffer */
+        for(n=0;n < todo;n++)
+            *obufptr++ = nextOut[n];
+        written += todo;
+
+        i += todo;
+        outCount += todo;
+        for(j = 0; j < todo; j++)
+            *nextOut++ = 0.0f;
+        if (nextOut >= (output + obuflen))
+            nextOut -= obuflen;
+    }
+
+    if (flag)   /* flag means do this operation only once */
+        if ((nI > 0) && (Dd < D)){  /* EOF detected */
+            flag = 0;
+            nMax = nI + analWinLen - (D - Dd);
+        }
+
+
+    /*  D = some_function(nI);      for variable time-scaling */
+    /*  rIn = ((float) R / D);      for variable time-scaling */
+    /*  RoverTwoPi =  rIn / TwoPi;  for variable time-scaling */
+
+    nI += D;                /* increment time */
+    nO += IO;
+
+    /* Dd = D except when the end of the sample stream intervenes */
+    /* RWD handle offline and streaming separately - 
+        can't count an infinite number of real-time samples! */
+    if(m_mode == PVPP_OFFLINE)
+        Dd = MIN(D, MAX(0L, D+nMax-nI-analWinLen));   /*  CARL */
+    else
+        Dd = D;                                       /* RWD */
+
+    if (nO > (synWinLen + I))
+        Ii = I;
+    else
+        if (nO > synWinLen)
+            Ii = nO - synWinLen;
+        else {
+            Ii = 0;
+            for (i=nO+synWinLen; i<obuflen; i++)
+                if (i > 0)
+                    *(output+i) = 0.0f;
+        }
+    IOi = (int)((float) Ii / P);
+
+
+
+    return written;
+
+}
+
+
+/*RWD arrgh! */
+long phasevocoder::process_frame(float *anal,short *outbuf,pvoc_frametype frametype)
+{
+
+    /*RWD vars */
+    int n;
+    long written;
+    short *obufptr;
+
+        /* reconversion: The magnitude and angle-difference-per-second in syn
+        (assuming an intermediate sampling rate of rOut) are
+        converted to real and imaginary values and are returned in syn.
+        This automatically incorporates the proper phase scaling for
+        time modifications. */
+
+    if (NO <= N){
+        for (i = 0; i < NO+2; i++)
+            *(syn+i) = *(anal+i);
+    }
+    else {
+        for (i = 0; i <= N+1; i++) 
+            *(syn+i) = *(anal+i);
+        for (i = N+2; i < NO+2; i++) 
+            *(syn+i) = 0.0f;
+    }
+    if(frametype != PVOC_COMPLEX){      /* assume AMP_FREQ otherwise, for now */
+        for(i=0, i0=syn, i1=syn+1; i<= NO2; i++, i0+=2,  i1+=2){
+            mag = *i0;
+            *(oldOutPhase + i) += *i1 - ((float) i * /*F*/ Fexact);
+            phase = *(oldOutPhase + i) * TwoPioverR;
+            *i0 = (float)((double)mag * cos((double)phase));
+            *i1 = (float)((double)mag * sin((double)phase));
+        }
+    }
+    if (P != 1.)
+        for (i = 0; i < NO+2; i++)
+            *(syn+i) *= Pinv;
+
+    /* synthesis: The synthesis subroutine uses the Weighted Overlap-Add
+            technique to reconstruct the time-domain signal.  The (N/2 + 1)
+            phase vocoder channel 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). The subroutines reals and fft
+            together perform an efficient inverse FFT.  */
+
+
+#ifdef USE_FFTW
+    rfftwnd_one_complex_to_real(inverse_plan,(fftw_complex * )syn,NULL);
+#else
+    reals_(syn,bsyn,NO2,2);
+    fft_(syn,bsyn,1,NO2,1,2);
+#endif
+    j = nO - synWinLen - 1;
+    while (j < 0)
+        j += obuflen;
+    j = j % obuflen;
+
+    k = nO - synWinLen - 1;
+    while (k < 0)
+        k += NO;
+    k = k % NO;
+
+    for (i = -synWinLen; i <= synWinLen; i++) { /*overlap-add*/
+        if (++j >= obuflen)
+            j -= obuflen;
+        if (++k >= NO)
+            k -= NO;
+        *(output + j) += *(syn + k) * *(synWindow + i);
+    }
+
+    obufptr = outbuf;   /*RWD */
+    written = 0;
+    for (i = 0; i < IOi;){  /* shift out next IOi values */
+        int j;
+        int todo = MIN(IOi-i, output+obuflen-nextOut);
+        
+        /*copy data to external buffer */
+        for(n=0;n < todo;n++)
+            *obufptr++ = (short) round(32767.0 * nextOut[n]);
+        written += todo;
+
+        i += todo;
+        outCount += todo;
+        for(j = 0; j < todo; j++)
+            *nextOut++ = 0.0f;
+        if (nextOut >= (output + obuflen))
+            nextOut -= obuflen;
+    }
+                
+    if (flag)   /* flag means do this operation only once */
+        if ((nI > 0) && (Dd < D)){  /* EOF detected */
+            flag = 0;
+            nMax = nI + analWinLen - (D - Dd);
+        }
+
+
+    /*  D = some_function(nI);      for variable time-scaling */
+    /*  rIn = ((float) R / D);      for variable time-scaling */
+    /*  RoverTwoPi =  rIn / TwoPi;  for variable time-scaling */
+
+    nI += D;                /* increment time */
+    nO += IO;
+
+    /* Dd = D except when the end of the sample stream intervenes */
+
+    Dd = MIN(D, MAX(0L, D+nMax-nI-analWinLen));
+
+
+    if (nO > (synWinLen + I))
+        Ii = I;
+    else
+        if (nO > synWinLen)
+            Ii = nO - synWinLen;
+        else {
+            Ii = 0;
+            for (i=nO+synWinLen; i<obuflen; i++)
+                if (i > 0)
+                    *(output+i) = 0.0f;
+        }
+    IOi = (int)((float) Ii / P);
+
+
+
+    return written;
+
+}
+/* trying to get clean playback when looping! */
+void phasevocoder::reset_phases(void)
+{
+    int i;
+    if(oldInPhase){
+        for(i=0;i < N2+1;i++)
+            oldInPhase[i] = 0.0f;
+    }
+    if(oldOutPhase){
+        for(i=0;i < NO2+1;i++)
+            oldOutPhase[i] = 0.0f;
+    }
+    if(syn){
+        for(i=0;i < NO+2;i++)
+            syn[i] = 0.0f;
+    }
+    
+    
+#ifdef _DEBUG
+    //printf("\nnI = %d; nO = %d; j = %d; k = %d\n",nI,nO,j,k);
+#endif
+
+    /* these seem definitely necessary, but don't solve everything... */
+    nI = -(analWinLen / D) * D; /* input time (in samples) */
+    nO = (long)((float) T/P * nI);  /* output time (in samples) */
+
+}
+
+
+/* we don't read in a single sample, a la pvoc, but just output an empty first frame*/
+/* we assume final block zero-padded if necessary */
+long phasevocoder::generate_frame(float *fbuf,float *outanal,long samps,pvoc_frametype frametype)
+{
+    
+    /*sblen = decfac = D */
+    //static int sblen = 0;
+    int got, tocp;
+    float *fp,*ofp;
+
+    got = samps;     /*always assume */
+    if(got < Dd)
+        Dd = got;
+
+    fp = fbuf;
+    
+
+    tocp = MIN(got, input+ibuflen-nextIn);
+    got -= tocp;
+    while(tocp-- > 0)
+        *nextIn++ = *fp++; 
+
+    if(got > 0) {
+        nextIn -= ibuflen;
+        while(got-- > 0)
+            *nextIn++ = *fp++;
+    }
+    if (nextIn >= (input + ibuflen))
+        nextIn -= ibuflen;
+
+    if (nI > 0)
+        for (i = Dd; i < D; i++){   /* zero fill at EOF */
+            *(nextIn++) = 0.0f;
+            if (nextIn >= (input + ibuflen))
+                nextIn -= ibuflen;
+        }
+
+    /* analysis: The analysis subroutine computes the complex output at
+        time n of (N/2 + 1) of the phase vocoder channels.  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.   The subroutines fft and reals together implement
+        one efficient FFT call for a real input sequence.  */
+
+
+    for (i = 0; i < N+2; i++)
+        *(anal + i) = 0.0f; /*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);
+    }
+#ifdef USE_FFTW
+    rfftwnd_one_real_to_complex(forward_plan,(fftw_real*) anal,NULL);
+            //reals_(anal,banal,N2,-2);
+#else
+    fft_(anal,banal,1,N2,1,-2);
+    reals_(anal,banal,N2,-2);
+#endif
+    /* conversion: The real and imaginary values in anal are converted to
+        magnitude and angle-difference-per-second (assuming an 
+        intermediate sampling rate of rIn) and are returned in
+        anal. */
+    if(frametype == PVOC_AMP_PHASE){
+        /* PVOCEX uses plain (wrapped) phase format, ref Soundhack */
+        for(i=0,i0=anal,i1=anal+1,oi=oldInPhase; i <= N2; i++,i0+=2,i1+=2, oi++){
+            real = *i0;
+            imag = *i1;
+            *i0 =(float) sqrt((double)(real * real + imag * imag));
+            /* phase unwrapping */
+            /*if (*i0 == 0.)*/
+            if(*i0 < 1.0E-10f)
+                //angleDif = 0.0f;
+                phase = 0.0f;
+
+            else {
+#ifdef OLDCALC
+                rratio = atan((double)imag/(double)real);
+                if(real<0.0) {
+                    if(imag<0.0)
+                        rratio -= PI;
+                    else
+                        rratio += PI;
+                }
+#else
+                rratio = atan2((double)imag,(double)real);
+#endif
+                /*angleDif  = (phase = (float)rratio) - *oi;
+                *oi = phase;
+                */
+                phase = (float)rratio;
+            }
+
+            *i1 = phase;
+        }
+    }
+    if(frametype==PVOC_AMP_FREQ){
+        for(i=0,i0=anal,i1=anal+1,oi=oldInPhase; i <= N2; i++,i0+=2,i1+=2, oi++){
+            real = *i0;
+            imag = *i1;
+            *i0 =(float) sqrt((double)(real * real + imag * imag));
+            /* phase unwrapping */
+            /*if (*i0 == 0.)*/
+            if(*i0 < 1.0E-10f)
+                angleDif = 0.0f;
+
+            else {
+#ifdef OLDCALC
+/*RWD 12.99: slower with Pentium Pro build, but otherwise faster??!?? */
+                rratio = atan((double)imag/(double)real);
+                if(real<0.0) {
+                    if(imag<0.0)
+                        rratio -= PI;
+                    else
+                        rratio += PI;
+                }
+#else
+/*RWD98*/       rratio = atan2((double)imag,(double)real);
+#endif
+                angleDif  = (phase = (float)rratio) - *oi;
+                *oi = phase;
+            }
+            if (angleDif > PI)
+                angleDif = (float)(angleDif - TWOPI);
+            if (angleDif < -PI)
+                angleDif = (float)(angleDif + TWOPI);
+
+            /* add in filter center freq.*/
+            *i1 = angleDif * RoverTwoPi + ((float) i * /*F*/Fexact);
+        }
+    }
+    /* else must be PVOC_COMPLEX */
+    fp = anal;
+    ofp = outanal;
+    for(i=0;i < N+2;i++)
+        *ofp++ = *fp++;
+
+    nI += D;                /* increment time */
+    nO += IO;
+    /* deal with offline and streaming differently */
+    if(m_mode== PVPP_OFFLINE)
+        Dd = MIN(D, MAX(0L, D+nMax-nI-analWinLen));     /* CARL */
+    else
+        Dd = D;                         /* RWD */
+
+    if (nO > (synWinLen + I))
+        Ii = I;
+    else
+        if (nO > synWinLen)
+            Ii = nO - synWinLen;
+        else {
+            Ii = 0;
+            for (i=nO+synWinLen; i<obuflen; i++)
+                if (i > 0)
+                    *(output+i) = 0.0f;
+        }
+    IOi = (int)((float) Ii / P);
+
+
+    return D;
+}
+

+ 1435 - 0
dev/externals/paprogs/pvplay/pvplay.cpp

@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+
+/* pvplay.cpp
+ * Play a soundfile or analysis file using PortAudio
+ *
+ * Yes yes, I know, this is iffy C++, and there are some gotos to boot. Work in progress...
+ *  and I may yet redo the whole thing in plain C anyway, like paplay.
+ */ 
+/* OCT 2009 rebuilt with updated pvfileio.c for faster performance on G4 */
+/* Feb 2010 rebuilt with new stable portaudio */
+/* June 2012  new portaudio, settable audio block size */
+/* Jan 2013 changed (as also paplay), at last, to use threaded portaudio ringbuffer code (etc) */
+/* Jan 2014: completed pvx support */
+
+#include "pvplay.h"
+
+typedef enum  {PLAY_SFILE,PLAY_ANAFILE,PLAY_PVXFILE} PLAYTYPE;
+enum {FM_MONO,FM_STEREO,FM_SQUARE,FM_QUAD,FM_PENT,DM_5_0,DM_5_1,FM_HEX,FM_OCT1,FM_OCT2,FM_CUBE,FM_QUADCUBE,FM_NLAYOUTS};
+
+#define NUM_ANALFRAMES (1)
+#define RINGBUF_NFRAMES    (32768)
+#define NUM_WRITES_PER_BUFFER   (4)
+
+#ifndef min
+#define min(x,y)  ((x) < (y) ? (x) : (y))
+#endif
+
+#ifdef unix
+#include <ctype.h>
+int stricmp(const char *a, const char *b);
+int strnicmp(const char *a, const char *b, const int length);
+#endif
+
+#ifdef WIN32
+HANDLE ghEvent;
+#endif
+
+int file_playing;
+psfdata *g_pdata = NULL;
+
+void playhandler(int sig)
+{
+    if(sig == SIGINT) {
+        if(file_playing) {
+            file_playing = 0;
+            g_pdata->flag = 1;
+            Pa_Sleep(5);
+        }
+    }
+}
+
+#ifdef unix
+void alarm_wakeup (int i)
+{
+    struct itimerval tout_val;
+    
+    signal(SIGALRM,alarm_wakeup);
+    if(file_playing && g_pdata->stream) {
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        g_pdata->lastTime = Pa_GetStreamTime(g_pdata->stream ) - g_pdata->startTime;
+        printf("%.2f secs\r", g_pdata->lastTime); 
+        fflush(stdout);
+    }
+    tout_val.it_interval.tv_sec = 0;
+    tout_val.it_interval.tv_usec = 0;
+    tout_val.it_value.tv_sec = 0; 
+    tout_val.it_value.tv_usec = 200000; 
+    setitimer(ITIMER_REAL, &tout_val,0); 
+}
+#endif
+
+void finishedCallback(void *userData)
+{
+    psfdata *pdata = (psfdata*) userData;
+    //printf("stream finished!\n");
+    pdata->finished = 1;
+    file_playing = 0;
+}
+
+#ifdef WIN32
+VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
+{
+    psfdata *pdata = (psfdata*) lpParam;
+    
+    if(file_playing && pdata->stream) {
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        pdata->lastTime = Pa_GetStreamTime(pdata->stream ) - pdata->startTime;
+        printf("%.2f secs\r", pdata->lastTime); 
+        fflush(stdout);
+    }
+    SetEvent(ghEvent);
+}
+#endif
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+
+#ifdef NOTDEF
+// need this?
+static void porttimeCallback(PtTimestamp timestamp, void *userData)
+{
+    PtTimestamp curtime =  Pt_Time();
+    double timesecs = (double) curtime * 0.001;
+    printf("%.3lf\r",timesecs);
+}
+
+#endif
+
+/* Start up a new thread for given function */
+static PaError startThread( psfdata* pdata, threadfunc fn )
+{
+    pdata->flag = 1;
+#ifdef WIN32
+    pdata->hThread = (void*)_beginthreadex(NULL, 0, fn, pdata, 0, NULL);
+    if (pdata->hThread == NULL) 
+        return paUnanticipatedHostError;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    }
+    /* Set file thread to a little higher priority than normal */
+    SetThreadPriority(pdata->hThread, THREAD_PRIORITY_ABOVE_NORMAL);
+//#endif
+    
+#else
+#if defined(__APPLE__) || defined(__GNUC__)
+    if(pthread_create(&pdata->hThread,NULL,/*(void*)*/ fn,pdata))
+        return -1;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    } 
+#endif
+#endif
+    
+    return paNoError;
+}
+#if 0
+static int stopThread( psfdata* pdata )
+{
+    // just called when all data played; must be called before StopStream
+    //   pdata->flag = 1;
+    /* Wait for thread to stop */
+       while (pdata->flag==0) {
+           Pa_Sleep(10);
+       }
+#ifdef WIN32
+    CloseHandle(pdata->hThread);
+    pdata->hThread = 0;
+#else
+#if defined(__APPLE__) || defined(__GNUC__)
+    pthread_cancel(pdata->hThread);
+#endif
+#endif
+    return paNoError;
+}
+#endif
+
+static int paplayCallback(const void *inputBuffer, void *outputBuffer,
+                          unsigned long framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags statusFlags,
+                          void *userData )
+{
+    psfdata *data = (psfdata *)userData;
+    ring_buffer_size_t sampsAvailable = PaUtil_GetRingBufferReadAvailable(&data->ringbuf) * data->outchans;
+    ring_buffer_size_t sampsToPlay = min(sampsAvailable, (ring_buffer_size_t)(framesPerBuffer * data->outchans));
+    float *out = (float*) outputBuffer;
+    int framesToPlay = sampsToPlay / data->outchans;
+    int played;
+    
+    // if framestoplay < framesPerBuffer, need to clean up the remainder
+    memset(out,0,framesPerBuffer * data->outchans * sizeof(float));
+    played = PaUtil_ReadRingBuffer(&data->ringbuf, out, framesToPlay);
+    
+    data->frames_played += played;      
+    if(data->flag) {
+        //printf("callback - complete\n");
+        return paComplete;
+    }
+    return paContinue;
+}
+
+static int MemCallback(const    void *inputBuffer, void *outputBuffer,
+                       unsigned long framesPerBuffer,
+                       const PaStreamCallbackTimeInfo* timeInfo,
+                       PaStreamCallbackFlags statusFlags,
+                       void *userData )
+{
+    psfdata *pdata = (psfdata *)userData;
+    
+    float *out = (float*) outputBuffer;
+    unsigned long inSamps,outSamps, inSampPos;
+    unsigned long i;
+    unsigned long framesToPlay = framesPerBuffer;
+    
+    // if framesToPlay < framesPerBuffer, need to clean up the remainder
+    memset(out,0,framesPerBuffer * pdata->outchans * sizeof(float));
+    inSamps = pdata->memFramelength * pdata->inchans;
+    outSamps = framesPerBuffer * pdata->inchans;
+    inSampPos = pdata->memFramePos * pdata->inchans;
+    
+    if(pdata->play_looped){
+        for(i=0;i < outSamps; i++){
+            pdata->inbuf[i] = pdata->membuf[inSampPos++] * pdata->gain;
+            if(inSampPos == inSamps)
+                inSampPos = 0;
+        }
+    }
+    else {
+        for(i=0;i < outSamps; i++){
+            pdata->inbuf[i] = pdata->membuf[inSampPos++] * pdata->gain;
+            if(inSampPos == inSamps)
+                break;
+        }
+        framesToPlay = i / pdata->inchans;
+        for(; i < outSamps;i++){
+            pdata->inbuf[i] = 0.0;
+        }
+        
+    }
+    // write to output
+    // everything now in inbuf
+    if(pdata->do_decode) {
+        ABFSAMPLE abfsamp;
+        int j;    
+        for(j=0; j < framesToPlay; j++){
+            pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
+            pdata->decodefunc(&abfsamp,out + (j * pdata->outchans), 1);
+        }
+    }
+    else {  // inchans = outchans
+        memcpy(out,pdata->inbuf,framesToPlay * sizeof(float) * pdata->inchans);  
+    }
+    pdata->memFramePos = inSampPos / pdata->inchans;
+    pdata->frames_played += framesToPlay;
+    if(framesToPlay < framesPerBuffer)
+        pdata->flag = 1;           // end of stream
+    if(pdata->flag) {
+        return paComplete;
+    }
+    return paContinue;
+}
+
+static unsigned NextPowerOf2(unsigned val)
+{
+    val--;
+    val = (val >> 1) | val;
+    val = (val >> 2) | val;
+    val = (val >> 4) | val;
+    val = (val >> 8) | val;
+    val = (val >> 16) | val;
+    return ++val;
+}
+
+#define N_BFORMATS (10)
+// static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+// static const int layout_chans[] = {1,2,4,4,5,5,6,6,8,8,8,8};
+/* no M flag here */
+enum {FLAG_B = 0, FLAG_BM, FLAG_D, FLAG_G, FLAG_H, FLAG_I, FLAG_L, FLAG_NFLAGS};
+
+void usage(void){
+#ifdef WIN32
+    printf("usage:\n  pvplay [-BN][-dN][-gN]-hN][-i][-l][-b[N]][-u][-x] infile [from] [to] \n"
+#else
+    printf("usage:\n  pvplay [-BN][-dN][-gN]-hN][-i][-l][-b[N]][-u] infile [from] [to]\n"
+#endif
+                  "       -dN  : use output Device N.\n"
+                  "       -gN  : apply gain factor N to input.\n"
+                  "       -BN  : set memory buffer size to N frames (default: %d).\n"
+                  "                N must be a power of 2 (e.g 4096, 8192, 16384 etc).\n"
+                  "       -hN  : set hardware blocksize to N frames (32 < N <= BN/4), default is set internally.\n"
+                  "               (N recommended to be a power of two size).\n"
+                  "            : NB: for sample rates > 48KHz, buffer sizes are doubled internally.\n"
+                  "       -i   : play immediately (do not wait for keypress).\n"
+                  "       -l   : loop file continuously, from start-time to end-time.\n"
+                  "       -u   : suppress elapsed time updates\n"
+#ifdef WIN32
+                  "       -x   : Apply WAVE_EX infile channel mask to DS audio stream\n"
+                  "              (ignored if -m or -b used)\n"
+#endif
+                  "       from : set start time (secs) for playback and looping. Default: 0.\n"
+                  "       to   : set end time (secs) for playback and looping. Default: EOF.\n"
+                  "       -b[N]: apply 1st-order B-Format decoding to standard soundfile.\n"
+                  "              (file must have at least 3 channels)\n"
+                  "               B-Format files (.amb) will be decoded automatically.\n"
+                  "               N sets speaker layout to one of the following:\n"
+                  "               1    :  *  mono (= W signal only)\n"
+                  "               2    :  *  stereo (quasi mid/side, = W +- Y)\n"
+                  "               3    :     square\n"
+                  "               4    :  *  quad FL,FR,RL,RR order   (default)\n"
+                  "               5    :     pentagon\n"
+                  "               6    :  *  5.0 surround (WAVEX order)\n"
+                  "               7    :  *  5.1 surround (WAVEX order, silent LFE)\n"
+                  "               8    :     hexagon\n"
+                  "               9    :     octagon 1 (front pair, 45deg)\n"
+                  "              10    :     octagon 2 (front centre speaker)\n"
+                  "              11    :     cube (as 3, low-high interleaved. Csound-compatible.)\n"
+                  "              12    :  *  cube (as 4, low quad followed by high quad).\n"
+                  "              Default decode layout is 4 (quad).\n"
+                  "              NB: no decoding is performed if -m flag used.\n"
+                  "  pvplay reads PEAK chunk if present to rescale over-range floatsam files.\n\n"
+                  ,RINGBUF_NFRAMES);
+}
+           
+
+/*******************************************************************/
+int main(int argc,char **argv)
+{
+    PaStreamParameters outputParameters;
+#ifdef WIN32
+    /* portaudio sets default channel masks we can't use; we must do all this to set default mask = 0! */
+    PaWinDirectSoundStreamInfo directSoundStreamInfo;
+//    PaWinMmeStreamInfo winmmeStreamInfo;
+#endif
+    PaDeviceInfo *devinfo = NULL;
+    PaStream *stream = NULL;
+    PaStreamCallback *playcallback = paplayCallback;
+    PaError err = paNoError;
+    SFPROPS props; 
+    psfdata sfdata;
+    CHPEAK *fpeaks = NULL;
+    //ABFSAMPLE *abf_frame = NULL;
+    int res,inorder = 1;
+    time_t in_peaktime = 0;
+    int waitkey = 1;
+    //int play_looped = 0;
+    double fromdur = 0.0;
+    double totime  = 0.0;
+    int from_frame = 0, to_frame = 0;
+    unsigned int i=0;
+    long anal_nframes;
+    PaDeviceIndex device;
+    //long framesread;
+    long filesize;
+    //long framesize = 0;
+    unsigned long ringframelen = RINGBUF_NFRAMES;   // length of ring buffer in m/c frames
+    unsigned long frames_per_buffer = 0;            // default is to let portaudio decide
+    unsigned long nFramesToPlay = 0;
+    double gain = 1.0;
+    unsigned int inchans = 0,outchans = 0;          // may be different if decoding B-Format
+    int layout = 4;                                 // default, quad decode */
+    fmhcopyfunc copyfunc = NULL;
+    fmhdecodefunc decodefunc = NULL;
+    /* chorder facility not included in pvplay - use paplay! */
+    unsigned int flags[FLAG_NFLAGS] = {0};
+    int speakermask = 0;
+    int do_speakermask = 0;
+    int do_updatemessages = 1;
+#ifdef unix
+    struct itimerval tout_val;    
+    tout_val.it_interval.tv_sec = 0;
+    tout_val.it_interval.tv_usec = 0;
+    tout_val.it_value.tv_sec = 0; 
+    tout_val.it_value.tv_usec = 200000;
+#endif
+    char* ext = 0;
+    PVOCDATA *p_pvdata = NULL;
+    WAVEFORMATEX *p_wfx = NULL;
+    //int pvfile = -1;
+    //pvoc_frametype inframetype;
+    pvoc_windowtype wtype;
+    
+    //double oneovrsr;
+    int anal_buflen,overlap,winlen;
+    // for sfiles only, for now 
+    //int framesize_factor = 0;
+    //phasevocoder  *pv = NULL, *pv_r = NULL;
+    PLAYTYPE playtype = PLAY_SFILE;
+    
+    
+#ifdef WIN32
+    HANDLE hTimerQueue;
+    ghEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+    if(ghEvent==NULL){
+        printf("Failed to start internal timer (1).\n");
+        return 1;
+    }
+    hTimerQueue = CreateTimerQueue();
+    if(hTimerQueue == NULL){
+        printf("Failed to start internal timer (2).\n");
+        return 1;
+    }
+#endif
+    
+    
+    /* CDP version number; now set for release 7 */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("7.1.0\n");
+        return 0;
+    }
+    
+    signal(SIGINT,playhandler);
+    //sf_frames = FRAMESIZE;  // need this for pvoc playback reporting
+    sfdata.inbuf = NULL;
+    sfdata.membuf = NULL;
+    sfdata.memFramelength = 0;
+    sfdata.memFramePos = 0;
+    sfdata.current_frame = 0;
+    sfdata.ringbufData = NULL;
+    sfdata.outbuf_l = NULL;
+    sfdata.outbuf_r = NULL;
+    sfdata.pvfile = -1;
+    
+    printf("PvPlay: play multi-channel PCM and analysis files (.ana, .pvx). V7.0.0 RWD,CDP 2014\n");
+    file_playing = 0;
+    err = Pa_Initialize();
+    if( err != paNoError ) {
+        fprintf(stderr,"Portaudio startup error.\n");
+        return 1;
+    }
+    device =  Pa_GetDefaultOutputDevice();
+    if(device < 0){
+        fprintf(stderr,"Cannot find any audio output device\n");
+        return 1;
+    }
+
+    if(argc < 2){
+        usage();
+        show_devices();
+        Pa_Terminate();
+            return 1;
+    }
+
+    while(argv[1][0]=='-'){
+        unsigned long userbuflen;
+        switch(argv[1][1]){
+            case('b'):
+                if(flags[FLAG_B]){
+                    printf("error: multiple -b flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2] != '\0'){
+                    layout = atoi(&argv[1][2]);
+                    if(layout < 1 || layout > 12){
+                        fprintf(stderr,"value for -b flag out of range.\n");
+                        Pa_Terminate();
+                        return 1;
+                    }
+                }
+                flags[FLAG_B] = 1;
+                break;
+            case('d'):
+                if(flags[FLAG_D]){
+                    printf("error: multiple -d flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-d flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                device = atoi(&(argv[1][2]));
+                flags[FLAG_D] = 1;
+                break;
+            case('g'):
+                if(flags[FLAG_G]){
+                    printf("error: multiple -g flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-g flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                gain = atof(&(argv[1][2]));
+                if(gain <= 0.0){
+                    fprintf(stderr,"gain value must be positive.\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_G] = 1;
+                break;
+            case 'h':
+                if(flags[FLAG_H]){
+                    printf("error: multiple -h flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-h flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                frames_per_buffer = atoi(&(argv[1][2]));
+                if((frames_per_buffer > 0) && (frames_per_buffer < 32)){
+                    printf("-h value too small: must be at least 32.\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_H] = 1;
+                break;
+            case('i'):
+                if(flags[FLAG_I]){
+                    printf("error: multiple -i flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_I] = 1;
+                waitkey = 0;
+                break;
+            case ('l'):
+                flags[FLAG_L] = 1;
+                break;
+            case 'u':
+                do_updatemessages = 0;
+                break;
+            case 'x':
+                do_speakermask = 1;
+                break;
+            case 'B':
+                if(flags[FLAG_BM]){
+                    printf("error: multiple -B flags\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                if(argv[1][2]=='\0'){
+                    printf("-B flag requires parameter\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                
+                ringframelen = atoi(&(argv[1][2]));
+                if(ringframelen <= 1024){
+                    printf("-B: framesize value must be >=1024!n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                userbuflen = NextPowerOf2(ringframelen);
+                if(userbuflen != ringframelen){
+                    printf("-B: framesize value must be power of 2 size\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                flags[FLAG_BM] = 1;
+                break;
+            default:
+                printf("unrecognised flag option\n");
+                Pa_Terminate();
+                return 1;
+        }
+        argv++;  argc--;
+    }
+    
+    if(frames_per_buffer > ringframelen/4){
+        printf(" hardware (-h) framesize %lu too large for file buffer (-B) size %lu\n",
+               frames_per_buffer,ringframelen);
+        Pa_Terminate();
+        return 1;
+    }
+    
+    if(argc < 2 || argc > 4){
+        usage();
+        show_devices();
+        Pa_Terminate();
+        return 1;
+    }
+    
+    if(argc>=3){        
+        fromdur = atof(argv[2]);
+        if(fromdur < 0.0){
+            printf("Error: start position must be positive\n");
+            Pa_Terminate();
+            return 1;
+        }
+    }
+    if(argc==4){
+        totime = atof(argv[3]);
+        if(totime <= fromdur){
+            printf("Error: end time must be after from time\n");
+            Pa_Terminate();
+            return 1;
+        }
+    }
+    
+    if(!init_pvsys()){
+        puts("\nUnable to start pvsys.");
+        return 1;
+    }
+    if(sflinit("pvplay")){
+        puts("\nUnable to start sfsys.");
+        return 1;
+    }   
+    
+    /* get pvocex filetype from extension */
+    ext = strrchr(argv[1],'.');   
+    if(ext && stricmp(ext,".pvx")==0){
+        p_pvdata =  new PVOCDATA;
+        if(p_pvdata==NULL){
+            puts("\nNo Memory!");
+            return 1;
+        }
+        p_wfx =  new WAVEFORMATEX;
+        if(p_wfx==NULL){
+            puts("\nNo Memory!");
+            return 1;
+        }
+
+        sfdata.pvfile   = pvoc_openfile(argv[1],p_pvdata,p_wfx);
+        if(sfdata.pvfile< 0){
+            fprintf(stderr,"\npvplay: unable to open pvx file: %s",pvoc_errorstr());
+            return 1;
+        }
+        props.srate = p_wfx->nSamplesPerSec;
+        if(props.srate <=0){
+            fprintf(stderr,"\nbad srate found: corrupted file?\n");
+            return 1;
+        }
+        sfdata.srate = props.srate;
+        /*stick to mono/stereo for now */
+        if(p_wfx->nChannels > 2){
+            fprintf(stderr,"\nSorry: can only read mono or stereo pvx files.");
+            return 1;
+        }
+        inchans = p_wfx->nChannels;
+        props.chans = inchans;
+        outchans = inchans;
+        /* calc size in m/c analysis frames of this analysis file */
+        filesize = pvoc_framecount(sfdata.pvfile) / inchans;
+        double dur = (double) filesize  / (double)(p_pvdata->fAnalysisRate);
+        filesize = (long)(dur * props.srate);
+
+        sfdata.anal_framesize = (p_pvdata->nAnalysisBins/*- 1*/) * 2; 
+        sfdata.fftsize = sfdata.anal_framesize-2;
+        winlen = p_pvdata->dwWinlen;
+        overlap = p_pvdata->dwOverlap;
+        sfdata.inframetype = (pvoc_frametype) p_pvdata->wAnalFormat;
+        
+//        oneovrsr = 1.0 / (double) props.srate;
+        wtype = (pvoc_windowtype) p_pvdata->wWindowType;        
+        
+        /* adjust buffer size to multiple of overlap   */
+        anal_buflen = sfdata.anal_framesize * NUM_ANALFRAMES;
+        anal_buflen = (anal_buflen / overlap) * overlap;
+        /* mono */
+        sfdata.analframe1 = new float[sfdata.anal_framesize];
+        memset(sfdata.analframe1,0,sfdata.anal_framesize * sizeof(float));
+
+        sfdata.pv_l = new phasevocoder();
+        if(!sfdata.pv_l->init(props.srate,sfdata.anal_framesize-2,winlen,overlap,1.0,PVOC_S_NONE,wtype,PVPP_STREAMING)){
+            fprintf(stderr,"\nUnable to initialize pvoc Channel 1.");
+            //delete sfdata.pv_l;
+            //delete sfdata.analframe1;
+            return 1;
+        }
+        if(inchans==2){
+            /* need two intermediate buffers, to be interleaved into sampbuf */
+            sfdata.analframe2 = new float[sfdata.anal_framesize];
+            memset(sfdata.analframe2,0,sfdata.anal_framesize * sizeof(float));
+            sfdata.pv_r = new phasevocoder();
+            if(!sfdata.pv_r->init(props.srate,sfdata.anal_framesize-2,winlen,overlap,1.0,PVOC_S_NONE,wtype,PVPP_STREAMING)){
+                fprintf(stderr,"\nUnable to initialize pvoc Channel 2.");
+                //delete sfdata.pv_l;
+                //delete sfdata.pv_r;
+                //delete sfdata.outbuf_l;
+                //delete sfdata.outbuf_r;
+                //delete sfdata.analframe1;
+                //delete sfdata.analframe2;
+                return 1;
+            }
+        }
+        to_frame = (pvoc_framecount(sfdata.pvfile) / inchans) - 1;
+        if(fromdur > 0.0){
+            //printf("SORRY: from parameter ignored: not yet supported for pvx files\n");
+            from_frame = (long)(fromdur * (props.srate/overlap));
+            if(from_frame >= to_frame){
+                printf("Error: start frame %d is beyond end of file\n", from_frame);
+                goto error;
+            }
+            if(pvoc_seek_mcframe(sfdata.pvfile,from_frame,SEEK_SET)){
+                printf("Error: from: failed to seek to frame %d\n",from_frame);
+                goto error;
+            }
+        } 
+                
+        if(totime > 0.0){
+            long targetframe = (long)(totime * (props.srate/overlap));
+            if(targetframe >= to_frame){
+                printf("Error: end time is beyond end of file\n");
+                goto error;
+            }
+                    
+            if(targetframe <= from_frame){
+                printf("Start time must be less than end time.\n");
+                goto error;
+            }
+            to_frame = targetframe;       
+            if(to_frame - from_frame < 1){
+                to_frame = from_frame+1;   // maybe we can in fact freeze on a single frame!
+            // but we will not use in-memory system in this case
+            }
+            nFramesToPlay = to_frame - from_frame;
+                    
+            printf("Playing %d analysis frames\n",(int) nFramesToPlay);
+        } 
+        playtype = PLAY_PVXFILE; 
+    }
+
+    /* soundfile or anafile*/
+
+    else {
+        /* allow auto rescaling of overrange floats via PEAK chunk */
+        sfdata.ifd = sndopenEx(argv[1],1,CDP_OPEN_RDONLY);
+        if(sfdata.ifd < 0) {
+            printf("unable to open soundfile %s : %s\n",argv[1],sferrstr());
+           return 1;
+        }
+        if(!snd_headread(sfdata.ifd,&props)) {
+            fprintf(stderr,"\nError reading sfile header");
+            fprintf(stderr,"\n%s",props_errstr);
+            return 1;
+        }
+        inchans = props.chans;
+        filesize = sndsizeEx(sfdata.ifd);  // get size in frames
+        if(filesize == 0){
+            printf("File is empty!\n");
+            return 1;
+        }
+        
+        bool adjusted = false; // in case we have to increase ringframelen for huge FFT size
+        switch(props.type){
+            case wt_analysis:
+                sfdata.anal_framesize = props.chans;
+                outchans = inchans = props.chans = 1;
+                props.srate = props.origrate;
+                anal_nframes =  filesize / sfdata.anal_framesize ;   // * chans ?
+                if(anal_nframes <= 1){
+                    fprintf(stderr,"Bad analysis file - not enough frames\n");
+                    return 1;
+                }
+                sfdata.analframe1  = new float[sfdata.anal_framesize];                                      
+                winlen = props.winlen;
+                /* TODO: match ringbuf length to multiple of FFT size */
+                sfdata.fftsize = sfdata.anal_framesize - 2;
+                        
+                sfdata.pv_l = new phasevocoder;
+                if(!sfdata.pv_l->init(props.origrate,sfdata.anal_framesize-2,props.winlen,props.decfac,1.0,
+                        PVOC_S_NONE,PVOC_HAMMING,PVPP_STREAMING)){
+                    fprintf(stderr,"Error: unable to initialize pvoc engine.\n");           
+                    return 1;
+                }
+                overlap = sfdata.pv_l->anal_overlap();
+                //anal_buflen = /*FRAMESIZE * NUM_ANALFRAMES*/ anal_framesize;  // always mono
+                //anal_buflen = (anal_buflen / overlap) * overlap;
+                // buflen = anal_buflen;
+                from_frame = (long)(fromdur * props.arate);
+                if(from_frame >= anal_nframes){
+                    printf("Error: start is beyond end of file\n");
+                    goto error;
+                }
+                
+                to_frame = anal_nframes -1;
+                
+                if(totime > 0.0){
+                    to_frame = (long)(totime * props.arate + 0.5);
+                    if(to_frame >= anal_nframes){
+                        printf("Error: end time is beyond end of file\n");
+                        goto error;
+                    }
+                    
+                    if(to_frame <= from_frame){
+                        printf("Start time must be less than end time.\n");
+                        goto error;
+                    }
+                    
+                    if(to_frame - from_frame < 1){
+                        to_frame = from_frame +1;   // maybe we can in fact freeze on a single frame!
+                        // but we will not use in-memory system in this case
+                    }
+                    nFramesToPlay = to_frame - from_frame;
+                    
+                    printf("Playing %d analysis frames\n",(int) nFramesToPlay);
+                }
+               if(from_frame > 0){
+                    if(sndseekEx(sfdata.ifd,from_frame * sfdata.anal_framesize,SEEK_SET) < 0)   {
+                        printf("Error setting start position to frame %d\n", from_frame);
+                        printf("%s", sferrstr());
+                        goto error;
+                    }
+                    sfdata.current_frame = from_frame;
+                }
+                // ensure ring buffer is large enough even for huge FFT lengths!
+                adjusted = false;
+                while(ringframelen < (sfdata.fftsize * 4)) {
+                    ringframelen <<= 1;
+                    adjusted = true;
+                }
+                    
+                //printf("from = %d, to = %d\n",from_frame, to_frame);
+                playtype = PLAY_ANAFILE; 
+                printf("Opened %s: %ld frames, %d channels\n",argv[1],anal_nframes,props.chans);
+                if(adjusted)
+                    printf("FFT frame size = %d, expanding buffer to suit...\n", sfdata.fftsize);
+                break;
+            case wt_wave:
+                playtype = PLAY_SFILE;
+                printf("Opened %s: %ld frames, %d channels\n",argv[1],filesize,props.chans);
+                //framesize_factor = (long) (48000/ props.srate);
+                //framesize_factor += 1;
+                filesize /= inchans;
+                                
+                if(props.format == WAVE_EX){
+                    if(do_speakermask){
+                        int mask;
+                        if(flags[FLAG_B]){
+                            printf("-x flag ignored if -B used\n");
+                        }
+                        else {
+                            mask = sndgetchanmask(sfdata.ifd);
+                            if(mask < 0){
+                                printf("Could not read speaker mask. Using 0\n");
+                            }
+                            else 
+                                speakermask = mask;
+                        }
+                    }
+                }
+                from_frame = (long)(fromdur * props.srate);
+                if(from_frame >= filesize){
+                    printf("Error: start is beyond end of file\n");
+                    goto error;
+                }
+                nFramesToPlay = filesize - from_frame;
+                to_frame = filesize -1;
+                if(totime > 0.0){
+                    to_frame = (long)(totime * props.srate);
+                    if(to_frame >= filesize){
+                        printf("Error: end time is beyond end of file\n");
+                        goto error;
+                    }
+                    //printf("fromframe = %d, toframe = %d\n",(int)from_frame,(int) to_frame);
+                    if(to_frame <= from_frame){
+                        printf("End time must be later than from time\n");
+                        goto error;
+                    }
+                    nFramesToPlay = to_frame - from_frame;
+                    printf("Playing %d frames\n",(int) nFramesToPlay);
+                }
+                if(from_frame > 0){
+                    if(sndseekEx(sfdata.ifd,from_frame * inchans,SEEK_SET) < 0) {
+                        printf("Error setting start position\n");
+                        goto error;
+                    }
+                    sfdata.current_frame = from_frame;
+                }
+                
+                
+                outchans = inchans = props.chans;
+                if(props.chformat==MC_BFMT){
+                    flags[FLAG_B] = 1;
+                    if(inchans > 4) {
+                        inorder = 2;
+                        printf("%u-channel input: performing 2nd-order decode.\n",inchans);
+                    }
+                    switch(inchans){
+                    case 3:
+                       copyfunc = fmhcopy_3;
+                        break;
+                    case 4:
+                        copyfunc = fmhcopy_4;
+                        break;
+                    case 5:
+                        copyfunc = fmhcopy_5;
+                        break;
+                    case 6:
+                        copyfunc = fmhcopy_6;
+                        break;
+                    case 9:
+                        copyfunc = fmhcopy_9;
+                        break;
+                    case 11:
+                        copyfunc = fmhcopy_11;
+                        break;
+                    case 16:
+                        copyfunc = fmhcopy_16;
+                        break;
+                    default:
+                        fprintf(stderr,"Unsupported channel count (%u) for B-format file.\n",inchans);
+                        return 1;
+                    }
+                    
+                    switch(layout-1) {
+                    case FM_MONO:
+                        printf("Decoding to Mono\n");
+                        decodefunc = fm_i1_mono;
+                        outchans = 1;
+                        break;
+                    case FM_STEREO:
+                        printf("Decoding to Stereo\n");
+                        decodefunc = fm_i1_stereo;
+                        outchans = 2;
+                        break;
+                    case FM_SQUARE:
+                        printf("Decoding to Square\n");
+                        if(inorder == 1)
+                            decodefunc = fm_i1_square;
+                        else
+                            decodefunc = fm_i2_square;
+                        outchans = 4;
+                        break;
+                    case FM_PENT:
+                        printf("Decoding to pentagon\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_pent;
+                        else
+                            decodefunc = fm_i2_pent;
+                        outchans = 5;
+                        break;
+                    case DM_5_0:
+                        printf("Decoding to 5.0 surround (David Moore)\n");
+                        if(inorder==1)
+                            decodefunc = dm_i1_surr;
+                        else
+                            decodefunc = dm_i2_surr;
+                        outchans = 5;
+                        break;
+                    case DM_5_1:
+                        printf("Decoding to  5.1 surround (David Moore)\n");
+                        if(inorder==1)
+                            decodefunc = dm_i1_surr6;
+                        else
+                            decodefunc = dm_i2_surr6;
+                        outchans = 6;
+                        break;
+                    case FM_HEX:
+                        printf("Decoding to Hexagon\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_hex;
+                        else
+                            decodefunc = fm_i2_hex;
+                        outchans = 6;
+                        break;
+                    case FM_OCT1:
+                        printf("Decoding to Octagon 1\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_oct1;
+                        else
+                            decodefunc = fm_i2_oct1;
+                        outchans = 8;
+                        break;
+                    case FM_OCT2:
+                        printf("Decoding to Octagon 2\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_oct2;
+                        else
+                            decodefunc = fm_i2_oct2;
+                        outchans = 8;
+                        break;
+                    case FM_CUBE:
+                        printf("Decoding to Cube (FM interleaved)\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_cube;
+                        else
+                            decodefunc = fm_i2_cube;
+                        outchans = 8;
+                        break; 
+                    case FM_QUADCUBE:
+                        printf("Decoding to Octagon 1 (WAVEX order)\n");
+                        if(inorder==1)
+                            decodefunc = fm_i1_cubex;
+                        else
+                            decodefunc = fm_i2_cubex;
+                        outchans = 8;
+                        break;
+                    default:
+                        //quad
+                        printf("Decoding to quad surround (WAVEX order)\n");
+                        if(inorder == 1)
+                            decodefunc = fm_i1_quad;
+                        else
+                            decodefunc = fm_i2_quad;
+                        outchans = 4;
+                        break;
+                    }
+                }  // MC_BFMT            
+                
+                /* just read peaks for soundfile */
+                fpeaks = (CHPEAK *) calloc(inchans,sizeof(CHPEAK));
+                if(fpeaks==NULL){
+                    puts("No memory for PEAK data\n");
+                    goto error;
+                }
+                int thispeaktime;
+                res = sndreadpeaks(sfdata.ifd,inchans,fpeaks, &thispeaktime);
+                in_peaktime = (time_t) thispeaktime;
+                if(res==0){
+                    printf("No PEAK data in this file\n");
+                }
+                if(res > 0) {
+                    printf("PEAK data:\ncreation time: %s",ctime( &in_peaktime));
+                    
+                    for(i=0;i < inchans;i++){
+                        printf("CH %d: %.4f at frame %u: \t%.4f secs\n",
+                               i,fpeaks[i].value,fpeaks[i].position,(double)(fpeaks[i].position / (double) props.srate));
+                    }
+                }        
+                break;
+            default:
+                fprintf(stderr,"\nSorry: Pvplay can only play soundfiles and analysis files.");
+                return 1;
+        }
+    }
+
+    /* memory playback mode? */
+    if(playtype == PLAY_SFILE && nFramesToPlay <= ringframelen){
+        sfdata.membuf =  (float *) PaUtil_AllocateMemory(nFramesToPlay * sizeof(float) * /*inchans*/ outchans);
+        if( sfdata.membuf == NULL )   {
+            puts("Could not allocate memory play buffer.\n");
+            goto error;
+        }
+        sfdata.memFramelength = nFramesToPlay;
+        sfdata.memFramePos = 0;
+        playcallback = MemCallback;
+        printf("RAM block size =  %lu\n",nFramesToPlay);
+                    
+    }
+    else {
+        // set up pcm ring buffer  NB must be power of 2 size
+        if(props.srate > 48000)
+            ringframelen <<= 1;
+        printf("File buffer size set to %ld sample frames\n",ringframelen);
+        // NB ring buffer sized for decoded data, hence outchans here; otherwise inchans = outchans
+        sfdata.ringbufData = (float *) PaUtil_AllocateMemory( ringframelen * sizeof(float) * outchans); /* From now on, recordedSamples is initialised. */
+        if( sfdata.ringbufData == NULL )   {
+            puts("Could not allocate play buffer.\n");
+            goto error;
+        }                 
+        // number of elements has to be a power of two, so each element has to be a full m/c frame
+        if (PaUtil_InitializeRingBuffer(&sfdata.ringbuf, sizeof(float) * outchans, ringframelen , sfdata.ringbufData) < 0) {
+            puts("Could not initialise play buffer.\n");
+            goto error;
+        }            
+    } 
+    
+    
+    /* create input side workspace buffer, used for both soundfiles and analysis files! */
+    sfdata.inbuf = (float *) PaUtil_AllocateMemory(ringframelen * sizeof(float) * inchans);
+    if(sfdata.inbuf==NULL){
+        puts("No memory for read buffer\n");
+        goto error;
+    }
+    sfdata.inbuflen = ringframelen;
+    
+    if(playtype == PLAY_PVXFILE && (inchans==2)){
+        sfdata.outbuf_l = (float *) PaUtil_AllocateMemory(ringframelen * sizeof(float) );
+        sfdata.outbuf_r = (float *) PaUtil_AllocateMemory(ringframelen * sizeof(float) );
+        
+    }
+    
+    if(props.srate > 48000)
+        frames_per_buffer *= 2;
+    if(frames_per_buffer > 0)
+        printf("Audio buffer size = %lu\n",frames_per_buffer);
+    
+    sfdata.inchans      = inchans;
+    sfdata.outchans     = outchans;
+    sfdata.frames_played  = 0;
+    sfdata.gain         = gain;
+    sfdata.copyfunc     = copyfunc;
+    sfdata.decodefunc   = decodefunc;
+    sfdata.do_decode    = flags[FLAG_B];
+    sfdata.from_frame   = from_frame;  // interpreted according to Play Type
+    sfdata.to_frame     = to_frame;
+    sfdata.play_looped  = flags[FLAG_L];
+    sfdata.srate = props.srate;
+    sfdata.finished = 0;
+    g_pdata = &sfdata;
+    
+    outputParameters.device = device;   /*Pa_GetDefaultOutputDevice(); */ /* default output device */
+    outputParameters.channelCount = outchans;                     
+    outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output */
+    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    devinfo = (PaDeviceInfo *) Pa_GetDeviceInfo(device);
+#if defined MAC || defined unix
+    if(devinfo){
+        printf("Using device %d: %s\n",device,devinfo->name);
+    }
+#endif
+    
+#ifdef WIN32
+    
+# ifdef NOTDEF
+    if(devinfo){
+        if(apiinfo->type  == paMME ){
+            winmmeStreamInfo.size = sizeof(winmmeStreamInfo);
+            winmmeStreamInfo.hostApiType = paMME; 
+            winmmeStreamInfo.version = 1;
+            winmmeStreamInfo.flags = paWinMmeUseChannelMask;
+            winmmeStreamInfo.channelMask = 0;
+            outputParameters.hostApiSpecificStreamInfo = &winmmeStreamInfo; 
+#  ifdef _DEBUG
+            printf("WIN DEBUG: WinMME device channel mask set to 0.\n");
+#  endif
+        }
+    }
+# endif
+    /* change val to 1 if also using MME */
+    if(devinfo){
+        int apitype = devinfo->hostApi; // get index of api type
+        const PaHostApiInfo *apiinfo =  Pa_GetHostApiInfo( apitype );
+        printf("Using device %d: %s:",device,devinfo->name);
+        if(apiinfo->type  == paDirectSound) {
+            printf ("(DS)\n");
+            /* set this IF we are using Dsound device. */
+            directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
+            directSoundStreamInfo.hostApiType = paDirectSound; 
+            directSoundStreamInfo.version = 2;
+            directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
+            directSoundStreamInfo.channelMask = speakermask;
+            outputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo;
+        }
+        else if(apiinfo->type == paASIO)
+            printf("(ASIO)\n");
+        // else
+        //    printf("API unknown!);
+    }
+#endif    
+    
+    
+    err = Pa_OpenStream(
+            &stream,
+            NULL,  /* No input */
+            &outputParameters, /* As above. */
+            props.srate,
+            frames_per_buffer,          /* Frames per buffer. */
+            paClipOff,      /* we won't output out of range samples so don't bother clipping them */
+            playcallback,
+            &sfdata );
+    
+    if( err != paNoError ){
+        fprintf(stderr,"Unable to open audio device: err = %d\n", err);   
+        exit(1);
+    }
+    err =  Pa_SetStreamFinishedCallback( stream, finishedCallback );
+    if( err != paNoError ) {
+        printf("Unable to set finish callback\n");
+        goto error;
+    }
+    sfdata.stream = stream;
+    file_playing = 1;
+
+    if(waitkey){
+        printf("Press any key to start:\n");
+        printf("Hit CTRL-C to stop.\n");
+        fflush(stdout);
+        while (!kbhit()){   
+            if(!file_playing)    //check for instant CTRL-C
+                goto error;     
+            };
+#ifdef _WIN32
+        if(kbhit())
+            _getch();            //prevent display of char
+#endif
+    }
+
+    // should this go in read thread func too?
+    
+    
+#ifdef NOTDEF
+    // TODO: any benefits in using this?
+    pterror = Pt_Start(200, porttimeCallback, NULL);
+    if(pterror != ptNoError){
+        printf("porttime timer failed to initialise!\n");
+        return 1;
+    }
+#endif
+    
+    /* if small block, read it all into memory
+     NB not doing paplay channel mapping  */
+    if(sfdata.memFramelength > 0){
+        //if(psf_sndReadFloatFrames(sfdata.ifd,sfdata.membuf,sfdata.memFramelength) 
+        if(fgetfbufEx(sfdata.membuf,sfdata.memFramelength*sfdata.inchans,sfdata.ifd,0)
+           != (int) sfdata.memFramelength*sfdata.inchans) {
+            printf("Error reading soundfile into memory\n");
+            goto error;
+        }
+    }
+    
+    // set up timer
+#ifdef unix
+    if(do_updatemessages) {
+        setitimer(ITIMER_REAL, &tout_val,0);
+        signal(SIGALRM,alarm_wakeup); /* set the Alarm signal capture */
+    }
+#endif
+//#ifdef _WIN32
+    // not sure of the best position for this
+    //if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
+    //    (WAITORTIMERCALLBACK) TimerCallback, &sfdata,250,250,0)) {
+    //    printf("failed to start timer (3).\n");
+    //    return 1;
+    // }
+//#endif
+    /* Start the file reading thread */
+    
+    sfdata.startTime = Pa_GetStreamTime(stream );
+    
+    switch(playtype){
+        case PLAY_SFILE:    
+            if(sfdata.memFramelength == 0){
+                err = startThread(&sfdata, threadFunctionReadFromRawFile);
+                if( err != paNoError ) goto error;
+            }
+            else {
+                sfdata.flag = 0;
+            }
+            break;
+        case PLAY_ANAFILE:
+            err = startThread(&sfdata, threadFunctionReadFromAnalFile);
+            if( err != paNoError ) goto error;
+            break;
+        case PLAY_PVXFILE:
+            err = startThread(&sfdata, threadFunctionReadFromPVXFile);
+            if( err != paNoError ) goto error;
+            break;
+        default:
+            printf("Internal error: no playback file type!\n");
+            return 1;
+            
+    }
+    
+#ifdef _WIN32
+    if(do_updatemessages){
+        if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
+                                  (WAITORTIMERCALLBACK) TimerCallback, &sfdata,200,200,0)) {
+            printf("failed to start timer (3).\n");
+            return 1;
+        }
+    }
+#endif
+    err = Pa_StartStream( stream );
+    if( err != paNoError ) {
+        fprintf(stderr,"Unable to start playback.\n");
+        goto error;
+    }
+    
+    while((!sfdata.finished) && file_playing){
+        // nothing to do!
+        Pa_Sleep(20);
+    }
+    // note to the curious: any bug in audio buffer arithmetic will likely cause crash here!
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) {
+        printf("Error stopping stream\n");
+        goto error;
+    }
+    /*  read thread should exit always, no need to Stop it here */
+    
+    err = Pa_CloseStream( stream ); 
+    if( err != paNoError ) {
+        printf("Error closing stream\n");
+        goto error;
+    }
+    printf("%.2f secs\n",(float)(sfdata.lastTime));
+    fflush(stdout);
+    printf("Playback finished.\n");
+error:
+    Pa_Terminate();
+#ifdef _WIN32
+    CloseHandle(ghEvent);
+    DeleteTimerQueue(hTimerQueue);
+#endif
+    if( sfdata.ringbufData )       
+        PaUtil_FreeMemory(sfdata.ringbufData);
+    
+    if(sfdata.inbuf)
+        PaUtil_FreeMemory(sfdata.inbuf);
+    
+    if(sfdata.membuf)
+        PaUtil_FreeMemory(sfdata.membuf);
+    
+    if(sfdata.outbuf_l)
+        PaUtil_FreeMemory(sfdata.outbuf_l);
+    
+    if(sfdata.outbuf_r)
+        PaUtil_FreeMemory(sfdata.outbuf_r); 
+    
+    if(sfdata.ifd >= 0)
+        sndcloseEx(sfdata.ifd);
+    
+    if(sfdata.pvfile >= 0)
+        pvoc_closefile(sfdata.pvfile);
+        
+    if(fpeaks)
+        free(fpeaks);
+    
+//    sffinish();
+    
+    pvsys_release();
+    
+    if(err != paNoError){
+        fprintf( stderr, "An error occured while using the portaudio stream\n" ); 
+        fprintf( stderr, "Error number: %d\n", err );
+        fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+        return err;
+    }
+    return 0;
+}
+
+int show_devices(void)
+{
+//      int i,j;
+        PaDeviceIndex numDevices,p;
+        const    PaDeviceInfo *pdi;
+#ifdef _WIN32
+        const PaHostApiInfo* api_info;
+#endif
+        PaError  err;
+        int nOutputDevices = 0;
+        
+#ifdef USE_ASIO
+        printf("For ASIO multi-channel, you may need to select the highest device no.\n");      
+#endif
+        /*Pa_Initialize();*/
+        numDevices =  Pa_GetDeviceCount();
+        if( numDevices < 0 )
+        {
+            printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+            err = numDevices;
+            return err;
+        }
+#ifdef WIN32
+        printf("Driver\tDevice\tInput\tOutput\tName\n");
+#else
+        printf("Device\tInput\tOutput\tName\n");
+#endif
+        //printf("Number of devices = %d\n", numDevices );
+        for( p=0; p<numDevices; p++ )
+        {
+            pdi = Pa_GetDeviceInfo( p );
+            nOutputDevices++;
+            
+            if( p == Pa_GetDefaultOutputDevice() ) 
+                printf("*");
+            else
+                printf(" ");
+#ifdef WIN32
+            api_info = Pa_GetHostApiInfo(pdi->hostApi);
+            const char *apiname = api_info->name;
+            if(strcmp(apiname,"Windows DirectSound")==0)
+                apiname = "DS ";
+            printf("(%s)\t%d\t%d\t%d\t%s\n",apiname,p,
+                pdi->maxInputChannels,
+                pdi->maxOutputChannels,
+                pdi->name); 
+#else
+            printf("%d\t%d\t%d\t%s\n",p,
+                pdi->maxInputChannels,
+                pdi->maxOutputChannels,
+                pdi->name);
+
+
+#endif
+        }
+        /*Pa_Terminate();*/
+        return 0;
+}
+
+#ifdef unix
+int stricmp(const char *a, const char *b)
+{
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+        
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+        
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+
+int
+strnicmp(const char *a, const char *b, const int length)
+{
+    int len = length;
+    
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+        
+        if(len-- < 1)
+            return 0;
+        
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+        
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+#endif
+
+
+

+ 233 - 0
dev/externals/paprogs/pvplay/pvplay.h

@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+ 
+//
+//  pvplay.h
+//  
+//
+//  Created by Archer on 28/01/2013.
+//  Copyright (c) 2013 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef _pvthreads_h
+#define _pvthreads_h
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <time.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "portaudio.h"
+//#include "porttime.h"
+#include "pa_ringbuffer.h"
+#include "pa_util.h"
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <windows.h>
+#include <conio.h>
+#include <process.h>
+#include "pa_win_wmme.h"
+#include "pa_win_ds.h"
+#endif
+
+
+#ifdef unix
+#include <sys/types.h>
+#include <sys/timeb.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <ctype.h>
+/* in portsf.lib */
+int stricmp(const char *a, const char *b);
+int strnicmp(const char *a, const char *b, const int length);
+
+#endif
+
+#ifdef CPLONG64
+
+#define MYLONG int
+#else
+#define MYLONG long
+#endif
+
+
+/* need this to avoid clashes of GUID defs, etc */
+#ifdef _WIN32
+#ifndef _WINDOWS
+#define _WINDOWS
+#endif
+#endif
+
+
+
+/* sfsys just for analysis files! */
+#ifdef __cplusplus
+extern "C" {	
+#endif
+#include "sfsys.h"
+#include "fmdcode.h"
+#include "pvfileio.h"
+#ifdef __cplusplus
+}
+#endif
+
+#include "pvpp.h"
+
+#if defined MAC || defined unix
+#include <aaio.h>
+#endif
+
+#ifdef MAC
+#include <libkern/OSAtomic.h>
+#endif
+
+#ifndef min
+#define min(x,y)  ((x) < (y) ? (x) : (y))
+#endif
+
+#ifdef _WIN32
+extern HANDLE ghEvent;
+#endif
+
+// paplayt
+//typedef struct {
+//	int sfd;
+//    int chans;
+//	psf_buffer *outbuf;
+//	unsigned long frames_played;
+//	int last_buffer; /* init to 1; at detected eof send one extra buffer to flush, and set this to 0 */
+//} psfdata;
+
+
+typedef struct {
+	float *buffer;
+	long nframes;
+	MYLONG used;
+}
+psf_buffer;
+
+typedef struct {
+    PaUtilRingBuffer ringbuf;
+    PaStream *stream;
+    PaTime startTime;
+    PaTime lastTime;
+	float *ringbufData;
+    float *inbuf;   //for decoding and other tx; used as temp working buffer for read thread func.
+    /* for memory read func */
+    float *membuf;
+/* analysis file vars */
+    // pv objects
+    phasevocoder *pv_l;
+    phasevocoder *pv_r;
+    float *analframe1;
+    float *analframe2;
+    int anal_framesize; // in floats
+    int fftsize;        // sample frames from resynthesis of analysis frame(s)
+    float *outbuf_l;    // stereo pvx files - needed?
+    float *outbuf_r;
+    
+//    float** orderptr; // for chan mapping;
+#ifdef _WIN32
+    void *hThread;
+    HANDLE hTimer;
+    HANDLE hTimerCount;
+#endif
+  
+#ifdef unix
+    pthread_t hThread;
+#endif
+    fmhdecodefunc decodefunc;
+    fmhcopyfunc copyfunc;
+    double gain;
+	unsigned long frames_played;  // for .ana, .pvx files, these are analysis frames
+    unsigned long from_frame;
+    unsigned long to_frame;
+    unsigned long memFramelength;
+    unsigned long memFramePos;
+    unsigned long inbuflen;
+    unsigned long current_frame;
+    int srate;
+    int inchans;
+    int outchans;
+    int flag;
+	int ifd; // soundfiles, .ana files
+    int pvfile; // pvx file
+    pvoc_frametype inframetype;
+#ifdef DEBUG
+    int ofd;
+#endif
+    int do_decode;
+//    int do_mapping;
+    int play_looped;
+    int finished;
+} psfdata;
+
+#ifdef NOTDEF
+typedef struct {
+    PaStream *stream;
+    PaTime starttime /* = Pa_GetStreamTime(stream)*/;
+} ptimedata;
+#endif
+
+//extern int file_playing;
+//extern psfdata *g_pdata = NULL;  // for timer interrupt routine
+
+void stereo_interls(const float *in_l,const float *in_r,float *out,long insize);
+int show_devices(void);
+
+
+/* handlers */
+void playhandler(int sig);
+void finishedCallback(void *userData);
+#ifdef unix
+void alarm_wakeup (int i);
+#endif
+#ifdef _WIN32
+VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired);
+#endif
+
+
+
+
+/* playback thread functions */
+
+
+#ifdef unix
+// TODO: unix return type should be void*
+typedef void* (*threadfunc)(void*);
+void*  threadFunctionReadFromRawFile(void* ptr);
+void*  threadFunctionReadFromAnalFile(void* ptr);
+void*  threadFunctionReadFromPVXFile(void* ptr);
+#endif
+
+
+#ifdef _WIN32
+typedef unsigned int (__stdcall *threadfunc)(void*);
+// soundfile
+unsigned int __stdcall threadFunctionReadFromRawFile(void* ptr);
+unsigned int __stdcall threadFunctionReadFromAnalFile(void* ptr);
+unsigned int __stdcall threadFunctionReadFromPVXFile(void* ptr);
+#endif
+
+
+#endif

+ 247 - 0
dev/externals/paprogs/pvplay/pvpp.h

@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+ 
+/*pvpp.h*/
+/* class def for pvoc wrapper */
+#ifndef __PVPP_H_INCLUDED__
+#define __PVPP_H_INCLUDED__
+
+#ifndef MIN
+#define MIN(x,y)    ( ((x)>(y)) ? (y) : (x) )
+#endif
+
+#ifndef MAX
+#define MAX(x,y)    ( ((x)>(y)) ? (x) : (y) )
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+//#include <pvfileio.h>
+#endif
+    
+#include "pvdefs.h"
+#ifdef USE_FFTW
+#include <rfftw.h>
+#endif
+    
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef MAC
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <CoreServices/CoreServices.h>
+#include <Accelerate/Accelerate.h>
+#endif      
+    
+//}
+
+
+// must have -faltivec in C compiler flags
+#if defined(__VEC__)
+Boolean HasAltiVec(void);
+void TurnJavaModeOffOnG4(void);
+void RestoreJavaModeOnG4(void);
+#endif
+
+#ifdef NOTDEF
+typedef enum pvocmode {  PVPP_NOT_SET,PVPP_OFFLINE,PVPP_STREAMING};
+
+typedef enum pvoc_overlapfac {PVOC_O_W0,PVOC_O_W1,PVOC_O_W2,PVOC_O_W3,PVOC_O_DEFAULT};
+typedef enum pvoc_scaletype {PVOC_S_TIME,PVOC_S_PITCH,PVOC_S_NONE};
+
+typedef enum pvoc_frametype { PVOC_AMP_FREQ,PVOC_AMP_PHASE,PVOC_COMPLEX };
+typedef enum pvoc_windowtype {PVOC_DEFAULT,
+                                PVOC_HAMMING,
+                                PVOC_HANN,
+                                PVOC_KAISER,
+                                PVOC_RECT,
+                                PVOC_CUSTOM};
+#endif
+class phasevocoder
+{
+/* some may be made protected in due course, for derived classes */
+private:
+    double  rratio;
+    float   *input,     /* pointer to start of input buffer */
+        *output,        /* pointer to start of output buffer */
+        *anal,          /* pointer to start of analysis buffer */
+        *syn,           /* pointer to start of synthesis buffer */
+        *banal,         /* pointer to anal[1] (for FFT calls) */
+        *bsyn,          /* pointer to syn[1]  (for FFT calls) */
+        *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 */
+        *maxAmp,        /* pointer to start of max amp buffer */
+        *avgAmp,        /* pointer to start of avg amp buffer */
+        *avgFrq,        /* pointer to start of avg frq buffer */
+        *env,           /* pointer to start of spectral envelope */
+        *i0,            /* pointer to amplitude channels */
+        *i1,            /* pointer to frequency channels */
+        *oi,            /* pointer to old phase channels */
+        *oldInPhase,    /* pointer to start of input phase buffer */
+        *oldOutPhase;   /* pointer to start of output phase buffer */
+
+    int   m, n;
+
+    int N ,         /* number of phase vocoder channels (bands) */
+        M,          /* length of analWindow impulse response */
+        L,          /* length of synWindow impulse response */
+        D,          /* decimation factor (default will be M/8) */
+        I,          /* interpolation factor (default will be I=D)*/
+        W ,         /* filter overlap factor (determines M, L) */
+        /*RWD: want EXACT frequency! */
+    //  F,          /* fundamental frequency (determines N) */
+    //  F2,         /* F/2 */    /* RWD NOT USED */
+        
+        K,          /* set kaiser window*/
+        H,          /* set vonHann window */
+        analWinLen,     /* half-length of analysis window */
+        synWinLen;      /* half-length of synthesis window */
+    /* RWD see above */
+    float Fexact;
+
+    long // tmprate,    /* temporary variable */
+        sampsize,       /* sample size for output file */
+        //origrate,     /* sample rate of file analysed */
+        outCount,       /* number of samples written to output */
+        ibuflen,        /* length of input buffer */
+        obuflen,        /* length of output buffer */
+        nI,         /* current input (analysis) sample */
+        nO,             /* current output (synthesis) sample */
+        nMaxOut,        /* last output (synthesis) sample */
+        nMin,       /* first input (analysis) sample */
+        nMax;   /* last input sample (unless EOF) */
+/***************************** 6:2:91  OLD CODE **************
+                        long    origsize;
+*******************************NEW CODE **********************/
+    long    origsize;   /* sample type of file analysed */
+
+    char    ch;     /* needed for crack (commandline interpreter)*/
+    
+    int ifd, ofd;   /* CDP sound file handles */
+    float   beta ,  /* parameter for Kaiser window */
+        real,       /* real part of analysis data */
+        imag,       /* imaginary part of analysis data */
+        mag,        /* magnitude of analysis data */
+        phase,      /* phase of analysis data */
+        angleDif,   /* angle difference */
+        RoverTwoPi, /* R/D divided by 2*Pi */
+        TwoPioverR, /* 2*Pi divided by R/I */
+        sum,        /* scale factor for renormalizing windows */
+        ftot,   /* scale factor for calculating statistics */
+        rIn,        /* decimated sampling rate */
+        rOut,       /* pre-interpolated sampling rate */
+        invR,       /* 1. / srate */
+        time,       /* nI / srate */
+        tvx0,       /* current x value of time-var function */
+        tvx1,       /* next x value of time-var function */
+        tvdx,       /* tvx1 - tvx0 */
+        tvy0,       /* current y value of time-var function */
+        tvy1,       /* next y value of time-var function */
+        tvdy,       /* tvy1 - tvy0 */
+        frac,       /* tvdy / tvdx */
+        warp,   /* spectral envelope warp factor */
+        R ,     /* input sampling rate */
+        P ,     /* pitch scale factor */
+        Pinv,       /* 1. / P */
+        T;      /* time scale factor ( >1 to expand)*/
+
+    int i,j,k,      /* index variables */
+        Dd,     /* number of new inputs to read (Dd <= D) */
+        Ii,     /* number of new outputs to write (Ii <= I) */
+        N2,     /* N/2 */
+        NO,     /* synthesis NO = N / P */
+        NO2,        /* NO/2 */
+        IO,     /* synthesis IO = I / P */
+        IOi,        /* synthesis IOi = Ii / P */
+        //Mlen,
+        Mf,     /* flag for even M */
+        Lf,     /* flag for even L */
+        //Dfac,
+        flag,       /* end-of-input flag */
+        C,      /* flag for resynthesizing even or odd chans */
+        Ci,     /* flag for resynthesizing chans i to j only */
+        Cj,     /* flag for resynthesizing chans i to j only */
+        CC,     /* flag for selected channel resynthesis */
+        X,      /* flag for magnitude output */
+        E,      /* flag for spectral envelope output */ 
+        tvflg,  /* flag for time-varying time-scaling */
+        tvnxt,      /* counter for stepping thru time-var func */
+        tvlen;      /* length of time-varying function */
+    
+    float   srate;      /* sample rate from header on stdin */
+        
+    float   timecheckf;
+    long    isr,            /* sampling rate */
+    Nchans;         /* no of chans */
+
+    /* my vars */
+    int vH;             /* von Hann window */
+    pvocmode m_mode;
+    long  bin_index;    /* index into oldOutPhase to do fast norm_phase */ 
+    float *synWindow_base;
+    float *analWindow_base;
+#ifdef USE_FFTW
+    rfftwnd_plan forward_plan, inverse_plan;
+    int in_fftw_size,out_fftw_size;
+    float Ninv;
+#endif
+protected:
+    double besseli( double x);           /* from Csound*/
+    void hamming(float *win,int winLen,int even);
+    void kaiser(float *win,int len,double Beta);     /* from Csound */
+    void vonhann(float *win,int winLen,int even);
+
+public:
+    phasevocoder();
+    virtual ~phasevocoder();
+    
+
+    /* neither init func inputs all possible pvoc parameters - these are two possible subsets */
+    bool init(long outsrate,long fftlen,long winlen,long decfac,float scalefac,
+                        pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode);
+    /* when pvoc -W flag used (ofac), it sets anal window size, decimation, etc */
+    bool init(long outsrate,long fftlen,pvoc_overlapfac ofac,float scalefac,
+                        pvoc_scaletype stype,pvoc_windowtype wtype,pvocmode mode);
+    /* to come: full init using PVOCDATA etc, or directly from another phasevocoder */
+    long anal_overlap(void) { return D;}
+    long syn_interp(void)   { 
+                    if(P != 1.0f) 
+                        return D;
+                    else 
+                        return I;}
+    long fftlen(void)  {return N;}
+    long winlen(void) { return M;}
+    /*retval gives numsamps written to outbuf, -1 for error */
+    long process_frame(float *frame,float *outbuf,pvoc_frametype frametype);    
+    long process_frame(float *frame,short *outbuf,pvoc_frametype frametype);
+    /* returns (variable) numsamps written to outbuf*/
+    long generate_frame(float *fbuf,float *outanal,long samps,pvoc_frametype frametype);
+    void reset_phases(void);
+#ifdef USE_CDP
+    void setprops(SFPROPS *props);
+#endif
+};
+
+#endif

+ 487 - 0
dev/externals/paprogs/pvplay/pvthreads.cpp

@@ -0,0 +1,487 @@
+
+/*
+ * Copyright (c) 1983-2020 Richard Dobson and Composers Desktop Project Ltd
+ * http://www.rwdobson.com
+ * 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
+ *
+ */
+
+/*  pvthreads.c: thread functions for pvplay */
+/* Jan 2014: completed pvx support */
+
+#include "pvplay.h"
+#include <assert.h>
+
+extern int file_playing;
+extern psfdata *g_pdata;
+
+__inline void stereo_interls(const float *in_l,const float *in_r,float *out,long insize)
+{
+    long i;
+    const float *pfl_l,*pfl_r;
+    float*pfl_o;
+    pfl_o = out;
+    pfl_l = in_l;
+    pfl_r = in_r;
+
+    for(i=insize;i;--i){
+        *pfl_o++ = *pfl_l++;
+        *pfl_o++ = *pfl_r++;
+    }
+}
+
+/* soundfile */
+
+/* writes decoded data to ring buffer */
+/* size of element is full m/c frame */
+// TODO: unix thread should return void*
+#ifdef unix
+/*int*/void*  threadFunctionReadFromRawFile(void* ptr)
+#else
+unsigned int __stdcall threadFunctionReadFromRawFile(void* ptr)
+#endif
+{
+    psfdata* pdata = (psfdata*)ptr;
+    
+    /* Mark thread started */
+    pdata->flag = 0;
+    //printf("thread: from_frame = %d, to_frame = %d, current_Frame = %d\n",pdata->from_frame, pdata->to_frame, pdata->current_frame);
+    while (1) {
+        ring_buffer_size_t nElementsProcessed = 0;
+        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
+           
+        if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
+            void* ptr[2] = {NULL};
+            ring_buffer_size_t sizes[2] = {0};
+            
+            /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
+            PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
+            
+            if (file_playing)  {
+                ring_buffer_size_t itemsReadFromFile;
+                int i,j;
+                int framesread = 0;
+                int sampswanted = 0;
+                int sampsread = 0;
+                // we work with frames, = constant across inchans and outchans
+                itemsReadFromFile = 0;
+                
+                for(i = 0; i < 2 && (ptr[i] != NULL); i++) {
+                    // NB ringbuf is sized by m/c frames
+                    int frameswanted = sizes[i];
+                    
+                    pdata->current_frame = sndtellEx(pdata->ifd) / pdata->inchans;
+                    // read up to end frame if requested
+                    if(pdata->to_frame < pdata->current_frame + frameswanted)
+                        frameswanted = pdata->to_frame - pdata->current_frame;
+                    
+                    if(frameswanted > 0){
+                        sampswanted = frameswanted * pdata->inchans;
+                        sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
+                        framesread = sampsread / pdata->inchans;
+                        
+                        if(framesread < 0){ // read error!
+                            printf("Error reading soundfile: %s ifd = %d\n", sferrstr(),pdata->ifd);
+                            pdata->flag = 1;
+                            file_playing = 0;
+                            break;  // just out of for loop - need to return instead?
+                        }
+                    }
+                    if(framesread == 0){
+                        /* EOF. EITHER: finish, or rewind if looping playback*/
+                        if(pdata->play_looped){
+                            if(sndseekEx(pdata->ifd,pdata->from_frame * pdata->inchans,SEEK_SET) < 0){
+                                printf("Error looping soundfile\n");
+                                pdata->flag = 1;
+                                file_playing = 0;
+                                break;
+                            }
+                            // sizes[1] especially may well = 0
+                            if(frameswanted==0)
+                                break;
+                            sampswanted = frameswanted * pdata->inchans;
+                            sampsread = fgetfbufEx(pdata->inbuf,sampswanted,pdata->ifd,0);
+                            framesread = sampsread / pdata->inchans;
+                            if(framesread < 0){ // read error!
+                                printf("Error reading soundfile\n");
+                                pdata->flag = 1;
+                                file_playing = 0;
+                                break;
+                            }
+                        }
+                        else {
+                            // we must watch the ring buffer to make sure all data has been rendered,
+                            // over several callback blocks
+                            //printf("End of data. playing = %d:\n", file_playing);
+                            //printf("\telements remaining = %d\n",elementsInBuffer);
+                            if(elementsInBuffer == pdata->ringbuf.bufferSize)  {
+                                pdata->flag = 1;
+                                break;
+                            }
+                        }
+                    }
+                    else {
+                        // ringbuf calcs always relative to outchans
+                        itemsReadFromFile += framesread;
+                        
+                        // now ready to apply decoding or other processing
+                        if(pdata->gain != 1.0){
+                            for(j = 0; j < framesread * pdata->inchans; j++)
+                                pdata->inbuf[j]  = (float)(pdata->inbuf[j] * pdata->gain);
+                        }
+                        // no channel mapping in pvplay
+                        if(pdata->do_decode) {
+                            ABFSAMPLE abfsamp;
+                            
+                            for(j=0; j < framesread; j++){
+                                // single frame only
+                                pdata->copyfunc(&abfsamp,pdata->inbuf + (j * pdata->inchans));
+                                /* BIG TODO: update funcs to process large frame buffer! */
+                                /* NB: ring buffer is effectively defined as raw bytes */
+                                pdata->decodefunc(&abfsamp, (float*)(ptr[i]) + (j * pdata->outchans), 1);
+                                nElementsProcessed++;
+                            }
+                        }
+                        else {  // inchans = outchans
+                            memcpy(ptr[i],pdata->inbuf,framesread * sizeof(float) * pdata->inchans);
+                            nElementsProcessed += framesread;
+                        }
+                    }
+                }
+                PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, itemsReadFromFile);
+            }
+            else {
+                // this code is activated on Ctrl-C. Can do immediate finish by setting flag
+                //    printf("file done\n");
+                pdata->flag = 1;  
+                break;
+            }
+        }
+        //  else {
+        //      printf("ringbuf size = %d, elements remaining = %d, playing = %d\n",pdata->ringbuf.bufferSize,elementsInBuffer,file_playing);
+        //  }
+
+        /* Sleep a little while...! */
+        Pa_Sleep(10);
+    }
+    return 0;
+}
+
+/*  versions for .ana file (always mono only) */
+
+#ifdef unix
+/*int*/void*  threadFunctionReadFromAnalFile(void* ptr)
+#else
+unsigned int __stdcall threadFunctionReadFromAnalFile(void* ptr)
+#endif
+{
+    psfdata* pdata = (psfdata*)ptr;
+    
+    /* Mark thread started */
+    pdata->flag = 0;
+    
+    while (1) {
+        ring_buffer_size_t nElementsProcessed = 0;
+        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
+        
+        if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
+            void* ptr[2] = {NULL};
+            ring_buffer_size_t sizes[2] = {0};
+            
+            //read and analyse  as many frames as resynth space is available for, into inbuf
+            // then distribute to ringbuf sections as necessary
+            //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
+            int framestoget = elementsInBuffer / pdata->fftsize;
+            //assert(framestoget); // must be at least one, or ring buffer is too small!
+            if(framestoget==0)
+                break;
+            float *pbuf = pdata->inbuf;
+            int i, got = 0;
+            int elementstowrite = 0;
+            
+            if (file_playing)  {
+                /* find out where we are ... */
+                long framepos1 = sndtellEx(pdata->ifd) / pdata->anal_framesize;
+                
+                for(i = 0;i < framestoget;i++){ 
+                    /* read one frame*/
+                    got = fgetfbufEx(pdata->analframe1,pdata->anal_framesize,pdata->ifd,0);
+                    if(got == -1) {
+                        fprintf(stderr,"\nError reading from sfile\n");
+                        file_playing  = 0;
+                        break;
+                    }
+                    if(got != pdata->anal_framesize) {
+                        if(got > 0) {
+                            fprintf(stderr,"Infile error: incomplete analysis frame encountered\n");
+                            file_playing  = 0;
+                            break;
+                        }
+                    }
+                    framepos1++;
+                    
+                    if(got && (framepos1 <= pdata->to_frame)) {
+                        long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
+                        elementstowrite += samps;
+                        pbuf += samps;
+                    }
+                    
+                    if (got==0 || framepos1 >= pdata->to_frame){
+                        if(pdata->play_looped) {
+                            long pos;
+                            pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
+                            if(pos < 0){
+                                fprintf(stderr,"Error rewinding frame loop.\n");
+                                file_playing  = 0;
+                                break;
+                            }
+                        }
+                        else if(elementsInBuffer == pdata->ringbuf.bufferSize)  {
+                            //printf("buffer empty!\n");
+                            pdata->flag = 1;
+                            break;
+                        }
+                    }  
+                }
+                
+                //apply gain to inbuf first, then copy to ringbuf
+                if(pdata->gain != 1.0) {
+                    for(i=0;i < elementstowrite;i++)
+                        pdata->inbuf[i]  = (float)(pdata->inbuf[i] * pdata->gain);
+                }
+                /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
+                PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
+                pbuf = pdata->inbuf;
+                //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
+                for(i = 0; i < 2; i++) {
+                    if(ptr[i]) {
+                        int frameswanted = sizes[i];
+                        memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
+                        pbuf += frameswanted * pdata->inchans;
+                        nElementsProcessed += frameswanted;
+                    }
+                }
+                if(elementstowrite)
+                    PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
+            } // file_playing
+            else {
+                // this code is activated on Ctrl-C. Can do immediate finish by setting flag
+                //               printf("file done\n");
+                pdata->flag = 1;  
+                break;
+            }
+        }
+        /* Sleep a little while... */
+        Pa_Sleep(5);
+    }    
+    return 0;
+}
+
+// version for pvx format, just mono and stereo supported for now
+#ifdef unix
+/*int*/void*  threadFunctionReadFromPVXFile(void* ptr)
+#else
+unsigned int __stdcall threadFunctionReadFromPVXFile(void* ptr)
+#endif
+{
+    psfdata* pdata = (psfdata*)ptr;
+
+    /* Mark thread started */
+    pdata->flag = 0;
+
+    while (1) {
+        ring_buffer_size_t nElementsProcessed = 0;
+        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pdata->ringbuf);
+
+        // we remember that an element is a frame (mono or stereo)
+        if (elementsInBuffer >= pdata->ringbuf.bufferSize / 4) {
+            void* ptr[2] = {NULL};
+            ring_buffer_size_t sizes[2] = {0};
+
+            //read and analyse  as many frames as resynth space is available for, into inbuf
+            // then distribute to ringbuf sections as necessary
+            //printf("from_frame = %d,to_frame = %d\n",pdata->from_frame,pdata->to_frame);
+            /* pvx : file framecount is of single frames - need enough space to synth a single mc analysis frame */
+            /* but to_frame and from_frame are m/c frame count */
+            int framestoget = (elementsInBuffer  / pdata->fftsize);
+
+            if(framestoget==0)
+                break;
+
+            int i, got = 0;
+            int elementstowrite = 0;
+
+            if(pdata->inchans==1) {
+                float *pbuf = pdata->inbuf;
+                if (file_playing)  {
+                    /* find out where we are ...multi-chan frame count */
+                    long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
+                    if(framepos < 0){
+                        fprintf(stderr,"\nError reading file frame position\n");
+                        file_playing  = 0;
+                        break;  
+                    }
+                    for(i = 0;i < framestoget;i++){ 
+                        /* read one frame*/
+                        got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
+                        if(got == -1) {
+                            fprintf(stderr,"\nError reading from sfile\n");
+                            file_playing  = 0;
+                            break;
+                        }
+
+                        framepos++;
+
+                        if(got && (framepos <= pdata->to_frame)) {
+                            long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
+                            elementstowrite += samps;
+                            pbuf += samps;
+                        }
+
+                        if (got==0 || framepos >= pdata->to_frame){
+                            if(pdata->play_looped) {
+                                if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
+                                    fprintf(stderr,"Error rewinding frame loop.\n");
+                                    file_playing  = 0;
+                                    break;
+                                }
+                            }
+                            else if(elementsInBuffer == pdata->ringbuf.bufferSize)  {
+                                //printf("buffer empty!\n");  
+                                pdata->flag = 1;
+                                break;
+                            }
+                        }
+                    }  // framestoget
+
+                    //apply gain to inbuf first, then copy to ringbuf
+                    if(pdata->gain != 1.0) {
+                        for(i=0;i < elementstowrite;i++)
+                            pdata->inbuf[i]  = (float)(pdata->inbuf[i] * pdata->gain);
+                    }
+                    /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
+                    PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
+                    pbuf = pdata->inbuf;
+                    //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
+                    for(i = 0; i < 2; i++) {        
+                        if(ptr[i]) {
+                            int frameswanted = sizes[i];
+                            memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
+                            pbuf += frameswanted * pdata->inchans;
+                            nElementsProcessed += frameswanted;
+                        }
+                    }
+                    if(elementstowrite)
+                        PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
+                } // file_playing
+                else {
+                    // this code is activated on Ctrl-C. Can do immediate finish by setting flag
+                    //               printf("file done\n");
+                    pdata->flag = 1;  
+                    break;
+                }
+            }  //inchans==1
+            else if (pdata->inchans==2) {
+                float *pbuf = pdata->outbuf_l;
+                float *pbuf_r = pdata->outbuf_r;
+                // framestoget is count of m/c analysis frames to read
+                if(file_playing){
+                    /* find out where we are ...multi-chan frame count */
+                    long framepos = pvoc_framepos(pdata->pvfile) / pdata->inchans; // NB
+                    if(framepos < 0){
+                        fprintf(stderr,"\nError reading file frame position\n");
+                        file_playing  = 0;
+                        break;
+                    }
+                    for(i = 0;i < framestoget;i++){
+                        /* read one stereo frame*/
+                        got = pvoc_getframes(pdata->pvfile,pdata->analframe1,1);
+                        if(got == -1) {
+                            fprintf(stderr,"\nError reading ch 1 from pvx file\n");
+                            file_playing  = 0;
+                            break;
+                        }
+                        if(got){
+                            got = pvoc_getframes(pdata->pvfile,pdata->analframe2,1);
+                            if(got == -1) {
+                                fprintf(stderr,"\nError reading ch 2 from pvx file, frame %lu\n",framepos);
+                                file_playing  = 0;
+                                break;
+                            }
+                        }
+                        framepos++;
+                        if(got && (framepos <= pdata->to_frame)) {
+                            // each call returns <overlap> samples
+                            long samps = pdata->pv_l->process_frame(pdata->analframe1,pbuf,PVOC_AMP_FREQ);
+                            pbuf += samps;
+                            samps = pdata->pv_r->process_frame(pdata->analframe2,pbuf_r,PVOC_AMP_FREQ);
+                            pbuf_r += samps;
+                            
+                            elementstowrite += samps; // element is a stereo sample frame
+                        }
+                        if (got==0 || framepos >= pdata->to_frame){
+                            if(pdata->play_looped) {
+                                //pos = sndseekEx(pdata->ifd,pdata->from_frame * pdata->anal_framesize,SEEK_SET);
+                                //if(pvoc_rewind(pdata->pvfile,1)){  /* 1 = skip empty frame */
+                                if(pvoc_seek_mcframe(pdata->pvfile,pdata->from_frame,SEEK_SET)) {
+                                    fprintf(stderr,"Error rewinding frame loop.\n");
+                                    file_playing  = 0;
+                                    break;
+                                }
+                            }
+                            else if(elementsInBuffer == pdata->ringbuf.bufferSize)  {
+                                //printf("buffer empty!\n");
+                                pdata->flag = 1;
+                                break;
+                            }
+                        }
+                    }  // framestogeet
+                    assert(elementstowrite <= elementsInBuffer);
+                    //apply gain to inbuf first, then copy to ringbuf
+                    pbuf = pdata->outbuf_l;
+                    pbuf_r = pdata->outbuf_r;
+                    stereo_interls(pbuf,pbuf_r,pdata->inbuf,elementstowrite);
+                    
+                    if(pdata->gain != 1.0) {
+                        for(i=0;i < elementstowrite * pdata->inchans;i++)
+                            pdata->inbuf[i]  = (float)(pdata->inbuf[i] * pdata->gain);
+                    }
+                    /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
+                    PaUtil_GetRingBufferWriteRegions(&pdata->ringbuf, elementsInBuffer, &ptr[0], &sizes[0], &ptr[1], &sizes[1]);
+                    pbuf = pdata->inbuf;
+                    //printf("ring buffer sizes : %d:%d",(int)sizes[0], (int)sizes[1]);
+                    for(i = 0; i < 2; i++) {
+                        if(ptr[i]) {
+                            int frameswanted = sizes[i];
+                            memcpy(ptr[i],pbuf,frameswanted * sizeof(float) * pdata->inchans);
+                            pbuf += frameswanted * pdata->inchans;
+                            nElementsProcessed += frameswanted;
+                        }
+                    }
+                    if(elementstowrite)
+                        PaUtil_AdvanceRingBufferWriteIndex(&pdata->ringbuf, elementstowrite);
+                } // file_playing
+                else {
+                    pdata->flag = 1;
+                    break;
+                }
+            }
+        }
+        /* Sleep a little while... */
+        Pa_Sleep(5);
+    }
+    return 0;
+}

+ 27 - 0
dev/externals/paprogs/recsf/CMakeLists.txt

@@ -0,0 +1,27 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -Dunix  -fomit-frame-pointer -funroll-loops")
+  include_directories ( /Developer/Headers/FlatCarbon )
+  find_library(COREAUDIOLIB CoreAudio)
+  find_library(AUDIOTOOLBOX AudioToolbox)
+  find_library(AULIB AudioUnit)
+  find_library(CARBONLIB Carbon)
+  find_library(AAIOLIB names libaaio.a paths /usr/local/lib)
+  set(EXTRA_LIBRARIES1 portsf pthread ${AAIOLIB} ${COREAUDIOLIB} ${AUDIOTOOLBOX} ${AULIB} ${CARBONLIB} ${EXTRA_LIBRARIES})
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -DUSE_ASIO -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 ${EXTRA_LIBARIES})
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+    set(EXTRA_LIBRARIES1 asound jack pthread m ${EXTRA_LIBARIES})
+  endif()
+endif()
+
+link_directories(../../lib ../portaudio/lib/.libs)
+
+include_directories(../../include ../portaudio/include ../portaudio/src/common /usr/local/include)
+
+add_executable(recsf recsf.c)
+target_link_libraries(recsf portaudio.a portsf ${EXTRA_LIBRARIES1})
+
+my_install(recsf)

+ 974 - 0
dev/externals/paprogs/recsf/recsf.c

@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/*
+ *  recsf.c
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0500
+#include <conio.h>
+#include <windows.h>
+#include <process.h>
+// NB: need to have PAWIN_USE_WDMKS_DEVICE_INFO defined
+#include "pa_win_ds.h"
+#endif
+
+#ifdef unix
+
+#include <sys/types.h>
+#include <sys/timeb.h>
+#endif
+
+#include <signal.h>
+
+#ifdef MAC
+
+#include <aaio.h>
+#include <libkern/OSAtomic.h>
+#endif
+
+#ifdef unix
+#include <sys/time.h>
+#include <pthread.h>
+/* in portsf.lib */
+extern int stricmp(const char *a, const char *b);
+#endif
+
+#include "portaudio.h"
+#include "pa_ringbuffer.h"
+#include "pa_util.h"
+#include "portsf.h"
+
+#ifndef min
+#define min(x,y)  ((x) < (y) ? (x) : (y))
+#endif
+
+#define N_BFORMATS (10)
+static const int bformats[N_BFORMATS] = {2,3,4,5,6,7,8,9,11,16};
+
+
+//#define FRAMES_PER_BUFFER (4096)
+// want nice big buffer for recording!
+#define RINGBUF_NFRAMES    (32768)
+#define NUM_WRITES_PER_BUFFER   (4)
+#define DEFAULT_SRATE (44100)
+#define DEFAULT_STYPE (1)
+#define DEFAULT_CHANS (2)
+#define DBRANGE (64)
+
+enum { ARG_PROGNAME, ARG_OUTFILE, ARG_DUR };
+
+#ifdef _WIN32
+HANDLE ghEvent;
+#endif
+
+
+typedef struct {
+    PaUtilRingBuffer ringbuf;
+    PaStream *stream;
+	float *ringbufData;
+    char  peakstr[(DBRANGE/2)+1];
+    PaTime startTime;
+    PaTime lastTime;
+#ifdef WIN32
+    void *hThread;
+    HANDLE hTimer;
+    HANDLE hTimerCount;
+#endif
+#ifdef unix
+    pthread_t hThread;
+#endif
+    double peak;
+	unsigned long frames_written;
+    unsigned long frames_to_write; // for optional duration arg
+    unsigned long current_frame;
+    int srate;
+    int chans;
+    int flag;
+    int ofd;
+    int finished;
+    int showlevels;
+} psfdata;
+
+static int file_recording;
+static psfdata *g_pdata = NULL;  // for timer interrupt routine
+
+
+int show_devices(void);
+
+void playhandler(int sig)
+{
+	if(sig == SIGINT)
+		file_recording = 0;
+}
+
+
+#ifdef unix
+void alarm_wakeup (int i)
+{
+    struct itimerval tout_val;
+    
+    signal(SIGALRM,alarm_wakeup);
+    
+    
+    if(file_recording && g_pdata->stream) {
+        double dBpeak  = (int)( 20.0 * log10(sqrt(g_pdata->peak)));
+        int dBmin = -DBRANGE;
+        int dBval = dBmin;
+        int i;
+        for(i=0;i < DBRANGE/2;i++){
+            g_pdata->peakstr[i] = dBpeak > dBmin+(i*2) ? '*' : '.';    
+        }
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        g_pdata->lastTime = Pa_GetStreamTime(g_pdata->stream ) - g_pdata->startTime;
+        //printf("%.2f secs\r", g_pdata->lastTime);
+        if(g_pdata->showlevels)
+            printf("%.2f secs\t\t\%s\r", g_pdata->lastTime,g_pdata->peakstr );
+        else 
+            printf("%.2f secs\r", g_pdata->lastTime);
+        fflush(stdout);
+    }
+    tout_val.it_interval.tv_sec = 0;
+    tout_val.it_interval.tv_usec = 0;
+    tout_val.it_value.tv_sec = 0; 
+    tout_val.it_value.tv_usec = 250000;
+    
+    setitimer(ITIMER_REAL, &tout_val,0);
+    
+}
+#endif
+
+
+void finishedCallback(void *userData)
+{
+    psfdata *pdata = (psfdata*) userData;
+    //printf("stream finished!\n");
+    pdata->finished = 1;
+    file_recording = 0;    
+}
+
+
+#ifdef WIN32
+
+VOID CALLBACK TimerCallback(PVOID lpParam, BOOLEAN TimerOrWaitFired)
+{
+    psfdata *pdata = (psfdata*) lpParam;
+    
+    
+    if(file_recording && pdata->stream) {
+        //printf("%.4f secs\r",(float)(g_pdata->frames_played /(double) g_pdata->srate));
+        double dBpeak  = (int)( 20.0 * log10(sqrt(pdata->peak)));
+        int dBmin = -DBRANGE;
+        int dBval = dBmin;
+        int i;
+        for(i=0;i < DBRANGE/2;i++) {
+            pdata->peakstr[i] = dBpeak > dBmin+(i*2) ? '*' : '.';    
+        }
+        
+        pdata->lastTime = Pa_GetStreamTime(pdata->stream ) - pdata->startTime;
+        
+        printf("%.2f secs\t\t%s\r", pdata->lastTime,pdata->peakstr ); 
+        fflush(stdout);
+        SetEvent(ghEvent);
+    }
+    else
+        printf("\n");
+}
+
+#endif
+
+// TODO: implement optional duration arg
+
+
+#ifdef unix
+static int threadFunctionWriteFile(void* ptr)
+#else
+static unsigned int __stdcall threadFunctionWriteFile(void* ptr)
+#endif
+{
+    psfdata* pData = (psfdata*)ptr;
+    
+    /* Mark thread started */
+    pData->flag = 0;
+    
+    while (1) {
+        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferReadAvailable(&pData->ringbuf);
+        if(file_recording == 0){
+            //write out whatever remains in ring buffer
+            void* ptr[2] = {0};
+            ring_buffer_size_t sizes[2] = {0};
+            
+            //printf("flushing ring buffer...\n");
+            
+            ring_buffer_size_t elementsRead = PaUtil_GetRingBufferReadRegions(&pData->ringbuf, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
+            if (elementsRead > 0) {       
+                int i;
+                
+                for (i = 0; i < 2 && ptr[i] != NULL; ++i)  {
+                    unsigned long towrite = sizes[i];
+                    if(pData->frames_to_write){  
+                        if(pData->frames_written + towrite > pData->frames_to_write)
+                            towrite = pData->frames_to_write - pData->frames_written;
+                    }
+                    if(psf_sndWriteFloatFrames(pData->ofd,(float*) ptr[i],towrite) != towrite) {
+                        printf("File %d write error\n",pData->ofd);
+                        pData->flag = 0;
+                        break;
+                    }
+                    pData->frames_written += towrite;
+                    if(pData->frames_written == pData->frames_to_write){
+                        //printf("recording done\n");
+                        pData->flag = 1;
+                        file_recording = 0;
+                        break;
+                    }
+                }
+                PaUtil_AdvanceRingBufferReadIndex(&pData->ringbuf, elementsRead);
+            }
+
+            break;
+        }
+        if ( (elementsInBuffer >= pData->ringbuf.bufferSize / NUM_WRITES_PER_BUFFER) || pData->flag ) {
+            void* ptr[2] = {0};
+            ring_buffer_size_t sizes[2] = {0};
+            
+            /* By using PaUtil_GetRingBufferReadRegions, we can read directly from the ring buffer */
+            ring_buffer_size_t elementsRead = PaUtil_GetRingBufferReadRegions(&pData->ringbuf, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
+            if (elementsRead > 0) {       
+                int i;
+                
+                for (i = 0; i < 2 && ptr[i] != NULL; ++i)  {
+                    unsigned long towrite = sizes[i];
+                    if(pData->frames_to_write){  
+                        if(pData->frames_written + towrite > pData->frames_to_write)
+                            towrite = pData->frames_to_write - pData->frames_written;
+                    }
+                    if(psf_sndWriteFloatFrames(pData->ofd,(float*) ptr[i],towrite) != towrite) {
+                        printf("File %d write error\n",pData->ofd);
+                        pData->flag = 0;
+                        break;
+                    }
+                    pData->frames_written += towrite;
+                    if(pData->frames_written == pData->frames_to_write){
+                        //printf("recording done\n");
+                        pData->flag = 1;
+                        file_recording = 0;
+                        break;
+                    }
+                }
+                PaUtil_AdvanceRingBufferReadIndex(&pData->ringbuf, elementsRead);
+            }
+            
+            if (pData->flag) {
+                break;
+            }
+        }
+        
+        /* Sleep a little while... */
+        Pa_Sleep(10);
+    }
+       
+    return 0;
+}
+
+#ifdef unix
+// TODO: unix return type should be void*
+typedef int (*threadfunc)(void*);
+#endif
+#ifdef WIN32
+typedef unsigned int (__stdcall *threadfunc)(void*);
+#endif
+
+
+/* Start up a new thread for given function */
+static PaError startThread( psfdata* pdata, threadfunc fn )
+{
+    pdata->flag = 1;
+#ifdef _WIN32
+    pdata->hThread = (void*)_beginthreadex(NULL, 0, fn, pdata, 0, NULL);
+    if (pdata->hThread == NULL) 
+        return paUnanticipatedHostError;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    }
+    /* Set file thread to a little higher prio than normal */
+    SetThreadPriority(pdata->hThread, THREAD_PRIORITY_ABOVE_NORMAL);
+#else
+    
+#if defined(__APPLE__) || defined(__GNUC__)
+    if(pthread_create(&pdata->hThread,NULL,(void*) fn,pdata))
+        return -1;
+    /* Wait for thread to startup */
+    while (pdata->flag) {
+        Pa_Sleep(10);
+    } 
+#endif
+#endif   
+    return paNoError;
+}
+// for sake of completion - curently NOT USED 
+static int stopThread( psfdata* pdata )
+{
+    // RWD: just called when all data played; must be called before StopStream
+    //   pdata->flag = 1;
+    /* Wait for thread to stop */
+       while (pdata->flag == 0) {
+           Pa_Sleep(10);
+       }
+#ifdef _WIN32
+    CloseHandle(pdata->hThread);
+    pdata->hThread = 0;
+#else
+    
+#if defined(__APPLE__) || defined(__GNUC__)
+    pthread_cancel(pdata->hThread);
+#endif
+#endif
+    return paNoError;
+}
+
+static int recordCallback( const void *inputBuffer, void *outputBuffer,
+                          unsigned long framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags statusFlags,
+                          void *userData )
+{
+    psfdata *data = (psfdata*) userData;
+    const float *rptr = (const float*) inputBuffer;
+    double peak = 0.0, val;
+    unsigned int i;
+    
+    (void) outputBuffer; /* Prevent unused variable warnings. */
+    (void) timeInfo;
+    (void) statusFlags;
+    (void) userData;
+    
+    data->current_frame += PaUtil_WriteRingBuffer(&data->ringbuf, rptr, framesPerBuffer);
+    
+    // simple level meter!
+    val = rptr[0];
+    val  =  val * val;
+    peak = val;
+    for(i=0;i < framesPerBuffer * data->chans ;i++){
+        val = rptr[i];
+        val  =  val * val;
+        peak =  peak < val ? val : peak;
+    }
+    data->peak = peak;
+    if(data->flag) {
+        return paComplete; // or paAbort?
+    }
+    return paContinue;
+}
+
+void usage(void)
+{
+        printf("usage: recsf [-BN][-cN][-dN][-hN][-i][-p][-rN][-tN] outfile [dur]\n"
+               "outfile:   output file in WAVE,AIFF or AIFC formats,\n"
+               "           determined by the file extension.\n"
+               "           use extension .amb to create a B-Format file.\n"
+               "-BN    :   set memory buffer size to N frames (default: %d).\n"
+               "              N must be a power of 2 (e.g 4096, 8192, 16384 etc).\n"
+               "-cN    :   set channels to N (default: 2).\n"
+               "-hN    :   set hardware buffer size to N frames (default: set by device)\n"
+               "-p     :   suppress running peak level indicator\n"
+               "-rN    :   set sample rate to N Hz (default: 44100)\n"
+               "-tN    :   set sample type to N (default: 1)\n"
+               "           0 :  16 bits\n"
+               "           1 :  24 bits\n"
+               "           2 :  32bit floats\n"
+               "dur    :   optional fixed duration for outfile\n"
+               "             (overriden by Ctrl-C)\n"
+               "           Otherwise, use Ctrl_C to terminate recording.\n"
+               "-dN    :   use input device N\n"
+               "-i     :   start recording immediately (default: wait for keypress)\n"
+               ,RINGBUF_NFRAMES);
+}
+
+static unsigned NextPowerOf2(unsigned val)
+{
+    val--;
+    val = (val >> 1) | val;
+    val = (val >> 2) | val;
+    val = (val >> 4) | val;
+    val = (val >> 8) | val;
+    val = (val >> 16) | val;
+    return ++val;
+}
+
+int main(int argc,char **argv)
+{
+	PaStreamParameters inputParameters;
+#ifdef _WIN32
+    /* portaudio sets default channel masks we can't use; we must do all this to set default mask = 0! */
+    PaWinDirectSoundStreamInfo directSoundStreamInfo;
+    //    PaWinMmeStreamInfo winmmeStreamInfo;
+    
+#endif
+    PaDeviceInfo *devinfo = NULL;
+    PaStream *stream = NULL;
+    PaStreamCallback *callback = recordCallback; 
+	PaError err = paNoError;
+	psfdata sfdata;
+	PSF_CHPEAK *fpeaks = NULL;
+    PSF_PROPS props;
+    const char* outfilename = NULL;
+    time_t in_peaktime = 0;
+    MYLONG lpeaktime;
+	int waitkey = 1;
+    int showlevels = 1;
+    double duration = 0.0;
+    double framedurms;
+    int i,j= 0;
+    int res;
+    int samptype = DEFAULT_STYPE;
+    char* ext = NULL;
+	PaDeviceIndex device;
+    long framesize = 0;   // Apr 2010 to be scaled by sample rate
+    unsigned long ringframelen = RINGBUF_NFRAMES;  // length of ring buffer in m/c frames
+    unsigned long frames_per_buffer = 0;
+    unsigned int frameBlocksize = 0;
+    double maxdurFrames = 0.0;
+    unsigned long LmaxdurFrames = 0;
+    unsigned long LmaxdurSecs = 0;
+    unsigned long LmaxdurMins = 0;
+    
+#ifdef unix
+    struct itimerval tout_val;    
+    tout_val.it_interval.tv_sec = 0;
+    tout_val.it_interval.tv_usec = 0;
+    tout_val.it_value.tv_sec = 0; 
+    tout_val.it_value.tv_usec = 200000;
+#endif
+    
+#ifdef _WIN32
+    HANDLE hTimerQueue;
+    ghEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
+    if(ghEvent==NULL){
+        printf("Failed to start internal timer (1).\n");
+        return 1;
+    }
+    hTimerQueue = CreateTimerQueue();
+    if(hTimerQueue == NULL){
+        printf("Failed to start internal timer (2).\n");
+        return 1;
+    }
+#endif
+    
+    
+    props.chans = DEFAULT_CHANS;
+    props.srate = DEFAULT_SRATE;
+    props.samptype = samptype;
+    
+    
+	signal(SIGINT,playhandler);
+    
+	sfdata.ringbufData = NULL;
+    sfdata.frames_to_write = 0;
+    printf("RECSF: multi-channel record to file. v 1.1.0 RWD,CDP 2013\n");
+    file_recording = 0;
+    err = Pa_Initialize();
+	if( err != paNoError ) {
+		printf("Failed to initialize Portaudio.\n");
+        Pa_Terminate();
+        return 1;
+    }
+	device =  Pa_GetDefaultInputDevice();
+    
+    /* CDP version number */
+    if(argc==2 && (stricmp(argv[1],"--version")==0)){
+        printf("1.1.0\n");
+        return 0;
+    }
+    
+    if(argc < ARG_DUR) {
+        printf("insufficient args\n");
+        usage();
+        show_devices();
+		Pa_Terminate();
+        return 1;
+    }
+    
+    while(argv[1][0]=='-'){
+        int err = 0;
+        unsigned long userbuflen = 0;
+		switch(argv[1][1]){
+            case 'c':
+                if(argv[1][2]=='\0'){
+                    printf("-c flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                props.chans = atoi(&(argv[1][2]));
+                if(props.chans < 1){
+                    printf("bad value %d for channels\n",props.chans);
+                    err++;
+                }
+                break;
+            case 'd':
+                if(argv[1][2]=='\0'){
+                    printf("-d flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                device = atoi(&(argv[1][2]));
+                break;
+            case 'h':
+                if(argv[1][2]=='\0'){
+                    printf("-h flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                frames_per_buffer = atoi(&(argv[1][2]));
+                if(frames_per_buffer < 32){
+                    printf("-h value too small; must be >= 32\n");
+                    err++;
+                }
+                break;
+            case 'i':
+                waitkey = 0;
+                break;
+            case 'p':
+                showlevels = 0;
+                break;
+            case 'r':
+                if(argv[1][2]=='\0'){
+                    printf("-r flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                props.srate = atoi(&(argv[1][2]));
+                if(props.srate <= 0){
+                    printf("bad value %d for srate\n",props.srate);
+                    err++;
+                }
+                break;
+            case 't':
+                if(argv[1][2]=='\0'){
+                    printf("-t flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                samptype = atoi(&(argv[1][2]));
+                if(samptype < 0 || samptype > 2){
+                    printf("bad value %d for sample type\n",samptype);
+                    err++;
+                }
+                break;
+            case 'B':
+                if(argv[1][2]=='\0'){
+                    printf("-B flag requires parameter\n");
+                    err++;
+                    break;
+                }
+                ringframelen = atoi(&(argv[1][2]));
+                if(ringframelen < 1024){
+                    printf("-B: buffer size must be >=1024\n");
+                    err++;
+                }
+                userbuflen = NextPowerOf2(ringframelen);
+                if(userbuflen != ringframelen){
+                    printf("-B: buffer size must be power of 2 size\n");
+                    Pa_Terminate();
+                    return 1;
+                }
+                
+                break;
+            default:
+                printf("unrecognised flag option\n");
+                err++;
+                break;
+		}
+        if(err){
+            Pa_Terminate();
+            return 1;
+        }
+		argv++;	 argc--;
+	}
+
+    if(argc < ARG_DUR || argc > ARG_DUR+1) {
+        usage();
+        show_devices();
+		Pa_Terminate();
+        return 1;
+    }
+    outfilename = argv[ARG_OUTFILE];
+    
+    switch(samptype){
+        case 0:
+            props.samptype = PSF_SAMP_16;
+            frameBlocksize = 2;
+            break;
+        case 1:
+            props.samptype = PSF_SAMP_24;
+            frameBlocksize = 3;
+            break;
+        case 2:
+            props.samptype = PSF_SAMP_IEEE_FLOAT;
+            frameBlocksize = 4;
+            break;
+        default:
+            printf("stype must be 0,1 or 2\n");
+            Pa_Terminate();
+            return 1;
+    }
+    
+    // find max recording time, with safety margin
+    frameBlocksize *= props.chans; 
+    maxdurFrames = (pow(2.0,32.0) / frameBlocksize) - 1000;
+    LmaxdurFrames = (unsigned long) maxdurFrames;
+    LmaxdurSecs =  LmaxdurFrames / props.srate;
+    LmaxdurMins =  LmaxdurSecs / 60;
+    printf("Max recording time: %lu mins, %lu secs\n",LmaxdurMins,LmaxdurSecs - (LmaxdurMins * 60));
+    
+    if(argc==ARG_DUR+1){
+        duration = atof(argv[ARG_DUR]);
+        if(duration <=0.0){
+            printf("duration must be positive!\n");
+            Pa_Terminate();
+            return 1;
+        }
+        if(duration > (double) LmaxdurSecs){
+            printf("specified duration too long for file format.\n");
+            Pa_Terminate();
+            return 1;
+        }
+        sfdata.frames_to_write = (unsigned long) (duration * props.srate);
+    }
+    
+    ext = strrchr(outfilename,'.');
+    if(ext && stricmp(ext,".amb")==0) {
+        int matched = 0;
+		for(i=0;i < N_BFORMATS;i++)	{
+			if(props.chans == bformats[i]){
+				matched = 1;
+				break;
+			}
+		}
+		if(!matched){
+			printf("WARNING: No Bformat definition for %d-channel file.\n",props.chans);
+		}
+		
+        props.format = PSF_WAVE_EX;
+        props.chformat = MC_BFMT;
+    }
+    else {
+        // we must be strictly correct with WAVE formats!
+        props.chformat = props.chans > 2 ? MC_STD : STDWAVE;
+        props.format = psf_getFormatExt(outfilename);
+        if(props.chans > 2 || props.samptype > PSF_SAMP_16 || props.srate > 48000)
+            props.format = PSF_WAVE_EX;
+            
+    }
+    sfdata.ofd = psf_sndCreate(outfilename,&props,0,0,PSF_CREATE_RDWR);
+    if(sfdata.ofd < 0){
+        printf("Sorry - unable to create outfile\n");
+        goto error;
+    } 
+    
+    fpeaks = (PSF_CHPEAK *) calloc(props.chans,sizeof(PSF_CHPEAK));
+	if(fpeaks==NULL){
+		puts("no memory for PEAK data\n");
+		goto error;
+	}
+    
+    if(props.srate > 48000)
+        ringframelen <<= 1;
+    printf("File buffer size = %ld\n",ringframelen);
+    // NB ring buffer sized for decoded data, hence outchans here; otherwise inchans = outchans
+    sfdata.ringbufData = (float *) PaUtil_AllocateMemory( ringframelen * sizeof(float) * props.chans); /* From now on, recordedSamples is initialised. */
+    if( sfdata.ringbufData == NULL )   {
+        puts("Could not allocate play buffer.\n");
+        goto error;
+    }
+    // number of elements has to be a power of two, so each element has to be a full m/c frame
+    if (PaUtil_InitializeRingBuffer(&sfdata.ringbuf, sizeof(float) * props.chans, ringframelen , sfdata.ringbufData) < 0) {
+        puts("Could not initialise play buffer.\n");
+        goto error;
+    }
+    // scale frame buf to sample rate
+    if(props.srate > 48000)
+         frames_per_buffer *= 2;
+    if(frames_per_buffer > 0)
+        printf("Audio buffer size = %lu frames\n",frames_per_buffer);
+
+	sfdata.chans      = props.chans;
+	sfdata.frames_written  = 0;
+    sfdata.current_frame = 0;
+    sfdata.srate = props.srate;
+    sfdata.peakstr[DBRANGE/2] = '\0';
+    
+    sfdata.finished = 0;
+    sfdata.showlevels = showlevels;
+    g_pdata = &sfdata;
+    
+    inputParameters.device = device;   /*Pa_GetDefaultOutputDevice(); */ /* default output device */
+    inputParameters.channelCount = props.chans;                     
+    inputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output */
+    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+    
+    devinfo = (PaDeviceInfo *) Pa_GetDeviceInfo(device);
+#ifdef MAC
+    if(devinfo){
+        printf("Using device %d: %s\n",device,devinfo->name);
+    }
+#endif
+    
+#ifdef WIN32
+    if(devinfo) {
+        int apitype = devinfo->hostApi;
+        const PaHostApiInfo *apiinfo = Pa_GetHostApiInfo(apitype);
+        printf("Using device %d: %s:\n",device,devinfo->name);
+        
+        if(apiinfo->type  == paDirectSound ){
+            printf("(DS)\n");
+            /* set this IF we are using Dsound device. */
+            directSoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);
+            directSoundStreamInfo.hostApiType = paDirectSound; 
+            directSoundStreamInfo.version = 2;
+            directSoundStreamInfo.flags = paWinDirectSoundUseChannelMask;
+            directSoundStreamInfo.channelMask = 0;
+            inputParameters.hostApiSpecificStreamInfo = &directSoundStreamInfo; 
+        }
+        else if(apiinfo->type == paASIO)
+            printf("(ASIO)\n");
+        // else
+        //    printf("API unknown!);
+    }
+    
+#endif  
+    // TODO; move this up to before file is created?
+    err = Pa_IsFormatSupported(&inputParameters, NULL,props.srate);
+    if(err != paNoError){
+        printf("Selected device does not support this format.\n");
+        goto error;
+    }
+
+    err = Pa_OpenStream(
+                        &stream,
+                        &inputParameters, 
+                        NULL,  /* No output */
+                        props.srate,
+                        frames_per_buffer,			
+                        paClipOff,  
+                        recordCallback,
+                        &sfdata );
+    
+    
+	if( err != paNoError ) {
+        printf("Unable to open output device for %d-channel file.\n",props.chans);
+        goto error;
+    }
+    err =  Pa_SetStreamFinishedCallback( stream, finishedCallback );
+    if( err != paNoError ) {
+        printf("Internal error: unable to set finish callback\n");
+        goto error;
+    }
+	sfdata.stream = stream;
+    
+    
+ 
+    file_recording = 1;
+    if(waitkey){
+		printf("Press any key to start:\n");	
+		while (!kbhit()){	
+			if(!file_recording)	 //check for instant CTRL-C
+                goto error;		
+        };
+#ifdef WIN32
+		if(kbhit())
+			_getch();			 //prevent display of char
+#endif
+	}
+    
+    // set up timer
+#ifdef unix   
+    setitimer(ITIMER_REAL, &tout_val,0);
+    signal(SIGALRM,alarm_wakeup); /* set the Alarm signal capture */
+#endif
+    /* Start the file reading thread */
+    sfdata.startTime = Pa_GetStreamTime(stream );
+    err = startThread(&sfdata, threadFunctionWriteFile);
+    if( err != paNoError ) goto error;
+    
+#ifdef WIN32
+    if(!CreateTimerQueueTimer(&sfdata.hTimer, hTimerQueue,
+                              (WAITORTIMERCALLBACK) TimerCallback, &sfdata,200,200,0)) {
+        printf("failed to start timer (3).\n");
+        return 1;
+    }
+#endif
+	err = Pa_StartStream( stream );
+	if( err != paNoError ) 
+		goto error;
+    
+	printf("Hit CTRL-C to stop.\n");
+    
+    while((!sfdata.finished) && file_recording){
+        // nothing to do!
+		Pa_Sleep(10);
+	}
+    // note to programmer: any bug in audio buffer arithmetic will likely cause crash here!
+    err = Pa_StopStream( stream );
+    if( err != paNoError ) {
+        printf("Error stopping stream\n");
+		goto error;
+    }
+    
+    // need to stop thread explicitly?
+    
+    err = Pa_CloseStream( stream ); 
+	if( err != paNoError ) {
+        printf("Error closing stream\n");
+		goto error;
+    }
+#ifdef WIN32
+    CloseHandle(ghEvent);
+    DeleteTimerQueue(hTimerQueue);
+#endif
+    printf("%.2f secs\n",(float)(sfdata.lastTime));
+	fflush(stdout);
+	printf("Recording finished.\n");
+    res = psf_sndReadPeaks(sfdata.ofd,fpeaks,(MYLONG *) &lpeaktime);
+	if(res==0) {
+		printf("no PEAK data in this soundfile\n");
+	}
+	else if(res < 0){
+		printf("Error reading PEAK data\n");
+		goto error;
+	}
+	else{
+        // creation time not available until file closed; don't need it here!
+		printf("PEAK data:\n");
+		for(i=0;i < sfdata.chans;i++){
+            if(fpeaks[i].val > 0.0){
+                double dBval = 20.0 * log10(fpeaks[i].val);
+			    printf("CH %d: %.4f (%.2lfdB) at frame %u: \t%.4f secs\n",
+                   i,fpeaks[i].val,dBval,fpeaks[i].pos,(double)(fpeaks[i].pos / (double) props.srate));
+            }
+            else {
+                printf("CH %d: %.4f (-infdB)at frame %u: \t%.4f secs\n",
+                   i,fpeaks[i].val,fpeaks[i].pos,(double)(fpeaks[i].pos / (double) props.srate));
+
+            }
+		}
+	}
+    
+error:
+	Pa_Terminate();
+    
+//#ifdef _WIN32
+//    CloseHandle(ghEvent);
+//    DeleteTimerQueue(hTimerQueue);
+//#endif
+    if( sfdata.ringbufData )       
+        PaUtil_FreeMemory(sfdata.ringbufData);
+    
+	if(sfdata.ofd >=0)
+		psf_sndClose(sfdata.ofd);
+	if(fpeaks)
+		free(fpeaks);
+
+	psf_finish();
+	
+	return 0;    
+    
+}
+
+
+
+int show_devices(void)
+{
+    int i,j;
+    PaDeviceIndex numDevices,p;
+    const    PaDeviceInfo *pdi;
+#ifdef _WIN32
+    const PaHostApiInfo* api_info;
+    const char *apiname;
+#endif
+    PaError  err;
+    int nInputDevices = 0;
+    
+#ifdef USE_ASIO
+    printf("For ASIO multi-channel, you may need to select the highest device no.\n");		
+#endif
+    /*Pa_Initialize();*/
+    numDevices =  Pa_GetDeviceCount();
+    if( numDevices < 0 )
+    {
+        printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+        err = numDevices;
+        return err;
+    }
+#ifdef WIN32
+    printf("Driver\tDevice\tInput\tOutput\tName\n");
+#else
+    printf("Device\tInput\tOutput\tName\n");
+#endif
+    //printf("Number of devices = %d\n", numDevices );
+    for( p=0; p<numDevices; p++ )
+    {
+        pdi = Pa_GetDeviceInfo( p );
+        
+        //#ifdef _WIN32
+        //			/*RWD: skip, info on inputs */
+        //			if(pdi->maxOutputChannels == 0)
+        //				continue;
+        //#endif
+        nInputDevices++;
+        
+        if( p == Pa_GetDefaultInputDevice() ) 
+            printf("*");
+        else
+            printf(" ");
+#ifdef WIN32
+        api_info = Pa_GetHostApiInfo(pdi->hostApi);
+        apiname = api_info->name;
+        if(strcmp(apiname,"Windows DirectSound")==0)
+            apiname = "DS ";
+        printf("(%s)\t%d\t%d\t%d\t%s\n",apiname,p,
+               pdi->maxInputChannels,
+               pdi->maxOutputChannels,
+               pdi->name);	
+#else
+        printf("%d\t%d\t%d\t%s\n",p,
+               pdi->maxInputChannels,
+               pdi->maxOutputChannels,
+               pdi->name);
+        
+        
+#endif
+    }
+    
+    return 0;
+}
+

+ 14 - 0
dev/externals/portsf/CMakeLists.txt

@@ -0,0 +1,14 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -arch i386 -arch x86_64 -Dunix")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O2")
+  else()
+    set(CMAKE_C_FLAGS "-O2 -Dunix -Dlinux")
+  endif()
+endif()
+
+include_directories(../include)
+set(PORTSF_SRCS portsf.c ieee80.c)
+
+add_library(portsf ${PORTSF_SRCS})

+ 149 - 0
dev/externals/portsf/ieee80.c

@@ -0,0 +1,149 @@
+/***************************************************************\
+*   IEEE80.c							*
+*   Convert between "double" and IEEE 80 bit format  		*
+*   in machine independent manner.				*
+*   Assumes array of char is a continuous region of 8 bit frames*
+*   Assumes (unsigned long) has 32 useable bits			*
+*   billg, dpwe @media.mit.edu					*
+*   01aug91							*
+*   19aug91  aldel/dpwe  workaround top bit problem in Ultrix   *
+*                        cc's double->ulong cast		*
+*   05feb92  dpwe/billg  workaround top bit problem in 		*
+*                        THINKC4 + 68881 casting		*
+\***************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include 	<math.h>
+
+#include	"ieee80.h"
+
+/* #define MAIN	1	 to compile test routines */
+
+#define ULPOW2TO31	((unsigned long)0x80000000L)
+#define DPOW2TO31	((double)2147483648.0)	/* 2^31 */
+
+/* have to deal with ulong's 32nd bit conditionally as double<->ulong casts 
+   don't work in some C compilers */
+
+static double   myUlongToDouble (unsigned long ul);
+static unsigned long myDoubleToUlong (double val);
+
+static double myUlongToDouble(unsigned long ul)
+{
+	double val;
+	
+	/* in THINK_C, ulong -> double apparently goes via long, so can only 
+	   apply to 31 bit numbers.  If 32nd bit is set, explicitly add on its
+	   value */
+	if(ul & ULPOW2TO31)
+		val = DPOW2TO31 + (ul & (~ULPOW2TO31));
+	else
+		val = ul;
+	return val;
+}
+
+static unsigned long myDoubleToUlong(double	val)
+{
+	unsigned long ul;
+	
+	/* cannot cast negative numbers into unsigned longs */
+	if(val < 0)	
+	{ 
+		fprintf(stderr,"IEEE80:DoubleToUlong: val < 0\n"); 
+	}
+	
+	/* in ultrix 4.1's cc, double -> unsigned long loses the top bit, 
+	   so we do the conversion only on the bottom 31 bits and set the 
+	   last one by hand, if val is truly that big */
+	/* should maybe test for val > (double)(unsigned long)0xFFFFFFFF ? */
+	if(val < DPOW2TO31)
+		ul = (unsigned long)val;
+	else
+		ul = ULPOW2TO31 | (unsigned long)(val-DPOW2TO31);
+	return ul;
+}
+
+
+/*
+* Convert IEEE 80 bit floating point to double.
+* Should be portable to all C compilers.
+*/
+double ieee_80_to_double(unsigned char *p)
+{
+	char sign;
+	short exp = 0;
+	unsigned long mant1 = 0;
+	unsigned long mant0 = 0;
+	double val;
+	
+	exp = *p++;
+	exp <<= 8;
+	exp |= *p++;
+	sign = (exp & 0x8000) ? 1 : 0;
+	exp &= 0x7FFF;
+	
+	mant1 = *p++;
+	mant1 <<= 8;
+	mant1 |= *p++;
+	mant1 <<= 8;
+	mant1 |= *p++;
+	mant1 <<= 8;
+	mant1 |= *p++;
+	
+	mant0 = *p++;
+	mant0 <<= 8;
+	mant0 |= *p++;
+	mant0 <<= 8;
+	mant0 |= *p++;
+	mant0 <<= 8;
+	mant0 |= *p++;
+	
+	/* special test for all bits zero meaning zero 
+	   - else pow(2,-16383) bombs */
+	if(mant1 == 0 && mant0 == 0 && exp == 0 && sign == 0)
+		return 0.0;
+	else{
+		val = myUlongToDouble(mant0) * pow(2.0,-63.0);
+		val += myUlongToDouble(mant1) * pow(2.0,-31.0);
+		val *= pow(2.0,((double) exp) - 16383.0);
+		return sign ? -val : val;
+	}
+}
+
+/*
+* Convert double to IEEE 80 bit floating point
+* Should be portable to all C compilers.
+* 19aug91 aldel/dpwe  covered for MSB bug in Ultrix 'cc'
+*/
+
+void double_to_ieee_80(double val,unsigned char *p)
+{
+	char sign = 0;
+	short exp = 0;
+	unsigned long mant1 = 0;
+	unsigned long mant0 = 0;
+	
+	if(val < 0.0)	{  sign = 1;  val = -val; }
+	
+	if(val != 0.0)	/* val identically zero -> all elements zero */
+	{
+		exp = (short)(log(val)/log(2.0) + 16383.0);
+		val *= pow(2.0, 31.0+16383.0-(double)exp);
+		mant1 = myDoubleToUlong(val);
+		val -= myUlongToDouble(mant1);
+		val *= pow(2.0, 32.0);
+		mant0 = myDoubleToUlong(val);
+	}
+	
+	*p++ = ((sign<<7)|(exp>>8));
+	*p++ = 0xFF & exp;
+	*p++ = (char)(0xFF & (mant1>>24));
+	*p++ = (char)(0xFF & (mant1>>16));
+	*p++ = (char)(0xFF & (mant1>> 8));
+	*p++ = (char)(0xFF & (mant1));
+	*p++ = (char)(0xFF & (mant0>>24));
+	*p++ = (char)(0xFF & (mant0>>16));
+	*p++ = (char)(0xFF & (mant0>> 8));
+	*p++ = (char)(0xFF & (mant0));
+	
+}

+ 17 - 0
dev/externals/portsf/ieee80.h

@@ -0,0 +1,17 @@
+
+/***************************************************************\
+*   IEEE80.h							*
+*   Convert between "double" and IEEE 80 bit format  		*
+*   in machine independent manner.				*
+*   Assumes array of char is a continuous region of 8 bit frames*
+*   Assumes (unsigned long) has 32 useable bits			*
+*   billg, dpwe @media.mit.edu					*
+*   01aug91							*
+*   19aug91  aldel/dpwe  workaround top bit problem in Ultrix   *
+*                        cc's double->ulong cast		*
+*   05feb92  dpwe/billg  workaround top bit problem in 		*
+*                        THINKC4 + 68881 casting		*
+\***************************************************************/
+
+double ieee_80_to_double(unsigned char *p);
+void   double_to_ieee_80(double val, unsigned char *p);

+ 3573 - 0
dev/externals/portsf/portsf.c

@@ -0,0 +1,3573 @@
+/* Copyright (c) 1999-2009 Richard Dobson
+ 
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ 
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Changes Oct 28 2002: support overwrite mode (seek-back->read->wrtite) */
+/* fix bug in floats WAVE header reading! */
+/* more comprehensive use of assert in _DEBUG mode (still not complete ....) */
+/* sundry tidy-ups inspired by Intel compiler messages! */
+/* changes Apr 27 2003: fix reversal bug in wave format writing on big-endian platforms */
+/* changes Aug 2003: added basic TPDF dither to 16bit output */
+/* Nov 2003: fixed aiffsize bug in aiff write func: 8 bytes too big! */
+/* Jan 2004 fixed error reading from WAVE_EX file */
+
+/*Oct 19 2006: cleared up some memory leaks on SFFILE (psf_sndOpen etc)*/
+/* July 2009: revised to use fget/setpos, 4GB file support, 64bit platforms
+ Move to 2LSB TPDF dither
+ */
+
+/* POST BOOK!*/
+/* OCT 2009 ADDED MC_CUBE, completed bformat support */
+/* fixed bad omission in byteswaaping wavex elements! */
+/* added recognition of speaker layouts */
+/* corrected absent assignment of chmask on little-endian */
+
+/* Apr 2010 upped MAXFILES to 256! */
+/* Aug 2010, and now to 512! */
+
+/* Aug 2012 TODO (?):  add running check for 4GB limit to writeFrame funcs */
+/* Aug 2012 corrected SPKRS_MONO value (portsf.h) */
+/* Nov 2013: added SPKRS_6_1 to list */
+/* Mar 2020: fix fault parsing floats amb files! */
+
+#include <stdio.h>
+#ifdef unix
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include "ieee80.h"
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+
+#include <portsf.h>
+
+#ifndef DBGFPRINTF
+# ifdef _DEBUG
+# define DBGFPRINTF(a) fprintf a
+# else
+# define DBGFPRINTF(a)
+# endif
+#endif
+
+#ifndef max
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+#ifndef min
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef BITS_PER_BYTE
+#define BITS_PER_BYTE (8)
+#endif
+#ifndef WIN32
+#include <ctype.h>
+int stricmp(const char *a, const char *b);
+int strnicmp(const char *a, const char *b, const int length);
+#endif
+
+#ifdef linux
+#define POS64(x) (x.__pos)
+#else
+#define POS64(x) (x)
+#endif
+
+
+/* probably no good for 64bit platforms */
+#define REVDWBYTES(t)   ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
+#define REVWBYTES(t)    ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
+#define TAG(a,b,c,d)    ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
+/* RW changed 15:10:2002 - one less! */
+/* RWD Dec 2009 changed back again for bit-accurate scaling */
+#define MAX_16BIT  (32768.0)
+#define MAX_32BIT  (2147483648.0)
+
+#define AIFC_VERSION_1 (0xA2805140)
+/*pstring for AIFC  - includes the pad byte*/
+static const char aifc_floatstring[10] = { 0x08,'F','l','o','a','t',0x20,'3','2',0x00};
+static const char aifc_notcompressed[16] = {0x0e,'n','o','t',0x20,'c','o','m','p','r','e','s','s','e','d',0x00};
+
+static float trirand();
+static psf_channelformat get_speakerlayout(DWORD chmask,DWORD chans);
+static double inv_randmax  = 1.0 / RAND_MAX;
+static const float dclip16 = (float)(32767.0/32768.0);
+static const float dclip24 = (float)(8388607.0/8388608.0);
+static const float dclip32 = (float)(2147483647.0/2147483648.0);
+
+/* we need the standard Windows defs, when compiling on other platforms.
+ <windows.h> defines _INC_WINDOWS
+ */
+#ifndef _INC_WINDOWS
+
+#define WAVE_FORMAT_PCM         (0x0001)
+
+typedef union {
+    int lsamp;
+    float fsamp;
+    unsigned char bytes[4];
+} SND_SAMP;
+
+typedef struct _GUID 
+{ 
+    unsigned int      Data1;
+    unsigned short    Data2;
+    unsigned short    Data3;
+    unsigned char     Data4[8];
+} GUID; 
+
+
+typedef struct  {
+    WORD  wFormatTag;
+    WORD  nChannels;
+    DWORD nSamplesPerSec;
+    DWORD nAvgBytesPerSec;
+    WORD  nBlockAlign;
+    WORD  wBitsPerSample;
+    
+} WAVEFORMAT;
+
+
+typedef struct { 
+    WORD  wFormatTag;
+    WORD  nChannels;
+    DWORD nSamplesPerSec;
+    DWORD nAvgBytesPerSec;
+    WORD  nBlockAlign;
+    WORD  wBitsPerSample;
+    WORD  cbSize;
+} WAVEFORMATEX; 
+#endif
+
+
+/* basic support for WAVE_FORMAT_EXTENSIBLE */
+
+typedef struct {
+    WAVEFORMATEX    Format;             /* 18 bytes */
+    union {
+        WORD wValidBitsPerSample;       /* bits of precision  */
+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
+        WORD wReserved;                 /* If neither applies, set to */
+        /* zero. */
+    } Samples;
+    DWORD    dwChannelMask;             /* which channels are */
+    /* present in stream  */
+    GUID     SubFormat;
+} WAVEFORMATEXTENSIBLE;
+
+/* sizeof(WAVEFORMATEXTENSIBLE) gives size plus alignment padding; not good here */
+/* size = 18 + 2 + 4 + 16 */
+#define sizeof_WFMTEX  (40)
+
+/* std WAVE-EX GUIDS from <ksmedia.h> */
+static const GUID  KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,
+    {0x80,
+        0x00,
+        0x00,
+        0xaa,
+        0x00,
+        0x38,
+        0x9b,
+        0x71}};
+
+static const GUID  KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003,0x0000,0x0010,
+    {0x80,
+        0x00,
+        0x00,
+        0xaa,
+        0x00,
+        0x38,
+        0x9b,
+        0x71}};
+
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_PCM = { 0x00000001, 0x0721, 0x11d3, 
+    { 0x86,
+        0x44,
+        0xc8,
+        0xc1,
+        0xca,
+        0x0,
+        0x0,
+        0x0 } };
+
+
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = { 0x00000003, 0x0721, 0x11d3, 
+    { 0x86,
+        0x44,
+        0xc8,
+        0xc1,
+        0xca,
+        0x0,
+        0x0,
+        0x0 } };
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT  (0x0003)
+#endif
+#ifndef WAVE_FORMAT_EXTENSIBLE
+#define WAVE_FORMAT_EXTENSIBLE  (0xfffe)
+#endif
+/*RWD Feb 2010 */
+#define WARNSTRING_SIZE (64)
+/******** the private structure holding all sfile stuff */
+enum lastop {PSF_OP_READ,PSF_OP_WRITE};
+
+typedef struct psffile {
+    FILE            *file;
+    char            *filename;
+    DWORD           curframepos;    /* for read operations */
+    DWORD           nFrames;        /* multi-channel sample frames */
+    int             isRead;         /* how we are using it */
+    int             clip_floats;
+    int             rescale;
+    float           rescale_fac;
+    psf_format      riff_format;
+    /*int               isSeekable;*/    /* any use ? */
+    int             is_little_endian;
+    psf_stype       samptype;       /* = nBlockAlign / nChannels */
+    fpos_t          dataoffset;     /* = sizeof(header) */
+    fpos_t          fmtoffset;
+    fpos_t          peakoffset;
+    WAVEFORMATEXTENSIBLE fmt;           /* serves all WAVE,AIFF support.*/
+    psf_channelformat chformat;
+    PSF_CHPEAK      *pPeaks;
+    time_t          peaktime;
+    fpos_t          lastwritepos;
+    int             lastop;         /* last op was read or write? */
+    int             dithertype;
+    /* RWD Feb 2010 ; to warn user of ill-formed files*/
+    int             illformed;
+    char            warnstring[WARNSTRING_SIZE];
+} PSFFILE;
+
+
+static int compare_guids(const GUID *gleft, const GUID *gright)
+{
+    const char *left = (const char *) gleft, *right = (const char *) gright;
+    return !memcmp(left,right,sizeof(GUID));
+}
+
+
+
+#define psf_maxfiles (512)
+
+/* could make this dynamically allocated, via psf_init, one day, if it matters. */
+
+static PSFFILE *psf_files[psf_maxfiles];
+
+/* return 0 for success, non-zero for error */
+int psf_init(void)
+{
+    int i;
+    
+    for(i=0;i < psf_maxfiles;i++)
+        psf_files[i] = NULL;
+    /* do any other inits we need.... */
+    return 0;
+}
+
+/* return zero for success, non-zero for error*/
+static int psf_release_file(PSFFILE *psff)
+{
+    int rc = 0;
+#ifdef _DEBUG
+    assert(psff);
+#endif
+    
+    if(psff->file){
+        rc = fclose(psff->file);
+        if(rc)
+            return rc;
+        psff->file = NULL;
+    }
+    if(psff->filename){
+        free(psff->filename);
+        psff->filename = NULL;
+    }
+    if(psff->pPeaks) {
+        free(psff->pPeaks);
+        psff->pPeaks = NULL;
+    }
+    return rc;
+}
+
+/* return zero for success, non-zero for error*/
+int psf_finish(void)
+{
+    int i,rc = 0;
+    for(i=0;i < psf_maxfiles;i++) {
+        if(psf_files[i]!= NULL){
+#ifdef _DEBUG
+            printf("sfile %s not closed: closing.\n",psf_files[i]->filename);
+#endif
+            rc = psf_release_file(psf_files[i]);
+            /* an alternative is to continue, and write error info to a logfile */
+            if(rc)
+                return rc;
+            
+        }
+        free(psf_files[i]);
+        psf_files[i] = NULL;
+    }
+    return rc;
+}
+
+
+/* thanks to the SNDAN programmers for this! */
+/* return 0 for big-endian machine, 1 for little-endian machine*/
+/* probably no good for 16bit swapping though */
+static int byte_order()                 
+{                           
+    int   one = 1;
+    char* endptr = (char *) &one;
+    return (*endptr);
+}
+
+
+static void fmtSwapBytes(PSFFILE *sfdat)
+{
+    WAVEFORMATEX  *pfmt = (WAVEFORMATEX *) &(sfdat->fmt.Format);
+    
+    pfmt->wFormatTag    = (WORD) REVWBYTES(pfmt->wFormatTag);
+    pfmt->nChannels     = (WORD) REVWBYTES(pfmt->nChannels);
+    pfmt->nSamplesPerSec    = REVDWBYTES(pfmt->nSamplesPerSec);
+    pfmt->nAvgBytesPerSec   = REVDWBYTES(pfmt->nAvgBytesPerSec);
+    pfmt->nBlockAlign   = (WORD) REVWBYTES(pfmt->nBlockAlign);
+    pfmt->wBitsPerSample    = (WORD) REVWBYTES(pfmt->wBitsPerSample);
+}
+
+static void fmtExSwapBytes(PSFFILE *sfdat)
+{
+    WAVEFORMATEXTENSIBLE  *pfmtEx =  &(sfdat->fmt);
+    WAVEFORMATEX          *pfmt   = &(pfmtEx->Format);
+    
+    pfmt->wFormatTag    = (WORD) REVWBYTES(pfmt->wFormatTag);
+    pfmt->nChannels     = (WORD) REVWBYTES(pfmt->nChannels);
+    pfmt->nSamplesPerSec    = REVDWBYTES(pfmt->nSamplesPerSec);
+    pfmt->nAvgBytesPerSec   = REVDWBYTES(pfmt->nAvgBytesPerSec);
+    pfmt->nBlockAlign   = (WORD) REVWBYTES(pfmt->nBlockAlign);
+    pfmt->wBitsPerSample    = (WORD) REVWBYTES(pfmt->wBitsPerSample);
+    pfmt->cbSize            = (WORD) REVWBYTES(pfmt->cbSize);
+    // OCT 09: missing!
+    pfmtEx->Samples.wValidBitsPerSample = (WORD) REVWBYTES(pfmtEx->Samples.wValidBitsPerSample);
+    pfmtEx->dwChannelMask     = (DWORD) REVDWBYTES(pfmtEx->dwChannelMask);
+    /* we swap numeric fields of GUID, but not the char string */
+    pfmtEx->SubFormat.Data1 = REVDWBYTES(pfmtEx->SubFormat.Data1);
+    pfmtEx->SubFormat.Data2 = (WORD) REVWBYTES(pfmtEx->SubFormat.Data2);
+    pfmtEx->SubFormat.Data3 = (WORD) REVWBYTES(pfmtEx->SubFormat.Data3);
+}
+
+static int check_guid(PSFFILE *sfdat)
+{
+    /* expects a GUID to be loaded already into sfdat.*/
+    if(sfdat->riff_format != PSF_WAVE_EX)
+        return 1;
+    
+    if(compare_guids(&(sfdat->fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_PCM))){
+        switch(sfdat->fmt.Format.wBitsPerSample){
+            case(16):
+                sfdat->samptype = PSF_SAMP_16;
+                break;
+            case(24):
+                /* only support packed format for now */
+                if((sfdat->fmt.Format.nBlockAlign / sfdat->fmt.Format.nChannels) != 3){
+                    sfdat->samptype = PSF_SAMP_UNKNOWN;
+                    return 1;
+                }
+                sfdat->samptype = PSF_SAMP_24;
+                break;
+            case(32):
+                sfdat->samptype = PSF_SAMP_32;
+                break;
+            default:
+                sfdat->samptype = PSF_SAMP_UNKNOWN;
+                return 1;
+        }
+        return 0;
+    }
+    if(compare_guids(&(sfdat->fmt.SubFormat),&(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
+        if(sfdat->fmt.Format.wBitsPerSample == 32) {
+            sfdat->samptype = PSF_SAMP_IEEE_FLOAT;
+            return 0;
+        }
+    /* add other recognised GUIDs here... */
+    if(compare_guids(&(sfdat->fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT)))
+        if(sfdat->fmt.Format.wBitsPerSample == 32) {
+            sfdat->samptype = PSF_SAMP_IEEE_FLOAT;
+            sfdat->chformat = MC_BFMT;
+            return 0;
+        }
+    if(compare_guids(&(sfdat->fmt.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_PCM))) {
+        switch(sfdat->fmt.Format.wBitsPerSample){
+            case(16):
+                sfdat->samptype = PSF_SAMP_16;
+                break;
+            case(24):
+                /* only support packed format for now */
+                if((sfdat->fmt.Format.nBlockAlign / sfdat->fmt.Format.nChannels) != 3){
+                    sfdat->samptype = PSF_SAMP_UNKNOWN;
+                    return 1;
+                }
+                sfdat->samptype = PSF_SAMP_24;
+                break;
+            case(32):
+                sfdat->samptype = PSF_SAMP_32;
+                break;
+            default:
+                sfdat->samptype = PSF_SAMP_UNKNOWN;
+                return 1;
+        }
+        sfdat->chformat = MC_BFMT;
+        return 0;
+    }
+    return 1;
+}
+/* return actual validbits */
+static int psf_bitsize(psf_stype type)
+{
+    int size = 0;
+    switch(type){
+        case(PSF_SAMP_16):
+            size = 16;
+            break;
+        case (PSF_SAMP_24):
+            size = 24;
+            break;
+        case(PSF_SAMP_32):
+        case(PSF_SAMP_IEEE_FLOAT):
+            size = 32;
+            break;
+        default:
+            break;
+    }
+    return size;
+}
+/* return sample size in bytes */
+static int psf_wordsize(psf_stype type)
+{
+    int size = 0;
+    switch(type){
+        case(PSF_SAMP_16):
+            size = 2;
+            break;
+        case (PSF_SAMP_24):
+            size = 3;
+            break;
+        case(PSF_SAMP_32):
+        case(PSF_SAMP_IEEE_FLOAT):
+            size = 4;
+            break;
+        default:
+            break;
+    }
+    return size;
+    
+}
+
+
+#if defined _WIN32 && defined _MSC_VER
+/* fast convergent rounding */
+__inline long psf_round(double fval)
+{
+    int result;
+    _asm{
+        fld fval
+        fistp   result
+        mov eax,result
+    }
+    return result;
+}
+
+#else
+/* slow convergent rounding ! */
+/* TODO: implement IEEE round-to-even */
+long psf_round(double val);
+
+long psf_round(double val)
+{
+    long k;
+    k = (long)(fabs(val)+0.5);
+    if(val < 0.0)
+        k = -k;
+    return k;
+}
+#endif
+
+#ifndef WIN32
+int stricmp(const char *a, const char *b)
+{
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+        
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+        
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+
+int
+strnicmp(const char *a, const char *b, const int length)
+{
+    int len = length;
+    
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+        
+        if(len-- < 1)
+            return 0;
+        
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+        
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+#endif
+
+/* create a new soundfile, from input props, or with default format  if props==NULL */
+/* current default = sr 44100, ch 1, WAVE, 16bit */
+/* could have func to define a new default format...*/
+
+static PSFFILE *psf_newFile(const PSF_PROPS *props)
+{
+    PSFFILE *sfdat;
+    
+    if(props){
+        if(props->srate <=0)
+            return NULL;
+        if(props->chans <=0)
+            return NULL;
+        /* NO support for PSF_SAMP_8 yet...*/
+        if(props->samptype < PSF_SAMP_16 || props->samptype > PSF_SAMP_IEEE_FLOAT)
+            return NULL;
+        if(props->format    <= PSF_FMT_UNKNOWN || props->format > PSF_AIFC)
+            return NULL;
+        if(props->chformat < STDWAVE || props->chformat > MC_WAVE_EX)
+            return NULL;
+    }
+    
+    
+    sfdat = (PSFFILE *) malloc(sizeof(PSFFILE));
+    if(sfdat==NULL)
+        return sfdat;
+    
+    POS64(sfdat->lastwritepos)      = 0;
+    sfdat->file         = NULL;
+    sfdat->filename         = NULL;
+    sfdat->nFrames          = 0;
+    sfdat->curframepos      = 0;
+    sfdat->isRead           = 1;                /* OK. who knows?    */
+    /* or use platform default format.... */
+    sfdat->riff_format      = props ? props->format : PSF_STDWAVE;      /* almost certainly! */
+    /*sfdat->isSeekable     = 1;*/
+    sfdat->clip_floats      = 1;
+    sfdat->rescale          = 0;
+    sfdat->rescale_fac      = 1.0f;
+    sfdat->is_little_endian = byte_order();
+    sfdat->samptype         = props ? props->samptype : PSF_SAMP_16;        /* reasonable...     */
+    POS64(sfdat->dataoffset)        = 0;
+    POS64(sfdat->fmtoffset)     = 0;
+    POS64(sfdat->peakoffset)        = 0;
+    sfdat->chformat         = props ? props->chformat : STDWAVE;
+    /*setup Format */
+    if(props)
+        sfdat->fmt.Format.wFormatTag  = (WORD) (props->samptype == PSF_SAMP_IEEE_FLOAT ?  WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
+    else
+        sfdat->fmt.Format.wFormatTag  = WAVE_FORMAT_PCM;
+    sfdat->fmt.Format.nChannels       = (WORD) (props ?  props->chans : 1);
+    sfdat->fmt.Format.nSamplesPerSec  = props ? props->srate : 44100;
+    sfdat->fmt.Format.nBlockAlign     = (WORD) (props  ?  sfdat->fmt.Format.nChannels * psf_wordsize(props->samptype) : sfdat->fmt.Format.nChannels * sizeof(short));
+    sfdat->fmt.Format.wBitsPerSample  = (WORD) (props ?  psf_bitsize(props->samptype)  : sizeof(short) * BITS_PER_BYTE);
+    sfdat->fmt.Format.nAvgBytesPerSec = sfdat->fmt.Format.nSamplesPerSec
+    *sfdat->fmt.Format.nChannels
+    * (sfdat->fmt.Format.wBitsPerSample / BITS_PER_BYTE);
+    sfdat->pPeaks           = NULL;
+    sfdat->peaktime         = 0;
+    sfdat->fmt.Format.cbSize = 0;
+    /* set initial defaults for WAVE-EX stuff; may change */
+    /* but nobody should look at these fields unless we have a real WAVE-EX file anyway... */
+    sfdat->fmt.dwChannelMask = SPKRS_UNASSIGNED;
+    sfdat->fmt.Samples.wValidBitsPerSample  = sfdat->fmt.Format.wBitsPerSample;
+    /* 0 should be a guaranteed non-valid GUID! */
+    memset((char *) &(sfdat->fmt.SubFormat),0,sizeof(GUID));
+    
+    if(props && (props->format == PSF_WAVE_EX)) {
+        sfdat->fmt.Format.cbSize = 22;
+        /* NB we will set the GUID from wFormatTag in waveExWriteHeader() */
+        /* should really flag an error if user sets this */
+        if(sfdat->chformat==STDWAVE)
+            sfdat->chformat = MC_STD;
+        
+        /* set wavex speaker mask */
+        /* TODO: support custom speaker masks, wordsizes, etc */
+        switch(sfdat->chformat){
+            case MC_MONO:
+                if(props->chans != 1){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_MONO;
+                break;
+            case MC_STEREO:
+                if(props->chans != 2){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_STEREO;
+                break;
+            case MC_QUAD:
+                if(props->chans != 4){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_GENERIC_QUAD;
+                break;
+            case MC_LCRS:
+                if(props->chans != 4){
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_SURROUND_LCRS;
+                break;
+                
+            case MC_DOLBY_5_1:
+                if(props->chans != 6){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_DOLBY5_1;
+                break;
+            case MC_SURR_5_0:
+                if(props->chans != 5){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_SURR_5_0;
+                break;
+            case MC_SURR_6_1:
+                if(props->chans != 7){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_6_1;
+                break;
+            case MC_SURR_7_1:
+                if(props->chans != 8){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_7_1;
+                break;
+            case MC_CUBE:
+                if(props->chans != 8){
+                    //rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                    free(sfdat);
+                    return NULL;
+                }
+                sfdat->fmt.dwChannelMask = SPKRS_CUBE;
+                break;
+            default:
+                /*MC_STD, MC_BFMT */
+                sfdat->fmt.dwChannelMask = SPKRS_UNASSIGNED;
+                break;
+        }
+    }
+    /* no dither, by default */
+    sfdat->dithertype = PSF_DITHER_OFF;
+    sfdat->illformed = 0;                   /*RWD Feb 2010 */
+    memset(sfdat->warnstring,0,WARNSTRING_SIZE);
+    return sfdat;
+}
+
+int psf_getWarning(int sfd,const char** warnstring)
+{
+    int retval = 0;
+    PSFFILE *sfdat;
+    if(sfd < 0 || sfd > psf_maxfiles)
+        retval = PSF_E_BADARG;
+    sfdat  = psf_files[sfd];
+    
+    if(sfdat->illformed==0)
+        retval = 0;
+    else{
+        *warnstring = sfdat->warnstring;
+        retval =  1;
+    }
+    return retval;
+}
+/* complete header before closing file; return PSF_E_NOERROR[= 0] on success */
+static int wavUpdate(PSFFILE *sfdat)
+{
+    DWORD riffsize,datasize;
+    fpos_t bytepos;
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(POS64(sfdat->dataoffset) != 0);
+#endif      
+    POS64(bytepos) = sizeof(int);
+    if((fsetpos(sfdat->file,&bytepos))==0) {
+        riffsize = (sfdat->nFrames * sfdat->fmt.Format.nBlockAlign) +  (MYLONG) POS64(sfdat->dataoffset);
+        riffsize -= 2 * sizeof(DWORD);
+        if(!sfdat->is_little_endian)
+            riffsize = REVDWBYTES(riffsize);
+        if(fwrite((char *) &riffsize,sizeof(int),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    }
+    else
+        return PSF_E_CANT_SEEK;
+    if(sfdat->pPeaks){
+        if(POS64(sfdat->peakoffset)==0)
+            return PSF_E_BADARG;
+        
+        /*do byterev if necessary...*/
+        if((fsetpos(sfdat->file,&sfdat->peakoffset))==0){
+            /*set current time*/
+            DWORD *pblock;
+            int i;
+            time_t now = time(0);
+            if(!sfdat->is_little_endian){
+                now = REVDWBYTES(now);
+                pblock = (DWORD *) (sfdat->pPeaks);
+                for(i=0;i < sfdat->fmt.Format.nChannels * 2; i++)
+                    pblock[i] = REVDWBYTES(pblock[i]);
+            }
+            if((fwrite((char*)&now,sizeof(DWORD),1,sfdat->file)) != 1)
+                return PSF_E_CANT_WRITE;
+            
+            if((fwrite((char *) (sfdat->pPeaks),sizeof(PSF_CHPEAK),sfdat->fmt.Format.nChannels,sfdat->file))
+               != sfdat->fmt.Format.nChannels )
+                return PSF_E_CANT_WRITE;
+        }
+        else
+            return PSF_E_CANT_SEEK;
+    }
+    POS64(bytepos) = POS64(sfdat->dataoffset) -  sizeof(int);
+    if((fsetpos(sfdat->file,&bytepos))==0) {
+        datasize = sfdat->nFrames * sfdat->fmt.Format.nBlockAlign;
+        if(!sfdat->is_little_endian)
+            datasize = REVDWBYTES(datasize);
+        if(fwrite((char *) & datasize,sizeof(DWORD),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    }
+    if(fseek(sfdat->file,0,SEEK_END)){
+        /*DBGFPRINTF((stderr,"wavUpdate: error reseeking to end of file\n"));*/
+        return PSF_E_CANT_SEEK;
+    }
+    
+    return PSF_E_NOERROR;
+}
+
+/* ditto for AIFF... */
+
+/* NB: the AIFF spec is unclear on type of size field. We decide on unsigned long (DWORD) here;
+ on the principle that a COMM chunk with an unsigned long nSampleFrames really needs the
+ chunk size to be unsigned long too!.
+ 
+ */
+static int aiffUpdate(PSFFILE *sfdat)
+{
+    DWORD aiffsize,datasize,rev_datasize,frames;
+    fpos_t bytepos,filesize;
+    unsigned char pad = 0x00;
+    
+    if(sfdat==NULL || sfdat->file== NULL)
+        return PSF_E_BADARG;
+    
+    if(POS64(sfdat->dataoffset)  == 0)
+        return PSF_E_BADARG;
+    POS64(bytepos) = sizeof(int);
+    if((fsetpos(sfdat->file,&bytepos))==0) {
+        /* RWD 26:10:2002 */
+        aiffsize = (sfdat->nFrames * sfdat->fmt.Format.nBlockAlign)
+        + (MYLONG) POS64(sfdat->dataoffset);
+        // need to count any needed pad byte
+        aiffsize += (aiffsize % 2);
+        // deduct 8 bytes for FORM<size>
+        aiffsize -= 2 * sizeof(DWORD);
+        if(sfdat->is_little_endian)
+            aiffsize = REVDWBYTES(aiffsize);
+        if(fwrite((char *) &aiffsize,sizeof(DWORD),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    }
+    else
+        return PSF_E_CANT_SEEK;
+    POS64(bytepos)  = POS64(sfdat->fmtoffset) + sizeof(WORD);
+    if((fsetpos(sfdat->file,&bytepos))==0) {
+        frames = sfdat->nFrames;
+        if(sfdat->is_little_endian)
+            frames = REVDWBYTES(frames);
+        if(fwrite((char *) &frames,sizeof(DWORD),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    }
+    else
+        return PSF_E_CANT_SEEK;
+    if(sfdat->pPeaks){
+        if(POS64(sfdat->peakoffset)==0)
+            return PSF_E_BADARG;
+        
+        /*do byterev if necessary...*/
+        if((fsetpos(sfdat->file,&sfdat->peakoffset))==0){
+            /*set current time*/
+            DWORD *pblock;
+            int i;
+            time_t now = time(0);
+            if(sfdat->is_little_endian){
+                now = REVDWBYTES(now);
+                pblock = (DWORD *) (sfdat->pPeaks);
+                for(i=0;i < sfdat->fmt.Format.nChannels * 2; i++)
+                    pblock[i] = REVDWBYTES(pblock[i]);
+            }
+            if((fwrite((char*)&now,sizeof(DWORD),1,sfdat->file)) != 1)
+                return PSF_E_CANT_WRITE;
+            
+            if((fwrite((char *) (sfdat->pPeaks),sizeof(PSF_CHPEAK),sfdat->fmt.Format.nChannels,sfdat->file))
+               != sfdat->fmt.Format.nChannels )
+                return PSF_E_CANT_WRITE;
+        }
+        else
+            return PSF_E_CANT_SEEK;
+    }
+    POS64(bytepos) = POS64(sfdat->dataoffset) - (3 * sizeof(int));
+    if((fsetpos(sfdat->file,&bytepos))==0) {
+        datasize = sfdat->nFrames * sfdat->fmt.Format.nBlockAlign;
+        datasize += 2* sizeof(DWORD);   /* add offset and blocksize fields */
+        rev_datasize = datasize; /* preserve this for the seek later on */
+        if(sfdat->is_little_endian)
+            rev_datasize = REVDWBYTES(datasize);
+        if(fwrite((char *) & rev_datasize,sizeof(DWORD),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    }
+    else
+        return PSF_E_CANT_SEEK;
+    /* datachunk needs added pad byte if odd, not included in saved chunksize*/
+    POS64(bytepos) = POS64(sfdat->dataoffset) + datasize;
+    if((fsetpos(sfdat->file,&bytepos))){
+        return PSF_E_CANT_SEEK;
+    }
+    if(fgetpos(sfdat->file,&filesize))
+        return PSF_E_CANT_SEEK;
+#ifdef _DEBUG   
+    assert(POS64(filesize) == POS64(bytepos));
+#endif  
+    if(POS64(filesize) % 2)
+        if(fwrite(&pad,sizeof(unsigned char),1,sfdat->file) != 1)
+            return PSF_E_CANT_WRITE;
+    
+    return PSF_E_NOERROR;
+}
+
+
+/* internal write func: return 0 for success */
+static int wavDoWrite(PSFFILE *sfdat, const void* buf, DWORD nBytes)
+{
+    
+    DWORD written = 0;
+    if(sfdat==NULL || buf==NULL)
+        return PSF_E_BADARG;
+    
+    if(sfdat->file==NULL)
+        return PSF_E_CANT_WRITE;
+    
+    if((written = fwrite(buf,sizeof(char),nBytes,sfdat->file)) != nBytes) {
+        DBGFPRINTF((stderr, "wavDoWrite: wanted %d got %d.\n",
+                    (int) nBytes,(int) written));
+        return PSF_E_CANT_WRITE;
+    }
+    sfdat->lastop  = PSF_OP_WRITE;
+    return PSF_E_NOERROR;
+}
+
+static int wavDoRead(PSFFILE *sfdat, void* buf, DWORD nBytes)
+{
+    
+    DWORD got = 0;
+    if(sfdat==NULL || buf==NULL)
+        return PSF_E_BADARG;
+    
+    if(sfdat->file==NULL)
+        return PSF_E_CANT_READ;
+    
+    if((got = fread(buf,sizeof(char),nBytes,sfdat->file)) != nBytes) {
+        DBGFPRINTF((stderr, "wavDoRead: wanted %d got %d.\n",
+                    (int) nBytes,(int) got));
+        return PSF_E_CANT_READ;
+    }
+    sfdat->lastop = PSF_OP_READ;
+    return PSF_E_NOERROR;
+    
+}
+
+/* write PEAK chunk if we have the data */
+static int wavWriteHeader(PSFFILE *sfdat)
+{
+    DWORD tag,size;
+    WORD cbSize = 0;
+    WAVEFORMATEX *pfmt;
+    PSF_CHPEAK *peaks;
+    fpos_t bytepos;
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->riff_format == PSF_STDWAVE);
+    assert(sfdat->nFrames == 0);
+    assert(!sfdat->isRead);
+    assert(sfdat->fmt.Format.nChannels != 0);
+#endif
+    
+    /*clear pPeaks array*/
+    if(sfdat->pPeaks)
+        memset((char *)sfdat->pPeaks,0,sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+    
+    
+    tag = TAG('R','I','F','F');
+    size = 0;
+    if(!sfdat->is_little_endian)
+        size = REVDWBYTES(size);
+    else
+        tag = REVDWBYTES(tag);
+    
+    
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    tag = TAG('W','A','V','E');
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    pfmt = &(sfdat->fmt.Format);
+    
+    tag = TAG('f','m','t',' ');
+    size = sizeof(WAVEFORMAT);
+    if(sfdat->samptype==PSF_SAMP_IEEE_FLOAT)
+        size += sizeof(WORD);         /* for cbSize: WAVEOFRMATEX */
+    if(!sfdat->is_little_endian){
+        size = REVDWBYTES(size);
+        fmtSwapBytes(sfdat);
+    }
+    else
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->fmtoffset = bytepos;
+    
+    if(wavDoWrite(sfdat,(char *)pfmt,sizeof(WAVEFORMAT)))
+        return PSF_E_CANT_WRITE;
+    /*add cbSize if floatsams */
+    if(sfdat->samptype==PSF_SAMP_IEEE_FLOAT)
+        if(wavDoWrite(sfdat,(char *)&cbSize,sizeof(WORD)))
+            return PSF_E_CANT_WRITE;
+    /* reswap it all */
+    if(!sfdat->is_little_endian){
+        fmtSwapBytes(sfdat);
+    }
+    
+    if(sfdat->pPeaks){
+        DWORD version = 1, now = 0;
+        peaks = sfdat->pPeaks;
+        
+        tag = TAG('P','E','A','K');
+        size = 2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * pfmt->nChannels;
+        if(!sfdat->is_little_endian){
+            size = REVDWBYTES(size);
+            version  = REVDWBYTES(version);
+        }
+        else
+            tag = REVDWBYTES(tag);
+        
+        if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&version,sizeof(DWORD)))
+            return PSF_E_CANT_WRITE;
+        if(fgetpos(sfdat->file,&bytepos))
+            return PSF_E_CANT_SEEK;
+        sfdat->peakoffset = bytepos;  /*we need to update time*/
+        
+        if(wavDoWrite(sfdat,(char *) &now,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *) peaks, sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels))
+            return PSF_E_CANT_WRITE;
+    }
+    tag = TAG('d','a','t','a');
+    size = 0;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->dataoffset = bytepos;
+    return PSF_E_NOERROR;
+}
+
+
+static int waveExWriteHeader(PSFFILE *sfdat)
+{
+    DWORD tag,size;
+    WAVEFORMATEXTENSIBLE *pfmt;
+    PSF_CHPEAK *peaks;
+    GUID *pGuid = NULL;
+    fpos_t bytepos;
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->chformat > STDWAVE);
+    assert(sfdat->nFrames==0);
+    assert(!sfdat->isRead);
+    assert(sfdat->fmt.Format.nChannels != 0);
+#endif
+    /*clear pPeaks array*/
+    if(sfdat->pPeaks)
+        memset((char *)sfdat->pPeaks,0,sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+    /* complete WAVE-EX format fields: */
+    if(sfdat->chformat==MC_BFMT){
+        if(sfdat->samptype== PSF_SAMP_IEEE_FLOAT){
+            pGuid = (GUID *)  &SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT;
+        }
+        else{
+            pGuid =(GUID *) &SUBTYPE_AMBISONIC_B_FORMAT_PCM;
+        }
+        
+    }else {
+        if(sfdat->fmt.Format.wFormatTag==  WAVE_FORMAT_IEEE_FLOAT){
+            pGuid = (GUID *)  &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+        }
+        else{
+            pGuid =(GUID *) &KSDATAFORMAT_SUBTYPE_PCM;
+        }
+    }
+    sfdat->fmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    memcpy((char *) &(sfdat->fmt.SubFormat),(char *)pGuid,sizeof(GUID));
+    tag = TAG('R','I','F','F');
+    size = 0;
+    if(!sfdat->is_little_endian)
+        size = REVDWBYTES(size);
+    else
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    tag = TAG('W','A','V','E');
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    pfmt = &(sfdat->fmt);
+    tag = TAG('f','m','t',' ');
+    size = sizeof_WFMTEX;
+    if(!sfdat->is_little_endian){
+        size = REVDWBYTES(size);
+        fmtExSwapBytes(sfdat);
+    }
+    else
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->fmtoffset = bytepos;
+    /* write fmt elementwise, to avoid C alignment traps with WORD */
+    /* 16byte format...*/
+    if(wavDoWrite(sfdat,(char *)pfmt,sizeof(WAVEFORMAT))
+       /** cbSize... */
+       ||wavDoWrite(sfdat,(char *) &(pfmt->Format.cbSize),sizeof(WORD))
+       /* validbits... */
+       ||wavDoWrite(sfdat,(char *) &(pfmt->Samples.wValidBitsPerSample),sizeof(WORD))
+       /* ChannelMask .... */
+       ||wavDoWrite(sfdat,(char *) &(pfmt->dwChannelMask),sizeof(DWORD))
+       /*  and the GUID */
+       ||wavDoWrite(sfdat,(char *) &(pfmt->SubFormat),sizeof(GUID)))
+        return PSF_E_CANT_WRITE;
+    /* reswap it all */
+    if(!sfdat->is_little_endian){
+        fmtExSwapBytes(sfdat);
+    }
+    if(sfdat->pPeaks){
+        DWORD version = 1, now = 0;
+        
+        peaks = sfdat->pPeaks;
+        tag = TAG('P','E','A','K');
+        size = 2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * pfmt->Format.nChannels;
+        if(!sfdat->is_little_endian){
+            size = REVDWBYTES(size);
+            version  = REVDWBYTES(version);
+        }
+        else
+            tag = REVDWBYTES(tag);
+        if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&version,sizeof(DWORD)))
+            return PSF_E_CANT_WRITE;
+        if(fgetpos(sfdat->file,&bytepos))
+            return PSF_E_CANT_SEEK;
+        sfdat->peakoffset = bytepos;  /*we need to update time*/
+        if(wavDoWrite(sfdat,(char *) &now,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *) peaks, sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels))
+            return PSF_E_CANT_WRITE;
+    }
+    
+    tag = TAG('d','a','t','a');
+    size = 0;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->dataoffset = bytepos;
+    return PSF_E_NOERROR;
+}
+
+static int aiffWriteHeader(PSFFILE *sfdat)
+{
+    DWORD tag,size;
+    PSF_CHPEAK *peaks;
+    DWORD dwData,offset,blocksize;
+    unsigned char ieee[10];
+    WORD wData;
+    fpos_t bytepos;
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->chformat == STDWAVE);
+    assert(sfdat->nFrames==0);
+    assert(!sfdat->isRead);
+    assert(sfdat->riff_format == PSF_AIFF);
+    assert(sfdat->fmt.Format.nChannels != 0);
+#endif
+    
+    /*clear pPeaks array*/
+    if(sfdat->pPeaks)
+        memset((char *)sfdat->pPeaks,0,sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+    
+    tag = TAG('F','O','R','M');
+    size = 0;
+    
+    if(sfdat->is_little_endian) {
+        size = REVDWBYTES(size);
+        tag = REVDWBYTES(tag);
+    }
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    tag = TAG('A','I','F','F');
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    tag = TAG('C','O','M','M');
+    size = 18;
+    if(sfdat->is_little_endian){
+        size = REVDWBYTES(size);
+        tag = REVDWBYTES(tag);
+    }
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->fmtoffset = bytepos;
+    
+    wData = sfdat->fmt.Format.nChannels;
+    if(sfdat->is_little_endian)
+        wData = (WORD) REVWBYTES(wData);
+    if(wavDoWrite(sfdat,(char *)&wData,sizeof(WORD)))
+        return PSF_E_CANT_WRITE;
+    
+    dwData = 0;         /* nFrames */
+    if(wavDoWrite(sfdat,(char *)&dwData,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    wData = sfdat->fmt.Format.wBitsPerSample;
+    if(sfdat->is_little_endian)
+        wData = (WORD) REVWBYTES(wData);
+    if(wavDoWrite(sfdat,(char *)&wData,sizeof(WORD)))
+        return PSF_E_CANT_WRITE;
+    double_to_ieee_80((double)sfdat->fmt.Format.nSamplesPerSec,ieee);
+    
+    if(wavDoWrite(sfdat,ieee,10))
+        return PSF_E_CANT_WRITE;
+    if(sfdat->pPeaks){
+        DWORD version = 1, now = 0;
+        peaks = sfdat->pPeaks;
+        
+        tag = TAG('P','E','A','K');
+        size = 2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels;
+        if(sfdat->is_little_endian){
+            size = REVDWBYTES(size);
+            version  = REVDWBYTES(version);
+            tag = REVDWBYTES(tag);
+        }
+        if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&version,sizeof(DWORD)))
+            return PSF_E_CANT_WRITE;
+        if(fgetpos(sfdat->file,&bytepos))
+            return PSF_E_CANT_SEEK;
+        sfdat->peakoffset = bytepos;  /*we need to update time*/
+        
+        if(wavDoWrite(sfdat,(char *) &now,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *) peaks, sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels))
+            return PSF_E_CANT_WRITE;
+    }
+    
+    tag = TAG('S','S','N','D');
+    size = offset = blocksize = 0;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&offset,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&blocksize,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->dataoffset = bytepos;
+    return PSF_E_NOERROR;
+}
+
+
+static int aifcWriteHeader(PSFFILE *sfdat)
+{
+    DWORD tag,size;
+    PSF_CHPEAK *peaks;
+    DWORD dwData,offset,blocksize,aifcver = AIFC_VERSION_1,ID_compression;
+    /*assume 32bit floats, but we may be asked to use aifc for integer formats too*/
+    char *str_compressed = (char *) aifc_floatstring;
+    int pstring_size = 10;
+    unsigned char ieee[10];
+    WORD wData;
+    fpos_t bytepos;
+    
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->nFrames==0);
+    assert(!sfdat->isRead);
+    assert(sfdat->riff_format == PSF_AIFC);
+    assert(sfdat->fmt.Format.nChannels != 0);
+#endif
+    
+    if(sfdat->samptype==PSF_SAMP_IEEE_FLOAT)
+        ID_compression = TAG('f','l','3','2');
+    else {
+        ID_compression = TAG('N','O','N','E');
+        pstring_size = 16;
+        str_compressed = (char *) aifc_notcompressed;
+    }
+    /*clear pPeaks array*/
+    if(sfdat->pPeaks)
+        memset((char *)sfdat->pPeaks,0,sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+    
+    tag = TAG('F','O','R','M');
+    size = 0;
+    
+    if(sfdat->is_little_endian) {
+        size = REVDWBYTES(size);
+        tag = REVDWBYTES(tag);
+    }
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    tag = TAG('A','I','F','C');
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    tag = TAG('F','V','E','R');
+    size = sizeof(DWORD);
+    if(sfdat->is_little_endian){
+        size = REVDWBYTES(size);
+        tag = REVDWBYTES(tag);
+        aifcver = REVDWBYTES(aifcver);
+    }
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&aifcver,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    tag = TAG('C','O','M','M');
+    size = 22 + pstring_size;
+    if(sfdat->is_little_endian){
+        size = REVDWBYTES(size);
+        tag = REVDWBYTES(tag);
+    }
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->fmtoffset = bytepos;
+    
+    wData = sfdat->fmt.Format.nChannels;
+    if(sfdat->is_little_endian)
+        wData = (WORD) REVWBYTES(wData);
+    if(wavDoWrite(sfdat,(char *)&wData,sizeof(WORD)))
+        return PSF_E_CANT_WRITE;
+    
+    dwData = 0;         /* nFrames */
+    if(wavDoWrite(sfdat,(char *)&dwData,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    
+    wData = sfdat->fmt.Format.wBitsPerSample;
+    if(sfdat->is_little_endian)
+        wData = (WORD) REVWBYTES(wData);
+    if(wavDoWrite(sfdat,(char *)&wData,sizeof(WORD)))
+        return PSF_E_CANT_WRITE;
+    double_to_ieee_80((double)sfdat->fmt.Format.nSamplesPerSec,ieee);
+    
+    if(wavDoWrite(sfdat,ieee,10))
+        return PSF_E_CANT_WRITE;
+    /*AIFC bits */
+    if(sfdat->is_little_endian)
+        ID_compression = REVDWBYTES(ID_compression);
+    if(wavDoWrite(sfdat,(char *)&ID_compression,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(wavDoWrite(sfdat,str_compressed,pstring_size))
+        return PSF_E_CANT_WRITE;
+    
+    if(sfdat->pPeaks){
+        DWORD version = 1, now = 0;
+        
+        peaks = sfdat->pPeaks;
+        tag = TAG('P','E','A','K');
+        size = 2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels;
+        if(sfdat->is_little_endian){
+            size = REVDWBYTES(size);
+            version  = REVDWBYTES(version);
+            tag = REVDWBYTES(tag);
+        }
+        if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *)&version,sizeof(DWORD)))
+            return PSF_E_CANT_WRITE;
+        if(fgetpos(sfdat->file,&bytepos))
+            return PSF_E_CANT_SEEK;
+        sfdat->peakoffset = bytepos;  /*we need to update time*/
+        
+        if(wavDoWrite(sfdat,(char *) &now,sizeof(DWORD))
+           || wavDoWrite(sfdat,(char *) peaks, sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels))
+            return PSF_E_CANT_WRITE;
+    }
+    
+    tag = TAG('S','S','N','D');
+    size = offset = blocksize = 0;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(wavDoWrite(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&size,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&offset,sizeof(DWORD))
+       || wavDoWrite(sfdat,(char *)&blocksize,sizeof(DWORD)))
+        return PSF_E_CANT_WRITE;
+    if(fgetpos(sfdat->file,&bytepos))
+        return PSF_E_CANT_SEEK;
+    sfdat->dataoffset = bytepos;
+    return PSF_E_NOERROR;
+}
+
+/* create soundfile. return descriptor, or some PSF_error value < 0 */
+/* supported clipping or non-clipping of floats to 0dbFS, 
+ minimum header (or PEAK), and RDWR or RDONLY (but last not implemented yet!) */
+/* we expect full format info to be set in props */
+/* I want to offer share-read access (easy with WIN32), but can't with  ANSI! */
+/* possible TODO:  enforce non-destructive by e.g. rejecting create on existing file */
+int psf_sndCreate(const char *path,const PSF_PROPS *props,int clip_floats,int minheader, int mode)
+{       
+    int i,rc = PSF_E_UNSUPPORTED;
+    psf_format fmt;
+    PSFFILE *sfdat;
+    char *fmtstr = "wb+";   /* default is READ+WRITE */
+    /*  disallow props = NULL here, until/unless I can offer mechanism to set default props via psf_init() */
+    if(path == NULL || props == NULL)
+        return PSF_E_BADARG;
+    
+    for(i=0; i < psf_maxfiles; i++) {
+        if(psf_files[i] == NULL)
+            break;
+    }
+    if(i==psf_maxfiles)
+        return PSF_E_TOOMANYFILES;
+    
+    sfdat = psf_newFile(props);
+    if(sfdat == NULL)
+        return PSF_E_NOMEM;
+    
+    sfdat->clip_floats = clip_floats;
+    fmt = psf_getFormatExt(path);
+    if(fmt==PSF_FMT_UNKNOWN)
+        return PSF_E_UNSUPPORTED;
+    if(sfdat->samptype == PSF_SAMP_UNKNOWN)
+        return PSF_E_BADARG;
+    
+    sfdat->filename = (char *) malloc(strlen(path)+1);
+    if(sfdat->filename==NULL) {
+        DBGFPRINTF((stderr, "wavOpenWrite: no memory for filename\n"));
+        return PSF_E_NOMEM;
+    }
+    if(!minheader){
+        sfdat->pPeaks = (PSF_CHPEAK *) malloc(sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+        if(sfdat->pPeaks==NULL){
+            DBGFPRINTF((stderr, "wavOpenWrite: no memory for peak data\n"));
+            return PSF_E_NOMEM;
+        }
+    }
+    /*switch (mode).... */
+    if(mode==PSF_CREATE_WRONLY)
+        fmtstr = "wb";
+    /* deal with CREATE_TEMPORARY later on! */
+    if((sfdat->file = fopen(path,fmtstr))  == NULL) {
+        DBGFPRINTF((stderr, "wavOpenWrite: cannot create '%s'\n", path));
+        return PSF_E_CANT_OPEN;
+    }
+    
+    strcpy(sfdat->filename, path);
+    sfdat->isRead = 0;
+    sfdat->nFrames = 0;
+    /* force aif f/p data to go to aifc format */
+    if(sfdat->samptype==PSF_SAMP_IEEE_FLOAT && fmt==PSF_AIFF){
+        DBGFPRINTF((stderr, "Warning: writing floating point data in AIFC format\n"));
+        fmt= PSF_AIFC;
+    }
+    /* .wav extension can be either std WAVE or WAVE-EX */
+    if(fmt==PSF_STDWAVE){
+        if(props->format==PSF_WAVE_EX)
+            fmt = PSF_WAVE_EX;
+    }
+    sfdat->riff_format = fmt;
+    
+    switch(fmt){
+        case(PSF_STDWAVE):
+            rc = wavWriteHeader(sfdat);
+            break;
+        case(PSF_AIFF):
+            
+            rc = aiffWriteHeader(sfdat);
+            break;
+        case(PSF_AIFC):
+            
+            rc = aifcWriteHeader(sfdat);
+            break;
+        case (PSF_WAVE_EX):
+            rc = waveExWriteHeader(sfdat);
+            break;
+        default:
+            sfdat->riff_format = PSF_FMT_UNKNOWN;
+            /* RAW? */
+            break;
+    }
+    if(rc < PSF_E_NOERROR)
+        return rc;
+    psf_files[i] = sfdat;
+    return i;
+}
+
+/* snd close:  automatically completes PEAK data when writing */
+/* return 0 for success */
+int psf_sndClose(int sfd)
+{
+    int rc = PSF_E_NOERROR;
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    sfdat  = psf_files[sfd];
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    if(sfdat==NULL || sfdat->file==NULL)
+        return PSF_E_BADARG;
+    if(!sfdat->isRead){
+        switch(sfdat->riff_format){
+            case(PSF_STDWAVE):
+            case(PSF_WAVE_EX):
+                rc = wavUpdate(sfdat);
+                break;
+            case(PSF_AIFF):
+            case(PSF_AIFC):
+                rc = aiffUpdate(sfdat);
+                break;
+            default:
+                rc = PSF_E_CANT_CLOSE;
+                break;
+        }
+    }
+    if(psf_release_file(sfdat))
+        rc = PSF_E_CANT_CLOSE;
+    else {
+        free(sfdat);
+        psf_files[sfd]= NULL;
+    }
+    return rc;
+}
+
+/* write floats (multi-channel) framebuf to whichever target format. tracks PEAK data.*/ 
+/* bend over backwards not to modify source data */
+/* returns nFrames, or errval < 0 */
+int psf_sndWriteFloatFrames(int sfd, const float *buf, DWORD nFrames)
+{
+    int chans,lsamp;
+    DWORD i;
+    int j,do_reverse;
+    const float *pbuf = buf;
+    float fsamp,absfsamp;
+    int do_shift = 1;
+    PSFFILE *sfdat;
+    SND_SAMP s_samp;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    
+    if(buf==NULL)
+        return PSF_E_BADARG;
+    if(nFrames == 0)
+        return nFrames;
+    if(sfdat->isRead)
+        return PSF_E_FILE_READONLY;
+    chans = sfdat->fmt.Format.nChannels;
+    
+    switch(sfdat->riff_format){
+        case(PSF_STDWAVE):
+        case(PSF_WAVE_EX):
+            do_reverse = (sfdat->is_little_endian ? 0 : 1 );
+            do_shift = 1;
+            break;
+        case(PSF_AIFF):
+        case(PSF_AIFC):
+            do_reverse = (sfdat->is_little_endian ? 1 : 0 );
+            do_shift = 0;
+            break;
+        default:
+            return PSF_E_UNSUPPORTED;
+    }
+    if(sfdat->lastop  == PSF_OP_READ)
+        fflush(sfdat->file);
+    switch(sfdat->samptype){
+        case(PSF_SAMP_IEEE_FLOAT):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = *pbuf++;
+                        if(sfdat->clip_floats){
+                            fsamp = min(fsamp,1.0f);
+                            fsamp = max(fsamp,-1.0f);
+                        }
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        // lsamp = * (int *) pbuf++;
+                        s_samp.fsamp = fsamp;
+                        lsamp = s_samp.lsamp;
+                        lsamp = REVDWBYTES(lsamp);
+                        if(wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, pbuf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = pbuf[j];
+                        if(sfdat->clip_floats){
+                            fsamp = min(fsamp,1.0f);
+                            fsamp = max(fsamp,-1.0f);
+                        }
+                        absfsamp = (float)fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                    }
+                }
+                if(wavDoWrite(sfdat,(char *)buf,nFrames * chans * sizeof(float))){
+                    DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                    return PSF_E_CANT_WRITE;
+                }
+            }
+            break;
+        case(PSF_SAMP_16):
+            /* TODO: optimise all this with func pointers etc */
+            if(do_reverse){
+                short ssamp;
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip16);
+                        fsamp = max(fsamp,-dclip16);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        if(sfdat->dithertype == PSF_DITHER_TPDF)
+                            ssamp = (short) psf_round(fsamp * 32766.0 + 2.0 * trirand());
+                        else
+                            ssamp = (short) psf_round(fsamp * MAX_16BIT);
+                        ssamp = (short) REVWBYTES(ssamp);
+                        if( wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                short ssamp;
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip16);
+                        fsamp = max(fsamp,-dclip16);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        if(sfdat->dithertype == PSF_DITHER_TPDF)
+                            ssamp = (short) psf_round(fsamp * 32766.0 + 2.0 * trirand());
+                        else
+                            ssamp = (short) psf_round(fsamp * MAX_16BIT);
+                        
+                        if(wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        case(PSF_SAMP_24):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip24);
+                        fsamp = max(fsamp,-dclip24);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * (MAX_32BIT));
+                        lsamp = REVDWBYTES(lsamp);
+                        if(do_shift){
+                            if(sfdat->is_little_endian)
+                                lsamp >>= 8;
+                            else
+                                lsamp <<= 8;
+                        }
+                        if( wavDoWrite(sfdat,(char *) &lsamp,3)){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip24);
+                        fsamp = max(fsamp,-dclip24);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * (MAX_32BIT));
+                        if(do_shift){
+                            if(sfdat->is_little_endian)
+                                lsamp >>= 8;
+                            else
+                                lsamp <<= 8;
+                        }
+                        if(wavDoWrite(sfdat,(char *) &lsamp,3)){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        case(PSF_SAMP_32):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip32);
+                        fsamp = max(fsamp,-dclip32);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * (MAX_32BIT));
+                        lsamp = REVDWBYTES(lsamp);
+                        if( wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip32);
+                        fsamp = max(fsamp,-dclip32);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * (MAX_32BIT));
+                        
+                        if(wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        default:
+            DBGFPRINTF((stderr, "wavOpenWrite: unsupported sample format\n"));
+            return PSF_E_UNSUPPORTED;
+            
+    }
+    POS64(sfdat->lastwritepos) += nFrames;
+    sfdat->curframepos = (MYLONG) POS64(sfdat->lastwritepos);
+    sfdat->nFrames = max(sfdat->nFrames,(DWORD) POS64(sfdat->lastwritepos));
+    /*  fflush(sfdat->file); */ /* ? may need this if reading/seeking as well as  write, etc */
+    return nFrames;
+    
+}
+
+int psf_sndWriteDoubleFrames(int sfd, const double *buf, DWORD nFrames)
+{
+    int chans,lsamp;
+    DWORD i;
+    int j,do_reverse;
+    const double *pbuf = buf;
+    float fsamp,absfsamp;
+    PSFFILE *sfdat;
+    int do_shift = 1;
+    SND_SAMP s_samp;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    if(buf==NULL)
+        return PSF_E_BADARG;
+    if(nFrames == 0)
+        return nFrames;
+    if(sfdat->isRead)
+        return PSF_E_FILE_READONLY;
+    chans = sfdat->fmt.Format.nChannels;
+    
+    switch(sfdat->riff_format){
+        case(PSF_STDWAVE):
+        case(PSF_WAVE_EX):
+            do_reverse = (sfdat->is_little_endian ? 0 : 1 );
+            do_shift = 1;
+            break;
+        case(PSF_AIFF):
+        case(PSF_AIFC):
+            do_reverse = (sfdat->is_little_endian ? 1 : 0 );
+            do_shift = 0;
+            break;
+        default:
+            return PSF_E_UNSUPPORTED;
+    }
+    if(sfdat->lastop  == PSF_OP_READ)
+        fflush(sfdat->file);
+    switch(sfdat->samptype){
+        case(PSF_SAMP_IEEE_FLOAT):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float) *pbuf++;
+                        if(sfdat->clip_floats){
+                            fsamp = min(fsamp,1.0f);
+                            fsamp = max(fsamp,-1.0f);
+                        }
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        s_samp.fsamp = fsamp;
+                        lsamp = s_samp.lsamp;
+                        //   lsamp = * (int *) &fsamp;
+                        lsamp = REVDWBYTES(lsamp);
+                        if(wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, pbuf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float) pbuf[j];
+                        if(sfdat->clip_floats){
+                            fsamp = min(fsamp,1.0f);
+                            fsamp = max(fsamp,-1.0f);
+                        }
+                        if(wavDoWrite(sfdat,(char*)&fsamp,sizeof(float))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                        absfsamp = (float)fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                    }
+                }
+            }
+            break;
+        case(PSF_SAMP_16):
+            /* TODO: optimise all this with func pointers etc */
+            if(do_reverse){
+                short ssamp;
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float)  *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip16);
+                        fsamp = max(fsamp,-dclip16);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        if(sfdat->dithertype == PSF_DITHER_TPDF)
+                            ssamp = (short) psf_round(fsamp * 32766.0 + 2.0 * trirand());
+                        else
+                            ssamp = (short) psf_round(fsamp * MAX_16BIT);
+                        ssamp = (short) REVWBYTES(ssamp);
+                        if( wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                short ssamp;
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float) buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip16);
+                        fsamp = max(fsamp,-dclip16);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        if(sfdat->dithertype == PSF_DITHER_TPDF)
+                            ssamp = (short) psf_round(fsamp * 32766.0 + 2.0 * trirand());
+                        else
+                            ssamp = (short) psf_round(fsamp * MAX_16BIT);
+                        
+                        if(wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        case(PSF_SAMP_24):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp =(float)  *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip24);
+                        fsamp = max(fsamp,-dclip24);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * MAX_32BIT);
+                        lsamp = REVDWBYTES(lsamp);
+                        if(do_shift){
+                            if(sfdat->is_little_endian)
+                                lsamp >>= 8;
+                            else
+                                lsamp <<= 8;
+                        }
+                        if( wavDoWrite(sfdat,(char *) &lsamp,3)){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float)  buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip24);
+                        fsamp = max(fsamp,-dclip24);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * MAX_32BIT);
+                        if(do_shift){
+                            if(sfdat->is_little_endian)
+                                lsamp >>= 8;
+                            else
+                                lsamp <<= 8;
+                        }
+                        if(wavDoWrite(sfdat,(char *) &lsamp,3)){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        case(PSF_SAMP_32):
+            if(do_reverse){
+                for(i=0; i < nFrames; i++){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float) *buf++;
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip32);
+                        fsamp = max(fsamp,-dclip32);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * MAX_32BIT);
+                        lsamp = REVDWBYTES(lsamp);
+                        if( wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            else {
+                for(i=0; i < nFrames; i++, buf += chans){
+                    for(j=0;j < chans; j++) {
+                        fsamp = (float) buf[j];
+                        /* clip now! we may have a flag to rescale first...one day */
+                        fsamp = min(fsamp,dclip32);
+                        fsamp = max(fsamp,-dclip32);
+                        absfsamp = (float) fabs((double)fsamp);
+                        if(sfdat->pPeaks && (sfdat->pPeaks[j].val < absfsamp)){
+                            sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                            sfdat->pPeaks[j].val = absfsamp;
+                        }
+                        lsamp = psf_round(fsamp * MAX_32BIT);
+                        
+                        if(wavDoWrite(sfdat,(char *) &lsamp,sizeof(int))){
+                            DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                            return PSF_E_CANT_WRITE;
+                        }
+                    }
+                }
+            }
+            break;
+        default:
+            DBGFPRINTF((stderr, "wavOpenWrite: unsupported sample format\n"));
+            return PSF_E_UNSUPPORTED;
+            
+    }
+    POS64(sfdat->lastwritepos) += nFrames;
+    /* keep this as is for now, don't optimize, work in progress, etc */
+    sfdat->curframepos =  (DWORD) POS64(sfdat->lastwritepos);
+    sfdat->nFrames = max(sfdat->nFrames, ((DWORD) POS64(sfdat->lastwritepos)));
+    /*  fflush(sfdat->file);*/  /* ? need this if reading/seeking as well as  write, etc */
+    return nFrames;
+    
+}
+
+
+/* deprecated! Do not use. */
+
+int psf_sndWriteShortFrames(int sfd, const short *buf, DWORD nFrames)
+{
+    int chans;
+    DWORD i;
+    int j;
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    
+    if(buf==NULL)
+        return PSF_E_BADARG;
+    if(nFrames == 0)
+        return nFrames;
+    if(sfdat->isRead)
+        return PSF_E_FILE_READONLY;
+    chans = sfdat->fmt.Format.nChannels;
+    
+    /* well, it can't be ~less~ efficient than converting twice! */
+    if(!sfdat->is_little_endian){
+        short ssamp;
+        double fval;
+        for(i=0; i < nFrames; i++){
+            for(j=0;j < chans; j++) {
+                ssamp = *buf++;
+                fval = ((double) ssamp / MAX_16BIT);
+                if(sfdat->pPeaks && (sfdat->pPeaks[j].val < (float)(fabs(fval)))){
+                    sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                    sfdat->pPeaks[j].val = (float)fval;
+                }
+                
+                ssamp = (short) REVWBYTES(ssamp);
+                if(wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                    DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                    return PSF_E_CANT_WRITE;
+                }
+            }
+        }
+    }
+    else {
+        short ssamp;
+        double fval;
+        for(i=0; i < nFrames; i++){
+            for(j=0;j < chans; j++) {
+                ssamp = *buf++;
+                fval = ((double) ssamp / MAX_16BIT);
+                if(sfdat->pPeaks && (sfdat->pPeaks[j].val < (float)(fabs(fval)))){
+                    sfdat->pPeaks[j].pos = sfdat->nFrames + i;
+                    sfdat->pPeaks[j].val = (float)fval;
+                }
+                
+                if(wavDoWrite(sfdat,(char *) &ssamp,sizeof(short))){
+                    DBGFPRINTF((stderr, "wavOpenWrite: write error\n"));
+                    return PSF_E_CANT_WRITE;
+                }
+            }
+        }
+    }
+    POS64(sfdat->lastwritepos) += nFrames;
+    sfdat->nFrames = max(sfdat->nFrames, ((DWORD) POS64(sfdat->lastwritepos)));
+    fflush(sfdat->file);
+    return nFrames;
+}
+
+/******** READ ***********/
+static int wavReadHeader(PSFFILE *sfdat)
+{
+    DWORD tag,version,peaktime;
+    DWORD size;
+    WORD cbSize;
+    fpos_t bytepos;
+    
+    if(sfdat==NULL || sfdat->file == NULL)
+        return PSF_E_BADARG;
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoRead(sfdat,(char *) &size,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(!sfdat->is_little_endian)
+        size = REVDWBYTES(size);
+    else
+        tag = REVDWBYTES(tag);
+    if(tag != TAG('R','I','F','F'))
+        return PSF_E_NOT_WAVE;
+    if(size < (sizeof(WAVEFORMAT) + 3 * sizeof(WORD)))
+        return PSF_E_BAD_FORMAT;
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(tag != TAG('W','A','V','E'))
+        return PSF_E_NOT_WAVE;
+    for(;;){
+        if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoRead(sfdat,(char *) &size,sizeof(DWORD)))
+            return PSF_E_CANT_READ;
+        if(!sfdat->is_little_endian)
+            size = REVDWBYTES(size);
+        else
+            tag = REVDWBYTES(tag);
+        switch(tag){
+            case(TAG('f','m','t',' ')):
+                if( size < sizeof(WAVEFORMAT))
+                    return PSF_E_BAD_FORMAT;
+                if(size > sizeof_WFMTEX)
+                    return PSF_E_UNSUPPORTED;
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->fmtoffset = bytepos;
+                if(wavDoRead(sfdat,(char *)&(sfdat->fmt.Format),sizeof(WAVEFORMAT))){
+                    return PSF_E_CANT_READ;
+                }
+                if(!sfdat->is_little_endian)
+                    fmtSwapBytes(sfdat);
+                /* calling function decides if format is supported*/
+                if(size > sizeof(WAVEFORMAT)) {
+                    if(wavDoRead(sfdat,(char*)&cbSize,sizeof(WORD)))
+                        return PSF_E_CANT_READ;
+                    if(!sfdat->is_little_endian)
+                        cbSize = (WORD) REVWBYTES(cbSize);
+                    if(cbSize != (WORD)0) {
+                        if(sfdat->fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE){
+                            if(cbSize != 22)
+                                return PSF_E_BAD_FORMAT;
+                            sfdat->riff_format = PSF_WAVE_EX;
+                        }
+                    }
+                    else {
+                        int fmtsize = 18;
+                        /* cbSize = 0: has to be 18-byte WAVEFORMATEX */
+                        if((sfdat->fmt.Format.wFormatTag == WAVE_FORMAT_PCM
+                            || sfdat->fmt.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT))
+                            sfdat->riff_format = PSF_STDWAVE;
+                        else  /* some horribly mangled format! */
+                            return PSF_E_BAD_FORMAT;
+                        /* Feb 2010: hack to handle bad files with overlarge fmt chunk! */
+                        while (size > fmtsize){
+                            char dummy;
+                            if(wavDoRead(sfdat,(char*)&dummy,sizeof(char)))
+                                return PSF_E_CANT_READ;
+                            fmtsize++;
+                            strcpy(sfdat->warnstring,"fmt chunk too large for format");
+                            sfdat->illformed = 1;
+                        }
+                        
+                    }
+                    sfdat->fmt.Format.cbSize = cbSize;
+                    /* fill in as if basic Format; may change later from WAVE-EX */
+                    sfdat->fmt.Samples.wValidBitsPerSample = sfdat->fmt.Format.wBitsPerSample;
+                    sfdat->fmt.dwChannelMask = 0;
+                    /* get rest of WAVE-EX, if we have it */
+                    if(sfdat->fmt.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE){
+                        WORD validbits;
+                        DWORD chmask;
+                        if(wavDoRead(sfdat,(char *) &validbits,sizeof(WORD)))
+                            return PSF_E_CANT_READ;
+                        if(!sfdat->is_little_endian)
+                            sfdat->fmt.Samples.wValidBitsPerSample = (WORD) REVWBYTES(validbits);
+                        if(wavDoRead(sfdat,(char *) &chmask,sizeof(DWORD)))
+                            return PSF_E_CANT_READ;
+                        sfdat->fmt.dwChannelMask = chmask;      /* RWD OCT 2009 ! */
+                        if(!sfdat->is_little_endian)
+                            sfdat->fmt.dwChannelMask = REVDWBYTES(chmask);
+                        /* OCT 2009 identify speaker layout */
+                        sfdat->chformat = get_speakerlayout(sfdat->fmt.dwChannelMask,sfdat->fmt.Format.nChannels);
+                        if(wavDoRead(sfdat,(char *) &(sfdat->fmt.SubFormat),sizeof(GUID)))
+                            return PSF_E_CANT_READ;
+                        if(!sfdat->is_little_endian){
+                            sfdat->fmt.SubFormat.Data1 = REVDWBYTES(sfdat->fmt.SubFormat.Data1);
+                            sfdat->fmt.SubFormat.Data2 = (WORD) REVWBYTES(sfdat->fmt.SubFormat.Data2);
+                            sfdat->fmt.SubFormat.Data3 = (WORD) REVWBYTES(sfdat->fmt.SubFormat.Data3);
+                        }
+                        /* if we get a good GUID, this sets up sfdat with samplesize info */
+                        if(check_guid(sfdat))
+                            return PSF_E_UNSUPPORTED;
+                    }
+                }
+                break;
+            case(TAG('P','E','A','K')):
+                /* I SHOULD  report an error if this is after data chunk;
+                 but I suppose innocent users (e.g. of Cubase ) will grumble... */
+                if(wavDoRead(sfdat,(char  *) &version,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(!sfdat->is_little_endian)
+                    version = REVDWBYTES(version);
+                if(version != 1) {
+                    DBGFPRINTF((stderr, "Unexpected version level for PEAK chunk!\n"));
+                    return PSF_E_UNSUPPORTED;
+                }
+                if(size != (2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)) {
+                    DBGFPRINTF((stderr, "\nBad size for PEAK chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->peakoffset = bytepos;
+                if(wavDoRead(sfdat,(char *) &peaktime,sizeof(DWORD))){
+                    DBGFPRINTF((stderr,"Error reading PEAK time\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(!sfdat->is_little_endian)
+                    peaktime = REVDWBYTES(peaktime);
+                sfdat->peaktime = (time_t) peaktime;
+                sfdat->pPeaks = (PSF_CHPEAK *) malloc(sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+                if(sfdat->pPeaks==NULL){
+                    DBGFPRINTF((stderr, "wavOpenWrite: no memory for peak data\n"));
+                    return PSF_E_NOMEM;
+                }
+                if(wavDoRead(sfdat,(char *) sfdat->pPeaks,sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)) {
+                    DBGFPRINTF((stderr,"Error reading PEAK peak data\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(!sfdat->is_little_endian){
+                    DWORD *pBlock;
+                    int i;
+                    pBlock = (DWORD *) (sfdat->pPeaks);
+                    for(i=0;i < sfdat->fmt.Format.nChannels * 2; i++)
+                        pBlock[i] = REVDWBYTES(pBlock[i]);
+                }
+                break;
+            case(TAG('d','a','t','a')):
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->dataoffset = bytepos;
+                if(POS64(sfdat->fmtoffset)==0)
+                    return PSF_E_BAD_FORMAT;
+                sfdat->nFrames = size / sfdat->fmt.Format.nBlockAlign;
+                /* get rescale factor if available */
+                /* NB in correct format, val is always >= 0.0 */
+                if(sfdat->pPeaks && POS64(sfdat->peakoffset) != 0){
+                    float fac = 0.0f;
+                    int i;
+                    for(i=0;i < sfdat->fmt.Format.nChannels; i++)
+                        fac = max(fac,sfdat->pPeaks[i].val);
+                    if(fac > 1.0f)
+                        sfdat->rescale_fac = 1.0f / fac;
+                }
+                /* set sampletype */
+                switch(sfdat->fmt.Format.wFormatTag){
+                    case(WAVE_FORMAT_IEEE_FLOAT):
+                        sfdat->samptype = PSF_SAMP_IEEE_FLOAT;
+                        if(sfdat->fmt.Format.wBitsPerSample != 32) {
+                            /*return PSF_E_BAD_FORMAT;*/
+                            if(sfdat->fmt.Format.wBitsPerSample == sizeof(double))
+                                return PSF_E_UNSUPPORTED;           /*RWD 26:10:2002 allow possibility of doubles! */
+                            else
+                                return PSF_E_BAD_FORMAT;
+                        }
+                        sfdat->lastop  = PSF_OP_READ;
+                        /* RWD 26:10:2002 IMPORTANT BUGFIX! */
+                        return PSF_E_NOERROR;
+                    case(WAVE_FORMAT_PCM):
+                    case(WAVE_FORMAT_EXTENSIBLE):
+                        switch(sfdat->fmt.Format.wBitsPerSample){
+                            case(8):
+                                sfdat->samptype = PSF_SAMP_8;
+                                break;
+                            case(16):
+                                sfdat->samptype = PSF_SAMP_16;
+                                break;
+                            case(24):
+                                sfdat->samptype = PSF_SAMP_24;
+                                break;
+                            case(32):
+                                sfdat->samptype = PSF_SAMP_32;
+                                /* RWD 03/20: fix fault parsing floats amb files */
+                                if(sfdat->chformat == MC_BFMT) {
+                                    if(compare_guids(&(sfdat->fmt.SubFormat),&SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT)) {
+                                        sfdat->samptype = PSF_SAMP_IEEE_FLOAT;
+                                    }
+                                    else if(!compare_guids(&(sfdat->fmt.SubFormat),&SUBTYPE_AMBISONIC_B_FORMAT_PCM)) {
+                                        sfdat->samptype = PSF_SAMP_UNKNOWN;
+                                    }
+                                }
+                                break;
+                            default:
+                                sfdat->samptype = PSF_SAMP_UNKNOWN;
+                                break;
+                        }
+                        if(sfdat->samptype == PSF_SAMP_UNKNOWN)
+                            return PSF_E_UNSUPPORTED;
+                        sfdat->lastop  = PSF_OP_READ;
+                        return PSF_E_NOERROR;
+                }
+            default:
+                //RWD Apr 2019
+                // handle odd chunk sizes
+                size = (size+1)&~1;
+                /* unknown chunk - skip */
+                if(fseek(sfdat->file,size,SEEK_CUR))
+                    return PSF_E_CANT_READ;
+                break;
+        }
+    }
+}
+
+static int aiffReadHeader(PSFFILE *sfdat)
+{
+    DWORD tag,version,peaktime,remain,offset,blocksize;
+    int have_comm =0,have_ssnd =0;
+    DWORD dwData,size;
+    unsigned char ieee[10];
+    WORD wData;
+    fpos_t bytepos;
+    
+    if(sfdat==NULL || sfdat->file == NULL)
+        return PSF_E_BADARG;
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoRead(sfdat,(char *) &remain,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(sfdat->is_little_endian) {
+        remain = REVDWBYTES(remain);
+        tag = REVDWBYTES(tag);
+    }
+    if(tag != TAG('F','O','R','M')){
+        DBGFPRINTF((stderr, "file is not AIFF: no PSF chunk\n"));
+        return PSF_E_BADARG;
+    }
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(tag != TAG('A','I','F','F')){
+        DBGFPRINTF((stderr, "file is not AIFF: no AIFF chunk\n"));
+        return PSF_E_BADARG;
+    }
+    remain -= sizeof(int);
+    
+    
+    while(remain > 0){
+        if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoRead(sfdat,(char *) &size,sizeof(DWORD)))
+            return PSF_E_CANT_READ;
+        if(sfdat->is_little_endian) {
+            size = REVDWBYTES(size);
+            tag = REVDWBYTES(tag);
+        }
+        remain -=(int)( 2 * sizeof(DWORD));
+        switch(tag){
+            case(TAG('C','O','M','M')):
+                if(size != 18){
+                    DBGFPRINTF((stderr,"AIFF file has bad size for COMM chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->fmtoffset = bytepos;
+                if(wavDoRead(sfdat,(char *)&wData,sizeof(WORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    wData = (WORD) REVWBYTES(wData);
+                sfdat->fmt.Format.nChannels = wData;
+                if(wavDoRead(sfdat,(char *)&dwData,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    dwData = REVDWBYTES(dwData);
+                sfdat->nFrames = dwData;
+                if(wavDoRead(sfdat,(char *)&wData,sizeof(WORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    wData = (WORD) REVWBYTES(wData);
+                sfdat->fmt.Format.wBitsPerSample = wData;
+                if(wavDoRead(sfdat,ieee,10))
+                    return PSF_E_CANT_READ;
+                sfdat->fmt.Format.nSamplesPerSec = (DWORD)(ieee_80_to_double(ieee));
+                /*we have to deduce blockalign, and hence containersize*/
+                /* no support (yet) for strange wordsizes such as 20 in 24 */
+                switch(sfdat->fmt.Format.wBitsPerSample){
+                    case(32):
+                        sfdat->fmt.Format.nBlockAlign = sizeof(int);
+                        sfdat->samptype = PSF_SAMP_32;
+                        break;
+                    case(24):
+                        sfdat->fmt.Format.nBlockAlign = 3;
+                        sfdat->samptype = PSF_SAMP_24;
+                        break;
+                    case(16):
+                        sfdat->fmt.Format.nBlockAlign = sizeof(short);
+                        sfdat->samptype = PSF_SAMP_16;
+                        break;
+                    default:
+                        DBGFPRINTF((stderr, "unsupported sample format for AIFF file\n"));
+                        return PSF_E_UNSUPPORTED;
+                }
+                sfdat->fmt.Format.nBlockAlign = (WORD) (sfdat->fmt.Format.nBlockAlign * sfdat->fmt.Format.nChannels);
+                remain -= 18;
+                have_comm = 1;
+                break;
+            case (TAG('P','E','A','K')):
+                if(!have_comm){
+                    DBGFPRINTF((stderr, "AIFF file: found PEAK chunk before COMM chunk!\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(size != (2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)){
+                    DBGFPRINTF((stderr, "AIFF file has bad size for PEAK chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(wavDoRead(sfdat,(char *)&version,sizeof(DWORD))) {
+                    DBGFPRINTF((stderr,"Error reading PEAK version\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian)
+                    version  = REVDWBYTES(version);
+                if(version != 1){
+                    DBGFPRINTF((stderr, "AIFF file has unexpected version level for PEAK chunk!\n"));
+                    return PSF_E_UNSUPPORTED;
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->peakoffset = bytepos;
+                if(wavDoRead(sfdat,(char *) &peaktime,sizeof(DWORD))) {
+                    DBGFPRINTF((stderr,"Error reading PEAK time\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian)
+                    peaktime = REVDWBYTES(peaktime);
+                sfdat->peaktime = (time_t) peaktime;
+                sfdat->pPeaks = (PSF_CHPEAK *)malloc(sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+                if(sfdat->pPeaks==NULL){
+                    DBGFPRINTF((stderr, "wavOpenWrite: no memory for peak data\n"));
+                    return PSF_E_NOMEM;
+                }
+                if(wavDoRead(sfdat,(char *)(sfdat->pPeaks),sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)){
+                    DBGFPRINTF((stderr,"Error reading PEAK peak data\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian){
+                    DWORD *pBlock;
+                    int i;
+                    pBlock = (DWORD *) (sfdat->pPeaks);
+                    for(i=0;i < sfdat->fmt.Format.nChannels * 2; i++)
+                        pBlock[i] = REVDWBYTES(pBlock[i]);
+                }
+                remain -= size;
+                break;
+            case(TAG('S','S','N','D')):
+                if(wavDoRead(sfdat,(char *)&offset,sizeof(DWORD))
+                   || wavDoRead(sfdat,(char *) &blocksize,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian){
+                    offset  = REVDWBYTES(offset);
+                    blocksize = REVDWBYTES(blocksize);
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->dataoffset = bytepos;
+                POS64(sfdat->dataoffset) += offset;
+                sfdat->nFrames = (size - 2* sizeof(DWORD))/ sfdat->fmt.Format.nBlockAlign;
+                /* NB for seek: we used up 8 bytes with offset and blocksize */
+                /* if we already have COMM, we could finish here! */
+                /* TODO! this is still a signed int seek, no good for 4GB */
+                if(fseek(sfdat->file,((size - 2* sizeof(DWORD))+1)&~1,SEEK_CUR))
+                    return PSF_E_CANT_SEEK;
+                have_ssnd = 1;
+                remain -= (size+1)&~1;
+                break;
+                /*HARSH! traps linux sox error, for example */
+            case(0):
+                DBGFPRINTF((stderr, "AIFF file has bad main chunksize\n"));
+                return PSF_E_BAD_FORMAT;
+            default:
+                /* skip all unknown chunks */
+                if(fseek(sfdat->file,(size+1)&~1,SEEK_CUR))
+                    return PSF_E_CANT_SEEK;
+                remain -= (size+1)&~1;
+                break;
+        }
+    }
+    if(!(have_ssnd && have_comm)){
+        DBGFPRINTF((stderr, "AIFF file has missing chunks\n"));
+        return PSF_E_BAD_FORMAT;
+    }
+    /* we have seeked to EOF, so rewind to start of data */
+    if(fsetpos(sfdat->file,&sfdat->dataoffset))
+        return PSF_E_CANT_SEEK;
+    sfdat->curframepos = 0;
+    sfdat->riff_format = PSF_AIFF;
+    /* get rescale factor if available */
+    /* NB in correct format, val is always >= 0.0 */
+    if(sfdat->pPeaks &&  POS64(sfdat->peakoffset) != 0){
+        float fac = 0.0f;
+        int i;
+        for(i=0;i < sfdat->fmt.Format.nChannels; i++)
+            fac = max(fac,sfdat->pPeaks[i].val);
+        if(fac > 1.0f)
+            sfdat->rescale_fac = 1.0f / fac;
+    }
+    sfdat->lastop  = PSF_OP_READ;
+    return PSF_E_NOERROR;
+}
+
+static int aifcReadHeader(PSFFILE *sfdat)
+{
+    DWORD tag,version,peaktime,remain,offset,blocksize;
+    int have_comm =0,have_ssnd =0,have_fver = 0;
+    DWORD dwData,size,aifcver,ID_compression;
+    unsigned char ieee[10];
+    WORD wData;
+    fpos_t bytepos;
+    
+    if(sfdat==NULL || sfdat->file == NULL)
+        return PSF_E_BADARG;
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+       || wavDoRead(sfdat,(char *) &remain,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(sfdat->is_little_endian) {
+        remain = REVDWBYTES(remain);
+        tag = REVDWBYTES(tag);
+    }
+    if(tag != TAG('F','O','R','M')){
+        DBGFPRINTF((stderr, "file is not AIFC: no FORM chunk\n"));
+        return PSF_E_BADARG;
+    }
+    
+    if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD)))
+        return PSF_E_CANT_READ;
+    if(sfdat->is_little_endian)
+        tag = REVDWBYTES(tag);
+    if(tag != TAG('A','I','F','C')){
+        DBGFPRINTF((stderr, "file is not AIFC: no AIFC chunk\n"));
+        return PSF_E_BADARG;
+    }
+    remain -= sizeof(DWORD);
+    
+    
+    while(remain > 0){
+        if(wavDoRead(sfdat,(char *)&tag,sizeof(DWORD))
+           || wavDoRead(sfdat,(char *) &size,sizeof(DWORD)))
+            return PSF_E_CANT_READ;
+        if(sfdat->is_little_endian) {
+            size = REVDWBYTES(size);
+            tag = REVDWBYTES(tag);
+        }
+        remain -= 2 * sizeof(DWORD);
+        switch(tag){
+            case(TAG('F','V','E','R')):
+                if(size != sizeof(DWORD)){
+                    DBGFPRINTF((stderr, "AIFC file has bad size for FVER chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(wavDoRead(sfdat,(char *) &aifcver,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    aifcver = REVDWBYTES(aifcver);
+                remain-= sizeof(DWORD);
+                if(aifcver != AIFC_VERSION_1)
+                    return PSF_E_UNSUPPORTED;
+                have_fver = 1;
+                break;
+            case(TAG('C','O','M','M')):
+                if(size < 22) {
+                    DBGFPRINTF((stderr, "AIFC file has bad size for COMM chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(fgetpos(sfdat->file,&sfdat->fmtoffset))
+                    return PSF_E_CANT_SEEK;
+                if(wavDoRead(sfdat,(char *)&wData,sizeof(WORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    wData = (WORD) REVWBYTES(wData);
+                sfdat->fmt.Format.nChannels = wData;
+                if(wavDoRead(sfdat,(char *)&dwData,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    dwData = REVDWBYTES(dwData);
+                sfdat->nFrames = dwData;
+                if(wavDoRead(sfdat,(char *)&wData,sizeof(WORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    wData = (WORD) REVWBYTES(wData);
+                sfdat->fmt.Format.wBitsPerSample = wData;
+                if(wavDoRead(sfdat,ieee,10))
+                    return PSF_E_CANT_READ;
+                sfdat->fmt.Format.nSamplesPerSec = (DWORD)(ieee_80_to_double(ieee));
+                /*we have to deduce blockalign, and hence containersize*/
+                /* no support for strange wordsizes such as 20 in 24 */
+                switch(sfdat->fmt.Format.wBitsPerSample){
+                    case(32):
+                        sfdat->fmt.Format.nBlockAlign = sizeof(DWORD);
+                        sfdat->samptype = PSF_SAMP_32;
+                        break;
+                    case(24):
+                        sfdat->fmt.Format.nBlockAlign = 3;
+                        sfdat->samptype = PSF_SAMP_24;
+                        break;
+                    case(16):
+                        sfdat->fmt.Format.nBlockAlign = sizeof(short);
+                        sfdat->samptype = PSF_SAMP_16;
+                        break;
+                    default:
+                        DBGFPRINTF((stderr, "unsupported sample format for AIFC file\n"));
+                        return PSF_E_UNSUPPORTED;
+                }
+                sfdat->fmt.Format.nBlockAlign = (WORD) (sfdat->fmt.Format.nBlockAlign * sfdat->fmt.Format.nChannels);
+                if(wavDoRead(sfdat,(char *)&ID_compression,sizeof(DWORD))){
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian)
+                    ID_compression = REVDWBYTES(ID_compression);
+                if(!(    (ID_compression == TAG('N','O','N','E'))
+                     || (ID_compression == TAG('F','L','3','2'))
+                     || (ID_compression == TAG('f','l','3','2'))
+                     || (ID_compression == TAG('i','n','2','4')))
+                   ){
+                    DBGFPRINTF((stderr, "AIFC file: unsupported compression format\n"));
+                    return PSF_E_UNSUPPORTED;
+                }
+                /*set stype info */
+                if((ID_compression== TAG('F','L','3','2'))
+                   || ID_compression == TAG('f','l','3','2')){
+                    if(sfdat->fmt.Format.wBitsPerSample != 32){
+                        DBGFPRINTF((stderr, "AIFC file: samples not 32bit in floats file\n"));
+                        return PSF_E_BAD_FORMAT;
+                    }
+                    else {
+                        sfdat->fmt.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+                        sfdat->samptype = PSF_SAMP_IEEE_FLOAT;
+                    }
+                }
+                /* yes, lazy! skip past pascal string*/
+                if(fseek(sfdat->file,((size-22)+1)&~1,SEEK_CUR))    /*written for documentation, not terseness!*/
+                    return PSF_E_CANT_SEEK;
+                remain -= (size+1)&~1;
+                have_comm = 1;
+                break;
+            case (TAG('P','E','A','K')):
+                if(!have_comm){
+                    DBGFPRINTF((stderr, "\nAIFC file: found PEAK chunk before COMM chunk!\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(size != (2 * sizeof(DWORD) + sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)){
+                    DBGFPRINTF((stderr, "\nBad size for PEAK chunk\n"));
+                    return PSF_E_BAD_FORMAT;
+                }
+                if(wavDoRead(sfdat,(char *)&version,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian)
+                    version  = REVDWBYTES(version);
+                if(version != 1) {
+                    DBGFPRINTF((stderr, "Unexpected version level for PEAK chunk!\n"));
+                    return PSF_E_UNSUPPORTED;
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->peakoffset = bytepos;
+                if(wavDoRead(sfdat,(char *) &peaktime,sizeof(DWORD))){
+                    DBGFPRINTF((stderr,"Error reading PEAK time\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian)
+                    peaktime = REVDWBYTES(peaktime);
+                sfdat->peaktime = (time_t) peaktime;
+                sfdat->pPeaks = (PSF_CHPEAK *)malloc(sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels);
+                if(sfdat->pPeaks==NULL) {
+                    DBGFPRINTF((stderr, "wavOpenWrite: no memory for peak data\n"));
+                    return PSF_E_NOMEM;
+                }
+                if(wavDoRead(sfdat,(char *)(sfdat->pPeaks),sizeof(PSF_CHPEAK) * sfdat->fmt.Format.nChannels)) {
+                    DBGFPRINTF((stderr,"Error reading PEAK peak data\n"));
+                    return PSF_E_CANT_READ;
+                }
+                if(sfdat->is_little_endian){
+                    DWORD *pBlock;
+                    int i;
+                    pBlock = (DWORD *) (sfdat->pPeaks);
+                    for(i=0;i < sfdat->fmt.Format.nChannels * 2; i++)
+                        pBlock[i] = REVDWBYTES(pBlock[i]);
+                }
+                remain -= (size+1)&~1;
+                break;
+            case(TAG('S','S','N','D')):
+                if(wavDoRead(sfdat,(char *)&offset,sizeof(DWORD))
+                   || wavDoRead(sfdat,(char *) &blocksize,sizeof(DWORD)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->is_little_endian){
+                    offset  = REVDWBYTES(offset);
+                    blocksize = REVDWBYTES(blocksize);
+                }
+                if(fgetpos(sfdat->file,&bytepos))
+                    return PSF_E_CANT_SEEK;
+                sfdat->dataoffset = bytepos;
+                POS64(sfdat->dataoffset) += offset;
+                sfdat->nFrames = (size - 2* sizeof(DWORD))/ sfdat->fmt.Format.nBlockAlign;
+                if(fseek(sfdat->file,((size - 2* sizeof(DWORD))+1)&~1,SEEK_CUR))
+                    return PSF_E_CANT_SEEK;
+                have_ssnd = 1;
+                remain -= (size+1)&~1;
+                break;
+                /* HARSH! as above */
+            case(0):
+                DBGFPRINTF((stderr, "AIFC file has bad main chunksize\n"));
+                return PSF_E_BAD_FORMAT;
+            default:
+                /* skip all unknown chunks */
+                if(fseek(sfdat->file,(size+1)&~1,SEEK_CUR))
+                    return PSF_E_CANT_SEEK;
+                remain -= (size+1)&~1;
+                break;
+        }
+    }
+    if(!(have_ssnd && have_comm && have_fver)){
+        DBGFPRINTF((stderr, "AIFC file has bad format\n"));
+        return PSF_E_BAD_FORMAT;
+    }
+    /* we have seeked (ugh) to EOF, so rewind to start of data */
+    if(fsetpos(sfdat->file,&sfdat->dataoffset))
+        return PSF_E_CANT_SEEK;
+    sfdat->curframepos = 0;
+    sfdat->riff_format = PSF_AIFC;
+    /* get rescale factor if available */
+    /* NB in correct format, val is always >= 0.0 */
+    if(sfdat->pPeaks &&  POS64(sfdat->peakoffset) != 0){
+        float fac = 0.0f;
+        int i;
+        for(i=0;i < sfdat->fmt.Format.nChannels; i++)
+            fac = max(fac,sfdat->pPeaks[i].val);
+        if(fac > 1.0f)
+            sfdat->rescale_fac = 1.0f / fac;
+    }
+    sfdat->lastop  = PSF_OP_READ;
+    return PSF_E_NOERROR;
+}
+/* only RDONLY access supported */
+int psf_sndOpen(const char *path,PSF_PROPS *props, int rescale)
+{
+    int i,rc = 0;
+    PSFFILE *sfdat;
+    psf_format fmt;
+    
+    /* RWD interesting syntax issue: I need the curlies, or break doesn't work properly */
+    for(i=0;i < psf_maxfiles;i++) {
+        if(psf_files[i]==NULL)
+            break;
+    }
+    if(i==psf_maxfiles){
+        
+        return PSF_E_TOOMANYFILES;
+    }
+    
+    sfdat = psf_newFile(NULL);
+    if(sfdat==NULL){
+        return PSF_E_NOMEM;
+    }
+    sfdat->rescale = rescale;
+    sfdat->is_little_endian = byte_order();
+    fmt = psf_getFormatExt(path);
+    if(!(fmt==PSF_STDWAVE || fmt==PSF_WAVE_EX || fmt==PSF_AIFF || fmt==PSF_AIFC))
+        return PSF_E_BADARG;
+    
+    if((sfdat->file = fopen(path,"rb"))  == NULL) {
+        DBGFPRINTF((stderr, "psf_sndOpen: cannot open '%s'\n", path));
+        return PSF_E_CANT_OPEN;
+    }
+    sfdat->filename = (char *) malloc(strlen(path)+1);
+    if(sfdat->filename==NULL) {
+        return PSF_E_NOMEM;
+    }
+    strcpy(sfdat->filename, path);
+    sfdat->isRead =  1;
+    sfdat->nFrames = 0;
+    /* no need to calc header sizes */
+    switch(fmt){
+        case(PSF_STDWAVE):
+        case(PSF_WAVE_EX):
+            rc =  wavReadHeader(sfdat);
+            break;
+        case(PSF_AIFF):
+            /* some .aiff files may actually be aifc - esp if floats! */
+        case(PSF_AIFC):
+            rc = aiffReadHeader(sfdat);
+            /* try AIFC if AIFF fails */
+            if(rc < PSF_E_NOERROR) {
+                rewind(sfdat->file);
+                rc =  aifcReadHeader(sfdat);
+            }
+            break;
+        default:
+            DBGFPRINTF((stderr, "psf_sndOpen: unsupported file format\n"));
+            rc =  PSF_E_UNSUPPORTED;
+    }
+    if(rc < PSF_E_NOERROR)
+        return rc;
+    /* fill props info*/
+    props->srate    = sfdat->fmt.Format.nSamplesPerSec;
+    props->chans    = sfdat->fmt.Format.nChannels;
+    props->samptype = sfdat->samptype;
+    props->chformat = sfdat->chformat;
+    props->format      =  fmt;
+    
+    if(fmt==PSF_STDWAVE && (sfdat->riff_format == PSF_WAVE_EX))
+        props->format = PSF_WAVE_EX;
+    
+    psf_files[i] = sfdat;
+    return i;
+}
+
+
+int psf_sndReadFloatFrames(int sfd, float *buf, DWORD nFrames)
+{
+    int chans;
+    DWORD framesread;
+    int blocksize,lsamp;
+    int temp;
+    int i,do_reverse;
+    short ssamp;
+    float *pbuf = buf;
+    float fsamp;
+    PSFFILE *sfdat;
+    int do_shift;
+    SND_SAMP s_samp;
+#ifdef _DEBUG
+    static int debug = 1;
+#endif
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    if(buf==NULL)
+        return PSF_E_BADARG;
+    if(nFrames == 0)
+        return nFrames;
+    sfdat  = psf_files[sfd];
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->filename);
+    /* must check our calcs! */
+    assert(sfdat->curframepos <= sfdat->nFrames);
+#endif
+    /* how much do we have left? return immediately if none! */
+    chans = sfdat->fmt.Format.nChannels;
+    framesread = min(sfdat->nFrames - sfdat->curframepos,nFrames);
+    if(framesread==0)
+        return (long) framesread;
+    
+    blocksize =  framesread * chans;
+    switch(sfdat->riff_format){
+        case(PSF_STDWAVE):
+        case(PSF_WAVE_EX):
+            do_reverse = (sfdat->is_little_endian ? 0 : 1 );
+            do_shift = 1;
+            break;
+        case(PSF_AIFF):
+        case(PSF_AIFC):
+            do_reverse = (sfdat->is_little_endian ? 1 : 0 );
+            do_shift = 0;
+            break;
+        default:
+            return PSF_E_UNSUPPORTED;
+    }
+    if(sfdat->lastop == PSF_OP_WRITE)
+        fflush(sfdat->file);
+    switch(sfdat->samptype){
+        case(PSF_SAMP_IEEE_FLOAT):
+            
+            if(do_reverse){
+                for(i=0;i < blocksize;i ++){
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(lsamp);
+                    //    fsamp = * (float *)&lsamp;
+                    s_samp.lsamp = lsamp;
+                    fsamp = s_samp.fsamp;
+                    if(sfdat->rescale)
+                        fsamp *= sfdat->rescale_fac;
+                    *pbuf++ = fsamp;
+                }
+            }
+            else{
+                if(wavDoRead(sfdat,(char *) buf,blocksize * sizeof(float)))
+                    return PSF_E_CANT_READ;
+                if(sfdat->rescale){
+                    pbuf = buf;
+                    for(i=0;i < blocksize; i++)
+                        *pbuf++  *= sfdat->rescale_fac;
+                }
+            }
+            break;
+        case(PSF_SAMP_16):
+            if(do_reverse){
+                for(i = 0; i < blocksize; i++){
+                    if(wavDoRead(sfdat,(char *)&ssamp,sizeof(short)))
+                        return PSF_E_CANT_READ;
+                    ssamp = (short) REVWBYTES(ssamp);
+                    fsamp = (float)((double) ssamp / MAX_16BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            else{
+                for(i = 0; i < blocksize; i++){
+                    if(wavDoRead(sfdat,(char *)&ssamp,sizeof(short)))
+                        return PSF_E_CANT_READ;
+                    fsamp = (float)((double) ssamp / MAX_16BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            break;
+        case(PSF_SAMP_24):
+            if(do_reverse){
+#ifdef _DEBUG
+                if(debug){
+                    printf("do_reverse: riffformat=%d do_shift = %d little_endian = %d\n",
+                           sfdat->riff_format,do_shift,sfdat->is_little_endian);
+                    debug = 0;
+                }
+#endif            
+                for(i=0;i < blocksize;i++){
+                    temp = 0;
+                    if(wavDoRead(sfdat,(char *)&temp,3))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(temp);
+                    if(do_shift)
+                        lsamp <<= 8;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            else{
+                for(i=0;i < blocksize;i++){
+                    lsamp = 0;
+                    if(wavDoRead(sfdat,(char *)&lsamp,3))
+                        return PSF_E_CANT_READ;
+                    if(do_shift)
+                        lsamp <<= 8;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            break;
+        case(PSF_SAMP_32):
+            if(do_reverse){
+                for(i=0;i < blocksize;i++){
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(lsamp);
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            else{
+                for(i=0;i < blocksize;i++){
+                    lsamp = 0L;
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = fsamp;
+                }
+            }
+            break;
+        default:
+            DBGFPRINTF((stderr, "psf_sndOpen: unsupported sample format\n"));
+            return PSF_E_UNSUPPORTED;
+    }
+    sfdat->curframepos += framesread;
+    
+    return framesread;
+}
+
+
+/* read doubles version! */
+int psf_sndReadDoubleFrames(int sfd, double *buf, DWORD nFrames)
+{
+    int chans;
+    DWORD framesread;
+    int blocksize,lsamp;
+    int i,do_reverse;
+    short ssamp;
+    double *pbuf = buf;
+    float fsamp;
+    PSFFILE *sfdat;
+    SND_SAMP s_samp;
+    int do_shift;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    if(buf==NULL)
+        return PSF_E_BADARG;
+    if(nFrames == 0)
+        return nFrames;
+    sfdat  = psf_files[sfd];
+#ifdef _DEBUG
+    assert(sfdat);
+    assert(sfdat->file);
+    assert(sfdat->filename);
+    /* must check our calcs! */
+    assert(sfdat->curframepos <= sfdat->nFrames);
+#endif
+    /* how much do we have left? return immediately if none! */
+    chans = sfdat->fmt.Format.nChannels;
+    framesread = min(sfdat->nFrames - sfdat->curframepos,nFrames);
+    if(framesread==0)
+        return (long) framesread;
+    
+    blocksize =  framesread * chans;
+    switch(sfdat->riff_format){
+        case(PSF_STDWAVE):
+        case(PSF_WAVE_EX):
+            do_reverse = (sfdat->is_little_endian ? 0 : 1 );
+            do_shift = 1;
+            break;
+        case(PSF_AIFF):
+        case(PSF_AIFC):
+            do_reverse = (sfdat->is_little_endian ? 1 : 0 );
+            do_shift = 0;
+            break;
+        default:
+            return PSF_E_UNSUPPORTED;
+    }
+    if(sfdat->lastop == PSF_OP_WRITE)
+        fflush(sfdat->file);
+    switch(sfdat->samptype){
+        case(PSF_SAMP_IEEE_FLOAT):
+            if(do_reverse){
+                for(i=0;i < blocksize;i ++){
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(lsamp);
+                    //    fsamp = * (float *)&lsamp;
+                    s_samp.lsamp = lsamp;
+                    fsamp = s_samp.fsamp;
+                    if(sfdat->rescale)
+                        fsamp *= sfdat->rescale_fac;
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            else{
+                for(i=0;i < blocksize;i++){
+                    if(wavDoRead(sfdat,(char *) &fsamp, sizeof(float)))
+                        return PSF_E_CANT_READ;
+                    *pbuf++ = (double) fsamp;
+                }
+                if(sfdat->rescale){
+                    pbuf = buf;
+                    for(i=0;i < blocksize; i++)
+                        *pbuf++  *= sfdat->rescale_fac;
+                }
+            }
+            break;
+        case(PSF_SAMP_16):
+            if(do_reverse){
+                for(i = 0; i < blocksize; i++){
+                    if(wavDoRead(sfdat,(char *)&ssamp,sizeof(short)))
+                        return PSF_E_CANT_READ;
+                    ssamp = (short) REVWBYTES(ssamp);
+                    fsamp = (float)((double) ssamp / MAX_16BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            else{
+                for(i = 0; i < blocksize; i++){
+                    if(wavDoRead(sfdat,(char *)&ssamp,sizeof(short)))
+                        return PSF_E_CANT_READ;
+                    fsamp = (float)((double) ssamp / MAX_16BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            break;
+        case(PSF_SAMP_24):
+            if(do_reverse){
+                for(i=0;i < blocksize;i++){
+                    if(wavDoRead(sfdat,(char *)&lsamp,3))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(lsamp);
+                    if(do_shift)
+                        lsamp <<= 8;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            else{
+                for(i=0;i < blocksize;i++){
+                    lsamp = 0L;
+                    if(wavDoRead(sfdat,(char *)&lsamp,3))
+                        return PSF_E_CANT_READ;
+                    if(do_shift)
+                        lsamp <<= 8;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            break;
+        case(PSF_SAMP_32):
+            if(do_reverse){
+                for(i=0;i < blocksize;i++){
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    lsamp = REVDWBYTES(lsamp);
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            else{
+                for(i=0;i < blocksize;i++){
+                    lsamp = 0L;
+                    if(wavDoRead(sfdat,(char *)&lsamp,sizeof(int)))
+                        return PSF_E_CANT_READ;
+                    fsamp = (float)((double)(lsamp) / MAX_32BIT);
+                    *pbuf++ = (double) fsamp;
+                }
+            }
+            break;
+        default:
+            DBGFPRINTF((stderr, "psf_sndOpen: unsupported sample format\n"));
+            return PSF_E_UNSUPPORTED;
+    }
+    sfdat->curframepos += framesread;
+    
+    return framesread;
+}
+
+
+#ifdef _DEBUG
+/* private test func to get raw file size */
+/* verify sfdat->nFrames always <= true EOF position */
+/* NB: size value is wrong if there is junk after data chunk! */
+static fpos_t getsize(FILE* fp){
+    fpos_t curpos;
+    fpos_t size;
+    if(fgetpos(fp,&curpos))
+        return -1;
+    
+    if(fseek(fp,0,SEEK_END))
+        return -1;
+    
+    if(fgetpos(fp,&size))
+        return -1;
+    
+    if(fsetpos(fp,&curpos))
+        return -1;
+    
+    return  size;
+}
+#endif
+
+/* return size in m/c frames */
+/* signed long because we want error return */
+/* 64 bit: this would have to return a 64bit type (or unsigned 32bit int) IF we want to support 4GB 8bit sfiles! */
+/* (which we don't) */
+int psf_sndSize(int sfd)
+{
+    PSFFILE *sfdat;
+#ifdef _DEBUG
+    fpos_t size;
+    DWORD framesize;
+#endif
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+    if((size = getsize(sfdat->file)) < 0)   {
+        DBGFPRINTF((stderr, "getsize() error in psf_sndSize().\n"));
+        return -1;
+    }
+    /* NB deceived if any pad or other junk after data chunk! */
+    framesize = (DWORD)((POS64(size) - (MYLONG)(sfdat->dataoffset)) / sfdat->fmt.Format.nBlockAlign);
+    assert(framesize >= (DWORD) sfdat->nFrames);
+    
+#endif
+    
+    return sfdat->nFrames;
+    
+}
+
+/* returns multi-channel (frame)  position */
+/* 64bit see above! */
+int psf_sndTell(int sfd)
+{
+    fpos_t pos;
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    
+    if(fgetpos(sfdat->file,&pos))
+        return PSF_E_CANT_SEEK;
+    POS64(pos) -= POS64(sfdat->dataoffset);
+    POS64(pos) /= sfdat->fmt.Format.nBlockAlign;
+#ifdef _DEBUG
+    if(sfdat->lastop == PSF_OP_READ)
+        assert(pos== sfdat->curframepos);
+    else if(sfdat->lastop == PSF_OP_WRITE)
+    /* RWD this will be out (but == curframepos) if lastop was a read . so maybe say >=, or test for lastop ? */
+        assert(pos == sfdat->lastwritepos);
+#endif
+    return (int) POS64(pos);
+}
+
+
+int psf_sndSeek(int sfd,int offset, int mode)
+{
+    long byteoffset;    /* can be negative - limited to 2GB moves*/
+    fpos_t data_end,pos_target,cur_pos;
+    PSFFILE *sfdat;
+    
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    /* RWD NB:dataoffset test only valid for files with headers! */
+    if(POS64(sfdat->dataoffset)==0)
+        return PSF_E_BADARG;
+    /* or, it indicates a RAW file.... */
+    
+    byteoffset =  offset *  sfdat->fmt.Format.nBlockAlign;
+    POS64(data_end) = POS64(sfdat->dataoffset) + (sfdat->nFrames * sfdat->fmt.Format.nBlockAlign);
+    switch(mode){
+        case PSF_SEEK_SET:
+            POS64(pos_target) =  POS64(sfdat->dataoffset) + byteoffset;
+            if(fsetpos(sfdat->file,&pos_target))
+                return PSF_E_CANT_SEEK;
+            break;
+        case PSF_SEEK_END:
+            /* NB can't just seek to end of file as there may be junk after data chunk! */
+            POS64(pos_target) = POS64(data_end) + byteoffset;
+            if(fsetpos(sfdat->file,&pos_target))
+                return PSF_E_CANT_SEEK;
+            break;
+        case PSF_SEEK_CUR:
+            /* should be safe using fseek for SEEK_END */
+            /* Currently UNDECIDED whether to allow seeks beyond end of file! */
+            if(fseek(sfdat->file,byteoffset,SEEK_CUR))
+                return PSF_E_CANT_SEEK;
+            break;
+    }
+    if(fgetpos(sfdat->file,&cur_pos))
+        return PSF_E_CANT_SEEK;
+    if(POS64(cur_pos) >= POS64(sfdat->dataoffset)){
+        sfdat->curframepos = (DWORD)(POS64(cur_pos) -  POS64(sfdat->dataoffset))  / sfdat->fmt.Format.nBlockAlign;
+        if(!sfdat->isRead)  {       /*RWD NEW*/
+            /* we are rewinding a file open for writing */
+            POS64(sfdat->lastwritepos) = sfdat->curframepos;
+        }
+        return PSF_E_NOERROR;
+    }
+    else
+        return PSF_E_CANT_SEEK;
+}
+
+
+/* decide sfile format from the filename extension */
+/* TODO: add func to get format from file header */
+psf_format psf_getFormatExt(const char *path)
+{
+    char *lastdot;
+    if(path==NULL || (strlen(path) < 4))
+        return PSF_FMT_UNKNOWN;             /* TODO: support RAW data... */
+    lastdot = strrchr(path,'.');
+    if(lastdot==NULL)
+        return PSF_FMT_UNKNOWN;
+    
+    if(stricmp(lastdot,".wav")==0)
+        return PSF_STDWAVE;
+    else if((stricmp(lastdot,".aif")==0) || stricmp(lastdot,".aiff")==0)
+        return PSF_AIFF;
+    else if((stricmp(lastdot,".afc")==0) || stricmp(lastdot,".aifc")==0)
+        return PSF_AIFC;
+    /* Ambisonic b-format files */
+    else if(stricmp(lastdot,".wxyz")==0)
+        return PSF_STDWAVE;
+    else if(stricmp(lastdot,".amb")==0)
+        return PSF_WAVE_EX;
+    else
+        return PSF_FMT_UNKNOWN;
+    
+}
+
+/* return 0 for no PEAK data, 1 for success */
+/* NB: we read PEAK data from sfdat, so we can read peaks while writing the file, before closing */
+int psf_sndReadPeaks(int sfd,PSF_CHPEAK peakdata[],MYLONG *peaktime)
+{
+    int i,nchans;
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    /* TODO: we may want to have this, for RAW files, even though we won't write it */
+    if(sfdat->pPeaks==NULL){           /*NOT an error: just don't have the chunk*/
+        if(peaktime!= NULL)
+            *peaktime = 0;
+        return 0;
+        
+    }
+    if(peaktime != NULL)
+        *peaktime = (int) sfdat->peaktime;
+    nchans = sfdat->fmt.Format.nChannels;
+    for(i=0;i < nchans;i++){
+        peakdata[i].val = sfdat->pPeaks[i].val;
+        peakdata[i].pos = sfdat->pPeaks[i].pos;
+    }
+    return 1;
+}
+
+static float trirand(void)
+{
+    double r1,r2;
+    r1 = (double) rand() * inv_randmax;
+    r2 = (double) rand() * inv_randmax;
+    
+    return (float)((r1 + r2) - 1.0);
+    
+    
+}
+
+int psf_sndSetDither(int sfd,unsigned int dtype)
+{
+    PSFFILE *sfdat;;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    if(dtype < PSF_DITHER_OFF || dtype > PSF_DITHER_TPDF || sfdat->isRead)
+        return PSF_E_BADARG;
+    
+    sfdat->dithertype = dtype;
+    
+    return PSF_E_NOERROR;
+    
+}
+/* get current dither setting */
+int psf_sndGetDither(int sfd)
+{
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+#ifdef _DEBUG       
+    assert(sfdat->file);
+    assert(sfdat->filename);
+#endif
+    
+    return sfdat->dithertype;
+    
+}
+
+/* OCT 2009 */
+psf_channelformat get_speakerlayout(DWORD chmask,DWORD chans)
+{
+    psf_channelformat chformat = MC_WAVE_EX;    // default is some weird format!
+    
+    /* accept chancount > numbits set in speakermask */
+    switch(chmask){
+            /*check all cross-platform formats first...*/
+        case(SPKRS_UNASSIGNED):
+            chformat = MC_STD;
+            break;
+        case(SPKRS_MONO):
+            if(chans==1)
+                chformat = MC_MONO;
+            break;
+        case(SPKRS_STEREO):
+            if(chans==2)
+                chformat = MC_STEREO;
+            break;
+        case(SPKRS_GENERIC_QUAD):
+            if(chans==4)
+                chformat = MC_QUAD;
+            break;
+        case(SPKRS_SURROUND_LCRS):
+            if(chans==4)
+                chformat = MC_LCRS;
+            break;
+        case(SPKRS_DOLBY5_1):
+            if(chans==6)
+                chformat = MC_DOLBY_5_1;
+            break;
+        case(SPKRS_SURR_5_0):                   /* March 2008 */
+            if(chans==5)
+                chformat = MC_SURR_5_0;
+            break;
+        case(SPKRS_6_1):
+            if(chans==7)
+                chformat = MC_SURR_6_1;
+            break;
+        case(SPKRS_7_1):
+            if(chans==8)
+                chformat = MC_SURR_7_1;
+            break;
+        case SPKRS_CUBE:
+            if(chans==8)
+                chformat = MC_CUBE;
+            break;
+        default:
+            break;
+    }
+    return chformat;
+}
+
+/* NOV 2009 get raw speaker mask */
+
+int psf_speakermask(int sfd)
+{
+    PSFFILE *sfdat;
+    
+    if(sfd < 0 || sfd > psf_maxfiles)
+        return PSF_E_BADARG;
+    
+    sfdat  = psf_files[sfd];
+    if(sfdat==NULL)
+        return PSF_E_BADARG;
+    
+    return (int) sfdat->fmt.dwChannelMask;
+}
+
+/* September 2010 to check output file sizes */
+#define PSF_SIZE_SAFETY (512)
+
+int is_legalsize(unsigned long nFrames, const PSF_PROPS *props)
+{
+    int samplesize = 0;
+    int result  = 0;
+    int blocksize;
+    unsigned long long bytesize;
+    
+    switch(props->samptype){
+        case PSF_SAMP_8:
+            samplesize = 1;
+            break;
+        case PSF_SAMP_16:
+            samplesize = 2;
+            break;
+        case PSF_SAMP_24:
+            samplesize = 3;
+            break;
+        case PSF_SAMP_32:
+        case PSF_SAMP_IEEE_FLOAT:
+            samplesize = 4;
+            break;
+        default:
+            return result;
+    }
+    
+    blocksize = props->chans * samplesize;
+    bytesize = nFrames * blocksize;
+    if( bytesize <=  0xffffffffLL - PSF_SIZE_SAFETY)
+        result = 1;
+    return result;
+}
+
+/* TODO: define a psf_writePeak function; probably to a single nominated channel. 
+ This would be needed as soon as write is performed with random over-write activity.
+ This is probably something to discourage, however!
+ */
+
+

+ 31 - 0
dev/externals/reverb/CMakeLists.txt

@@ -0,0 +1,31 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.8  -Dunix -fomit-frame-pointer -funroll-loops")
+  set(CMAKE_CXX_FLAGS "-O2 -Wall -mmacosx-version-min=10.8  -Dunix -fomit-frame-pointer -funroll-loops -std=c++11 -stdlib=libc++")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O3 -DWIN32 -D_WIN32 -fomit-frame-pointer  -funroll-loops")
+    set(CMAKE_CXX_FLAGS "-O3 -DWIN32 -D_WIN32 -fomit-frame-pointer  -funroll-loops -static-libgcc -static-libstdc++")
+  else()
+    set(CMAKE_C_FLAGS "-O3 -Wall -Dlinux -Dunix -fomit-frame-pointer -funroll-loops")
+  endif()
+endif()
+
+link_directories(../lib)
+
+include_directories(../../../include ../include)
+  
+add_executable(rmverb rmverb.cpp reverberator.cpp wavetable.cpp)
+target_link_libraries(rmverb portsf sfsys ${EXTRA_LIBRARIES})
+my_install(rmverb)
+
+add_executable(reverb reverb.cpp reverberator.cpp wavetable.cpp)
+target_link_libraries(reverb portsf sfsys ${EXTRA_LIBRARIES})
+my_install(reverb)
+
+add_executable(rmresp roomresp.cpp)
+target_link_libraries(rmresp portsf sfsys ${EXTRA_LIBRARIES})
+my_install(rmresp)
+
+add_executable(tapdelay tdelaymain.cpp reverberator.cpp wavetable.cpp)
+target_link_libraries(tapdelay portsf sfsys ${EXTRA_LIBRARIES})
+my_install(tapdelay)

+ 7 - 0
dev/externals/reverb/LARGERM.TXT

@@ -0,0 +1,7 @@
+1.0               # Input gain for data file (float)
+0                 # Open Path = 0 Closed Path = 1 (int)
+43    31    19       # Room Dimensions (X,Y,Z) (float)
+0.99              # Reflectivity of walls (float)
+5                 # Number of rooms per dimension, 1 or greater
+20    15    2      # Listener coord (X,Y,Z) (float)
+7    15    2   0 # Source coord and time at coord (X,Y,Z,t) (float)

+ 7 - 0
dev/externals/reverb/MEDIUMRM.TXT

@@ -0,0 +1,7 @@
+1.0               # Input gain for data file (float)
+0                 # Open Path = 0 Closed Path = 1 (int)
+21   16    5       # Room Dimensions (X,Y,Z) (float)
+0.9              # Reflectivity of walls (float)
+5                 # Number of rooms per dimension, 1 or greater
+12    8    2      # Listener coord (X,Y,Z) (float)
+5    8    2   0 # Source coord and time at coord (X,Y,Z,t) (float)

+ 7 - 0
dev/externals/reverb/POS.TXT

@@ -0,0 +1,7 @@
+1.0               # Input gain for data file (float)
+0                 # Open Path = 0 Closed Path = 1 (int)
+20    10    6       # Room Dimensions (X,Y,Z) (float)
+0.5              # Reflectivity of walls (float)
+5                 # Number of rooms per dimension, 1 or greater
+15    5    2      # Listener coord (X,Y,Z) (float)
+5    4    2   0 # Source coord and time at coord (X,Y,Z,t) (float)

+ 252 - 0
dev/externals/reverb/allpass.c

@@ -0,0 +1,252 @@
+/*
+ * 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
+ *
+ */
+ 
+/*	allpass.c
+ *	allpass filter
+ *	A. Bentley, Composers' Desktop Project, Version Nov 1987
+ */
+
+
+/*
+ *	$Log: allpass.c%v $
+ * Revision 3.3  1994/10/31  22:08:15  martin
+ * first rcs version
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>	
+#include <math.h>
+#include <sfsys.h>
+#include <cdplib.h>
+static char *sfsys_h_rcsid = SFSYS_H_RCSID;
+static char *cdplib_h_rcsid = CDPLIB_H_RCSID;
+
+void usage();
+void allpass_long(long,long,long,long,const short*,short*,long*,short *);
+
+#define BUFLEN	(32768)
+#define FP_ONE  (65536.0)
+#define LOG_PS  (16)
+
+PROGRAM_NUMBER(0xf93c2700);
+
+void
+main(argc,argv)
+int argc;
+char *argv[];
+{
+	int ifd, ofd;
+	int sampsize;
+	int quickflag = 1;	/* default is fast fixed point calc */
+	register int rv;
+	float	ip, op;
+#ifdef ORIGINAL_VERSION
+	long	lip, lop;
+	long	i;
+#endif
+	float 	*delbuf1;
+	float   *delbuf2;
+	long	rdsamps;
+	long	*ldelbuf1;
+	short	*sdelbuf2;
+	short	*sinbuf;
+	short	*soutbuf;
+	double  delay; 
+	float	gain, prescale = 1.0f;
+	long	lgain, lprescale;
+	long 	delsamps;
+	long	dbp1 = 0, dbp2 = 0;
+	long	chans;
+	long	isr;
+	double	sr;
+
+	if(sflinit("allpass")){
+		sfperror("Allpass: initialisation");
+		exit(1);
+	}
+    	while(argv[1] != 0 && argv[1][0] == '-') {
+	      switch(argv[1][1]) {
+	      case 'f':	
+		 quickflag--;
+		 argv++;
+		 argc--;
+		 break;
+	      default:
+	         usage();
+	         break;
+	      }
+	}
+	if(argc < 5 || argc > 6) usage();
+
+	if(argv[1] != 0) {
+	      	if((ifd = sndopen(argv[1])) < 0) {
+		   fprintf(stderr,"Allpass: cannot open input file %s\n", argv[1]);
+		   exit(1);
+		}
+	}
+	if(sndgetprop(ifd,"sample rate",(char *) &isr, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read sample rate for file %s",argv[1]);
+		exit(1);
+	}
+	sr = (double) (isr);
+	if(sndgetprop(ifd,"channels",(char *) &chans, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read channel data for file %s", argv[1]);
+		exit(1);
+	}
+	if(chans != 1){
+		fprintf(stderr,"\nAllpass works only on mono files");
+		exit(1);
+	}
+	if(sndgetprop(ifd,"sample type",(char *) &sampsize, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read sample type for file %s", argv[1]);
+		exit(1);
+	}
+	/* get command line args */
+	delay = abexpr(argv[3],sr);
+	if(delay < 0.0){
+		fprintf(stderr,"\nAllpass: invalid delay parameter");
+		exit(1);
+	}
+	gain = (float)abexpr(argv[4],sr);
+	if((gain < -1.0) || (gain > 1.0)){
+		fprintf(stderr,"\nAllpass: gain out of range");
+		exit(1);
+	}
+
+	if((ofd = sndcreat(argv[2],-1,sampsize)) < 0) {
+		  fprintf(stderr,"Cannot open output file %s\n",argv[2]);
+		  exit(1);
+	}
+
+	lgain = (long) (gain * FP_ONE);
+	if(argc > 5) {
+		prescale = (float)abexpr(argv[5],sr);
+		if((prescale < -1.0) || (prescale > 1.0))
+		   fprintf(stderr,
+			"Allpass: warning - prescale exceeds +/-1\n");
+	}
+	lprescale = (long) (prescale * FP_ONE);		
+	/* allocate buffer for delay line */
+	delsamps = (long) (delay * sr);
+	if(quickflag) {
+	   if(( sinbuf = (short *) malloc(BUFLEN * sizeof(short))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for input buffer");
+		exit(1);
+	   }
+	   if(( soutbuf = (short *) malloc(BUFLEN * sizeof(short))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for output buffer");
+		exit(1);
+	   }
+	   if(( ldelbuf1 = (long *) calloc(delsamps, sizeof(long))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for delay buffer 1");
+		exit(1);
+	   }
+	   if(( sdelbuf2 = (short *) calloc(delsamps, sizeof(short))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for delay buffer 2");
+		exit(1);
+	   }
+	}else{
+	   if(( delbuf1 = (float *) calloc(delsamps, sizeof(float))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for delay buffer 1");
+		exit(1);
+	   }
+	   if(( delbuf2 = (float *) calloc(delsamps, sizeof(float))) == NULL) {
+		fprintf(stderr,"\nNot enough memory for delay buffer 2");
+		exit(1);
+	   }
+	}
+	stopwatch(1);
+    
+	for(;;){
+		if((rv = fgetfloat(&ip,ifd)) < 0){
+			fprintf(stderr,"\nError in reading file"); 
+			exit(1);
+		}
+		if(!rv) break;
+			ip *= prescale;
+		op = (-gain) * ip;
+		op += delbuf1[dbp1];
+		op += gain * delbuf2[dbp2];
+			delbuf1[dbp1++] = ip;
+		delbuf2[dbp2++] = op;
+
+		if(dbp1 >= delsamps) dbp1 = 0;
+		if(dbp2 >= delsamps) dbp2 = 0;
+
+   		if(fputfloat(&op,ofd) < 1){
+			fprintf(stderr,"\nError writing to output file"); 
+			exit(1);
+		}	
+    }
+	stopwatch(0);
+
+	sndclose(ifd);
+	if(sndputprop(ofd,"sample rate", (char *) &isr, sizeof(long)) < 0){
+		fprintf(stderr,"\nAllpass: failed to write sample rate");
+	}
+	if(sndputprop(ofd,"channels",(char *) &chans, sizeof(long)) < 0){
+		fprintf(stderr,"\nAllpass: failed to write channel data");
+	}
+	sndclose(ofd);
+}
+
+void
+usage()
+{
+	fprintf(stderr,"\nAllpass - A Groucho Program: $Revision: 3.3 $");
+	fprintf(stderr,"\n%s\n",
+	"usage: allpass [-f] infile outfile delay gain [prescale]");
+	exit(0);
+}
+
+/*RWD: my allpass functions */
+//TODO: optimize using ptr arithmetic
+void allpass_long(long prescale, 
+				   long buflen, 
+				   long l_gain,
+				   long l_delsamps,
+				   const short *s_inbuf,
+				   short *s_outbuf,
+				   long *l_delbuf1,
+				   short *s_delbuf2)
+{
+	int i; 
+	static long	db_p1 = 0;
+	static long db_p2 = 0;
+	//do allpass on block of samples
+	for(i = 0; i < buflen; i++){
+		   long lip,lop;
+		   lip = s_inbuf[i] * prescale;
+		   lop = (-l_gain) * (lip >> LOG_PS);
+		   lop += l_delbuf1[db_p1];
+		   lop += l_gain * s_delbuf2[db_p2];
+
+		   l_delbuf1[db_p1++] = lip;
+		   s_outbuf[i] = s_delbuf2[db_p2++] = (short) (lop >> LOG_PS);
+
+		   if(db_p1 >= l_delsamps) db_p1 = 0;
+		   if(db_p2 >= l_delsamps) db_p2 = 0;
+    }
+	   //block done
+	//return &soutbuf[0];			//if we return anything?
+}

+ 546 - 0
dev/externals/reverb/delay.c

@@ -0,0 +1,546 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#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<rdsamps;i++)
+		{
+			
+				/* delay line */
+			input = inbuf[i] * Prescale;
+			output = (input * Feedforgain) +		  //dry signal
+				(Fdelbuf[opptr] * Delaygain);
+			Fdelbuf[ipptr] = Fdelbuf[opptr++] * Feedbckgain; 
+			Fdelbuf[ipptr++] += input ;
+			outbuf[i] = (short) output;				  //dry + wet
+
+			if(ipptr >= 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<rdsamps;i++) {
+		       	output=(Fdelbuf[opptr] * Delaygain);
+		       	Fdelbuf[ipptr++]  = 
+				Fdelbuf[opptr++] *Feedbckgain;
+		       	outbuf[i] = (short) output;
+		       	if(ipptr >= 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);
+	}
+}

+ 62 - 0
dev/externals/reverb/diffuse.h

@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//diffuse.h
+//definition of classes for small, meduim large dense reverberators
+// based on gardner, using nested allpasses
+#ifndef __DIFFUSE_INCLUDED__
+#define __DIFFUSE_INCLUDED__
+
+#include "reverberator.h"
+#include "tapdelay.h"
+
+typedef struct nested_allpass_data{
+	unsigned int time1;
+	unsigned int time2;
+	unsigned int time3;
+	double gain1;
+	double gain2;
+	double gain3;
+}
+NESTDATA;
+
+
+class small_diffuser {
+public:
+	small_diffuser(unsigned int pre_delay,
+					const NESTDATA *apdata1,
+					const NESTDATA *apdata2,double lp_gain,double lp_coeff);
+	virtual ~small_diffuser();
+	bool create(void);
+	float tick(float insam);
+	//void clear(void);
+
+private:
+	NESTDATA ap1_data,ap2_data;
+	nested_allpass *ap1,*ap2;
+	delay *predelay;
+	lowpass1 *lp1;
+	unsigned int predelay_time;
+	double lpgain,lpcoeff;
+	float out1,out2;
+};
+
+
+#endif

+ 169 - 0
dev/externals/reverb/lpcomb.cpp

@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+//lpcomb.cpp
+extern "C" {
+#include <sfsys.h>
+#include <cdplib.h>	   //NB requires stdio.h etc - time to change this?
+}
+
+#include "reverberator.h"
+
+//straight from Csound: nreverb (vdelay.c)
+static bool prime( int val )
+{
+    int i, last;
+	
+    last = (int)sqrt( (double)val );
+    for ( i = 3; i <= last; i+=2 ) {
+      if ( (val % i) == 0 ) return false;
+    }
+    return true;
+}
+
+static int to_prime(double dur,double sr)
+{
+		int time = (int) (dur * sr);
+		if(time % 2== 0) time += 1;
+		while(!prime(time)) 
+			time +=2;
+		return time;
+}
+
+void
+usage()
+{
+
+	printf("\nusage: lpcomb  infile outfile looptime rvbtime filtgain");
+	exit(0);
+}
+
+void
+main(int argc,char *argv[])
+
+{
+	int		ifd, ofd;	
+	//double  delaytime,gain;
+	double	rvbtime,gain2,filtgain,dloop; 	
+	long	isr,chans,sampsize,ilooptime;
+	double	sr;
+	lpcomb	*comb1 = 0;
+
+
+	if(sflinit("lpcomb")){
+		sfperror("lpcomb: initialisation");
+		exit(1);
+	}
+
+
+	if(argc < 6)
+		usage();
+	dloop = atof(argv[3]);
+	if(dloop <= 0.0){
+		fprintf(stderr,"\nlpcomb: invalid looptime parameter");
+		exit(1);
+	}
+
+	rvbtime = atof(argv[4]);
+	if(rvbtime <= 0.0){
+		fprintf(stderr,"\nlpcomb: invalid delay parameter");
+		exit(1);
+	}
+	filtgain = atof(argv[5]);
+	if((filtgain < 0.0) || (filtgain > 1.0)){
+		fprintf(stderr,"\nlpcomb: filter gain %lf out of range",filtgain);
+		exit(1);
+	}
+
+	if(argv[1] != 0) {
+	    if((ifd = sndopen(argv[1])) < 0) {
+		   fprintf(stderr,"Allpass: cannot open input file %s\n", argv[1]);
+		   exit(1);
+		}
+	}
+	if(sndgetprop(ifd,"sample rate",(char *) &isr, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read sample rate for file %s",argv[1]);
+		exit(1);
+	}
+	sr = (double) (isr);
+	if(sndgetprop(ifd,"channels",(char *) &chans, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read channel data for file %s", argv[1]);
+		exit(1);
+	}
+	if(chans != 1){
+		fprintf(stderr,"\nAllpass works only on mono files");
+		exit(1);
+	}
+	if(sndgetprop(ifd,"sample type",(char *) &sampsize, sizeof(long)) < 0){
+		fprintf(stderr,
+		  "\nAllpass: failed to read sample type for file %s", argv[1]);
+		exit(1);
+	}
+
+
+	if((ofd = sndcreat(argv[2],-1,sampsize)) < 0) {
+		  fprintf(stderr,"Cannot open output file %s\n",argv[2]);
+		  exit(1);
+	}
+	ilooptime  = to_prime(dloop, sr);	
+	gain2 = exp( log(0.001)* (dloop/rvbtime)) * (1. - filtgain) ;
+	printf("\ngain2 = %lf\tfiltgain = %lf",gain2,filtgain);
+	comb1 = new lpcomb(ilooptime,gain2,filtgain,1.0);
+	if(!comb1->create()){
+		printf("\nFailed to create comb filter");
+		exit(1);
+	}
+
+	stopwatch(1);
+
+	for(;;){
+		int rv;
+		float ip,op;
+
+		if((rv = fgetfloat(&ip,ifd)) < 0){
+			fprintf(stderr,"\nError in reading file"); 
+			exit(1);
+		}
+		if(!rv)
+			break;
+		op = comb1->tick(ip);
+		if(fputfloat(&op,ofd) < 1){
+			fprintf(stderr,"\nError writing to output file"); 
+			exit(1);
+		}
+	}
+	stopwatch(0);
+	sndclose(ifd);
+	if(sndputprop(ofd,"sample rate", (char *) &isr, sizeof(long)) < 0){
+		fprintf(stderr,"\nAllpass: failed to write sample rate");
+	}
+	if(sndputprop(ofd,"channels",(char *) &chans, sizeof(long)) < 0){
+		fprintf(stderr,"\nAllpass: failed to write channel data");
+	}
+	sndclose(ofd);
+	delete comb1;
+	sffinish();
+}

+ 121 - 0
dev/externals/reverb/readme.txt

@@ -0,0 +1,121 @@
+readme file for CDP reverb programs
+
+ROOMRESP
+
+simple utility program to generate early reflections for reverbs, using "mirror room" model.
+
+The format for early reflection data is a series of lines containing
+<time> <value> pairs. The first time is non-zero (since the direct sound
+is presumed to be there):
+
+0.0082 0.500000
+0.0121 0.204198
+0.0134 0.169094
+0.0145 0.143638
+0.0170 0.093391
+0.0180 0.084478
+0.0200 0.067530
+.. etc
+
+Data obtained from roomresp is used internally for the early reflections in both "rmverb" and "reverb". 
+Roomresp usually generates many more taps than are necessary or efficient; typically the first
+19 taps (say) will be used. The two reverb programs offer the facility to import reflection data from
+roomresp directly into the reverb (see the -e flag option).  As a FIR filter is used to process
+these, the CPU load of the program is directly affected by the number of reflections used.
+
+the choice of early reflections has an influence on the character and tonality of the reverb 
+- experimentation is recommended here. Hand-written reflection data is especially good for "creative" reverbs
+with heavy ringing and colour!
+
+RMVERB
+
+The program "rmverb" uses nested allpass filters based on Bill Gardner's published models; hence three 
+room models are offered, "small", "medium" and "large".
+ 
+The "large room" model is modified with smaller delays (so really "large-ish"), to reduce the 
+obviousness of the slap-back echoes generated by the model. 
+
+Note that this relies on the fact that each reverb element is implemented in a self-contained manner, 
+unit-generator style (e.g. including plain delays between elements). If it is required to optimise 
+the architecture by having all elements share one delay line,  this change will not work - the outer 
+delay will need to be smaller than the  inner ones.
+
+Two allpass filters per channel are aded to the output, to obtain
+a stereo effect from the internal mono reverb. According to Gardner, his concept was to use one "diffuser" 
+(configuration of nested allpass filters) per speaker; this suggests that any thinness in the output can 
+be overcome (at the cost of processing time) by paralleling several diffusers per channel, with slightly 
+different parameters.
+
+Additionally (NEW from CDP!) each nested allpass filter has been extended with the addition of a first-order
+filter in the outer feedback loop. This acts in addition to the global feedback loop defined by Gardner.
+This is indicated in "rmverb" by the -d flag ("double damping"). This seems to reduce the metallic 
+ringing at the very end of the reverb decay, at the cost of shortening the overall decay time. It would
+easily be possible, with this change, to eliminate the global feedback filter and rely entirely on the
+filter inside the nested allpasses, to recover some of the decay time. This feature is experimental.
+
+As Gardner notes, pushing the feedback amount to obtain long decay times is not recommended, as the timbre
+deteriorates. The program is implemented in a relatively "raw" state; with a raw feedback level parameter 
+rather than a "reverb time"; some mapping will need to be computed for this!
+
+The programs also allow the delay between direct sound and first reflection (as defined in the reflection data)
+to be modified - see the -p flag option.	  Standard filters are provided to filter the input signal itself,
+and the signal entering the reverb. The latter is usual, the former is rarely necessary. 
+Some filters are second-order, offering a 12dB slope (currently fixed in the program). It would be easy to 
+make the slope variable as another user parameter.	 The sources include a library of low and high pass filters,
+first order and second order (shelving).
+
+
+ REVERB
+
+The program "reverb" uses a standard moorer/schroeder comb+allpass structure, with	6 comb filters 
+(with lp filters in the fedback loop) feeding 4 allpass filters. A conventional,"reverb time" is provided, but
+no "room size". This wouild have to be obtained via more or less extensive early reflections. 
+
+
+Not shown in the programs usage message is a possble extra argument which gives the name of a text data file
+containing delay times (in msecs) for the comb and allpass filters.	 This will be a plain text file containing
+10 positive floating-point values. The internal preset times corresponds to this data file:
+
+50 56 61 68 72 78 20 14 9 6
+
+ - which gives the canonical Moorer values.
+
+
+NOTE one importnat difference in the "absorb" argument to the programs.
+Reverb:  range 0 - 1, large values = more absorption.
+Rmverb:  value in Hzf for feedback filter (See Gardner for details). He suggests:
+	small room: 4200 Hz
+	medium:  2500 Hz
+	large:  2600 Hz
+
+Rmverb offers direct setting of this value. This then applies both the the global feedback loop
+and to the internal nested-allpass loop as described above.
+
+In both reverb programs, ~all~ delay times are adjusted to give prime number integral lengths in samples. This means
+that performance will not be ~identical~ at all sample rates. Naturally, if the sample rate is to be fixed, in a
+dsp implementation, the possibility exists to fine-tune delays for that sample rate.
+
+
+Both programs generate multi-channel (surround) signals by using a separate allpass filter on each channel for
+de-correlation. The direct sound is fed only to the front stereo pair.
+
+
+Note that these are command-line programs, and the various dsp elements are not fully re-entrant in terms of 
+running re-initializations.	 This would not be hard to do, howewver, especially given the goal is a 
+re-implementation on a DSP architecture.
+
+CDP plans to extend these algorithms with variable delay lines, and also to increase density by addition of 
+elements.  To say nothing of real-time versions in plugin form!
+
+ Richard Dobson
+ CDP
+ September 14th 2004
+
+
+
+
+
+
+
+
+

+ 189 - 0
dev/externals/reverb/reflect.cpp

@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//NB it is most important to avoid periodicities in these
+// using rmresp, best to place source and listener near opposite corners, or both near one corner 
+//preset early reflections - could get these from a brkfile, of course
+//except small room ; seems best to have src and listener same end of room, to get even earlier reflections
+//11 8 3.3
+//0.9
+//5
+//9 3 1.75
+//9 5.7 1.29
+//normed to 0.5
+
+deltap smalltaps[] = {
+	{0.0082,0.500000},
+	{0.0121,0.204198},
+	{0.0134,0.169094},
+	{0.0145,0.143638},
+	{0.0170,0.093391},
+	{0.0180,0.084478},
+	{0.0200,0.067530},
+	{0.0219,0.063096},
+	{0.0226,0.053176},
+	{0.0233,0.044833},
+	{0.0237,0.048586},
+	{0.0243,0.046058},
+	{0.0250,0.043713},
+	{0.0256,0.037388},
+	{0.0261,0.044475},
+	{0.0265,0.034818},
+	{0.0271,0.033360},
+	{0.0276,0.035772},
+	{0.0281,0.034382},
+	{0.0285,0.030051},
+	{0.0287,0.033059},
+	{0.0301,0.027091},
+	{0.0304,0.026513},
+	{0.0306,0.026200},
+	{0.0309,0.023001},
+	{0.0319,0.024114},
+	{0.0326,0.020657},
+	{0.0335,0.021782},
+	{0.0340,0.019019},
+	{0.0356,0.017387},
+	{0.0398,0.017155},
+	{0.0408,0.014690},
+	{0.0412,0.014424},
+	{0.0416,0.014160},
+	{0.0425,0.012175},
+	{0.0429,0.011971},
+	{0.0439,0.011468},
+	{0.0451,0.010854}
+	};
+
+//dim: 21 16 5
+// refl: 0.95
+//nrooms 5
+//listenerpos
+//17 8 2
+//srcpos:
+//6 9.35 2
+//RWD NB does sound worse with with only 32 taps, or refl-order 3
+// this is the 'no-compromise' reverb!
+deltap mediumtaps[] = 
+{
+	{0.0332,0.993303},
+	{0.0353,0.834879},
+	{0.0377,0.729745},
+	{0.0447,0.494138},
+	{0.0548,0.345328},
+	{0.0561,0.313134},
+	{0.0570,0.319440},
+	{0.0577,0.296282},
+	{0.0583,0.290651},
+	{0.0598,0.276075},
+	{0.0615,0.274630},
+	{0.0625,0.240116},
+	{0.0627,0.251368},
+	{0.0641,0.240392},
+	{0.0644,0.226004},
+	{0.0684,0.200374},
+	{0.0690,0.218340},
+	{0.0700,0.201354},
+	{0.0713,0.194250},
+	{0.0718,0.191280},
+	{0.0728,0.176802},
+	{0.0740,0.171020},
+	{0.0752,0.165815},
+	{0.0770,0.166316},
+	{0.0778,0.147079},
+	{0.0780,0.154272},
+	{0.0791,0.149852},
+	{0.0816,0.148066},
+	{0.0825,0.137700},
+	{0.0826,0.130403},
+	{0.0836,0.134167},
+	{0.0862,0.132653} ,
+	{0.0870,0.117789},
+	{0.0871,0.123637},
+	{0.0881,0.120782},
+	{0.0913,0.106847},
+	{0.0929,0.114356},
+	{0.0937,0.106863},
+	{0.0946,0.104723},
+    {0.0975,0.103831}
+	
+};
+
+//normed to 0.5:
+//43 31 19
+//0.95
+//7
+//7.6 15 2
+//30 23.7 6
+
+deltap largetaps[] = 
+{
+	{0.0729,0.500000},
+	{0.0758,0.439456},
+	{0.0975,0.265770},
+	{0.0997,0.241550},
+	{0.1151,0.190794},
+	{0.1162,0.187246},
+	{0.1180,0.172387},
+	{0.1247,0.154486},
+	{0.1320,0.137708},
+	{0.1330,0.135754},
+	{0.1344,0.139864},
+	{0.1346,0.125902},
+	{0.1360,0.129780},
+	{0.1404,0.115604},
+	{0.1449,0.114371},
+	{0.1464,0.112074},
+	{0.1477,0.115801},
+	{0.1492,0.107884},
+	{0.1540,0.096167}
+};
+
+//Moorer
+deltap longtaps[18] = {
+					{0.0043,0.841},
+					{0.0215,0.504},
+					{0.0225,0.491},
+					{0.0268,0.379},
+					{0.0270,0.380},
+					{0.0298,0.346},
+					{0.0458,0.289},
+					{0.0485,0.272},
+					{0.0572,0.192},
+					{0.0587,0.193},
+					{0.0595,0.217},
+					{0.0612,0.181},
+					{0.0707,0.180},
+					{0.0708,0.181},
+					{0.0726,0.176},
+					{0.0741,0.142},
+					{0.0753,0.167},
+					{0.0797,0.134}
+};
+
+
+deltap shorttaps[] = 
+{
+					{0.0099,1.02},			  //my times!
+					{0.0154,0.818},
+					{0.0179,0.635},
+					{0.0214,0.719},
+					{0.0309,0.267},
+					{0.0596,0.242}
+					};
+

+ 33 - 0
dev/externals/reverb/reflect.h

@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//reflect.h
+#ifndef __REFLECT_INCLUDED
+#define __REFLECT_INCLUDED
+#include "reverberator.h"
+
+
+extern deltap smalltaps[];
+extern deltap mediumtaps[];
+extern deltap largetaps[];
+extern deltap longtaps[];
+extern deltap shorttaps[];
+#endif
+

+ 745 - 0
dev/externals/reverb/reverb.cpp

@@ -0,0 +1,745 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/* MODULE FOR REVERB.EXE */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include <algorithm>
+#include <vector>
+using namespace std;
+/***** FORMULA FOR LOOP GAIN *****
+
+  g = pow(10, - ( ((60*ilooptime)/reverbtime) / 20))
+
+  derived from Moore:
+
+	reverbtime = 60/(-20.log10(g)) * ilooptime
+
+  then we set igain = g(1 - filtgain), officially...
+
+  */
+extern "C" {
+#include <sfsys.h>
+#include <osbind.h>
+#include <cdplib.h>	   //NB requires stdio.h etc - time to change this?
+}
+#include "reverberator.h"
+#include <string.h>
+void usage(void);
+long readtaps(FILE *fp, deltap **taps,double sr);
+
+//my library of early reflections...
+//by doing it this way I can use the sizeof() operator on the array names
+#include "reflect.cpp"
+
+enum cmdargs {
+	PROGNAME,
+	INFILE,
+	OUTFILE,
+	RGAIN,
+	MIX,
+	REVERBTIME,	
+	DAMPING,
+	LPFREQ,
+	TRTIME,
+	CONFIG
+};
+
+const char* cdp_version = "5.0.0";
+
+///////////// Moorer reverb
+int
+main(int argc,char *argv[])
+
+{
+	int		ifd=-1, ofd=-1,i;	
+	//double  delaytime,gain;
+	double	rvbtime, damping, trailertime = 0.0; 	
+	long	chans,outchans = 2;
+	double	sr;
+	//all this stuff from Brandt  Csound implementation of posh Moorer
+	MOORERDATA mrdata = {0.050,0.056,0.061,0.068,0.072,0.078,0.02,0.014,0.009,0.006};	
+	double predelay = 0.0;
+	moorer *moorerverb = 0;
+	tapdelay *tdelay = 0;
+	deltap *ptaps = 0;	
+	allpassfilter **chan_ap = 0;		//ptr to array of allpassfilter ptrs
+	tonecontrol *tc_l_lowcut = 0, *tc_r_lowcut = 0;
+	// could be replaced with a tonecontrol for deeper slope
+	onepole *l_highcut = 0, *r_highcut = 0, *lp = 0;
+	FILE *fp_earlies = 0; 
+	long ntaps;
+	bool want_mono = false;
+	bool want_floats = false;
+	bool usertaps = false;
+	double lowcut_freq = 0.0,highcut_freq = 0.0,lp_freq = 0.0;
+	float dry_gain,wet_gain,diff_gain;
+	CHPEAK *peaks;
+	SFPROPS props;
+	// test vcomb with variable delay taps 
+	//vmtcomb4 vcomb;
+	//fastlfo lfo1,lfo2,lfo3,lfo4;
+	if((argc==2) && strcmp(argv[1],"--version")==0) {
+		fprintf(stdout,"%s\n",cdp_version);
+		fflush(stdout);
+		return 0;
+	}
+
+	if(sflinit("reverb")){
+		sfperror("reverb: initialisation");
+		exit(1);
+	}
+	if(argc < CONFIG) usage();
+
+	while((argc > 1) && argv[1][0]=='-'){
+		char *arg = argv[1];
+		switch(*(++arg)){
+		case('p'):
+			if(*(++arg) =='\0'){
+				fprintf(stderr,"\npredelay requires a parameter");
+				usage();
+			}			
+			predelay = atof(arg);
+			if(predelay <0.0){
+				fprintf(stderr,"\npredelay must be >= 0.0");
+				usage();
+			}
+			predelay *= 0.001;			//arg is in msecs
+			break;
+		case('c'):			
+			if(*(++arg) == '\0') {
+				fprintf(stderr,"\n:reverb: -c flag requires a value");
+				usage();				
+			}
+			outchans = atoi(arg);
+			if(outchans < 1 || outchans > 16){
+				fprintf(stderr,"\nreverb: impossible channel value requested");
+				usage();
+			}
+			if(outchans==1)
+				want_mono = true;
+			break;
+		case('e'):
+			if(*(++arg)=='\0'){
+				fprintf(stderr,"\nreverb: -e flag needs a filename");
+				usage();
+			}
+			if((fp_earlies = fopen(arg,"r")) ==0){
+				fprintf(stderr,"\nreverb: unable to open breakpoint file %s",arg);
+				exit(1);
+			}
+			usertaps = true;
+			break;
+		case('f'):
+			want_floats = true;
+			if(*(++arg) != '\0')
+				fprintf(stderr,"\nreverb: WARNING: -f flag does not take a parameter");
+			break;
+		case('L'):
+			if(*(++arg) == '\0'){
+				fprintf(stderr,"\nreverb: Lowcut flag needs frequency argument");
+				usage();
+			}			
+			lowcut_freq = atof(arg);
+			if(lowcut_freq <= 0.0){
+				fprintf(stderr,"\nreverb: Lowcut freq must be greater than zero");
+				usage();
+			}
+			break;
+		case('H'):
+			if(*(++arg) == '\0'){
+				fprintf(stderr,"\nreverb: Highcut flag needs frequency argument");
+				usage();
+			}			
+			highcut_freq = atof(arg);
+			if(highcut_freq <= 0.0){
+				fprintf(stderr,"\nreverb: Highcut freq must be greater than zero");
+				usage();
+			}
+			break;
+		default:
+			fprintf(stderr,"\nreverb: illegal flag option %s",argv[1]);
+			usage();
+			break;
+		}
+		argc--;
+		argv++;
+	}
+	peaks = (CHPEAK *) malloc(sizeof(CHPEAK) * outchans);
+	if(peaks==NULL){
+		fputs("\nNo memory for PEAK data\n",stderr);
+		exit(1);
+	}
+	chan_ap = new allpassfilter*[outchans];
+	if(chan_ap==0){
+		fputs("\nreverb: no memory for multi-channel diffusion",stderr);
+		exit(1);
+	}
+
+
+	if(argc < CONFIG)
+		usage();
+
+	if(argv[INFILE] != 0) {
+	      	if((ifd = sndopenEx(argv[INFILE],0,CDP_OPEN_RDONLY)) < 0) {
+		   fprintf(stderr,"\nreverb: cannot open input file %s\n", argv[INFILE]);
+		   exit(1);
+		}
+	}
+	if(!snd_headread(ifd,&props)){
+		fprintf(stderr,"\nerror reading infile header: %s\n",rsferrstr);
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	if(props.type != wt_wave){
+		fprintf(stderr,"\ninfile is not a waveform file");
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	
+	sr = (double) props.srate;
+	chans = props.chans;
+
+	if(chans > 2){
+		fprintf(stderr,"\nreverb works only on mono or stereo files");
+		exit(1);
+	}
+	
+	
+	diff_gain = atof(argv[RGAIN]);
+	if(diff_gain < 0.0 || diff_gain > 1.0 ){
+		printf("\nreverb: rgain must be >= 0.0 and  <= 1.0");
+		usage();
+	}
+	dry_gain = (float) atof(argv[MIX]);			  //global output gain from diffuser
+	if(dry_gain < 0.0 || dry_gain >= 1.0 ){
+		printf("\nreverb: mix must be  between 0.0 and 1.0");
+		usage();
+	}		
+	wet_gain = 1.0f - dry_gain;
+	//probably not very scientific, but it works intuitively...
+	//wet_gain *= 2;		  
+	
+
+	rvbtime = atof(argv[REVERBTIME]);	  
+	if(rvbtime <= 0.0){
+	   fprintf(stderr,
+		"\nreverb: rvbtime must be > 0\n");
+		exit(1);
+	}
+
+	damping = atof(argv[DAMPING]);
+	if(damping < 0.0 || damping > 1.0){
+		fprintf(stderr,"\nreverb: absorb must be in the range 0.0 - 1.0\n");
+		exit(1);
+	}
+	double filt_limit = sr / 3.0;
+	lp_freq = atof(argv[LPFREQ]);
+	if(lp_freq < 0.0 || lp_freq > filt_limit){
+		printf("\napass: lp_freq must be within 0.0 to %.4lfHz",filt_limit);
+		usage();
+	}
+
+	trailertime = atof(argv[TRTIME]);
+	if(trailertime < 0.0)
+		trailertime = 0.0;
+	
+	if(argc==CONFIG+1){
+		int got = 0;
+		double dtime1,dtime2,dtime3,dtime4,dtime5,dtime6,
+			adtime1,adtime2,adtime3,adtime4;
+		FILE *fp = fopen(argv[CONFIG],"r");
+		if(!fp)
+			printf("\nreverb: can't open datafile %s: using presets",argv[CONFIG]);
+		else{
+			got = fscanf(fp,"%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
+				&dtime1,
+				&dtime2,
+				&dtime3,
+				&dtime4,
+				&dtime5,
+				&dtime6,
+				&adtime1,			 //allpasses
+				&adtime2,
+				&adtime3,
+				&adtime4				
+				);
+			fclose(fp);
+			if(got != 10)
+				printf("\nreverb: error reading looptime values");
+			else {
+				//create prime delay times from the msec vals
+				//get this in a loop later on...				
+				//RWD TODO: check times are not too huge!
+				mrdata.ctime1 = dtime1 * 0.001;
+				mrdata.ctime2 = dtime2 * 0.001;
+				mrdata.ctime3 = dtime3 * 0.001;
+				mrdata.ctime4 = dtime4 * 0.001;
+				mrdata.ctime5 = dtime5 * 0.001;
+				mrdata.ctime6 = dtime6 * 0.001;
+				mrdata.atime1 = adtime1 * 0.001;
+				mrdata.atime2 = adtime2 * 0.001;
+				mrdata.atime3 = adtime3 * 0.001;
+				mrdata.atime4 = adtime4 * 0.001;
+			}
+		}
+	}
+
+	//create input low/high filters, if wanted
+	if(lowcut_freq > 0.0){
+		if(highcut_freq > 0.0 && highcut_freq <= lowcut_freq){
+			fprintf(stderr,"\nreverb: Highcut frequency	 must be higher than Lowcut frequency");
+			usage();
+		}
+		//lowcut is based on low shelving eq filter; here fixed at 12dB
+		tc_l_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
+		if(!tc_l_lowcut->create()){
+			fprintf(stderr,"\nreverb: unable to create Lowcut filter");
+			exit(1);
+		}
+		if(chans==2){
+			tc_r_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
+			if(!tc_r_lowcut->create()){
+				fprintf(stderr,"\nreverb: unable to create Lowcut filter");
+				exit(1);
+			}
+		}
+	}
+
+	if(highcut_freq > 0.0){		
+		l_highcut = new onepole(highcut_freq,sr,LOW_PASS);
+		if(chans==2)
+			r_highcut = new onepole(highcut_freq,sr,LOW_PASS);
+	}
+
+	if(lp_freq > 0.0)
+		lp		= new onepole(lp_freq,sr,LOW_PASS);
+
+
+	moorerverb =  new moorer(&mrdata,rvbtime,damping,sr);
+	//further external allpasses will be used to create multi-channel diffusion
+	
+	for(i = 0; i < outchans; i++){
+		chan_ap[i] = new allpassfilter(sr,to_prime(0.005 + 0.00025 * (double)i,sr),0.4 ,0);
+		if(chan_ap[i] == 0) {
+			fprintf(stderr,"\nreverb: no memory for output diffusers");
+			exit(1);
+		}
+		if(!chan_ap[i]->create()){
+			fprintf(stderr,"\nreverb: no memory for output diffusers");
+			exit(1);
+		}
+	}
+	if(usertaps){
+#ifdef _DEBUG
+		assert(fp_earlies);
+#endif
+		ntaps = readtaps(fp_earlies,&ptaps,sr);
+		if(ntaps==0){
+			fprintf(stderr,"\nreverb: error reading tapfile");
+			exit(1);
+		}
+		printf("\nloaded %ld early reflections from tapfile",ntaps);
+		fclose(fp_earlies);
+		fp_earlies = 0;
+	}
+	else {
+		if(rvbtime <= 0.6){
+			ntaps = sizeof(smalltaps) / sizeof(deltap);
+			ptaps = smalltaps;
+			printf("\nsmall-room: using %ld early reflections",ntaps);
+		}
+		else if(rvbtime > 0.6 && rvbtime <= 1.3){
+			ntaps = sizeof(mediumtaps) / sizeof(deltap);
+			ptaps = mediumtaps;
+			printf("\nmedium-room: using %ld early reflections",ntaps);
+		}
+		else{
+			ntaps = sizeof(largetaps) / sizeof(deltap);
+			ptaps = largetaps;
+			printf("\nlarge-room: using %ld early reflections",ntaps);
+		}
+	}
+	tdelay = new tapdelay(ptaps,ntaps,sr);
+
+	//if user prescribes a predelay, adjust all taptimes
+	if(predelay > 0.0){
+		double current_delay = ptaps[0].pos;
+		double adjust = predelay - current_delay;
+		for(int i = 0; i < ntaps; i++)
+			ptaps[i].pos += adjust;
+	}
+	
+	if(!moorerverb->create()){
+		fprintf(stderr,"\nreverb: unable to create reverberator");
+		exit(1);
+	}
+	if(!tdelay->create()){
+		fprintf(stderr,"\nreverb: unable to create early reflections");
+		exit(1);
+	}
+	
+	//double maxgain = tdelay->get_maxgain();
+	//if(maxgain > 1.0)
+	//	tdelay->set_gain(maxgain * early_gain);
+	printf("\npredelay = %.4lf secs",ptaps[0].pos);
+	//wet_gain *= floor(maxgain);
+	if(want_floats)
+		props.samptype =FLOAT32;
+	props.chans = outchans;
+
+	if((ofd = sndcreat_ex(argv[OUTFILE],-1,&props,SFILE_CDP,CDP_CREATE_NORMAL)) < 0) {
+		  fprintf(stderr,"\nreverb: cannot open output file %s\n",argv[OUTFILE]);
+		  exit(1);
+	}
+
+
+	printf("\n");
+	long outsamps = 0;
+	long step = (long) (0.25 * sr);
+#ifdef NOTDEF
+	// vcomb test
+	long seed = lfo1.init((double)inprops.srate,0.0,seed);
+	lfo1.set_WaveType(LFO_RANDOM);
+	lfo1.set_freq(0.5);
+	lfo1.set_mod(1.0); /* no change - we control modf range externally */
+	lfo2.init((double)inprops.srate,0.0,seed);
+	lfo2.set_WaveType(LFO_RANDOM);
+	lfo2.set_freq(0.55);
+	lfo2.set_mod(1.0);
+	lfo3.init((double)inprops.srate,0.0,seed);
+	lfo3.set_WaveType(LFO_RANDOM);
+	lfo3.set_freq(0.61);
+	lfo3.set_mod(1.0);
+	lfo4.init((double)inprops.srate,0.0,seed);
+	lfo4.set_WaveType(LFO_RANDOM);
+	lfo4.set_freq(0.66);
+	lfo4.set_mod(1.0);
+	vcomb.init(double)srate,0.8);
+	double delay1,delay2,delay3,delay4;
+	double delaymod = 0.05;
+	delay1 = 0.05;
+	delay2 = 0.056;
+	delay3 = 0,061;
+	delay4 = 0.068;
+	vcomb.init(double)srate,delay4 + (delay4 * delaymod));
+	vcomb.setfgains(
+#endif
+	//stopwatch(1);
+	//run main single-sample loop
+	float l_ip,r_ip,l_out,r_out,l_direct,r_direct,mono_direct;
+	for(;;){
+		int rv;
+		float out,earlies;
+		//read mon/left channnel
+		if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){
+			fprintf(stderr,"\nreverb: error reading infile"); 
+			exit(1);
+		}
+		if(!rv) break;	   // end of inputfile	- handle any trailertime
+		//apply any conditioning to direct signal
+		if(tc_l_lowcut)
+			l_ip = (float) tc_l_lowcut->tick((double)l_ip);
+		if(l_highcut)
+			l_ip = (float) l_highcut->tick((double)l_ip);
+		l_direct = l_ip;
+		mono_direct = l_direct;
+		r_direct = l_direct;
+		//handle R channnel if active
+		if(chans==2){
+			if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){
+				fprintf(stderr,"\nError in reading file"); 
+				exit(1);			
+			}		
+			if(!rv) break;					
+			//apply any conditioning to direct signal
+			if(tc_r_lowcut)
+				r_ip = (float) tc_r_lowcut->tick((double)r_ip);
+			if(r_highcut)
+				r_ip = (float) r_highcut->tick((double)r_ip);
+			r_direct = r_ip;
+			if(want_mono)
+				mono_direct = (l_direct + r_direct) * 0.5f;			
+		}
+		//mono_direct = (mixed) input to reverberator
+		//possibly also filter it...
+		if(lp)
+			mono_direct = (float) lp->tick(mono_direct);					//input lowpass
+		earlies = tdelay->tick(mono_direct);				// ---> early reflections 	
+		//send (filtered) mono output from reverberator
+		out = earlies + moorerverb->tick(earlies + mono_direct) * diff_gain;
+		//and send thru multi_channel diffusers
+		//NB must handle first 2 chans as special cases, then loop thru the rest
+		//l_out = want_mono ? mono_direct : l_direct;
+		//l_out *= dry_gain;
+		l_out = chan_ap[0]->tick(out) * wet_gain;
+		if(want_mono)			
+			l_out += (mono_direct * dry_gain);
+		else 
+			l_out += l_direct * dry_gain;
+		
+		
+		if((float)fabs((double)l_out) > peaks[0].value) {
+			peaks[0].value = (float)fabs((double)l_out);
+			peaks[0].position = outsamps;
+		}
+		if(fputfloatEx(&l_out,ofd) < 1){
+			fprintf(stderr,"\nreverb: error writing output file"); 
+			exit(1);
+		}
+ 
+		if(outchans>=2){
+			r_out = r_direct * dry_gain;
+			r_out += chan_ap[1]->tick(out) * wet_gain;			
+			if((float)fabs((double)r_out) > peaks[1].value) {
+				peaks[1].value = (float)fabs((double)r_out);
+				peaks[1].position = outsamps;
+			}
+			if(fputfloatEx(&r_out,ofd) < 1){
+				fprintf(stderr,"\nreverb: error writing output file"); 
+				exit(1);
+			}
+		}
+
+		//now any further channels
+		for(i=2;i < outchans; i++){						
+			l_out = chan_ap[i]->tick(out) * wet_gain;		   			
+			if((float)fabs((double)l_out) > peaks[i].value) {
+				peaks[i].value = (float)fabs((double)l_out);
+				peaks[i].position = outsamps;
+			}
+			if(fputfloatEx(&out,ofd) < 1){
+				fprintf(stderr,"\nreverb: error writing output file"); 
+				exit(1);
+			}
+		}
+		outsamps++;
+		if((outsamps % step) == 0)
+			//inform(step,sr);
+            fprintf(stdout,"%.2f\r",(double)outsamps/(double)sr);
+
+	}
+	//end of infile; play out reverb tail as much as we're allowed
+	int trtime  = (int)( trailertime * sr);	
+	for(i = 0; i < 	trtime; i++){
+		float tr_l_ip,tr_r_ip = 0.0f, tr_out,tr_earlies,tr_l_direct,tr_mono_direct,tr_r_direct,
+			tr_l_out,tr_r_out;
+		tr_l_ip = 0.0;
+		if(tc_l_lowcut)
+			tr_l_ip = (float) tc_l_lowcut->tick(tr_l_ip);
+		if(l_highcut)
+			tr_l_ip = (float) l_highcut->tick(tr_l_ip);
+		tr_l_direct = tr_l_ip;
+		tr_mono_direct = tr_l_direct;
+		tr_r_direct = tr_l_direct;
+		//handle R channnel if active
+		if(chans==2){
+			tr_r_ip = 0.0;	   // inout is zero, except if using input filters
+			// get any samples from input conditioning filters
+			if(tc_r_lowcut)
+				tr_r_ip = (float) tc_r_lowcut->tick(tr_r_ip);						   
+			if(r_highcut)
+				tr_r_ip = (float) r_highcut->tick(tr_r_ip);
+			tr_r_direct = tr_r_ip;
+			if(want_mono)
+				tr_mono_direct = (tr_l_direct + tr_r_direct) *0.5f;			
+		}
+		if(lp)
+			tr_mono_direct = (float) lp->tick(tr_mono_direct);
+		tr_earlies = tdelay->tick(tr_mono_direct);
+		//send (filtered) mono output from reverberator
+		tr_out = tr_earlies +  moorerverb->tick(tr_earlies * tr_mono_direct) * diff_gain;
+		tr_l_out = chan_ap[0]->tick(tr_out) * wet_gain;
+		if(want_mono)			
+			tr_l_out += (tr_mono_direct * dry_gain);
+		else 
+			tr_l_out += tr_l_direct * dry_gain;
+		if((float)fabs((double)tr_l_out) > peaks[0].value) {
+			peaks[0].value = (float)fabs((double)tr_l_out);
+			peaks[0].position = outsamps;
+		}
+		if(fputfloatEx(&tr_l_out,ofd) < 1){
+			fprintf(stderr,"\nreverb: error writing to output file"); 
+			exit(1);
+		}
+		if(outchans>=2){
+			tr_r_out = tr_r_direct * dry_gain;
+			tr_r_out += chan_ap[1]->tick(tr_out) * wet_gain;	
+			if((float)fabs((double)tr_r_out) > peaks[1].value) {
+				peaks[1].value = (float)fabs((double)tr_r_out);
+				peaks[1].position = outsamps;
+			}
+			if(fputfloatEx(&tr_r_out,ofd) < 1){
+				fprintf(stderr,"\nreverb: error writing to output file"); 
+				exit(1);
+			}
+		}
+
+		for(int j = 2;j < outchans;j++){
+			float ch_out;
+			ch_out = chan_ap[j]->tick(tr_out)  * wet_gain;
+			if((float)fabs((double)ch_out) > peaks[j].value) {
+				peaks[j].value = (float)fabs((double)ch_out);
+				peaks[j].position = outsamps;
+			}
+			if(fputfloatEx(&ch_out,ofd) < 1){
+				fprintf(stderr,"\nreverb: error writing to output file"); 
+				exit(1);
+			}
+		}
+		outsamps++;
+		if((outsamps % step) ==0)
+			//inform(step,sr);
+            fprintf(stdout,"%.2f\r",(double)outsamps/(double)sr);
+	}
+	//stopwatch(0);
+	printf("\nPEAK data:\n");
+	for(i=0;i < outchans;i++)
+		printf("Channel %d: %.4f at frame %d: %.4lf secs\n",i+1,
+			peaks[i].value,peaks[i].position, (double) peaks[i].position / (double)props.srate);
+	sndputpeaks(ofd,outchans,peaks);
+	sndcloseEx(ifd);
+	
+	if(sndcloseEx(ofd) < 0)
+		fprintf(stderr,"\nwarning: error closing outfile");
+	delete [] peaks;
+	if(moorerverb)
+		delete moorerverb;
+	for(i=0;i < outchans;i++)
+		delete chan_ap[i];
+	delete [] chan_ap;
+	delete tdelay;
+	if(usertaps)
+		delete [] ptaps;
+	return 0;
+}
+
+
+
+void
+usage(void)
+{
+	fprintf(stderr,"\nreverb: Multi-channel reverberator\n\tVersion 1.1 Release 4 CDP 1998,1999\n");
+	fprintf(stderr,
+	"usage: reverb [flags] infile outfile rgain mix rvbtime absorb\n"
+	"                                 lpfreq trailertime [times.txt]\n"
+	"flags    : any or all of the following:\n"
+	"   -cN   : create outfile with N channels (1 <= N <= 16 : default =  2)\n"
+	"   -eFILE: read early reflections from breakpoint textfile FILE\n"
+	"   -f    : force floating-point output (default: infile format)\n"
+	"   -HN   : apply Highcut filter with cutoff freq N Hz to infile (6db/oct)\n"
+	"   -LN   : apply Lowcut filter with cutoff freq N Hz to infile	(12dB/oct)\n"
+	"   -pN   : force reverb predelay to N msecs (shifts early reflections)\n"
+	"rgain    : set level of dense reverb (0.0 <= rgain <= 1.0)\n"    
+	"mix      : dry/wet balance (source and reverb): 1.0<-- mix -->= 0.0\n"
+	"rvbtime  : reverb decay time (to -60dB) in seconds\n"
+	"absorb   : degree of hf damping to suggest air absorption: 0.0 <= absorb <= 1.0\n"
+	"lpfreq   : lowpass filter cutoff freq (Hz) applied at input to reverb\n" 
+	"           to disable either filter (absorb, lpfreq), use the value 0\n" 
+	"trtime   : trailer time added to outfile for reverb tail (secs)\n"
+	"times.txt: list of delay times (msecs) for 6 comb and 4 allpass filters\n"
+	);
+	exit(0);
+}
+
+long readtaps(FILE *fp, deltap **taps,double sr)
+{
+	vector<deltap> vtaps;
+	deltap thistap;
+	char line[256];
+	int ntaps = 0;
+	int linecount = 1;
+	bool error = false;
+	double time= 0.0, current_time = 0.0, val = 0.0;
+
+	double sample_duration = 1.0 / sr;
+	if(fp==0 || sr <= 0.0){
+		*taps = 0;
+		return 0;
+	}
+	while(fgets(line,256,fp))	{
+		int got;
+		if(line[0] == '\n' || line[0]== ';'){	//allow comment lines; this does not check for leading white-space...
+			linecount++;
+			continue;
+		}
+
+		if((got = sscanf(line,"%lf%lf",&time,&val))<2){			
+			fprintf(stderr,"\nerror in reflections file: line %d",linecount);
+			error =true;
+			break;
+		}
+		if(time < 0.0){
+			fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
+			error = true;
+			break;
+		}
+		//if (first) val = 0.0, or VERY close
+		if(time < sample_duration)	{ //non-zero time must be at least one sample on!			
+			fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero",time);
+			time = 0.0;			
+		}
+
+		if(current_time != 0.0 && time==current_time){
+			fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
+			linecount++;
+			continue;
+		}
+		if(time < current_time){
+			fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
+			error = true;
+			break;
+		}
+		current_time = time;
+		thistap.pos = time;
+		if(val <=0.0 || val > 1.0){
+			fprintf(stderr,"\nerror: bad amplitude in tapfile: line %d",linecount);
+			error = true;
+			break;
+		}
+		thistap.val = val;
+		vtaps.push_back(thistap);
+		linecount++;
+	}
+	
+	ntaps = vtaps.size();
+	if(ntaps==0){
+		fprintf(stderr,"\ntapfile contains no data!");
+		error = true;
+	}
+	if(error){
+		*taps = 0;
+		return 0;
+	}
+	*taps = new deltap[ntaps];
+	if(taps==0)
+		return 0;
+	vector<deltap>::iterator I =  vtaps.begin(); 
+	for(int i = 0; I != vtaps.end();i++, I++){
+		(*taps)[i].pos = I->pos;
+		(*taps)[i].val = I->val;
+	}
+
+	return ntaps;
+}

+ 1488 - 0
dev/externals/reverb/reverberator.cpp

@@ -0,0 +1,1488 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+// reverberator.cpp: implementation of the reverberator class.
+// see also tapdelay.cpp
+//TODO: simple delay for use with tapped delayline
+// NB blame VC6 for horrible code, new returns null...
+//////////////////////////////////////////////////////////////////////
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include "reverberator.h"
+#ifndef PI
+# define PI 3.141592653589793238462643
+#endif
+#ifndef TWOPI
+#define TWOPI (2.0 * PI)
+#endif
+
+#define ROOT2O2 (0.7071067811965475244)
+#define SPN 1.65436e-24			   //RWD: use a proper MSVC version...?
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+//for example: 
+//const deltap taps[4] = {{0.1,0.9},{0.25,.75},{0.5,.5},{0.618,.25}};
+//NB this accepts a zero taptime
+
+tapdelay::tapdelay(const deltap *taps,unsigned int ctaps,double sr) : dtaps(taps)
+{
+	tapbuf = 0;
+	ntaps = ctaps;
+	buflen = 0;
+	reader = 0;
+	tapptr = 0;
+	tapcoeff = 0;
+	srate = sr;
+	output_gain = 1.0;
+	max_gain = 1.0;
+}
+#define SAFETY (2)
+bool tapdelay::create(void)
+{
+#ifdef _DEBUG
+	assert(tapbuf==0);
+	assert(ntaps > 0);
+#endif
+	if(tapbuf)
+		return false;				//or some informative errorval...
+	if(ntaps <=0)
+		return false;
+	buflen = (unsigned int)((dtaps[ntaps-1].pos * srate) + SAFETY);
+	tapbuf = new double[buflen];
+	if(!tapbuf)
+		return false;
+	memset(tapbuf,0,buflen*sizeof(double));
+	
+	tapptr = new unsigned int[ntaps];
+	if(!tapptr){
+		delete [] tapbuf; tapbuf = 0;
+		return false;
+	}
+	tapcoeff = new double[ntaps];	
+	if(!tapcoeff) {
+		delete [] tapbuf; tapbuf = 0;
+		delete [] tapptr; tapptr = 0;
+		return false;
+	}
+	//copy taps data, and get scalefac from total amps
+	double sum = 0.0;
+	for(unsigned int i = 0;i < ntaps;i++){
+		//RWD.10.98 allow zero taptime, for input mix control...
+		if(dtaps[i].pos == 0.0)
+			tapptr[i] = 0;
+		else
+			tapptr[i] = buflen - (int)(dtaps[i].pos * srate);	
+			
+		tapcoeff[i] = dtaps[i].val;
+		sum += fabs(tapcoeff[i]);
+#ifdef _DEBUG
+		assert(tapptr[i] < buflen);
+#endif
+	}
+	//some idiot might give us empty taps! */
+	if(sum == 0.0){
+		delete [] tapbuf; tapbuf = 0;
+		delete [] tapptr; tapptr = 0;
+		delete [] tapcoeff; tapcoeff = 0;
+		return false;
+	}
+	max_gain = (double)ntaps / sum;
+	one_over_ntaps = 1.0 / (double)ntaps;
+	return true;
+}
+
+double tapdelay::get_maxgain(void)
+{
+	return max_gain;
+}
+
+void tapdelay::set_gain(double gain)
+{
+	output_gain = gain;
+}
+
+double tapdelay::tick(double insam)
+{
+	double val = 0.0;
+	tapbuf[reader++] = insam;
+	if(reader == buflen)
+		reader = 0;
+	for(unsigned int i = 0;i < ntaps;i++) {
+		val += 	((tapbuf[tapptr[i]++]) * (tapcoeff[i]));
+		if(tapptr[i] == buflen)
+			tapptr[i] = 0;
+	}
+	val *= one_over_ntaps;
+	return val * output_gain;
+
+}
+
+tapdelay::~tapdelay()
+{
+	delete [] tapbuf;
+	delete [] tapptr;
+	delete [] tapcoeff;
+}
+
+
+delay::delay(unsigned int len, double gain)
+{
+	delbuf= 0;
+	dgain = gain;
+	ptr = 0;
+	buflen = len;
+}
+
+delay::~delay()
+{
+	delete delbuf;
+}
+
+bool delay::create(void)
+{
+	 if(buflen <= 0)
+		 return false;
+	 if(delbuf)
+		 return false;
+
+	 delbuf = new double[buflen];
+	 if(!delbuf) {		
+		 return false;
+	 }
+	 memset(delbuf,0,buflen*sizeof(double));
+	 return true;
+}
+
+double delay::tick(double insam)
+{
+	double val;
+	val = delbuf[ptr];
+	delbuf[ptr++] = insam;
+	if(ptr==buflen)
+		ptr = 0;
+	return  val*dgain;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////
+// allpassfilter 
+//////////////////////////////////////////////////////////////////////
+//global helper functions: really belong in a new cdplib...except it's nice to have proper bool retval
+//straight from Csound: nreverb (vdelay.c)
+static bool prime( int val )
+{
+    int i, last;
+	
+    last = (int)sqrt( (double)val );
+    for ( i = 3; i <= last; i+=2 ) {
+      if ( (val % i) == 0 ) return false;
+    }
+    return true;
+}
+
+unsigned int to_prime(double dur,double sr)
+{
+		unsigned int time = (unsigned int) (dur * sr);
+		if(time % 2== 0) time += 1;
+		while(!prime(time)) 
+			time +=2;
+		return time;
+}
+#define VMODMAX (0.5)
+//standard allpass, with optional pre_delay
+allpassfilter::allpassfilter(long sr,unsigned int buflen, double ingain,unsigned int predelay = 0)
+{
+    vmod                = 0.1;
+    vfreq               = 0.5; 
+	rvbuf1				= 0;
+	pre_dline			= 0;
+	writer1 = reader1	= 0;
+    sinelfo             = 0;
+    noiselfo            = 0;
+    srate               = sr;
+	rvblen				=  buflen + (unsigned int)(buflen * VMODMAX);
+	gain				= ingain;
+	pre_delay			= predelay;
+    
+}
+
+bool allpassfilter::create(void)
+{
+	if((gain < -1.0) || (gain > 1.0))		//do I want negative gain vals?
+		return false;
+	rvbuf1 = new double[rvblen];	
+	if(!rvbuf1 ){
+#ifdef _DEBUG
+		printf("\nallpass: failed to allocate buffer size %d",rvblen);
+#endif		
+		return false;
+	}
+				
+	if(pre_delay > 0){
+		pre_dline = new delay(pre_delay,1.0);
+		if(!pre_dline->create()){
+#ifdef _DEBUG
+		    printf("\nallpass: can't create pre_dline" );
+#endif		
+		    delete []rvbuf1; rvbuf1 = 0; 
+		    return false;
+	    }
+	}
+	memset(rvbuf1,0,rvblen*sizeof(double));
+    sinelfo = new fastlfo();
+    sinelfo->init((double) srate,0.0,0,1);
+    sinelfo->set_WaveType(LFO_SINE);
+    sinelfo->set_freq(vfreq);
+    sinelfo->set_mod(1.0);
+    noiselfo = new fastlfo();
+    noiselfo->init((double) srate,0.0,0,1);
+    noiselfo->set_WaveType(LFO_RAND_GAUSS);
+    noiselfo->set_freq(vfreq * 2.5);
+    noiselfo->set_mod(1.0);
+
+	return true;
+}
+
+void allpassfilter::set_vmod(double amount)
+{
+    double numod = VMODMAX;
+    if(amount < numod)
+        numod = amount;
+    vmod = numod;
+}
+
+void allpassfilter::set_vfreq(double freq)
+{
+    vfreq = freq;
+    sinelfo->set_freq(vfreq);
+    noiselfo->set_freq(vfreq * 2.5);
+}
+
+/* TODO: optimize this:  separate  funcs for pre-dline and plain */
+double allpassfilter::tick(double insamp)
+{
+	double output, input; 
+	input = insamp;
+	output = (-gain) * input;
+	output += rvbuf1[reader1++];
+	input += gain * output;
+
+	if(pre_dline)
+		rvbuf1[writer1++] = pre_dline->tick(input);
+	else
+		rvbuf1[writer1++] = input;
+	if(reader1 == rvblen)
+		reader1 = 0;
+	if(writer1 == rvblen)
+		writer1 = 0;
+
+	return output;
+}
+/* TODO: implement this! */
+double allpassfilter::vtick(double insamp)
+{
+	double output, input; 
+	input = insamp;
+	output = (-gain) * input;
+	output += rvbuf1[reader1++];
+	input += gain * output;
+
+	if(pre_dline)
+		rvbuf1[writer1++] = pre_dline->tick(input);
+	else
+		rvbuf1[writer1++] = input;
+	if(reader1 == rvblen)
+		reader1 = 0;
+	if(writer1 == rvblen)
+		writer1 = 0;
+
+	return output;
+}
+
+
+
+allpassfilter::~allpassfilter()
+{
+	delete [] rvbuf1;
+	delete pre_dline;
+}
+
+//////////////////////////////////////////////////////////////////////
+//// nested_allpass
+//overall delay = ap1.pre_delay + ap2.pre_deay + ap1.length + ap2.length + post_delay
+nested_allpass::nested_allpass(double srate,double lpfreq, unsigned int outertime, unsigned int time1, unsigned int time2,
+							   double gain, double gain1, double gain2, 
+							   unsigned int delay1 = 0, unsigned int delay2 = 0)
+							   
+{
+	outer_gain		= gain;
+	outer_time		= outertime;
+	ap1_gain		= gain1;
+	ap2_gain		= gain2;	
+	ap1_length		= time1;
+	ap2_length		= time2;
+	ap1_delay		= delay1;
+	ap2_delay		= delay2;
+	buf				= 0;
+	if(ap2_gain == 0.0)
+		ap2_length = 0;
+	ap1				= ap2	= 0;		
+	writer = reader = 0;	
+	lp = 0;
+	lpfreq_ = lpfreq;
+	sr_ = srate;
+
+}
+
+nested_allpass::~nested_allpass()
+{	
+	delete [] buf;
+	delete ap1;
+	delete ap2;
+	//RWD
+	delete lp;
+}
+
+bool nested_allpass::create(void)
+{
+	if(outer_gain < -1.0 || outer_gain > 1.0)
+		return false;	
+	if(outer_time <= 0)
+		return false;
+	buf = new double[outer_time];
+	if(!buf)
+		return false;
+	memset(buf,0,outer_time * sizeof(double));
+	if(ap1_length > 0 && ap1_gain > 0.0){
+		ap1 = new allpassfilter((long)sr_,ap1_length,ap1_gain,ap1_delay);
+		if(!ap1->create()){
+			delete [] buf; buf = 0;
+			return false;
+		}
+	}
+	
+	if(ap2_length >0 && ap2_gain > 0.0){
+		ap2 = new allpassfilter((long)sr_,ap2_length,ap2_gain,ap2_delay);
+		if(!ap2->create()){
+			delete ap1;	 ap1 = 0;
+			delete [] buf;  buf = 0;
+			return false;
+		}
+	}
+	// RWD experiment: internal absorption damping
+	lp = new onepole(lpfreq_,sr_,LOW_PASS);
+	if(!lp){
+		delete ap1;	 ap1 = 0;
+		delete ap2;	 ap2 = 0;
+		delete [] buf;  buf = 0;
+		return false;
+	}
+	damping_ = false;
+	return true;
+}
+
+//messy casts - sort this out later...
+double nested_allpass::tick(double insam)
+{
+
+	double output, input,nest_out; 
+	input = insam;
+	output = (-outer_gain) * input;
+	output += buf[reader++];
+	input += outer_gain * output;
+	//input is to two allpasses, and the post_delay, which acts as the overall allpass
+	if(!ap2) {	//no second allpass, may be one or no first allpass
+		if(ap1)			
+			nest_out =  ap1->tick(input);
+		else
+			//behave as normal allpass
+			nest_out =  input;
+	}
+	else		
+		nest_out = 	 ap2->tick(ap1->tick(input));
+
+	//RWD experiment!
+	if(damping_)
+	   buf[writer++] = lp->tick(nest_out);
+	else
+		buf[writer++] = nest_out;
+	//wrap indices
+	if(writer == outer_time)
+		writer = 0;
+	if(reader == outer_time)
+		reader = 0;
+			
+	return output;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// lpcomb 
+//////////////////////////////////////////////////////////////////////
+
+lpcomb::lpcomb(unsigned int buflen, double fbgain, double lpcoeff, double pre = 1.0)
+{
+	combbuf = 0;
+	lp = 0;
+	gain = fbgain;
+	lpgain = lpcoeff;
+	rvblen = buflen;
+	prescale  = pre;
+	reader1 = writer1 = 0;
+}
+
+bool lpcomb::create(void)
+{
+	if(prescale <= 0.0) 
+		return false;
+	if((gain < -1.0 ) || (gain > 1.0))		//do I want negative gain vals?
+		return false;
+
+	combbuf = new double[rvblen];
+	if(!combbuf)
+		return false;
+	lp = new lowpass1(lpgain);
+	if(!lp){
+		delete [] combbuf; combbuf = 0;
+		return false;
+	}
+	memset(combbuf,0,rvblen*sizeof(double));
+	return true;
+}
+
+ //wet output only, unscaled
+double lpcomb::tick(double insam)
+{
+	double input,output,lpsig;
+	input = insam * prescale;
+	
+	output = combbuf[reader1++];		 //do gain scaling outside
+	lpsig = lp->tick(output);
+
+	combbuf[writer1++] = input + (gain * lpsig) ;	//feedback scaled output
+	
+
+	if(writer1 == rvblen)
+		writer1 = 0;
+	if(reader1 == rvblen)
+		reader1 = 0;
+	return  output;
+}
+
+lpcomb::~lpcomb()
+{
+	delete [] combbuf;
+	delete lp;
+}
+ 
+ //////////////////////////////////////////////////////////////////////
+// lowpass 	: perhaps a bit ott as a class, but it allows fairly independent fiddling
+//////////////////////////////////////////////////////////////////////
+lowpass1::lowpass1(double filtgain)
+{
+	temp = output = 0.0;
+	gain = filtgain;
+}
+
+lowpass1::~lowpass1()
+{
+}
+ //IIR lowpass
+inline double lowpass1::tick(double insam)
+{	 
+	output  = temp;
+	temp = insam + (output * gain);
+	return output;
+
+}
+// NB allows zero frequency: straight pass-through
+onepole::onepole(double freq,double srate, int low_high)
+{
+	if(freq == 0.0){
+		a1 = 1.0;
+		a2 = 0.0;
+	}
+	else {
+		b = 2.0 - cos(TWOPI * (freq/srate));
+		a2 = sqrt((b*b) - 1.0) - b;
+		a1 = 1.0 + a2;
+		if(low_high==HIGH_PASS)
+			a2 = -a2;
+	}
+	lastout = 0.0;
+}
+
+onepole::~onepole()
+{
+	 //nothing to do
+}
+
+double onepole::tick(double input)
+{
+	double output;
+	output  =  a1*input - a2*lastout;
+	lastout =  output;
+	return output;
+}
+
+//uses code from CDP eq.c
+
+tonecontrol::tonecontrol(double frq,double dbfac,int type,double srate)
+{
+	freq = frq;
+	boost = dbfac;
+	sr = srate;
+	tc_type = type;
+	x1 = x2 = y1 = y2 = 0.0;
+	a0 = a1 = a2 = b1 = b2 = 0.0;
+}
+
+tonecontrol::~tonecontrol()
+{
+   //nothing to do;
+}
+
+bool tonecontrol::create(void)
+{
+	if(sr <= 0.0)
+		return false;
+	if(freq <= 0.0)
+		return false;
+	//normalize freq
+	freq /= sr;
+	switch(tc_type){
+	case(LOW_SHELVE):
+		lshelve();
+		break;
+	case(HIGH_SHELVE):
+		hshelve();
+		break;
+	default:
+		return false;
+		break;
+	}
+	return true;
+}
+
+double tonecontrol::tick(double input)
+{
+	double out;
+
+	if(boost== 0.0)
+		return input;
+
+	out = ((a0 * input) + (a1 * x1) + (a2 * x2) - (b1 * y1) - (b2 * y2));
+	x2 = x1;
+	x1 = input;
+	y2 = y1;
+	y1 = out;
+	return out;
+}
+
+void tonecontrol::lshelve(void)
+{
+	double a, A, F, tmp, b0, recipb0, asq, F2, gamma2, siggam2, gam2p1;
+	double gamman, gammad, ta0, ta1, ta2, tb0, tb1, tb2, aa1, ab1;
+
+	a = tan(PI * (freq - 0.25));	/* Warp factor */ 
+	asq = a * a;
+	A = pow(10.0, boost/20.0);	/* Cvt dB to factor */
+	if((boost < 6.0) && (boost > -6.0)) F = sqrt(A);
+	else if (A > 1.0) F = A/sqrt(2.0);
+	else F = A * sqrt(2.0);
+	  /* If |boost/cut| < 6dB, then doesn't make sense to use 3dB point.
+	     Use of root makes bandedge at half the boost/cut amount
+	  */
+
+	F2 = F * F;
+	tmp = A * A - F2;
+	if(fabs(tmp) <= SPN) gammad = 1;
+	else gammad = pow( (F2 - 1)/ tmp, 0.25);	/* Fourth root */
+	gamman = sqrt(A) * gammad;
+
+   /* Once for the numerator */
+
+	gamma2 = gamman * gamman;
+	gam2p1 = 1 + gamma2;
+	siggam2 = 2 * ROOT2O2 * gamman;
+	
+	ta0 = gam2p1 + siggam2;
+	ta1 = -2 * (1 - gamma2);
+	ta2 = gam2p1 - siggam2;
+
+   /* And again for the denominator */
+
+	gamma2 = gammad * gammad;
+	gam2p1 = 1 + gamma2;
+	siggam2 = 2 * ROOT2O2 * gammad;
+	
+	tb0 = gam2p1 + siggam2;
+	tb1 = -2 * (1 - gamma2);
+	tb2 = gam2p1 - siggam2;
+
+   /* Now bilinear transform to proper centre frequency */
+
+	aa1 = a * ta1;
+	a0 = ta0 + aa1 + asq * ta2;
+	a1 = 2 * a * (ta0 + ta2) + (1 + asq) * ta1;
+	a2 = asq * ta0 + aa1 + ta2;
+
+	ab1 = a * tb1;
+	b0 = tb0 + ab1 + asq * tb2;
+	b1 = 2 * a * (tb0 + tb2) + (1 + asq) * tb1;
+	b2 = asq * tb0 + ab1 + tb2;
+	
+   /* Normalise b0 to 1.0 for realisability */
+
+	recipb0 = 1 / b0;
+	a0 *= recipb0;
+	a1 *= recipb0;
+	a2 *= recipb0;
+	b1 *= recipb0;
+	b2 *= recipb0;
+
+}
+
+void tonecontrol::hshelve(void)
+{
+	double a, A, F, tmp, b0, recipb0, asq, F2, gamma2, siggam2, gam2p1;
+	double gamman, gammad, ta0, ta1, ta2, tb0, tb1, tb2, aa1, ab1;
+
+	a = tan(PI * (freq - 0.25));	/* Warp factor */ 
+	asq = a * a;
+	A = pow(10.0, boost/20.0);	/* Cvt dB to factor */
+	if(boost < 6.0 && boost > -6.0) F = sqrt(A);
+	else if (A > 1.0) F = A/sqrt(2.0);
+	else F = A * sqrt(2.0);
+	  /* If |boost/cut| < 6dB, then doesn't make sense to use 3dB point.
+	     Use of root makes bandedge at half the boost/cut amount
+	  */
+
+	F2 = F * F;
+	tmp = A * A - F2;
+	if(fabs(tmp) <= SPN) gammad = 1;
+	else gammad = pow( (F2 - 1)/ tmp, 0.25);	/* Fourth root */
+	gamman = sqrt(A) * gammad;
+
+   /* Once for the numerator */
+
+	gamma2 = gamman * gamman;
+	gam2p1 = 1 + gamma2;
+	siggam2 = 2 * ROOT2O2 * gamman;
+	
+	ta0 = gam2p1 + siggam2;
+	ta1 = -2 * (1 - gamma2);
+	ta2 = gam2p1 - siggam2;
+
+	ta1 = -ta1;
+	
+   /* And again for the denominator */
+
+	gamma2 = gammad * gammad;
+	gam2p1 = 1 + gamma2;
+	siggam2 = 2 * ROOT2O2 * gammad;
+	
+	tb0 = gam2p1 + siggam2;
+	tb1 = -2 * (1 - gamma2);
+	tb2 = gam2p1 - siggam2;
+
+	tb1 = -tb1;
+
+   /* Now bilinear transform to proper centre frequency */
+
+	aa1 = a * ta1;
+	a0 = ta0 + aa1 + asq * ta2;
+	a1 = 2 * a * (ta0 + ta2) + (1 + asq) * ta1;
+	a2 = asq * ta0 + aa1 + ta2;
+
+	ab1 = a * tb1;
+	b0 = tb0 + ab1 + asq * tb2;
+	b1 = 2 * a * (tb0 + tb2) + (1 + asq) * tb1;
+	b2 = asq * tb0 + ab1 + tb2;
+	
+   /* Normalise b0 to 1.0 for realisability */
+
+	recipb0 = 1 / b0;
+	a0 *= recipb0;
+	a1 *= recipb0;
+	a2 *= recipb0;
+	b1 *= recipb0;
+	b2 *= recipb0;
+
+}
+
+
+/***************** GARDNER DIFFUSERS ***************/
+
+// NOTE: the published Gardner designs are here modified by adding a onepole lp filter
+// in the internal outer feedback loop of each nested allpass filter. 
+// When combined with the overall feedback filter, this will of course reduce the reverb decay time.
+// The option exists to bypass this overall feedback filter, and rely solely on the internal ones.
+
+
+// NOTE: to get an infinite reverb, all feedback filters have to be bypassed by setting lp_freq to 0.0
+// otherwise, the output will still decay eventually !
+
+small_diffuser::small_diffuser(unsigned int pre_delay, 
+							   const NESTDATA *apdata1,
+							   const NESTDATA *apdata2,double fb_gain,double lp_freq,double srate){
+
+	  ap1_data = *apdata1;
+	  ap2_data = *apdata2;
+	  predelay_time = pre_delay;
+	  lpgain = fb_gain;
+	  lpfreq = lp_freq;
+	  ap1 = ap2 = 0;
+	  lp1 = 0;
+	  predelay = 0;
+	  out1 = out2 = 0.0;
+	  sr = srate;
+	  damping_ = false;
+}
+
+small_diffuser::~small_diffuser()
+{
+	delete ap1;
+	delete ap2;
+	delete predelay;	
+	delete lp1;
+}
+
+bool small_diffuser::create(void)
+{
+	if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad parameters(1)");
+#endif		
+		return false;
+	}
+	if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad parameters(2)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad parameters(3)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad parameters(4)");
+#endif		
+		return false;
+	}
+
+	if(sr <=0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad srate parameter)");
+#endif		
+		return false;
+	}
+	if(lpfreq <0.0){
+#ifdef _DEBUG
+		printf("\ndiffuser: bad freq parameter)");
+#endif		
+		return false;
+	}
+
+	ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
+							ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
+	if(!ap1->create()){
+#ifdef _DEBUG
+		printf("\ndiffuser: can't create first allpass");
+#endif		
+		return false;
+	}
+	if(ap2_data.gain1 != 0.0){	 //allow ap to eliminate second block
+		ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
+							ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
+		if(!ap2->create()){
+#ifdef _DEBUG
+			printf("\ndiffuser: can't create second allpass");
+#endif		
+			delete ap1; ap1 = 0;
+			return false;
+		}
+	}
+	if(predelay_time > 0){
+		predelay = new delay(predelay_time,1.0);
+		if(!predelay->create()){
+#ifdef _DEBUG
+			printf("\ndiffuser: can't create predelay");
+#endif		
+			delete ap1;	ap1 = 0;
+			delete ap2;	ap2 = 0;
+			return false;
+		}
+	}
+	// NB if lpfreq == 0, onepole is no-op - returns input
+	lp1 = new onepole(lpfreq,sr,LOW_PASS);
+	if(lp1 == 0){
+		delete [] predelay; predelay = 0;
+		delete ap1;	ap1 = 0;
+		delete ap2;	ap2 = 0;
+		return false;
+	}
+	ap1->set_damping(damping_);
+	ap2->set_damping(damping_);
+	return true;
+}
+
+
+double small_diffuser::tick(double insam) 
+{
+	double filter_out;	
+	double lp_in;	
+	double output;
+	double ip;
+	// may only contain one nested allpass filter:
+	// input to filter is either out1 or out2
+	if(ap2)
+		lp_in = out2;	   //= both in series
+	else
+		lp_in = out1;
+	// RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and ...
+	filter_out =  lp1->tick(lp_in);
+	// ... just use this				 
+	//	filter_out = lp_in ;
+	
+	ip = insam + lpgain * filter_out;	
+	if(predelay)
+		ip = predelay->tick(ip);
+	out1 = ap1->tick( ip);
+	if(ap2){
+		out2 = ap2->tick(out1);
+		output =  (out1 + out2)  * 0.5;
+	}
+	else
+		output =  out1;
+	return  output;
+}
+
+//post-delay almost certainly doe not need to be prime...
+medium_diffuser::medium_diffuser(double post_delay,const NESTDATA *apdata1,
+					const NESTDATA *apdata2,
+					double gain,
+					double lp_freq,
+					double srate)
+{
+	ap1_data = *apdata1;
+	ap2_data = *apdata2;
+	postdelay_time = to_prime(post_delay,srate);
+	md_gain = gain;
+	lpfreq = lp_freq;
+	ap1 = ap2 = 0;
+	ap3 = 0;
+	lp1 = 0;
+	delay1 = delay2 = postdelay = 0;
+	sr = srate;
+	out1 = out2 =out3 = 0.0;
+	damping_  = false;
+}
+
+medium_diffuser::~medium_diffuser()
+{	
+		delete ap1;	
+		delete ap2;	
+		delete ap3;	
+		delete lp1;	
+		delete delay1;	
+		delete delay2;
+}
+
+
+bool medium_diffuser::create(void)
+{
+	if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
+#ifdef _DEBUG
+		printf("\nmedium_diffuser: bad parameters(1)");
+#endif		
+		return false;
+	}
+	if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(2)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(3)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(4)");
+#endif		
+		return false;
+	}
+	if(sr <=0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad srate parameter)");
+#endif		
+		return false;
+	}
+	if(lpfreq <0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad freq parameter)");
+#endif		
+		return false;
+	}
+
+	ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
+							ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
+	if(!ap1->create()){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: can't create first diffuser");
+#endif		
+		return false;
+	}
+	ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
+							ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
+	if(!ap2->create()){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: can't create second diffuser");
+#endif	
+		delete ap1; ap1 = 0;
+		return false;
+	}
+
+	ap3 = new allpassfilter((long)sr,to_prime(0.03,sr),0.5,to_prime(0.005,sr));
+	if(!ap3->create()){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: can't create internal allpass filter");
+#endif
+		delete ap1; ap1 = 0;
+		delete ap2; ap2 = 0;
+		return false;
+	}
+
+
+	if(postdelay_time > 0){
+		postdelay = new delay(postdelay_time,1.0);
+		if(!postdelay->create()){
+#ifdef _DEBUG
+			printf("\nmedium diffuser: can't create postdelay");
+#endif		
+
+			delete ap1; ap1 = 0;
+			delete ap2; ap2 = 0;
+			delete ap3; ap3 = 0;
+			return false;
+		}
+	}
+
+	//internal fixed delays
+	delay1 = new delay(to_prime(/*0.007*/0.067,sr),1.0);
+	if(!delay1->create()){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: can't create internal delay1");
+#endif
+		delete ap1; ap1 = 0;
+		delete ap2; ap2 = 0;
+		delete ap3; ap3 = 0;
+		delete [] postdelay; postdelay = 0;
+		return false;
+	}
+		delay2 = new delay(to_prime(0.015,sr),1.0);
+	if(!delay2->create()){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: can't create internal delay2");
+#endif
+		delete ap1; ap1 = 0;
+		delete ap2; ap2 = 0;
+		delete ap3; ap3 = 0;
+		delete [] postdelay; postdelay = 0;
+		delete delay1; delay1 = 0;
+		return false;
+	}
+	
+	lp1 = new onepole(lpfreq,sr,LOW_PASS);
+	if(lp1 == 0){
+		delete ap1; ap1 = 0;
+		delete ap2; ap2 = 0;
+		delete ap3; ap3 = 0;
+		delete [] postdelay; postdelay = 0;
+		delete delay1; delay1 = 0;
+		delete delay2; delay2 = 0;
+		return false;
+	}
+	if(lpfreq == 0.0)
+		damping_ = false;
+	ap1->set_damping(damping_);
+	ap2->set_damping(damping_);
+	return true;
+}
+
+double medium_diffuser::tick(double insam)
+{
+	double filter_out;
+	double output;
+
+	//feedback from postdelay, thru lp filter
+	// RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and...
+	filter_out = md_gain * lp1->tick(postdelay->tick(out3));
+	// ... just use this
+	//filter_out = (float)(md_gain * postdelay->tick(out3));
+	
+	//first nested-allpass, takes input plus feedback
+	out1 = ap1->tick(insam + filter_out);
+	//inner allpass with predelay, followed by plain delay
+	out2 = delay1->tick(ap3->tick(out1));
+	//second nested-allpass, takes direct input
+	out3 = ap2->tick(insam + md_gain * delay2->tick(out2));
+	output = 0.5 * (out1 + out2 + out3);
+	return output;
+}
+
+
+large_diffuser::large_diffuser(const NESTDATA *apdata1,
+					const NESTDATA *apdata2,
+					double gain,
+					double lp_freq,
+					double srate)
+{
+	ap1_data = *apdata1;
+	ap2_data = *apdata2;
+	ld_gain = gain;
+	lpfreq = lp_freq;
+	ap1 = ap2 = 0;
+	ap3 = ap4 = 0;
+	lp1 = 0;
+	delay1 = delay2 = delay3 = delay4 = 0;
+	sr = srate;
+	out1 = out2 =out3 = 0.0;
+	damping_ = false;
+}
+
+large_diffuser::~large_diffuser()
+{	
+	delete ap1;	
+	delete ap2;
+	delete ap3;
+	delete ap4;
+ 	delete lp1;
+	delete delay1;
+	delete delay2;
+	delete delay3;
+	delete delay4;
+}
+
+bool large_diffuser::create(void)
+{
+	if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0){
+#ifdef _DEBUG
+		printf("\nmedium_diffuser: bad parameters(1)");
+#endif		
+		return false;
+	}
+	if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(2)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(3)");
+#endif		
+		return false;
+	}
+	if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad parameters(4)");
+#endif		
+		return false;
+	}
+	if(sr <=0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad srate parameter)");
+#endif		
+		return false;
+	}
+	if(lpfreq <0.0){
+#ifdef _DEBUG
+		printf("\nmedium diffuser: bad freq parameter)");
+#endif		
+		return false;
+	}
+	ap1 = new nested_allpass(sr,lpfreq,ap1_data.time1,ap1_data.time2,ap1_data.time3,
+							ap1_data.gain1,ap1_data.gain2,ap1_data.gain3);
+	if(!ap1->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create first nested_allpass");
+#endif		
+		return false;
+	}
+	ap2 = new nested_allpass(sr,lpfreq,ap2_data.time1,ap2_data.time2,ap2_data.time3,
+							ap2_data.gain1,ap2_data.gain2,ap2_data.gain3);
+	if(!ap2->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create second netsed_allpass");
+#endif	
+		cleanup();
+		return false;
+	}
+
+	ap3 = new allpassfilter((long) sr,to_prime(0.008,sr),0.3);
+	if(!ap3->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create first internal allpass filter");
+#endif
+		cleanup();
+		return false;
+	}
+
+	ap4 = new allpassfilter((long)sr,to_prime(0.012,sr),0.3);
+	if(!ap4->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create second internal allpass filter");
+#endif
+		cleanup();
+		return false;
+	}
+	//internal fixed delays
+	delay1 = new delay((unsigned int)(0.004 * sr),1.0);
+	if(!delay1->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create internal delay1");
+#endif
+		cleanup();
+		return false;
+	}
+		delay2 = new delay((unsigned int)(0.017 * sr),1.0);
+	if(!delay2->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create internal delay2");
+#endif
+		cleanup();
+		return false;
+	}
+	delay3 = new delay((unsigned int)(0.031 * sr),1.0);
+	if(!delay3->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create internal delay3");
+#endif
+		cleanup();
+		return false;
+	}
+	delay4 = new delay((unsigned int)(0.003 * sr),1.0);
+	if(!delay4->create()){
+#ifdef _DEBUG
+		printf("\nlarge diffuser: can't create internal delay4");
+#endif
+		cleanup();
+		return false;
+	}
+	
+	lp1 = new onepole(lpfreq,sr,LOW_PASS);
+	if(lp1 == 0){
+		cleanup();
+		return false;
+	}
+	if(lpfreq == 0.0)
+		damping_ = false;
+	
+	ap1->set_damping(damping_);
+	ap2->set_damping(damping_);
+	return true;
+}
+void  large_diffuser::cleanup()
+{
+	delete lp1; lp1 = 0;
+	delete delay4; delay4 = 0;
+	delete delay3; delay3 = 0;
+	delete delay2; delay2 = 0;
+	delete delay1; delay1 = 0;
+	delete ap4; ap4 = 0;
+	delete ap3; ap3 = 0;
+	delete ap2; ap2 = 0;
+	delete ap1; ap1 = 0;
+}
+
+// NOTE: the three output taps from the diffuser can lead to discrete slap-back echoes in the output.
+// possibly authentic, but may not suit all purposes. See rmverb.cpp: some delay lengths reduced from Gardner.
+// This is possible because delays are discrete: they do not share single delay line buffer.
+// true also of medium diffuser, but not so apparent as delays are smaller
+double large_diffuser::tick(double insam)
+{
+	double filter_out;
+	double output;
+	//feedback from lpfilter
+	// RWD NOTE: if the damping_ mechanism is preferred, this filter can be omitted and ...
+	filter_out = ld_gain * lp1->tick(out3);		
+	// ... just use this
+	//filter_out = (float)(ld_gain * out3);			  
+
+	//first tap, from two plain allpasses, and trailing delay
+	out1 = delay1->tick(ap4->tick(ap3->tick(insam + filter_out)));
+	//second tap, from first nested allpass and delays at entrance and exit
+	out2 = delay3->tick(ap1->tick(delay2->tick(out1)));
+	//third tap, from second nested-allpass with leading delay
+	out3 = ap2->tick(delay4->tick(out2));
+	//out3 also goes into lpfilter... see above
+	//prescribed scale factors from Gardner
+	output = (out1 * 0.34) + (out2 * 0.14) + (out3 * 0.14);
+	// this seems to give relatively soft reverb tail:  could try this:
+	//output = (float)((out1 * 0.7) + (out2 * 0.25) + (out3 * 0.25));
+	return  output;
+}
+
+
+moorer::moorer(const MOORERDATA *pdata,double reverbtime,double damping,double srate)
+{
+	mdata		= *pdata;
+	reverb_time = reverbtime;
+	dampfac		= damping;
+	sr			= srate;
+	comb1 = comb2 = comb3 = comb4 = comb5 = comb6 = 0;
+	ap1 = ap2 = ap3 =  ap4  = 0;
+	out = 0.0;
+}
+
+moorer::~moorer()
+{
+	delete comb1;
+	delete comb2;
+	delete comb3;
+	delete comb4;
+	delete comb5;
+	delete comb6;
+	delete ap1;
+	delete ap2;		
+	delete ap3;
+	delete ap4;
+}
+
+bool moorer::create(void)
+{
+	fgain1 = 0.42 * dampfac;
+	fgain2 = 0.46 * dampfac;
+	fgain3 = 0.48 * dampfac;
+	fgain4 = 0.49 * dampfac;
+	fgain5 = 0.50 * dampfac;
+	fgain6 = 0.52 * dampfac;
+	scalefac = (1.0 / 6.0);	
+	cgain1 = exp(log(0.001)*(mdata.ctime1/reverb_time)) * (1. - fgain1);
+	cgain2 = exp(log(0.001)*(mdata.ctime2/reverb_time)) * (1. - fgain2);
+	cgain3 = exp(log(0.001)*(mdata.ctime3/reverb_time)) * (1. - fgain3);
+	cgain4 = exp(log(0.001)*(mdata.ctime4/reverb_time)) * (1. - fgain4);
+	cgain5 = exp(log(0.001)*(mdata.ctime5/reverb_time)) * (1. - fgain5);
+	cgain6 = exp(log(0.001)*(mdata.ctime6/reverb_time)) * (1. - fgain6);
+
+	comb1 = new lpcomb(to_prime(mdata.ctime1,sr),cgain1,fgain1,1.0);
+	comb2 = new lpcomb(to_prime(mdata.ctime2,sr),cgain2,fgain2,1.0);
+	comb3 = new lpcomb(to_prime(mdata.ctime3,sr),cgain3,fgain3,1.0);
+	comb4 = new lpcomb(to_prime(mdata.ctime4,sr),cgain4,fgain4,1.0);
+	comb5 = new lpcomb(to_prime(mdata.ctime5,sr),cgain5,fgain5,1.0);
+	comb6 = new lpcomb(to_prime(mdata.ctime6,sr),cgain6,fgain6,1.0);
+	ap1		= new allpassfilter((long)sr,to_prime(mdata.atime1,sr),-0.6,0);
+	ap2		= new allpassfilter((long)sr,to_prime(mdata.atime2,sr),0.4,0);
+	ap3		= new allpassfilter((long)sr,to_prime(mdata.atime3,sr),-0.61,0);
+	ap4		= new allpassfilter((long)sr,to_prime(mdata.atime4,sr),0.39,0);
+
+	if(!
+		(comb1->create()
+		&& comb2->create()
+		&& comb3->create()
+		&& comb4->create()
+		&& comb5->create()
+		&& comb6->create()
+		&& ap1->create()
+		&& ap2->create()
+		&& ap3->create()
+		&& ap4->create()
+		)) {
+		cleanup();
+		return false;
+	}
+	return true;
+}
+
+double moorer::tick(double insam)
+{
+	out =  comb1->tick(insam) +
+		  comb2->tick(insam) +
+		  comb3->tick(insam) +
+		  comb4->tick(insam) +
+		  comb5->tick(insam) +
+		  comb6->tick(insam)
+		  ;
+	out *= scalefac;
+	out = ap4->tick(ap3->tick(ap2->tick(ap1->tick(out))));
+	return out;
+}
+
+void moorer::cleanup()
+{
+	delete comb6; comb6 = 0;
+	delete comb5; comb5 = 0;
+	delete comb4; comb4 = 0;
+	delete comb3; comb3 = 0;
+	delete comb2; comb2 = 0;
+	delete comb1; comb1 = 0;
+	delete ap4; ap4 = 0;
+	delete ap3; ap3 = 0;
+	delete ap2; ap2 = 0;
+	delete ap1; ap1 = 0;
+}
+
+
+/* vmtdelay *******************************/
+vcomb4::vcomb4()
+{
+	dl_buf = 0;
+	dl_length = 0;
+	dl_input_index = 0;
+	dl_srate = 0.0;
+	
+}
+
+vcomb4::~vcomb4()
+{
+	if(dl_buf && dl_length > 0)
+		delete [] dl_buf;
+		
+}
+
+bool vcomb4::init(long srate,double length_secs)
+{
+	unsigned long len_frames = (unsigned long)(srate * length_secs );	
+	if(len_frames == 0)
+		return false;
+	/*  round upwards to allow for interpolation */
+	len_frames++;
+	/* reject init  if already created*/
+	/* TODO: more sexy error checking/reporting... */
+	if(dl_buf) 
+		return false;
+	try {
+		dl_buf = new double[len_frames];
+	}
+	catch(...){
+		return false;
+	}
+	/* for VC 6 */
+	if(dl_buf == 0)
+		return false;
+	for(unsigned long i = 0; i < len_frames;i++)
+		dl_buf[i] = 0.0;
+
+	dl_length = len_frames;
+	dl_input_index = 0;
+	dl_srate = srate;
+	gain = 0.5;
+	return true;
+}
+
+bool vcomb4::init(const MOORERDATA *p_mdata,double reverbtime,double damping,double sr)
+{
+	if(!init((long) sr,reverbtime))
+		return false;
+    return true;
+}
+
+double vcomb4::tick(double vdelay_1,double vdelay_2,double vdelay_3,double vdelay_4,double feedback,double input)
+{
+	unsigned long base_readpos1,base_readpos2,base_readpos3,base_readpos4; 
+	unsigned long next_index1,next_index2,next_index3,next_index4; 
+	double vlength,dlength,frac;
+	double readpos1,readpos2,readpos3,readpos4;
+	double* buf = dl_buf;
+	
+	dlength = (double) dl_length;					/* get maxlen, save a cast later on */
+	/* read pointer is vlength ~behind~ write pointer */
+	/* tap1*/
+	vlength = dlength -  (vdelay_1  * dl_srate);
+	vlength = vlength < dlength ? vlength : dlength;	  /* clip vdelay to max del length */
+	readpos1 = dl_input_index + vlength;
+	base_readpos1 = (unsigned long) ( readpos1);				  
+	if(base_readpos1 >= dl_length)					  /* wrap dl indices */
+		base_readpos1 -= dl_length;
+	next_index1 = base_readpos1 + 1;
+	if(next_index1 >= dl_length)
+		next_index1 -= dl_length;
+	/*tap2*/
+	vlength = dlength -  (vdelay_2  * dl_srate);
+	vlength = vlength < dlength ? vlength : dlength;
+	readpos2 = dl_input_index + vlength;
+	base_readpos2 = (unsigned long) ( readpos2);				  
+	if(base_readpos2 >= dl_length)					  /* wrap dl indices */
+		base_readpos2 -= dl_length;
+	next_index2 = base_readpos2 + 1;
+	if(next_index2 >= dl_length)
+		next_index2 -= dl_length;
+	/*tap3*/
+	vlength = dlength -  (vdelay_3  * dl_srate);
+	vlength = vlength < dlength ? vlength : dlength;
+	readpos3 = dl_input_index + vlength;
+	base_readpos3 = (unsigned long) ( readpos3);				  
+	if(base_readpos3 >= dl_length)					  /* wrap dl indices */
+		base_readpos3 -= dl_length;
+	next_index3 = base_readpos3 + 1;
+	if(next_index3 >= dl_length)
+		next_index3 -= dl_length;
+	/* tap4 */
+	vlength = dlength -  (vdelay_4  * dl_srate);
+	vlength = vlength < dlength ? vlength : dlength;
+	readpos4 = dl_input_index + vlength;
+	base_readpos4 = (unsigned long) ( readpos4);				  
+	if(base_readpos4 >= dl_length)					  /* wrap dl indices */
+		base_readpos4 -= dl_length;
+	next_index4 = base_readpos4 + 1;
+	if(next_index4 >= dl_length)
+		next_index4 -= dl_length;
+
+
+	double outsum = 0.0;
+	fb1 *= feedback;
+	fb2 *= feedback;
+	fb3 *= feedback;
+	fb4 *= feedback;
+	/* basic interp of variable delay pos */
+	frac = readpos1 - (int) readpos1;	
+	outsum += fb1 * (buf[base_readpos1]+((buf[next_index1] - buf[base_readpos1]) * frac));
+	frac = readpos2 - (int) readpos2;	
+	outsum += fb2 * (buf[base_readpos2]+((buf[next_index2] - buf[base_readpos2]) * frac));
+	frac = readpos3 - (int) readpos3;	
+	outsum += fb3 * (buf[base_readpos3]+((buf[next_index3] - buf[base_readpos3]) * frac));
+	frac = readpos4 - (int) readpos4;	
+	outsum += fb4 * (buf[base_readpos4]+((buf[next_index4] - buf[base_readpos4]) * frac));
+	/* how do we scale all this?*/
+	input += gain * (outsum);	
+	/* add in new sample + fraction of ouput, unscaled, for minimum decay at max feedback */
+	buf[dl_input_index++] = input   + outsum * 0.25;
+	if(dl_input_index == dl_length)
+		dl_input_index = 0;
+	return  input + outsum * 0.25;
+
+}

+ 360 - 0
dev/externals/reverb/reverberator.h

@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+
+// reverberator.h: interface for the reverberator class.
+// RWD.9.98 now contains all my delay/allpass etc  ugs
+//RWD 03.20  corrected def of tick function to double everywhere
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_REVERBERATOR_H__9DE45E43_83BA_11D1_96D4_444553540000__INCLUDED_)
+#define AFX_REVERBERATOR_H__9DE45E43_83BA_11D1_96D4_444553540000__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#include "wavetable.h"
+
+enum {LOW_PASS,HIGH_PASS};
+enum {LOW_SHELVE,HIGH_SHELVE};
+
+unsigned int to_prime(double dur,double sr);
+
+
+
+typedef struct __tap
+{
+	double pos;
+	double val;
+}
+deltap;
+
+typedef struct nested_allpass_data{
+	unsigned int time1;
+	unsigned int time2;
+	unsigned int time3;
+	unsigned int delay1;
+	unsigned int delay2;
+	double gain1;
+	double gain2;
+	double gain3;
+}
+NESTDATA;
+
+typedef struct moorer_data{
+	double ctime1;
+	double ctime2;
+	double ctime3;
+	double ctime4;
+	double ctime5;
+	double ctime6;
+	double atime1;
+	double atime2;
+	double atime3;
+	double atime4;
+}
+MOORERDATA;
+//abstract base class for all processors
+
+class cdp_processor 
+{
+public:
+	cdp_processor() {}
+	virtual ~cdp_processor(){}
+	//not all processes will need to use this
+	virtual bool create() {return true;}
+//	virtual float tick(float insam)	{return insam;}
+    virtual double tick(double insam) = 0;
+};
+
+
+//could have a method create_taps(uint ntaps, float maxtime)  ... ?
+class tapdelay 
+{
+public:
+	tapdelay(const deltap *taps,unsigned int ctaps,double sr);
+	bool	create(void);
+	double  get_maxgain(void);
+	void set_gain(double gain);
+	double	tick(double insam);
+	virtual ~tapdelay();
+private:
+	double *tapbuf;
+	double srate;
+	double one_over_ntaps;
+	double max_gain;		/* get rms value of whole array */
+	double output_gain;
+	unsigned int ntaps;
+	unsigned int buflen;
+	unsigned int *tapptr;
+	double *tapcoeff;
+	unsigned int reader;
+	const deltap *dtaps;
+};
+
+//basic feedthrough delay (independent of srate) - could be base class?
+class delay
+{
+public:
+	delay(unsigned int len, double gain);
+	virtual ~delay();
+	bool create(void);
+	double tick(double insam);
+private:
+	double *delbuf;
+	unsigned int buflen;
+	double dgain;
+	unsigned int ptr;
+};
+
+
+
+class allpassfilter  
+{
+public:
+	allpassfilter(long sr,unsigned int time, double ingain,unsigned int predelay);
+	virtual ~allpassfilter();
+	bool create(void);    
+    void set_vfreq(double freq);
+    void set_vmod(double amount);
+	double tick(double insam);
+    double vtick(double insam);
+
+private:
+	double gain;
+	double *rvbuf1;			 //CDP allpass uses 2 buffers!
+    double vfreq,vmod;
+    fastlfo *sinelfo;
+    fastlfo *noiselfo;
+	unsigned int rvblen, writer1, reader1;
+	delay *pre_dline;
+	unsigned int pre_delay;
+    long srate;
+};
+
+///////////////// DOUBLE-NESTED ALLPASS
+///  set either or both gain2, gain3 to zero to disable the respective allpass
+////	
+class onepole;
+
+class  nested_allpass
+{
+public:
+	nested_allpass(double srate,double lpfreq,unsigned int outertime, 
+					unsigned int time1,
+					unsigned int time2,
+					double gain, 
+					double gain1,
+					double gain2, 
+					unsigned int delay1, 
+					unsigned int delay2);
+	virtual ~nested_allpass();
+	bool create(void);
+	double tick(double insam);
+    void  set_vfreq(double freq);
+    void set_vmod(double amount);
+    double vtick(double  insam);
+	void set_damping(bool damping) { damping_ = damping;}
+	bool get_damping() const { return damping_;}
+private:
+	
+	double srate_, outer_gain, ap1_gain,ap2_gain;
+	unsigned int ap1_length,ap2_length;
+	unsigned int outer_time,ap1_delay,ap2_delay;
+	allpassfilter *ap1, *ap2;
+	double *buf;
+	unsigned int writer,reader;
+	// RWD experimental - internal lf damping
+	onepole* lp; 
+	bool damping_;
+	double sr_,lpfreq_;
+
+};
+
+
+//TODO: comb filter (with lopass), and tapped delay
+
+//basic lowpass for combs
+
+class lowpass1
+{
+public:
+	lowpass1(double filtgain);
+	virtual ~lowpass1();
+	double tick(double insam);
+private:
+	double temp,gain,output;	
+};
+
+
+class onepole
+{
+public:
+	onepole(double freq,double srate,int low_high);
+	virtual ~onepole();
+	double tick(double input);
+private:
+	double a1,a2,b,lastout;
+};
+
+class tonecontrol
+{
+public:
+	tonecontrol(double frq,double dbfac,int type,double srate);
+	virtual ~tonecontrol();
+	bool create(void);
+	double tick(double input);
+private:
+	double freq, boost, sr,a0, a1, a2, b1, b2;
+	double x1,x2,y1,y2;
+	int tc_type;
+	void lshelve(void);
+	void hshelve(void);
+};
+
+class lpcomb
+{
+public:
+	lpcomb(unsigned int time, double ingain, double lpcoeff, double prescale);
+	virtual ~lpcomb();
+	bool create(void);
+	double tick(double insam);
+private:
+	double prescale, gain, lpgain;
+	double *combbuf;
+	unsigned int rvblen, reader1,writer1;
+	lowpass1 *lp;
+
+};
+
+
+class small_diffuser : public cdp_processor
+{
+public:
+	small_diffuser(unsigned int pre_delay,
+					const NESTDATA *apdata1,
+					const NESTDATA *apdata2,double fb_gain,double lp_freq,double srate);
+	virtual ~small_diffuser();
+	bool create(void);
+	double tick(double insam);
+	void set_damping(bool damping) { damping_ = damping; }
+private:
+	NESTDATA ap1_data,ap2_data;
+	nested_allpass *ap1,*ap2;
+	delay *predelay;
+	//lowpass1 *lp1;
+	onepole *lp1;
+	unsigned int predelay_time;
+	double lpgain,lpfreq,sr;
+	double out1,out2;
+	bool damping_;
+};
+
+//no public output from postdelay, so internal only
+class medium_diffuser : public cdp_processor
+{
+public:
+	medium_diffuser(double post_delay,const NESTDATA *apdata1,
+					const NESTDATA *apdata2,
+					double gain,
+					double lp_freq,
+					double srate);
+	virtual ~medium_diffuser();
+	bool create(void);
+	double tick(double insam);
+	void set_damping(bool damping) { damping_ = damping;	}
+private:
+	NESTDATA ap1_data,ap2_data;
+	nested_allpass *ap1,*ap2;
+	allpassfilter *ap3;				//no public access to this, for now...
+	onepole *lp1;
+	delay *delay1,*delay2,*postdelay;			
+	double md_gain,lpfreq,sr;
+	double out1,out2,out3;
+	unsigned int postdelay_time;
+	bool damping_;
+};
+
+
+class large_diffuser : public cdp_processor
+{
+public:
+	large_diffuser(const NESTDATA *apdata1,
+					const NESTDATA *apdata2,
+					double gain,
+					double lp_freq,
+					double srate);
+	virtual ~large_diffuser();
+	bool create(void);
+	double tick(double insam);
+	void set_damping(bool damping) { damping_ = damping; }
+private:
+	void cleanup();
+	NESTDATA ap1_data,ap2_data;
+	nested_allpass *ap1,*ap2;
+	allpassfilter *ap3,*ap4;				//no public access to this, for now...
+	onepole *lp1;
+	delay *delay1,*delay2,*delay3,*delay4;			
+	double ld_gain,lpfreq,sr;
+	double out1,out2,out3;
+	bool damping_;
+};
+
+class moorer : public cdp_processor
+{
+public:
+	moorer(const MOORERDATA *p_mdata,double reverbtime,double damping,double sr);
+	virtual ~moorer();
+	bool create(void);
+	double tick(double insam);
+private:
+	void cleanup();
+	lpcomb *comb1,*comb2,*comb3,*comb4,*comb5,*comb6;
+	allpassfilter *ap1,*ap2,*ap3,*ap4;
+	MOORERDATA mdata;
+	double cgain1,cgain2,cgain3,cgain4,cgain5,cgain6;
+	double fgain1,fgain2,fgain3,fgain4,fgain5,fgain6;	
+	double reverb_time,dampfac,sr;
+	double out,scalefac;
+};
+
+/* vmtdelay ****************/
+class vcomb4 
+{
+public:
+	vcomb4();
+	virtual ~vcomb4();
+	bool init(long srate,double length_secs);
+	bool init(const MOORERDATA *p_mdata,double reverbtime,double damping,double sr);
+	double tick(double vdelay_1,double vdelay_2,double vdelay_3,double vdelay_4,double feedback,double input);
+	void setfgains(double fg1,double fg2,double fg3,double fg4) {fb1 = fg1;fb2 = fg2;fb3 = fg3;fb4 = fg4;}
+private:
+	double*			dl_buf;
+	unsigned long	dl_length;
+	unsigned  long	dl_input_index;
+	double			dl_srate;
+	double			fb1,fb2,fb3,fb4;
+//	lowpass1		lp1,lp2,lp3,lp4;
+	double		gain;
+};
+
+
+#endif // !defined(AFX_REVERBERATOR_H__9DE45E43_83BA_11D1_96D4_444553540000__INCLUDED_)

+ 949 - 0
dev/externals/reverb/rmverb.cpp

@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include <algorithm>
+#include <vector>
+
+using namespace std;
+// TODO: clean up "new" handling, move to VC7 and get to use exceptions!
+enum cmdargs {
+		PROGNAME,
+		INFILE,
+		OUTFILE,
+		REVERBTYPE,
+		RGAIN,
+		MIX,
+		FEEDBACK,		
+		LPABSORB,
+		LPFREQ,
+		TRTIME,
+		CONFIG
+};
+
+enum reverb_type {
+		DIFF_SMALL,
+		DIFF_MEDIUM,
+		DIFF_LARGE
+};
+
+extern "C" {
+#include "portsf.h"
+	   //NB requires stdio.h etc - time to change this?
+}
+
+#include "reverberator.h"
+//RWD.10.98 TODO: for final versions, MUST limit input delay times! can't have 108sec delays.....
+//get all reflection data
+#include "reflect.cpp"
+
+
+long readtaps(FILE *fp, deltap **taps,double sr);		
+
+const char* cdp_version = "6.0.0";
+
+void usage(void);
+
+int
+main(int argc,char *argv[])
+
+{
+	int i,ifd, ofd,reverbtype = DIFF_SMALL;
+	FILE *fp = 0;
+	long	chans,outchans = 2;
+	double	sr,trailertime;
+	cdp_processor *cdpdiff = 0;
+	NESTDATA data1,data2;
+	PSF_CHPEAK *peaks;
+	PSF_PROPS props;
+	unsigned int time1a,time1b,time1c,time2a,time2b,time2c;
+	double feedback,fb_lpfreq, lp_freq,predelay = -1.0;
+	double nyquist,pseudo_nyquist;
+	double dry_gain,wet_gain;
+	//double early_gain = 1.0;
+	double diff_gain = 1.0;
+	// nested-allpass diffusers
+	small_diffuser *s_diffuser = 0;
+	medium_diffuser *m_diffuser = 0;
+	large_diffuser *l_diffuser = 0;
+	// traioling plain allpasses for front pair de-correlation
+	allpassfilter *ap1_l = 0,*ap2_l = 0;		
+	allpassfilter *ap1_r = 0,*ap2_r = 0;
+	// decorrelation for other channels (surround)
+	allpassfilter **chan_ap = 0;
+	onepole *lp = 0;
+	// tonecontrols max -12dB slope, variable; 
+	tonecontrol *tc_l_lowcut = 0, *tc_r_lowcut = 0;	
+	tonecontrol *l_highcut = 0, *r_highcut = 0;
+	double lowcut_freq = 0.0,highcut_freq = 0.0;
+	tapdelay *tdelay = 0;	
+	long ntaps = 0;
+	deltap *ptaps = 0;
+	bool want_mono = false;
+	bool want_floats = false;
+	bool usertaps = false;
+	bool double_damping = false;
+
+	if((argc==2) && strcmp(argv[1],"--version")==0) {
+		fprintf(stdout,"%s\n",cdp_version);
+		fflush(stdout);
+		return 0;
+	}
+	if(psf_init()){
+		fprintf(stderr,"rmverb: initialisation failed\n");
+		return 1;
+	}
+    	
+	if(argc < CONFIG){
+		if(argc > 1)
+			fprintf(stderr,"rmverb: insufficient arguments\n");
+		usage();
+	}
+	
+	while((argc > 1) && argv[1][0]=='-'){
+		char *arg = argv[1];
+		switch(*(++arg)){
+		case('p'):
+			if(*(++arg) =='\0'){
+				fprintf(stderr,"rmverb: predelay requires a parameter\n");
+				usage();
+			}
+			
+			predelay = atof(arg);
+			if(predelay <0.0){
+				fprintf(stderr,"rmverb: predelay must be >= 0.0\n");
+				usage();
+			}
+			predelay *= 0.001;			//get msecs from user
+			break;
+		case('c'):
+			if(*(++arg) == '\0') {
+				fprintf(stderr,"rmverb: -c flag requires a value\n");
+				usage();
+			}
+			outchans = atoi(arg);
+			if(outchans < 1 || outchans > 16){
+				fprintf(stderr,"rmverb: impossible channel value requested\n");
+				usage();
+			}
+			if(outchans==1)
+				want_mono = true;			
+			break;
+		case('d'):
+			double_damping = true;
+			break;
+		case('e'):
+			if(*(++arg)=='\0'){
+				fprintf(stderr,"rmverb: -e flag needs a filename\n");
+				usage();
+			}
+			if((fp = fopen(arg,"r")) ==0){
+				fprintf(stderr,"rmverb: unable to open breakpoint file %s\n",arg);
+				exit(1);
+			}
+			usertaps = true;
+			break;
+			// -f is CDP-specific: read old 32bit int format as floats
+		case('f'):
+			want_floats = true;
+			if(*(++arg) != '\0')
+				fprintf(stderr,"rmverb WARNING: -f flag does not take a parameter\n");
+			break;
+		case('L'):
+			if(*(++arg) == '\0'){
+				fprintf(stderr,"rmverb: -L flag needs frequency argument\n");
+				usage();
+			}			
+			lowcut_freq = atof(arg);
+			if(lowcut_freq <= 0.0){
+				fprintf(stderr,"rmverb: Lowcut freq must be greater than zero\n");
+				usage();
+			}
+			break;
+		case('H'):
+			if(*(++arg) == '\0'){
+				fprintf(stderr,"rmverb: -H flag needs frequency argument\n");
+				usage();
+			}			
+			highcut_freq = atof(arg);
+			if(highcut_freq <= 0.0){
+				fprintf(stderr,"rmverb: Highcut freq must be greater than zero\n");
+				usage();
+			}
+			break;
+		default:
+			fprintf(stderr,"rmverb: illegal flag option %s\n",argv[1]);
+			usage();
+			break;
+		}
+		argc--;
+		argv++;
+	}
+
+	if(argc < CONFIG){
+		printf("rmverb: insufficient arguments\n");
+		usage();
+	}
+#ifdef _DEBUG
+	if(predelay > 0.0)
+		printf("got predelay = %.4lf\n",predelay);
+#endif
+
+	chan_ap = new allpassfilter*[outchans];
+	if(chan_ap==0){
+		fprintf(stderr,"rmverb: no memory for multi-channel diffusion\n");
+		exit(1);
+	}
+
+	if((ifd = psf_sndOpen(argv[INFILE],&props,0)) < 0) {
+				fprintf(stderr,"\nrmverb: cannot open input file %s", argv[INFILE]);
+				exit(1);
+	}
+	
+	if(props.format  <= PSF_FMT_UNKNOWN || props.format > PSF_AIFC){
+		fprintf(stderr,"infile is not a recognised soundfile\n");
+		psf_sndClose(ifd);
+		return 1;
+	}
+	
+	sr = (double) props.srate;
+	nyquist = sr / 2.0;
+	pseudo_nyquist = nyquist * 0.7; // filters not reliable close to nyquist
+
+	chans = props.chans;
+	if(chans > 2){
+		fprintf(stderr,"rmverb can only accept mono or stereo files\n");
+		exit(1);
+	}
+		
+	
+	reverbtype =  atoi(argv[REVERBTYPE]) - 1;
+	if((reverbtype < DIFF_SMALL) || (reverbtype > DIFF_LARGE)){
+		fprintf(stderr,"rmverb: rmsize must be 1,2 or 3\n");
+		exit(1);
+	}
+	
+	diff_gain = atof(argv[RGAIN]);
+	if(diff_gain <= 0.0 || diff_gain > 1.0){
+		fprintf(stderr,"rgain must be > 0 and <= 1.0\n");
+		exit(1);
+	}
+
+	dry_gain =  atof(argv[MIX]);			  //global output gain from diffuser
+	if(dry_gain < 0.0 || dry_gain > 1.0 ){
+		printf("reverb: mix must be  between 0.0 and 1.0\n");
+		usage();
+	}
+	wet_gain = 1.0f - dry_gain;
+	// some odd people like to have 100% wet signal...
+	wet_gain *= 2.0;					//not v scientific, but works...
+	
+	feedback = atof(argv[FEEDBACK]);			  //feedback in diffuser
+	if(feedback < 0.0 || feedback >1.0){
+		printf("rmverb: feedback must be within 0.0 - 1.0\n");
+		usage();
+	}
+	
+	fb_lpfreq = atof(argv[LPABSORB]);			 //feedback lp-filter in diffuser
+	if(fb_lpfreq < 0.0 || fb_lpfreq > pseudo_nyquist){
+		printf("rmverb: absorb must be within 0 to %f\n",pseudo_nyquist);
+		usage();
+	}
+
+	lp_freq = atof(argv[LPFREQ]);
+	if(lp_freq < 0.0 || lp_freq > pseudo_nyquist){
+		printf("rmverb: lpfreq must be within 0.0 to %.4lfHz\n",pseudo_nyquist);
+		usage();
+	}
+	trailertime = atof(argv[TRTIME]);
+	if(trailertime < 0.0){
+		fprintf(stderr,"rmverb: trtime must be >= 0.0\n");
+		usage();
+	}
+	//from -m flag; will override a stereo input too
+	if(want_mono)
+		outchans =  1;
+	// NB: now specifying two trailing allpasses for diffuser
+	// handles front pair; currently using chan_ap[] only for remaining channels
+	// values are experimental!
+	double apdelay = 0.02;
+	double apfac = 0.02 / outchans;
+	for(i = 0; i < outchans; i++){						// or use a random modifier?
+		chan_ap[i] =  new allpassfilter(sr,to_prime(apdelay - apfac * (double)i,sr),0.4,0);
+		if(chan_ap[i] ==0){
+			fprintf(stderr,"rmverb: no memory for output diffusers\n");
+			exit(1);
+		}
+		if(!chan_ap[i]->create()){
+			fprintf(stderr,"rmverb: no memory for output diffusers\n");
+			exit(1);
+		}
+	}
+    // custom early reflections from time/value text-file
+	if(usertaps){
+#ifdef _DEBUG
+		assert(fp);
+#endif
+		ntaps = readtaps(fp,&ptaps,sr);
+		if(ntaps==0){
+			fprintf(stderr,"rmverb: error reading tapfile\n");
+			exit(1);
+		}
+		printf("rmverb: loaded %ld early reflections from tapfile\n",ntaps);
+		fclose(fp);
+		fp = 0;
+	}
+
+
+	//create input low/high filters, if wanted
+	if(lowcut_freq > 0.0){
+		if(highcut_freq > 0.0 && highcut_freq <= lowcut_freq){
+			fprintf(stderr,"rmverb: Highcut frequency	 must be higher than Lowcut frequency\n");
+			usage();
+		}
+		//lowcut is based on low shelving eq filter; here fixed at 12dB
+		// but can easily add this as user param
+		tc_l_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
+		if(!tc_l_lowcut->create()){
+			fprintf(stderr,"rmverb: unable to create Lowcut filter\n");
+			exit(1);
+		}
+		if(chans==2){
+			tc_r_lowcut = new tonecontrol(lowcut_freq,-12.0,LOW_SHELVE,sr);
+			if(!tc_r_lowcut->create()){
+				fprintf(stderr,"rmverb: unable to create Lowcut filter\n");
+				exit(1);
+			}
+		}
+	}
+
+	if(highcut_freq > 0.0){		
+		l_highcut = new tonecontrol(highcut_freq,-12.0,HIGH_SHELVE,sr);
+		if(!l_highcut->create()){
+			fprintf(stderr,"rmverb: unable to create Highcut filter\n");
+				exit(1);
+		}
+		if(chans==2){
+			r_highcut = new tonecontrol(highcut_freq,-12.0,HIGH_SHELVE,sr);
+			if(!r_highcut->create()){
+				fprintf(stderr,"\nrmverb: unable to create Highcut filter");
+				exit(1);
+			}
+		}
+	}
+
+	//now create diffuser
+
+	switch(reverbtype){
+		case(DIFF_SMALL):
+			time1a = to_prime(0.0047, sr);
+			time1b = to_prime(0.022, sr);
+			time1c = to_prime(0.0083,sr);
+
+			time2a = to_prime(0.036,sr);
+			time2b = to_prime(0.03,sr);
+			time2c = 0;//dummy
+			data1.time1 = time1a;
+			data1.time2 = time1b;
+			data1.time3 = time1c;
+			data1.gain1 = 0.3;
+			data1.gain2 = 0.4;
+			data1.gain3 = 0.6;
+			data1.delay1 = 0;
+			data1.delay2 = 0;
+			data2.time1 = time2a;
+			data2.time2 = time2b;
+			data2.time3 = time2c;
+			data2.gain1 = 0.1;
+			data2.gain2 = 0.4;
+			data2.gain3 = 0.0;			//not used by gardner
+			data2.delay1 = 0;
+			data2.delay2 = 0;
+
+
+			break;
+		case(DIFF_MEDIUM):
+			time1a = to_prime(/*0.0047*/0.035, sr);
+			time1b = to_prime(0.0083, sr);
+			time1c = to_prime(0.022,sr);
+
+			time2a = to_prime(/*0.0292*/0.039,sr);
+			time2b = to_prime(0.0098,sr);
+			time2c = 0;//dummy
+			data1.time1 = time1a;
+			data1.time2 = time1b;
+			data1.time3 = time1c;
+			data1.gain1 = 0.3;
+			data1.gain2 = 0.7;
+			data1.gain3 = 0.5;
+			data1.delay1 = 0;
+			data1.delay2 = 0;
+			data2.time1 = time2a;
+			data2.time2 = time2b;
+			data2.time3 = time2c;
+			data2.gain1 = 0.3;
+			data2.gain2 = 0.6;
+			data2.gain3 = 0.0;			//not used by gardner
+			data2.delay1 = 0;
+			data2.delay2 = 0;
+
+
+
+			break;
+		case(DIFF_LARGE):
+			// VERY LARGE!
+			// NB: orig outer delays from Gardner generate very strong discrete echoes,
+			// and hence also some pulsing in the dense reverb
+			// for really large spaces (caverns) this is OK, otherwise we ant to control this
+			// via predelay, and earlies
+			// NOTE: this will NOT WORK when sharing a single delay line!
+			// in that case, outer delay MUST be > internal ones
+			// TODO: devise user param to control a range of delay values
+			// (maybe derived from feedback level?), to further scale room size
+			time1a = to_prime(0.03 /*0.087*/, sr);
+			time1b = to_prime(0.062, sr);
+			time1c = to_prime(0.022,sr);	//dummy: not used
+
+			time2a = to_prime(0.018/*0.12*/,sr);
+			time2b = to_prime(0.076,sr);
+			time2c = to_prime(0.03,sr);		
+			data1.time1 = time1a;
+			data1.time2 = time1b;
+			data1.time3 = time1c;
+			data1.gain1 = 0.5;
+			data1.gain2 = 0.25;
+			data1.gain3 = 0.0;			//not used
+			data1.delay1 = 0;
+			data1.delay2 = 0;
+			data2.time1 = time2a;
+			data2.time2 = time2b;
+			data2.time3 = time2c;
+			data2.gain1 = 0.5;
+			data2.gain2 = 0.25;
+			data2.gain3 = 0.25;		
+			data2.delay1 = 0;
+			data2.delay2 = 0;
+			break;
+		default:
+#ifdef _DEBUG
+			assert(false);
+#endif
+			break;
+	}
+#ifdef _DEBUG
+	//RWD: development only!
+	if(argc==CONFIG+1){
+		int got = 0;
+		double time1a,time1b,time1c,time2a,time2b,time2c,gain1a,gain1b,gain1c,gain2a,gain2b,gain2c;
+		FILE *fp = fopen(argv[CONFIG],"r");
+		if(!fp)
+			printf("rmverb: can't open datafile %s, using presets\n",argv[CONFIG]);
+		else {
+		   // time1a,gain1a,time2a,gain2a,time3a,gain3a...
+			printf("loading diffusion data...\n");
+			got = fscanf(fp,"%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
+			  &time1a,&gain1a,
+			  &time1b,&gain1b,
+			  &time1c,&gain1c,
+			  &time2a,&gain2a,
+			  &time2b,&gain2b,
+			  &time2c,&gain2c);
+			fclose(fp);
+			if(got != 12)
+				printf("rmverb: error reading data values\n");
+			else {
+				data1.time1 = to_prime(time1a,sr);
+				data1.time2 = to_prime(time1b,sr);
+				data1.time3 = to_prime(time1c,sr);
+				data1.gain1 = gain1a;
+				data1.gain2 = gain1b;
+				data1.gain3 = gain1c;
+
+				data2.time1 = to_prime(time2a,sr);
+				data2.time2 = to_prime(time2b,sr);
+				data2.time3 = to_prime(time2c,sr);
+				data2.gain1 = gain2a;
+				data2.gain2 = gain2b;
+				data2.gain3 = gain2c;
+			}
+		}
+	}
+	
+	else{	
+		printf("using preset parameters:\n");
+		printf("DATA 1: time1 = %u\n\ttime2 = %u\n\ttime3 = %u\n",data1.time1,data1.time2,data1.time3);
+		printf("DATA 2: time1 = %u\n\ttime2 = %u\n\ttime3 = %u\n",data2.time1,data2.time2,data2.time3);
+		printf("feedback = %.4lf,fb_lpfreq = %.4lf\n",feedback,fb_lpfreq);
+		printf("dry gain = %.4lf, wet gain = %.4lf\n",dry_gain,wet_gain);
+	}
+#endif	
+
+	
+	switch(reverbtype){
+	case(DIFF_SMALL):	
+		printf("rmverb: using small room model\n");
+		s_diffuser = new small_diffuser((unsigned int)(0.024 * sr),&data1,&data2,feedback,fb_lpfreq,sr);
+		s_diffuser->set_damping(double_damping);
+		cdpdiff =  dynamic_cast<cdp_processor *>(s_diffuser);
+		if(!usertaps){
+			ptaps = smalltaps;
+			ntaps = sizeof(smalltaps) / sizeof(deltap);
+		}
+		break;
+	case(DIFF_MEDIUM):
+		printf("rmverb: using medium room model\n");
+		m_diffuser = new medium_diffuser(0.108,&data1,&data2,feedback,fb_lpfreq,sr);
+		m_diffuser->set_damping(double_damping);
+		cdpdiff =  dynamic_cast<cdp_processor *>(m_diffuser);
+		if(!usertaps){
+			ptaps = mediumtaps;
+			ntaps = sizeof(mediumtaps) / sizeof(deltap);
+		}
+		//ptaps = longtaps;
+		//ntaps = sizeof(longtaps) / sizeof(deltap);
+
+		break;
+	case(DIFF_LARGE):
+		printf("using large room model\n");
+		//fb_lpfreq = 2600.0; // Gardner fixed val
+		l_diffuser = new large_diffuser(&data1,&data2,feedback,fb_lpfreq,sr);
+		l_diffuser->set_damping(double_damping);
+		cdpdiff =  dynamic_cast<cdp_processor *>(l_diffuser);
+		if(!usertaps){
+			ptaps = largetaps;
+			ntaps = sizeof(largetaps) / sizeof(deltap);
+		}
+		break;
+	default:
+#ifdef _DEBUG
+		assert(false);
+#endif
+		break;
+	}
+
+	if(!cdpdiff){
+		puts("rmverb: no memory for reverberator\n");
+		exit(1);
+	}
+	if(!cdpdiff->create()){
+		puts("rmverb: no memory for reverberator buffers\n");
+		exit(1);
+	}
+
+
+	/*********** GLOBAL COMPONENTS *************/
+
+	if(lp_freq > 0.0)
+		lp		= new onepole(lp_freq,sr,LOW_PASS);
+	
+	//if user prescribes a predelay, adjust all taptimes
+	if(predelay >= 0.0){
+		double current_delay = ptaps[0].pos;
+		double adjust = predelay - current_delay;
+
+		for(int i = 0; i < ntaps; i++)
+			ptaps[i].pos += adjust;
+
+	}
+	else
+		predelay = ptaps[0].pos;	//to report to user
+	tdelay = new tapdelay(ptaps,ntaps,sr);
+
+	if(!tdelay->create()) {
+		fputs("rmverb: no memory for early reflections\n",stderr);
+		return 1;
+	}
+
+	//max_gain = tdelay->get_maxgain();
+	//if(max_gain > 1.0)
+	//	tdelay->set_gain(max_gain * early_gain);
+
+
+	// final allpass chain: 0.02,0.014,0.009,0.006
+	ap1_l		= new allpassfilter(sr,to_prime(0.02,sr),-0.6,0);
+	ap2_l		= new allpassfilter(sr,to_prime(0.014,sr),0.4,0);
+//	ap3_l		= new allpassfilter(to_prime(0.009,sr),-0.6,0);
+//	ap4_l		= new allpassfilter(to_prime(0.006,sr),0.4,0);
+	if(!(ap1_l->create()
+		&& ap2_l->create()
+//		&& ap3_l->create()
+//		&& ap4_l->create()
+		)){
+		fprintf(stderr,"rmverb: no memory for allpass chain\n");
+		exit(1);
+	}
+	if(outchans >=2){
+		// slightly different params to make stereo!
+		// tune front pair precisely; further chans use formula
+		ap1_r		= new allpassfilter(sr,to_prime(0.025,sr),-0.6,0);
+		ap2_r		= new allpassfilter(sr,to_prime(0.0145,sr),0.4,0);
+//		ap3_r		= new allpassfilter(to_prime(0.0095,sr),-0.6,0);
+//		ap4_r		= new allpassfilter(to_prime(0.0065,sr),0.4,0);
+		if(!(ap1_r->create()
+			&& ap2_r->create()
+//			&& ap3_r->create()
+//			&& ap4_r->create()
+			)){
+			fprintf(stderr,"rmverb: no memory for allpass chain\n");
+			exit(1);
+		}
+	}
+
+	
+
+	printf("using %ld early reflections: predelay = %.2lf msecs\n",ntaps,predelay * 1000.0);
+    int clipfloats = 1;
+    int minheader = 0;
+	if(want_floats) {
+		props.samptype =PSF_SAMP_IEEE_FLOAT;
+        clipfloats = 0;        
+    }
+	props.chans = outchans;
+    
+	if((ofd = psf_sndCreate(argv[OUTFILE],&props,clipfloats,minheader,PSF_CREATE_RDWR)) < 0) {
+        fprintf(stderr,"rmverb: cannot open output file %s\n",argv[OUTFILE]);
+        return 1;
+	}
+
+/****
+ 																		|wet
+ *****   in-> ---o-->pre_lp -->earlies-o--->diffuser-->*diffgain-->--+--*---+----out
+				 |				       |							 |		|
+				 |					   |>--------*-(earlies)->-------|		*--dry
+				 |							     ^earlygain				    |
+				 |__________________________________________________________|
+
+ ****/
+	printf("\ninfile chans = %ld, outfile chans = %ld",chans,outchans);
+	printf("\n");
+	long outframes = 0;
+	long step = (long)(0.25 * sr);
+    float insamps[2], outsamps[16];
+	double l_out,r_out;
+//    float  finsamp, foutsamp;
+	for(;;){
+		int rv;
+		double l_ip,r_ip, out,l_direct,r_direct,mono_direct,earlies = 0.0f;
+        
+		//read mono/left
+		if((rv = psf_sndReadFloatFrames(ifd,insamps,1)) < 0){
+			fprintf(stderr,"rmverb: error reading file\n"); 
+			exit(1);			
+		}		
+		if(rv==0) break;		// end of inputfile	- handle any trailertime
+		l_ip = (double) insamps[0];
+		//apply any conditioning to direct signal
+		if(tc_l_lowcut)
+			l_ip =  tc_l_lowcut->tick(l_ip);
+		if(l_highcut)
+			l_ip =  l_highcut->tick(l_ip);
+		l_direct = l_ip;
+		mono_direct = l_direct;
+		r_direct = l_direct;
+		//handle R channnel if active
+		if(chans==2){
+            r_ip = (double) insamps[1];
+			//apply any conditioning to direct signal			
+			if(tc_r_lowcut)
+				r_ip =  tc_r_lowcut->tick(r_ip);
+			if(r_highcut)
+				r_ip =  r_highcut->tick(r_ip);
+			r_direct = r_ip;
+			if(want_mono)	//input merged sig to reverbreator
+				mono_direct = (l_direct + r_direct) * 0.5;			 
+		}
+		// mono_direct = (mixed) mono input to  reverb		
+		//possibly also filter it...
+		if(lp)
+			mono_direct =  lp->tick(mono_direct);					//input lowpass
+		earlies = tdelay->tick(mono_direct);				//early reflections	
+		//send (filtered) mono output from reverberator
+
+		// TODO: find formula for diffgain...
+		out = earlies + (cdpdiff->tick(earlies + mono_direct) * diff_gain * 0.707);		 //the dense reverberation		
+		// output:: use 2 allpasses per chan to generate stereo reverb, wider diffusion
+		l_out = wet_gain *  ap2_l->tick(ap1_l->tick(out));
+
+		// old method - 1 allpass per channel
+		//l_out = chan_ap[0]->tick(out) * wet_gain;
+
+		if(outchans == 1)			
+			l_out += (mono_direct * dry_gain);
+		else 
+			l_out += l_direct * dry_gain;
+        outsamps[0] = (float) l_out;
+		
+		if(outchans>=2){
+			r_out = wet_gain *  ap2_r->tick(ap1_r->tick(out));			
+			// old version
+			//r_out = chan_ap[1]->tick(out) * wet_gain;
+			r_out += r_direct * dry_gain;
+			outsamps[1] = (float) r_out;
+		}
+
+		//now any further channels; reduced level of direct sig
+		for(i=2;i < outchans; i++){
+            out = earlies + (cdpdiff->tick(earlies + (mono_direct * 0.3)) * diff_gain * 0.707);
+			l_out = wet_gain * chan_ap[i]->tick(out);
+            outsamps[i] = (float) l_out;
+			
+		}
+        if(psf_sndWriteFloatFrames(ofd,outsamps,1) != 1){
+            fprintf(stderr,"rmverb: error writing output file\n"); 
+			return 1;
+        }
+		outframes++;
+		if((outframes % step) == 0)
+			fprintf(stdout,"%.2f\r",(double)outframes/(double)sr);
+	}
+	
+	int trtime  = (int)( trailertime * sr);
+	for(i = 0; i < 	trtime; i++){		
+		double tr_l_ip,tr_r_ip = 0.0f,tr_out,tr_l_direct,tr_r_direct,
+			tr_mono_direct,tr_earlies = 0.0f,tr_l_out,tr_r_out;
+		//need last active samps from input filter(s)
+		tr_l_ip = 0.0;
+		if(tc_l_lowcut)
+			tr_l_ip =  tc_l_lowcut->tick(tr_l_ip);
+		if(l_highcut)
+			tr_l_ip =  l_highcut->tick(tr_l_ip);
+		tr_l_direct = tr_l_ip;
+		tr_mono_direct = tr_l_direct;
+		tr_r_direct = tr_l_direct;
+		//handle R channnel if active
+		if(chans==2){
+			tr_r_ip = 0.0;	   // inout is zero, except if using input filters
+			// get any samples from input conditioning filters
+			if(tc_r_lowcut)
+				tr_r_ip =  tc_r_lowcut->tick(0.0);						   
+			if(r_highcut)
+				tr_r_ip =  r_highcut->tick(tr_r_ip);
+			tr_r_direct = tr_r_ip;
+			if(want_mono)
+				tr_mono_direct = (tr_l_direct + tr_r_direct) *0.5f;			
+		}
+
+		// mono_direct = (mixed) mono input to  reverb
+		//possibly also filter it...
+		if(lp)
+			tr_mono_direct =  lp->tick(tr_mono_direct);		
+		tr_earlies = tdelay->tick(tr_mono_direct);
+		//send (filtered) mono output from reverberator
+		tr_out = tr_earlies + (cdpdiff->tick(tr_earlies + tr_mono_direct) * diff_gain * 0.707);		
+		// output:: use 2 allpasses to generate stereo reverb, further diffusion
+
+		// new version
+		tr_l_out = wet_gain *  ap2_l->tick(ap1_l->tick(tr_out));
+		// old version		
+		//tr_l_out = chan_ap[0]->tick(tr_out) * wet_gain;
+
+		if(want_mono)
+			tr_l_out += (tr_mono_direct * dry_gain);
+		else			
+			tr_l_out += tr_l_direct * dry_gain;
+
+        outsamps[0] = (float) tr_l_out;
+		
+		if(outchans>=2){						
+			tr_r_out = wet_gain * ap2_r->tick(ap1_r->tick(tr_out));
+			// old version:
+			//tr_r_out = chan_ap[1]->tick(tr_out) * wet_gain;
+
+			tr_r_out += tr_r_direct * dry_gain;	   // still need this cos of input filters
+            outsamps[1] = (float) tr_r_out;
+		}
+		for(int j = 2; j < outchans; j++){			
+			tr_r_out = chan_ap[j]->tick(tr_out) * wet_gain;			
+            outsamps[j] = (float) tr_r_out;
+			
+		}
+        if(psf_sndWriteFloatFrames(ofd,outsamps,1) != 1){
+            fprintf(stderr,"rmverb: error writing output file\n"); 
+			return 1;
+        }
+		outframes++;
+		if((outframes % step)==0)
+			//inform(step,sr);
+			fprintf(stdout,"%.2f\r",(double)outframes/(double)sr);
+	}
+	fprintf(stdout,"%.2f\n",(double)outframes/(double)sr);
+	//stopwatch(0);
+    
+	peaks = (PSF_CHPEAK *) malloc(sizeof(PSF_CHPEAK) * outchans);
+    if(peaks && psf_sndReadPeaks(ofd,peaks,NULL)) {
+        printf("\nPEAK data:\n");
+        for(i=0;i < outchans;i++)
+            printf("Channel %d: %.4f at frame %u: %.4lf secs\n",i+1,
+                   peaks[i].val,peaks[i].pos, (double) peaks[i].pos / (double)props.srate);
+	}
+	else {
+        fputs("rmverb: warning: unable to read PEAK data\n",stderr);
+    }
+	
+	psf_sndClose(ifd);
+	
+	if(psf_sndClose(ofd) < 0)
+		fprintf(stderr,"rmverb: error closing outfile\n");
+    printf("\n");
+    
+    psf_finish();
+	free(peaks);
+	
+	delete tdelay;
+	
+	if(usertaps)
+		delete [] ptaps;
+	//can I call delete on base-class ptr?
+	if(s_diffuser)
+		delete s_diffuser;
+	if(m_diffuser)
+		delete m_diffuser;
+	if(l_diffuser)
+		delete l_diffuser;
+	if(lp)
+		delete lp;
+	if(outchans > 1){
+		for(i=0;i < outchans; i++)
+			delete chan_ap[i];
+		delete [] chan_ap;
+	}	
+	delete tc_l_lowcut;	
+	delete tc_r_lowcut;	
+	delete l_highcut;
+	delete r_highcut;
+	return 0;
+}
+
+
+void
+usage(void)
+{
+	fprintf(stderr,"\nrmverb: Multi-channel reverb with room simulation\n\tVersion 1.3 Release 5 CDP 1998,1999,2011");
+	fprintf(stderr,
+	"\nusage:\nrmverb [flags] infile outfile rmsize rgain mix fback absorb lpfreq trtime"
+	"\nflags    : any or all of the following"	
+	"\n   -cN   : create outfile with N channels (1 <= N <= 16 : default =2)"
+	"\n   -d    : apply double lowpass damping (internal to nested allpasses)"
+	"\n            (see <absorb>. NB: reduces reverb time: "
+	"\n                  increase feedback to compensate)"
+	"\n   -eFILE: read early reflections data from breakpoint textfile FILE"
+	"\n   -f    : force floating-point output (default: infile format)"	
+	"\n   -HN   : apply 12dB Highcut filter with cutoff freq N Hz) to infile"
+	"\n   -LN   : apply 12dB Lowcut filter with cutoff freq N Hz to infile"
+	"\n           (NB: currently fixed at 12dB/oct - could be made variable)\n"
+	"\n   -pN   : force reverb predelay to N msecs (shifts early reflections)"
+	"\nrmsize   : 1,2 or 3: small, medium or large"
+	"\nrgain    : set level of dense reverb (0.0 < egain <= 1.0)"
+	"\nmix      : dry/wet balance (source and reverb). 1.0<-- mix -->=0.0"
+	"\nfback    : reverb feedback level: controls decay time. 0.0 <= fback <= 1.0"
+	"\nabsorb   : cutoff freq (Hz) of internal lowpass filters (models air absorption)\n"
+	"                (typ: 2500 for large room --- 4200 for small room "
+	"\nlpfreq   : lowpass filter cutoff freq(Hz) applied at input to reverb"	
+	"\n           NB: to disable either filter (absorb,lpfreq), use the value 0"
+	"\ntrtime   : trailer time added to outfile for reverb tail (secs)\n");	
+	exit(0);
+}
+
+long readtaps(FILE *fp, deltap **taps,double sr)
+{
+	vector<deltap> vtaps;
+	deltap thistap;
+	char line[256];
+	int ntaps = 0;
+	int linecount = 1;
+	bool error = false;
+	double time= 0.0, current_time = 0.0, val = 0.0;
+
+	double sample_duration = 1.0 / sr;
+	if(fp==0 || sr <= 0.0){
+		*taps = 0;
+		return 0;
+	}
+	while(fgets(line,256,fp))	{
+		int got;
+		if(line[0] == '\n' || line[0]== ';'){	//allow comment lines; this does not check for leading white-space...
+			linecount++;
+			continue;
+		}
+
+		if((got = sscanf(line,"%lf%lf",&time,&val))<2){			
+			fprintf(stderr,"\nerror in reflections file: line %d",linecount);
+			error =true;
+			break;
+		}
+		if(time < 0.0){
+			fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
+			error = true;
+			break;
+		}
+		//if (first) val = 0.0, or VERY close
+		if(time < sample_duration)	{ //non-zero time must be at least one sample on!			
+			fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero",time);
+			time = 0.0;			
+		}
+
+		if(current_time != 0.0 && time==current_time){
+			fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
+			linecount++;
+			continue;
+		}
+		if(time < current_time){
+			fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
+			error = true;
+			break;
+		}
+		current_time = time;
+		thistap.pos = time;
+		if(val <=0.0 || val > 1.0){
+			fprintf(stderr,"\nerror: bad amplitude in tapfile: line %d",linecount);
+			error = true;
+			break;
+		}
+		thistap.val = val;
+		vtaps.push_back(thistap);
+		linecount++;
+	}
+	
+	ntaps = vtaps.size();
+	if(ntaps==0){
+		fprintf(stderr,"\ntapfile contains no data!");
+		error = true;
+	}
+	if(error){
+		*taps = 0;
+		return 0;
+	}
+	*taps = new deltap[ntaps];
+	if(taps==0)
+		return 0;
+	vector<deltap>::iterator I =  vtaps.begin(); 
+	for(int i = 0; I != vtaps.end();i++, I++){
+		(*taps)[i].pos = I->pos;
+		(*taps)[i].val = I->val;
+	}
+
+	return ntaps;
+}

+ 713 - 0
dev/externals/reverb/roomresp.cpp

@@ -0,0 +1,713 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+/***********************************************************************
+Motorola
+
+Motorola does not assume any liability ...
+Author:   Tom Zudock ([email protected])
+
+see:
+ http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/DDJ/1996/9612/9612d/9612d.htm
+ 
+Presented in Dr Dobbs Journal Vol21:12, December 1996.
+ 
+Filename: roomresp.cpp
+
+This program calculates the room response for a simple room.  The output is not
+particularly delightful to listen to (it could sound phasey and
+have ringing due to the simple model used), but this program is the
+foundation for more complex virtual audio models which will eliminate
+these anomalies.
+
+Example form of the InputParametersTextFile:
+
+0.5               # Input gain for data file (float)
+1                 # Open Path = 0 Closed Path = 1 (int)
+6    5    4       # Room Dimensions (X,Y,Z) (float)
+0.85              # Reflectivity of walls (float)
+5                 # Number of rooms per dimension, 1 or greater
+2    3    2       # Listener coord (X,Y,Z) (float)
+2    2    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+3    2    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+4    3    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+3    4    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+2    4    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+
+***********************************************************************/
+
+// included header files
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+//#include <process.h>
+#include <string.h> 
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include <vector>
+#include <algorithm>
+using namespace std;
+
+#ifndef _MAX
+#define _MAX(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+// macro defined constants
+//#define Vs 334.0
+static const double Vs = 334.0;
+static const double srate = 20000;
+#define PI 3.14159265359
+#define RefDist 1.0
+enum args {
+		REFLECTIVITY,
+		REFLECTIONS,
+		R_X,
+		R_Y,
+		R_Z,
+		S_X,
+		S_Y,
+		S_Z,
+		L_X,
+		L_Y,
+		L_Z
+};
+
+// structure for a point in 3 dimensional space
+struct point {
+	double X;
+	double Y;
+	double Z;
+	point(){
+		X = Y = Z = 0.0;
+	}
+	virtual ~point() {}
+};
+
+// structure for a point paired with a time duration
+struct position {
+	point Coord;
+	double Time;
+	position *NextSource;
+};
+
+const char* cdp_version = "5.0.0";
+
+bool input_cmds(char *args[],int argc,point &room,point &source,point &listener,double &reflcoef,int &NR);
+
+void
+CalcImpResp(
+    long *,
+    double *,
+    point &,
+    point &,
+    point &,
+    double,
+    double,
+    int);
+
+position *
+InputParameters(
+    FILE *,
+    point &,
+ //   point &,
+    point &,
+    double &,
+    double &,
+   int &);
+
+void
+InputParamsError(
+    int);
+
+//RWD stuff
+typedef struct deltap {
+		long time;
+		double val;
+	} DELTAP;
+
+bool operator<(const DELTAP &a,const DELTAP &b)
+{
+	return (a.time < b.time);
+}
+
+void usage()					//RWD.1..12.98 left out flag -nNORM for now...
+{
+	printf("\n*********  ROOMRESP.EXE: by Tom Zudock  CDP build 1998,1999 ******"
+		   "\n  GENERATE ROOM-RESPONSE EARLY REFLECTIONS DATAFILE\n"
+		   "\nusage: roomresp [-aMAXAMP][-rRES] txtout.dat liveness nrefs "
+		   "\n                          roomL roomW roomH"
+		   "\n                             sourceL sourceW sourceH"
+		   "\n                                listenerL listenerW listenerH");
+	printf("\ntxtout.dat: text outfile containing early reflections in brkpoint format"
+			"\n             suitable for input to rmverb, reverb or tdelay"
+			"\nMAXAMP   : peak amp for data (0.0 < MAXAMP <= 1.0; default 1.0)"
+			"\nRES      : time resolution for reflections in msecs"
+			"\n             (0.1 <= RES <= 2; default 0.1)"
+			"\nliveness : degree of reflection from each surface (0 to 1; typ: 0.95)"
+			"\nnrefs    : number of reflections from each surface(nrefs > 0; typ: 2 to 5)"
+			"\n           (WARNING: high values will create extremely long data files!)"
+			"\nROOM PARAMETERS:"
+			"\nroomL roomW roomH            : room size: (Length, Width, Height)"
+			"\nsourceL sourceW ourceH       : position of sound source (as above)"
+			"\nlistenerL listenerW listenerH: position of listener (as above)"
+			"\n                               all dimensions are in metres"
+			"\nNB: first output time is non-zero."
+			);
+}
+
+
+/***********************************************************************
+main
+
+The main program is responsible for the primary program flow.
+It contains the primary processing loop which calls subroutines
+to handle the specialized functionality.
+***********************************************************************/
+int main (int argc, char **argv) {
+	
+//	FILE        *PositionFile = 0;  // input text parameters file
+	//RWD to print earlies as text
+	FILE		*earliesFile = 0;
+#ifdef TESTING
+	FILE		*rawfile = 0;				///just for testing...raw output from impresp funtionm
+#endif
+	int			i,NR = 0;							// num of mirrored rooms per dimension
+	long		*TapDelay = 0;			// pointer to array of tap delays	
+	double		Fs = 0.0;							// the sampling frequency
+	double		*TapGain = 0;				// pointer to array of tap gains
+	double		ReflCoef = 0.0;				// the reflectivity of the walls
+//	position	*CurrentSource = 0;	// current source in positions list 
+	point		Room;						// coords for the room size
+	point		Source;					// coords of current sound source
+	point		Listener;				// coords of the listener
+#ifdef _DEBUG
+	bool		write_c_struct = false;
+#endif
+	bool		do_normalize = true;
+	double		normfac = 1.0;
+	double      min_separation = 0.0001;
+
+	if((argc==2) && strcmp(argv[1],"--version")==0) {
+		fprintf(stdout,"%s\n",cdp_version);
+		fflush(stdout);
+		return 0;
+	}
+	if(argc==1){
+		usage();
+		exit(0);
+	}
+	if (argc < 13){
+		fprintf(stderr,"\ninsufficient arguments\n");
+  		usage();
+		exit(-1);
+	}
+  
+	while(argc > 0 && argv[1][0]=='-'){
+		double val;
+		switch(argv[1][1]){
+#ifdef _DEBUG
+//my private facility...
+		case('c'):
+			write_c_struct = true;
+			printf("\nwriting C-format file");
+			break;
+#endif
+		case('a'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"\n-a flag requires a value\n");
+				usage();
+				exit(1);
+			}
+			val = atof(&(argv[1][2]));
+#ifdef _DEBUG
+			if(val <0.0 || val > 1.0){
+#else
+			if(val <=0.0 || val > 1.0){
+#endif
+				fprintf(stderr,"\n normalization value %lf out of range\n",val);
+				usage();
+				exit(1);
+			}
+#ifdef _DEBUG
+			if(val==0.0) {
+				do_normalize = false;
+				normfac = 1.0;
+				printf("\nskipping normalization");
+			}
+			else
+#endif
+				normfac = val;
+			break;
+		case('r'):
+			if(argv[1][2]=='\0'){
+				fprintf(stderr,"\n-r flag requires a value\n");
+				usage();
+				exit(1);
+			}
+			val = atof(&(argv[1][2]));			
+			if(val < 0.1 || val > 2.0) {
+				fprintf(stderr,"\nvalue %.4lf for RES out of range\n",val);
+				usage();
+				exit(1);				
+			}
+			else
+				min_separation = val * 0.001;
+			break;
+
+
+
+		default:		
+			fprintf(stderr,"\nbad flag option\n");
+			exit(1);
+			break;
+		}
+		argc--;
+		argv++;
+	}
+
+	//step along arglist:
+	argc--; argv++;
+
+	
+	if(argc != 12){
+		fprintf(stderr,"\nincorrect number of numeric arguments\n");
+		usage();
+		exit(1);
+	}	
+	if((earliesFile = fopen(argv[0],"w"))==NULL){
+		printf ("\nUnable to open %s for output\n",argv[2]);
+		exit(-1);
+	}
+#ifdef TESTING
+	if((rawfile = fopen("rawdata.txt","w"))==NULL){
+		printf ("Unable to open rawdata.txt for output\n");
+		exit(-1);
+	}
+#endif
+	//step along again...
+	argc--; argv++;
+
+	//NB this sets the efective time-resolution for each tap, ie 1.0 / fs
+	//so 1000 is a minimum, for 1msec resolution
+
+	Fs = srate;
+
+	if(!input_cmds(argv,argc,Room,Source,Listener,ReflCoef,NR))
+		exit(1);	//already got the errmsg
+  
+	if((TapDelay = new long[NR*NR*NR])==NULL) {
+    	printf("Error: Unable to allocate memory\n");
+    	exit(-1);
+    }
+    if((TapGain = new double[NR*NR*NR])==NULL) {
+    	printf("Error: Unable to allocate memory\n");
+    	exit(-1);
+    }
+	
+  
+	CalcImpResp(
+	    TapDelay,
+		TapGain,
+		Room,
+		/*CurrentSource->Coord,*/Source,
+		Listener,
+		ReflCoef,
+		Fs,
+		NR);
+
+	//sort deltaps, eliminate duplicates,  write to file
+	vector<DELTAP> vtaps;
+	DELTAP thistap,prevtap;
+	prevtap.time = 0;
+	prevtap.val = 0.0;
+	for(i = 0; i < NR*NR*NR;i++) {
+		thistap.time = TapDelay[i];
+		thistap.val  =TapGain[i];
+#ifdef TESTING
+		fprintf(rawfile,"%ld\t%.6lf\n",thistap.time,thistap.val);
+#endif
+		//exclude tapval of 1.0: = direct sig at listening point
+		if(thistap.val > 0.0 && thistap.val < 1.0)			// get a problem in release build otherwise....
+			vtaps.push_back(thistap);	
+	}
+
+	//now we can sort them
+
+	sort(vtaps.begin(),vtaps.end());
+	
+	vector<DELTAP>::iterator it;
+	it = vtaps.begin();
+	prevtap.time = it->time;
+	prevtap.val =  it->val;
+
+	//print all taps as we got them, first!
+
+	double maxval = 0.0;
+	for(;it != vtaps.end();it++){
+#ifdef NOTDEF		
+		fprintf(earliesFile,"\n\t%.4lf\t%.6lf",(double)it->time / Fs,it->val);
+#endif
+		maxval = _MAX(it->val,maxval);
+		
+	}
+#ifdef _DEBUG
+	printf("\noverall maxamp = %.4lf",maxval);
+#endif
+
+	//print first line, formatted for code
+	//normalize all vals to max amplitude!
+	
+	if(do_normalize){
+		normfac  /= prevtap.val;
+#ifdef _DEBUG
+		printf("\nnormalizing by %.4lf",normfac);
+#endif
+	}
+	it = vtaps.begin();
+	
+	
+	//normfac= floor(normfac);
+#ifdef _DEBUG
+	printf("\ninitial time=  %ld, initial amp = %.4lf",it->time,it->val);
+
+	//RWD private facility!
+	if(write_c_struct){
+		fprintf(earliesFile,"\n\n{\n\t{%.4lf,%.6lf}",(double)prevtap.time / Fs,prevtap.val * normfac);
+		int count = 1;
+	
+		for(;it != vtaps.end();it++){
+			//get maxamp of any taps sharing a time (to 5 sig figs) - or should I add them somehow?
+			if(fabs(it->time - prevtap.time) < min_separation){
+				it->val = _MAX(it->val,prevtap.val);
+				continue;
+			}
+			fprintf(earliesFile,",\n\t{%.5lf,%.6lf}",(double)it->time / Fs,it->val * normfac);
+			prevtap = *it;
+			count++;
+		}
+		fprintf(earliesFile,"\n\t};\n\nNUMTAPS = %d\n",count);
+	}
+	else {
+#endif
+		int count = 1;
+		fprintf(earliesFile,"%.5lf\t%.6lf\n",(double)prevtap.time / Fs, prevtap.val * normfac);
+		for(;it != vtaps.end();it++){
+			if(fabs(it->time - prevtap.time) < min_separation){
+				it->val = _MAX(it->val,prevtap.val);
+				continue;
+			}
+			fprintf(earliesFile,"%.5lf\t%.6lf\n",(double)it->time / Fs, it->val * normfac);
+			prevtap = *it;
+			count++;
+		}
+		printf("\n\nNUMTAPS = %d\n",count);
+#ifdef _DEBUG
+	}
+#endif
+	fclose(earliesFile);
+#ifdef TESTING
+	fclose(rawfile);
+#endif
+	delete TapDelay,
+	delete TapGain,
+	printf ("done\n");
+	return(0);
+}               
+
+/***********************************************************************
+CalcImpResp
+
+This subroutine calculates the time delays (in samples) and
+attenuations for the early reflections in the room.
+***********************************************************************/
+void
+CalcImpResp(
+  long * TapDelay,
+  double * TapGain,
+  point & Room,
+  point & Source,
+  point & Listener,
+  double ReflCoef,
+  double Fs,
+  int NR)						
+{
+	double Dist = 0.0;                       // distance travelled by sound ray
+	double ReflOrd = 0.0;                    // reflection order of sound ray
+	//RWD.11.98 need volatile, or VC++ optimizer does more bad things!
+	volatile point MirrSource;                  // mirrored source x,y,z coords
+	//RWD
+	int NRovr2 = NR / 2;
+	int index = 0;
+  for (int i=-NRovr2;i<=NRovr2;i++) {    // loop through all 3 dimensions
+    for (int j=-NRovr2;j<=NRovr2;j++) {
+      for (int k=-NRovr2;k<=NRovr2;k++) {
+        // calc x,y,z sound source coords in mirrored room		  
+        MirrSource.X = (i)*Room.X
+					   +fabs(((i)%2)*Room.X)
+                       +pow(-1,(i))*Source.X;                       
+        MirrSource.Y = (j)*Room.Y
+                       +fabs(((j)%2)*Room.Y)
+                       +pow(-1,(j))*Source.Y;
+        MirrSource.Z = (k)*Room.Z
+                       +fabs(((k)%2)*Room.Z)
+                       +pow(-1,(k))*Source.Z;
+        // calculate distance to listener
+        Dist = sqrt(pow(MirrSource.X-Listener.X,2)
+                    +pow(MirrSource.Y-Listener.Y,2)
+                    +pow(MirrSource.Z-Listener.Z,2));
+        // calculate delayed arrival time of reflection
+		//RWD problem with release build...
+		
+		index = (i+NRovr2)*NR*NR+(j+NRovr2)*NR+(k+NRovr2);
+		//this also stopped the optimizer doing bad things....
+		//if(index%NR==0)
+		//	printf("\nindex = %d\tX= %.4lf\tY=%.4lf\tZ=%.4lf\tDist = %.4lf",
+		//		index,MirrSource.X,MirrSource.Y,MirrSource.Z,Dist);
+																					
+        TapDelay[index]=Dist/Vs*Fs;		
+        ReflOrd = abs(i)+abs(j)+abs(k);
+        // calculate attenuation for the reflection
+		//RWD div/zero avoidance:
+		if(Dist==0.0)
+			TapGain[index] = 1.0;
+		else
+			TapGain[index]= pow(RefDist/Dist,2.0)*pow(ReflCoef,ReflOrd);		
+      }
+    }
+  }
+}
+
+/***********************************************************************
+InputParameters
+
+This subroutine inputs parameters from a text file.  An example of
+the format of the parameters file is below.  It also checks the
+range of some of the parameters and creates a linked list representing
+how the sound source changes position in the modeled room.
+
+0.5               # Input gain for data file (float)
+1                 # Open Path = 0 Closed Path = 1 (int)
+6    5    4       # Room Dimensions (X,Y,Z) (float)
+0.85              # Reflectivity of walls (float)
+5                 # Number of rooms per dimension, 1 or greater
+2    3    2       # Listener coord (X,Y,Z) (float)
+2    2    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+3    2    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+4    3    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+3    4    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+2    4    2    1  # Source coord and time at coord (X,Y,Z,t) (float)
+
+***********************************************************************/
+position*
+InputParameters(
+  FILE *PositionFile,
+  point &Room,
+//  point &Source,
+  point &Listener,
+  double &ReflCoef,
+  double &InGain,
+  int &NR)
+{
+	position *First = new position,*Previous=First,*Next=Previous;
+	int ClosedPath,LineCount=1;
+	char StrLine[256];
+
+	#define ReadParms(arg1)                           \
+	if ((fgets(StrLine,256,PositionFile)) != NULL) {  \
+		if (arg1)                                       \
+			LineCount++;                                  \
+		else                                            \
+			InputParamsError(LineCount);                  \
+	}                                                 \
+	else                                              \
+			InputParamsError(0);
+		
+	
+#ifdef _DEBUG
+	assert(PositionFile);
+#endif
+	ReadParms(sscanf(StrLine,"%le",&InGain)==1)
+	ReadParms(sscanf(StrLine,"%d",&ClosedPath)==1)
+	ReadParms(sscanf(StrLine,"%le %le %le",&Room.X,&Room.Y,&Room.Z)==3)
+	ReadParms(sscanf(StrLine,"%le",&ReflCoef)==1)
+	ReadParms(sscanf(StrLine,"%d",&NR)==1)
+	ReadParms(sscanf(StrLine,"%le %le %le",&Listener.X,&Listener.Y,&Listener.Z)==3)
+
+	if (NR%2 != 1) {
+		printf ("Error: The number of reflected rooms per dimension must be odd (for symmetry)\n");
+		exit(-1);
+	}
+	//RWD!!! was bitwise OR:  '|'
+	if ((Listener.Y > Room.Y)||(Listener.X > Room.X)) { 					// Check listener position
+		printf ("Error: Listener position puts listener outside of room \n");
+		exit(-1);
+	}		
+  // Choose a default position for listener
+	//RWD check this! am I always getting these setings????
+	First->Coord.X = Room.X/2;
+	First->Coord.Y = Room.Y/2;
+	First->Coord.Z = Room.Z/2;
+	First->Time = 0;
+	First->NextSource=NULL;
+	while (feof(PositionFile)==0) {                                              
+		if ((fgets(StrLine,256,PositionFile)) != NULL) {
+			if(sscanf(StrLine,"%le %le %le %le",&(Next->Coord.X),&(Next->Coord.Y),&(Next->Coord.Z),&(Next->Time))==4) {
+				Next->NextSource = new position;
+				Previous = Next;
+				Next = Next->NextSource;
+				LineCount++;
+			}
+			else {
+				InputParamsError(LineCount);
+			}
+		}
+	}
+	delete (Previous->NextSource);
+	if (ClosedPath)
+		Previous->NextSource=First;
+	else
+		Previous->NextSource=NULL;
+	return(First);
+}
+
+//RWD get params from cmdline: refl,nrefs,rX,rY,rZ,sX,sY,sZ,lX,lY,lZ
+//fix ingain at 1.0
+bool input_cmds(char *args[],int argc,point &room,point &source,point &listener,double &reflcoef,int &NR)
+{
+	double dval;
+	int ival;
+#ifdef _DEBUG
+	assert(argc==11);
+#endif
+	if(argc != 11)
+		return false;
+
+	dval = atof(args[REFLECTIVITY]);
+	if(dval <= 0.0 || dval > 1.0){
+		fprintf(stderr,"\nbad value for liveliness\n");
+		return false;
+	}
+	reflcoef = dval;
+
+	ival = atoi(args[REFLECTIONS]);
+	if(ival < 1){
+		fprintf(stderr,"\nbad value for nrefs\n");
+		return false;
+	}
+
+	NR = 1 + (2* ival);
+
+	room.X = atof(args[R_X]);
+	if(room.X <= 0.0){
+		fprintf(stderr,"\nbad size for roomX\n");
+		return false;
+	}
+	room.Y = atof(args[R_Y]);
+	if(room.Y <= 0.0){
+		fprintf(stderr,"\nbad size for roomY\n");
+		return false;
+	}
+	room.Z = atof(args[R_Z]);
+	if(room.Z <= 0.0){
+		fprintf(stderr,"\nbad size for roomZ\n");
+		return false;
+	}
+	listener.X = atof(args[L_X]);
+	if(listener.X < 0.0){
+		fprintf(stderr,"\nbad size for listenerX\n");
+		return false;
+	}
+	listener.Y = atof(args[L_Y]);
+	if(listener.Y < 0.0){
+		fprintf(stderr,"\nbad size for listenerY\n");
+		return false;
+	}
+	listener.Z = atof(args[L_Z]);
+	if(listener.Z < 0.0){
+		fprintf(stderr,"\nbad size for listenerZ\n");
+		return false;
+	}
+
+	if ((listener.Y > room.Y)||(listener.X > room.X)|| (listener.Z > room.Z)) { // Check listener position
+		printf ("\nError: Listener position puts listener outside room\n");
+		return false;
+	}
+
+	source.X = atof(args[S_X]);
+	if(source.X <= 0.0){
+		fprintf(stderr,"\nbad size for sourceX\n");
+		return false;
+	}
+
+	source.Y = atof(args[S_Y]);
+	if(source.X <= 0.0){
+		fprintf(stderr,"\nbad size for sourceY\n");
+		return false;
+	}
+
+	source.Z = atof(args[S_Z]);
+	if(source.X <= 0.0){
+		fprintf(stderr,"\nbad size for sourceX\n");
+		return false;
+	}
+
+	if ((source.Y > room.Y)||(source.X > room.X)|| (source.Z > room.Z)) { // Check source position
+		printf ("\nError: Source position puts source outside room\n");
+		return false;
+	}
+
+
+	return true;
+}
+
+
+
+/***********************************************************************
+InputParamsError
+
+This subroutine outputs an text error message to standard output
+when an error is encountered in the InputParams subroutine.  The 
+error text that is output is selected by the line number of the
+input parameters file where the error occured.
+***********************************************************************/
+void
+InputParamsError(
+  int ErrNum)
+{
+	char ErrorStr[][256] = {
+	"Error: EOF reached before all parameters input\n",
+	"Error: Line %d requires 1 parameter the gain for the input file\n",
+	"Error: Line %d requires 1 parameter (0 or 1): 0=closed path, 1=open path\n",
+	"Error: Line %d requires 3 parameters (X,Y,Z): the room dimensions\n",
+	"Error: Line %d requires 1 parameter the wall refelctivity\n",
+	"Error: Line %d requires 1 parameter the number of mirrored rooms per dimension\n",
+	"Error: Line %d requires 3 parameters (X,Y,Z): the listeners position\n",
+	"Error: Line %d requires 4 parameters (X,Y,Z,t): sound position position and time\n"};
+	if (ErrNum < 7)
+		printf(ErrorStr[ErrNum],ErrNum);
+	else
+		printf(ErrorStr[7],ErrNum);
+	exit(-1);
+
+}
+
+ 

+ 95 - 0
dev/externals/reverb/smalldiff.cpp

@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//smalldiff.cpp
+//implements diffuse reverberator for small rooms, based on gardner nested allpasses
+#include "reverberator.h"
+
+small_diffuser::small_diffuser(unsigned int pre_delay, 
+							   const NESTDATA *apdata1,
+							   const NESTDATA *apdata2,double fb_gain,double lp_coeff){
+
+	  ap1_data = *apdata1;
+	  ap2_data = *apdata2;
+	  predelay_time = pre_delay;
+	  lpgain = fb_gain;
+	  lpcoeff = lp_coeff;
+	  ap1 = ap2 = 0;
+	  lp1 = 0;
+	  predelay = 0;
+	  out1 = out2 = 0.0f;
+}
+
+small_diffuser::~small_diffuser()
+{
+	delete ap1;
+	delete ap2;
+	if(predelay)
+		delete predelay;
+	delete lp1;
+}
+
+bool small_diffuser::create(void)
+{
+	if(ap1_data.time1 == 0 || ap1_data.gain1 <= 0.0)
+		return false;
+	if(ap1_data.time2 ==0 || ap1_data.gain2 <= 0.0)
+		return false;
+	if(ap2_data.time1 == 0 || ap2_data.gain1 < 0.0)
+		return false;
+	if(ap2_data.time2 ==0 || ap2_data.gain2 < 0.0)
+		return false;
+	ap1 = new nested_allpass(ap1_data.time1,ap1_data.time2,ap1_data.time3,
+							ap1_data.gain1,ap1_data.gain2,ap1_data.gain3,ap1_data.delay1,ap1_data.delay2);
+	if(!ap1->create())
+		return false;
+	if(ap2_data.gain1 != 0.0){	 //allow ap to eliminate second block
+		ap2 = new nested_allpass(ap2_data.time1,ap2_data.time2,ap2_data.time3,
+							ap2_data.gain1,ap2_data.gain2,ap2_data.gain3,ap2_data.delay1,ap2_data.delay2);
+		if(!ap2->create())
+			return false;
+	}
+	if(predelay_time > 0){
+		predelay = new delay(predelay_time,1.0);
+		if(!predelay->create())
+			return false;
+	}
+	if(lpcoeff > 0.0)
+		lp1 = new lowpass1(lpcoeff);
+	return true;
+}
+
+
+float small_diffuser::tick(float insam) 
+{
+	float ip = insam;
+	double temp;
+	if(lp1)
+		temp = lp1->tick((out2 + out1)*0.5f);
+	else
+		temp = (out2 + out1)*0.5f;
+	ip += (float)(lpgain * temp);	
+	if(predelay)
+		ip = predelay->tick(ip);
+	out1 = ap1->tick(ip);
+	if(ap2)
+		out2 = ap2->tick(out1);
+	return (out1 + out2)  /** 0.5f*/;
+}

+ 688 - 0
dev/externals/reverb/tdelaymain.cpp

@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+//tdelay.cpp
+//mono/stereo tapped delay line
+//RWD,CDP 1998
+//VERSION: initial, 0.01
+//usage: tdelay [-f] infile outfile feedback tapfile.txt [trailertime]
+//		-f : floatsam output
+//		feedback (double) - obvious...
+// tapfile.txt	lines of at least two values: time  amplitude  [pan]
+// times must be increasing, non-identical
+// amplitude +- 1.0
+// pan -1.0--1.0: -1 = hard Left, 1= hard Right, 0.0 = Centre
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+extern "C" {
+#include <sfsys.h>
+}
+#include <props.h>
+#include "reverberator.h"
+#include <vector>
+#include <algorithm>
+using namespace std;
+
+typedef struct stereo_tap {
+	double time;
+	double amp;
+	double pan;
+} STAP ;
+
+
+void usage(void);
+#define ROOT2	(1.4142136)
+enum pan_direction {SIGNAL_TO_LEFT,SIGNAL_TO_RIGHT};
+
+enum args {INFILE=1,OUTFILE,TAPGAIN,FEEDBACK,MIX,TAPFILE,TRTIME};
+
+void pancalc(double position,double *leftgain,double *rightgain);
+
+const char* cdp_version = "5.0.0";
+
+
+int main(int argc, char **argv)
+{
+	int ifd,ofd,inchans;
+	float feedback = 0.0f;
+	float dryfac = 0.0f,wetfac = 1.0f;
+	double trailertime = 0.0;
+	double current_time = 0.0;
+	deltap *l_taps = 0,*r_taps = 0;
+	tapdelay *l_dline = 0, *r_dline = 0;
+	unsigned int i,nchans = 1,ntaps = 0,trailersamps = 0;
+	bool is_stereo = false;
+	bool floatsams_wanted = false;
+	bool mix_input = true;
+	SFPROPS props;
+	FILE *tapfile = 0;
+	double sample_duration,tapgain;
+	double max_lgain = 1.0,max_rgain= 1.0;
+	float max_gain = 1.0f;
+	double sr; 
+	CHPEAK *peaks;
+	
+	if((argc==2) && strcmp(argv[1],"--version")==0) {
+		fprintf(stdout,"%s\n",cdp_version);
+		fflush(stdout);
+		return 0;
+	}
+
+	if(argc < TRTIME){
+		usage();
+		exit(1);
+	}
+	if(sflinit("tdelay")){
+		sfperror("tdelay: initialisation");
+		exit(1);
+	}
+	
+	while(argv[1][0] == '-'){
+		switch(argv[1][1]){
+		//case('s'):
+		//	is_stereo = true;
+		//	break;
+		case('f'):
+			floatsams_wanted = true;
+			break;
+		default:
+			fprintf(stderr,"\nillegal flag option %s",argv[1]);
+			usage();
+			exit(1);
+			break;
+		}
+		argv++;
+		argc--;
+	}
+	if(argc < TRTIME){
+		usage();
+		exit(1);
+	}
+
+	if((ifd = sndopenEx(argv[INFILE],0,CDP_OPEN_RDONLY)) < 0) {
+		fprintf(stderr,"reverb: cannot open input file %s: %s\n", argv[1],rsferrstr);
+		exit(1);
+	}
+	if(!snd_headread(ifd,&props)){
+		fprintf(stderr,"\nerror reading infile header: %s\n",rsferrstr);
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	if(props.type != wt_wave){
+		fprintf(stderr,"\ninfile is not a waveform file");
+		sndcloseEx(ifd);
+		exit(1);
+	}
+
+	inchans = props.chans;
+	if(inchans > 2){
+		fprintf(stderr,"\nSorry - cannot process files with more than two channels!");
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	sample_duration = 1.0 / props.srate;
+
+	if(floatsams_wanted)
+		props.samptype =  FLOAT32;
+	
+
+	tapgain = atof(argv[TAPGAIN]);
+	if(tapgain <= 0.0){
+		fprintf(stderr,"\nError: tapgain must be > 0.0\n");
+		exit(1);
+	}
+
+
+	feedback = (float) atof(argv[FEEDBACK]);
+	if(feedback < -1.0f || feedback > 1.0f){
+		fprintf(stderr,"\nfeedback out of range	-1.0 to +1.0");
+		usage();
+		sndcloseEx(ifd);
+		exit(1);
+	}
+
+	dryfac = (float) atof(argv[MIX]);
+	if(dryfac < 0.0f || dryfac >= 1.0f){
+		fprintf(stderr,"\nmix value %s out of range",argv[4]);
+		usage();
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	wetfac = 1.0f - dryfac;
+
+	if(argc==TRTIME+1){
+		trailertime = atof(argv[TRTIME]);
+		if(trailertime < 0.0){
+			fprintf(stderr,"\ntrailertime must be >= 0.0");
+			exit(1);
+		}
+		trailersamps = (unsigned int) (trailertime * props.srate);
+	}
+	tapfile = fopen(argv[TAPFILE],"r");
+	if(!tapfile){
+		fprintf(stderr,"\nunable to open tapfile %s",argv[TAPFILE]);
+		usage();
+		sndcloseEx(ifd);
+		exit(1);
+	}
+
+	//read all lines, into taps array
+	vector<STAP> ltaps;
+	STAP thistap;
+	double this_time,this_amp,this_pan;
+	char line[255];
+
+	int linecount = 1;
+	this_time = 0.0; this_amp = 0.0;
+	bool read_error = false;
+	vector<STAP>::iterator I;
+
+	//RWD TODO: take a tap at time = 0.0 as spec of direct sound : amp and pan
+	
+	while(fgets(line,255,tapfile) != NULL){
+		this_pan = 0.0;
+		int got;
+		if(line[0] == '\n' || line[0]== ';'){	//allow comment lines; this does not check for leading white-space...
+			linecount++;
+			continue;
+		}
+		if((got = sscanf(line,"%lf%lf%lf",&this_time,&this_amp,&this_pan)) < 2){
+			fprintf(stderr,"\nerror in tapfile: line %d: insufficient parameters",linecount);
+			read_error = true;
+			break;
+		}
+		//time= 0 no good for a taptime!
+		if(this_time < 0.0){
+			fprintf(stderr,"\nerror in tapfile: line %d: bad time value",linecount);
+			read_error = true;
+			break;
+		}
+		if(this_time < sample_duration)	{ //non-zero time must be at least one sample on!			
+			fprintf(stderr,"\nWARNING: very small taptime %.4lf treated as zero - ignoring mix value",this_time);
+			this_time = 0.0;
+			mix_input = false;
+		}
+		if(current_time != 0.0 && this_time==current_time){
+			fprintf(stderr,"\nWARNING: duplicate time in line %d: ignored",linecount);
+			linecount++;
+			continue;
+		}		
+		if(this_time < current_time){
+			fprintf(stderr,"\nerror in tapfile: time out of sequence: line %d",linecount);
+			read_error = true;
+			break;
+		}
+		current_time = this_time;
+		thistap.time = this_time;
+		if(this_amp < 0.0 || this_amp > 1.0){
+			fprintf(stderr,"\nerror in tapfile: line %d: bad amplitude value",linecount);
+			read_error = true;
+			break;
+		}
+		
+		thistap.amp = this_amp;
+		if(got==3){
+			thistap.pan = this_pan;
+			is_stereo = true;
+			nchans = 2;
+		}
+		else
+			thistap.pan = 0.0;
+		ltaps.push_back(thistap);
+		linecount++;
+	}
+
+	if(read_error){
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	ntaps = ltaps.size();
+	if(ntaps==0){
+		printf("\nfile contains no data!");
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	
+	if(!mix_input)
+		printf("\nzero taptime used: taking input mix control from tapfile");
+#ifdef _DEBUG
+	printf("\nread %d taps",ntaps);
+	
+	for(I = ltaps.begin(); I != ltaps.end();I++)
+		printf("\n%.4lf\t%.4lf\t%.4lf",I->time,I->amp,I->pan);	//or whatever.....
+
+	printf("\nfeedback =  %.4f",feedback);
+	printf("\ndryfac = %.4f, wetfac = %.4f",dryfac,wetfac);
+
+#endif
+	if(is_stereo)
+		printf("\ncreating stereo outfile");
+	else
+		printf("\ncreating mono outfile");
+	
+	
+	l_taps = new deltap[ntaps];
+	
+	if(is_stereo){
+		r_taps = new deltap[ntaps];
+		//and create stereo delay-lines
+		int j;
+		for(j = 0,I = ltaps.begin(); I != ltaps.end();j++,I++){
+		//printf("\n%.4lf\t%.4lf\t%.4lf",I->time,I->amp,I->pan);	//or whatever.....
+			double l_gain,r_gain;
+			pancalc(I->pan,&l_gain,&r_gain);
+			l_taps[j].pos = r_taps[j].pos = I->time;
+			l_taps[j].val = I->amp * l_gain;
+			r_taps[j].val = I->amp * r_gain;
+		}
+	}
+	//else create a mono delayline
+	else {
+		int j;
+		for(j = 0,I = ltaps.begin(); I != ltaps.end();j++,I++){				
+			l_taps[j].pos = I->time;
+			l_taps[j].val = I->amp;
+		}
+	}
+
+
+	l_dline  = new tapdelay(l_taps,ntaps,props.srate);
+	if(!l_dline->create()){
+		fputs("\nunable to  create delay line - probably no memory",stderr);
+		delete [] l_taps;
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	max_lgain = l_dline->get_maxgain();
+	if(is_stereo){
+		r_dline = new tapdelay(r_taps,ntaps,props.srate);
+		if(!r_dline->create()){
+			fputs("\nunable to  create stereo delay line - probably no memory",stderr);
+			delete [] l_taps;
+			delete [] r_taps;
+			delete l_dline;
+			sndcloseEx(ifd);
+			exit(1);
+		}
+		max_rgain = r_dline->get_maxgain();
+	}
+	if(is_stereo)
+		max_gain = (float) min(max_lgain,max_rgain);
+	else
+		max_gain = (float) max_lgain;
+
+	/* recale max_gain by user param, eg:*/
+	max_gain *= (float) tapgain;
+
+
+	l_dline->set_gain((double)max_gain);
+	if(is_stereo)
+		r_dline->set_gain((double)max_gain);
+
+
+	props.chans = nchans;
+	peaks = (CHPEAK *) malloc(sizeof(CHPEAK) * nchans);
+	if(peaks==NULL){
+		fputs("No memory for PEAK data",stderr);
+		exit(1);
+	}
+	for(i=0; i < nchans; i++){
+		peaks[i].value = 0.0f;
+		peaks[i].position = 0;
+	}
+	//OK, now open outfile!
+
+	ofd = sndcreat_ex(argv[OUTFILE], -1,&props,SFILE_CDP,CDP_CREATE_NORMAL); 
+	//ofd = sndcreat_formatted(argv[OUTFILE],-1,outsamp_type,nchans,props.srate,CDP_CREATE_NORMAL);
+	if(ofd < 0){
+		printf("\nunable to open outfile %s",argv[OUTFILE]);
+		delete [] l_taps;
+		delete l_dline;
+		if(r_taps)
+			delete [] r_taps;
+		sndcloseEx(ifd);
+		exit(1);
+	}
+	printf("\n");
+
+	float l_op = 0.0f;		  
+	float r_op = 0.0f;
+	float l_out = 0.0f;
+	float r_out = 0.0f;
+	long outsamps = 0;
+	long step = (long) (0.25 * props.srate);
+	//RWD.10.98 BIG TODO: optimize all this!
+	sr = (double) props.srate;
+	if(is_stereo){
+		for(;;){
+			int rv;
+			float ip,l_ip,r_ip;										
+			if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){ 
+					fprintf(stderr,"\nerror reading infile data");
+					delete [] l_taps;
+					delete l_dline;
+					if(r_taps)
+						delete [] r_taps;
+					sndcloseEx(ifd);
+					exit(1);				
+			}
+			if(!rv) 
+				break;
+
+			ip = l_ip;
+			if(inchans==2){
+				//mix stereo ip to mono for delay purposers
+				if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){ 
+					fprintf(stderr,"\nerror reading infile data");
+					delete [] l_taps;
+					delete l_dline;
+					if(r_taps)
+						delete [] r_taps;
+					sndcloseEx(ifd);
+					exit(1);				
+				}
+				if(!rv) 
+				break;
+				ip = (l_ip + r_ip) * 0.5f;
+			}
+			
+		
+			
+			if(feedback != 0.0f) {
+				l_op = l_dline->tick(ip + (l_op * feedback));
+				r_op = r_dline->tick(ip + (r_op * feedback));
+			}
+			else{
+				l_op = l_dline->tick(ip);
+				r_op = r_dline->tick(ip);
+			}
+
+			//mix mono or stereo input with panned output:
+			if(mix_input){
+				l_out = l_op * wetfac + l_ip * dryfac;
+				r_out = r_op * wetfac;
+				if(inchans==2)
+					r_out  += r_ip * dryfac;
+				else
+					r_out += l_ip * dryfac;
+			}
+			else{
+				l_out = l_op;
+				r_out = r_op;
+			}
+			/* rescale */
+			l_out *=  max_gain;
+			r_out *=  max_gain;
+			if((float)fabs((double)l_out) > peaks[0].value) {
+				peaks[0].value = (float)fabs((double)l_out);
+				peaks[0].position = outsamps;
+			}
+			if((float)fabs((double)r_out) > peaks[1].value) {
+				peaks[1].value = (float)fabs((double)r_out);
+				peaks[1].position = outsamps;
+			}
+
+
+			if(fputfloatEx(&l_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
+				delete [] l_taps;
+				delete l_dline;
+				if(r_taps)
+					delete [] r_taps;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			if(fputfloatEx(&r_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
+				delete [] l_taps;
+				delete l_dline;
+				if(r_taps)
+					delete [] r_taps;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			outsamps++;
+			if((outsamps % step) == 0)
+				//inform(step,props.srate);
+				fprintf(stdout,"%.2f\r",(double)outsamps/sr);
+		}
+		for(i = 0; i < trailersamps;i++){
+			if(feedback != 0.0f){
+				l_op = l_dline->tick(feedback * l_op);
+				r_op = r_dline->tick(feedback * r_op);
+			}
+			else {
+				l_op = l_dline->tick(0.0f);
+				r_op = r_dline->tick(0.0f);
+			}
+			if(mix_input){			
+				l_out = l_op * wetfac;
+				r_out = r_op * wetfac;
+			}
+			else {
+				l_out = l_op;
+				r_out = r_op;
+			}
+			/* rescale */
+			l_out *=  max_gain;
+			r_out *=  max_gain;
+			if((float)fabs((double)l_out) > peaks[0].value) {
+				peaks[0].value = (float)fabs((double)l_out);
+				peaks[0].position = outsamps;
+			}
+			if((float)fabs((double)r_out) > peaks[1].value) {
+				peaks[1].value = (float)fabs((double)r_out);
+				peaks[1].position = outsamps;
+			}
+			if(fputfloatEx(&l_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
+				delete [] l_taps;
+				delete l_dline;
+				if(r_taps)
+					delete [] r_taps;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			if(fputfloatEx(&r_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile: %s",sferrstr());
+				delete [] l_taps;
+				delete l_dline;
+				if(r_taps)
+					delete [] r_taps;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			outsamps++;
+			if((outsamps % step) == 0)
+				//inform(step,props.srate);
+				fprintf(stdout,"%.2f\r",(double)outsamps/sr);
+		}
+	}
+
+	else{
+		for(;;){
+			int rv;
+			float ip,l_ip,r_ip;			
+			if((rv = fgetfloatEx(&l_ip,ifd,0)) < 0){ 
+				fprintf(stderr,"\nerror reading infile data");
+				delete [] l_taps;
+				delete l_dline;
+				sndcloseEx(ifd);
+				exit(1);
+
+			}
+			if(!rv) 
+				break;
+			if(inchans==2){
+				//mix stereo ip to mono for delay purposers
+				if((rv = fgetfloatEx(&r_ip,ifd,0)) < 0){ 
+					fprintf(stderr,"\nerror reading infile data");
+					delete [] l_taps;
+					delete l_dline;
+					sndcloseEx(ifd);
+					exit(1);				
+				}
+				if(!rv) 
+				break;
+				ip = (l_ip + r_ip) * 0.5f;
+			}
+			else
+				ip = l_ip;
+			if(feedback != 0.0f)
+				l_op = l_dline->tick(ip + l_op * feedback);
+			else
+				l_op = l_dline->tick(ip);
+
+			if(mix_input)
+				l_out = l_op * wetfac + l_ip * dryfac;
+			else
+				l_out = l_op;
+			/* rescale */
+			l_out *=  max_gain;
+			if((float)fabs((double)l_out) > peaks[0].value) {
+				peaks[0].value = (float)fabs((double)l_out);
+				peaks[0].position = outsamps;
+			}
+			
+			if(fputfloatEx(&l_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile");
+				delete [] l_taps;
+				delete l_dline;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			outsamps++;
+			if((outsamps % step) == 0)
+				//inform(step,props.srate);
+				fprintf(stdout,"%.2f\r",(double)outsamps/sr);
+		}
+		for(i = 0; i < trailersamps;i++){						
+			l_op = l_dline->tick(feedback * l_op);
+			if(mix_input)
+				l_out = l_op * wetfac;
+			else
+				l_out = l_op;
+			if((float)fabs((double)l_out) > peaks[0].value) {
+				peaks[0].value = (float)fabs((double)l_out);
+				peaks[0].position = outsamps;
+			}
+			if(fputfloatEx(&l_out,ofd) < 1){
+				fprintf(stderr,"\nerror writing to outfile");
+				delete [] l_taps;
+				delete l_dline;
+				sndcloseEx(ifd);
+				exit(1);
+			}
+			outsamps++;
+			if((outsamps % step) == 0)
+				//inform(step,props.srate);
+				fprintf(stdout,"%.2f\r",(double)outsamps/sr);
+		}
+	}
+	fprintf(stdout,"%.2f\n",(double)outsamps/sr);
+	printf("\nPEAK data:\n");
+	for(i=0;i < nchans;i++)
+		printf("Channel %u: %.4f at frame %d: %.4lf secs\n",i+1,
+			peaks[i].value,peaks[i].position, (double) peaks[i].position / (double)props.srate);
+	sndputpeaks(ofd,nchans,peaks);
+	if(sndcloseEx(ofd) < 0){
+		fprintf(stderr,"\nwarning: error closing outfile");
+
+	}
+	delete [] l_taps;
+	delete l_dline;
+	if(is_stereo) {
+		delete [] r_taps;
+		delete r_dline;
+	}
+	
+	sndcloseEx(ifd);
+	return 0;
+}
+
+	
+void usage(void)
+{
+	printf("\n*******  STEREO MULTI-TAPPED DELAY WITH PANNING v1.0 1998 : CDP Release 4 ******\n");
+	printf("\nusage:\n\ttapdelay [-f] infile outfile tapgain feedback mix taps.txt [trailtime]\n");
+	printf("\n\t-f\t\twrite floating-point outfile");
+	printf("\n\t\t\t(default: outfile has same format as infile)");
+	printf("\n\ttapgain\t\tgain factor applied to output from delayline");
+	printf("\n\t\t\t(tapgain > 0.0, typ 0.25)");
+	printf("\n\tfeedback\trange: -1 <= feeedback <= 1.0");
+	printf("\n\tmix\t\tproportion of source mixed with delay output.\n\t\t\trange: 0.0 <= mix < 1.0");
+	printf("\n\ttrailtime\textra time in seconds (beyond infile dur) ");
+	printf("\n\t\t\t  for delays to play out.");
+	printf("\n\tStereo inputs are mixed to mono at input to the delay line.\n");
+	printf("\n\ttaps.txt consists of list of taps in the format:");
+	printf("\n\ttime amp [pan]");
+	printf("\n\n\tAll values are floating-point, one tap per line.");
+	printf("\n\tTimes (seconds) must be increasing. Duplicate times are ignored.");
+	printf("\n\tA zero time (no delay) overrides the mix parameter,");
+	printf("\n\t  and determines the level and pan of the (mono) input.");	
+	printf("\n\tAmp values must be in the range 0.0 to 1.0");
+	printf("\n\tEmpty lines are permitted, as are lines starting with  a semni-colon,");	
+	printf("\n\t  which may be used for comments.");
+	printf("\n\tIf a pan value is used in any line, outfile will be stereo.");
+	printf("\n\tPan values are nominally in the range -1 to +1: 0 = centre (mono)");
+	printf("\n\t within which constant-power panning is used.");
+	printf("\n\tValues beyond these limits result in attenuation according to the");
+	printf("\n\t   inverse-square law, to suggest distance beyond the speakers.\n");
+	
+}
+
+//TW's constant-power pan routine, even does inv-square reductions beyond the speakers
+void pancalc(double position,double *leftgain,double *rightgain)
+{
+	int dirflag;
+	double temp;
+	double relpos;
+	double reldist, invsquare;
+
+	if(position < 0.0)
+		dirflag = SIGNAL_TO_LEFT;		/* signal on left */
+	else
+		dirflag = SIGNAL_TO_RIGHT;
+
+	if(position < 0) 
+		relpos = -position;
+	else 
+		relpos = position;
+	if(relpos <= 1.0){		/* between the speakers */
+		temp = 1.0 + (relpos * relpos);
+		reldist = ROOT2 / sqrt(temp);
+		temp = (position + 1.0) / 2.0;
+		*rightgain = temp * reldist;
+		*leftgain = (1.0 - temp ) * reldist;
+	} else {				/* outside the speakers */
+		temp = (relpos * relpos) + 1.0;			 /*RWD: NB same in both cases! */
+		reldist  = sqrt(temp) / ROOT2;   /* relative distance to source */
+		invsquare = 1.0 / (reldist * reldist);
+		if(dirflag == SIGNAL_TO_LEFT){
+			*leftgain = invsquare;
+			*rightgain = 0.0;
+		} else {   /* SIGNAL_TO_RIGHT */
+			*rightgain = invsquare;
+			*leftgain = 0;
+		}
+	}
+}

+ 809 - 0
dev/externals/reverb/wavetable.cpp

@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+// Jan 2009  extended random lfo to support gaussian distribution
+
+
+//////////////////////////////////////////////////////////////////////
+
+#include <math.h>
+#include <memory.h>
+#include <time.h>
+#include "wavetable.h"
+#include <assert.h>
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+#ifndef PI
+#define PI (3.141592634)
+#endif
+
+#ifndef TWOPI
+#define TWOPI (2.0 * PI)
+#endif
+
+#define LAMBDA (10.0)
+
+
+/* rand stuff from Csound*/
+static const long RANDINT_MAX = 2147483647L;        // 2^^31 - 1 */
+static const long BIPOLAR = 0x4fffffff;
+static const long RIA = 16807;
+static const long RIM = 0x7fffffffL;
+static const double dv2_31 = 1.0 / 2147483648.0;
+static const long MAXLEN = 0x1000000L;
+static const long PHMASK = 0x0ffffffL;
+static const double FMAXLEN = (double)(0x1000000L);
+// for tpdf - bit of a hack, but seems close enough!
+static const long TPDFSHIFT = 0x18000000;
+// for gauss, trial and error!
+static const long BISHIFT = 0x16000000;
+
+
+
+fastlfo::fastlfo()
+{
+	param.freq = 0.0;
+	param.modrange = 0.0;
+	m_cur_WaveType = LFO_SINE;
+	curfreq = param.freq = 0.0;
+	curphase = 0.0;
+	incr = 0.0;
+	tf = 0;
+	kicvt = 0;
+	offset= 0.0;
+	b_sync = false;
+	tpdf = false;
+    gauss = false;
+    /* for csrand */
+    csseed = 12345;
+}
+
+double      fastlfo::csrand()  
+{ 
+    long rval;
+    rval =  randint31(csseed);
+    csseed = rval;
+    return (double) rval / (double) RANDINT_MAX;
+}
+
+
+long fastlfo::init(double srate, double normphase, long seedval, unsigned long ksmps)
+{
+	long seed;
+
+	m_srate = srate / ksmps;
+	twopiovrsr = TWOPI / m_srate;	
+	m_inv_srate = 1.0 / m_srate;
+	set_WaveType(LFO_SINE);
+	phs = (long)(MAXLEN * normphase);
+	curphase = TWOPI * normphase;
+	if(seedval == 0)
+		seed = (long) time(NULL);
+	else
+		seed = seedval;
+	rand = randint31(seed);
+	rand = randint31(rand);	
+    num1 = 0;
+    // get 2nd randon val for diff calc
+    // TODO: check this! why << 1 ?
+	rand = randint31(rand);
+	//num2 = (double)(rand<<1) * dv2_31;
+    num2 = (double)((long)((unsigned)rand<<1)-BIPOLAR) * dv2_31;
+
+	dfdmax = (num2 - num1) / FMAXLEN;
+	kicvt = (long)((double)MAXLEN / m_srate);
+	lastval = 0.0;
+	ksamps = ksmps;
+	if(ksamps==0)
+		ksamps++;
+	kcount = 1;
+	b_sync = false;
+	curphs = (long)(normphase * MAXLEN);
+	return seed;
+}
+
+
+// we keep current wavetype; no reset of random values ;
+/* phase ranged 0 -1 */
+void fastlfo::reset(double phase)
+{ 
+	curphase  = TWOPI * phase;
+	phs =  (long)(MAXLEN * phase);
+	curphs = phs; // assume phase offset = 0 here? 
+	lastval = 0.0;
+	b_sync = false;
+	kcount = 1;
+}
+
+
+
+#define OSC_WRAPPHASE  if(curphase >= TWOPI) curphase -= TWOPI;	\
+						if(curphase < 0.0) curphase += TWOPI
+// input range 0-1
+void fastlfo::sync_phase(double phase,double phaseoffset,double phaseincr)
+{
+	if(b_sync) {
+		curphase = phase * TWOPI;
+		offset = phaseoffset * TWOPI;
+		incr = phaseincr * TWOPI;
+		phs_incr = (long)(phaseincr*ksamps * MAXLEN);
+		phs = (long)(phase * MAXLEN);
+		phs &= PHMASK;
+		phs_offset = (long)(phaseoffset * MAXLEN);
+		curphs = phs + phs_offset;
+		curphs &= PHMASK;
+	}
+}
+
+inline double fastlfo::sinetick(void)
+{
+	double val;
+	double thisphase = curphase + offset;	
+	while(thisphase >= TWOPI)
+		thisphase -= TWOPI;
+	if(kcount==ksamps){
+		if(!b_sync){
+			val = sin(thisphase);
+			if(curfreq != param.freq){
+				curfreq = param.freq;
+				incr = twopiovrsr * param.freq;		
+			}
+		}
+		else
+			val = sin(thisphase);
+		curphase += incr;
+		//OSC_WRAPPHASE;
+		if(curphase >= TWOPI) 
+			curphase -= TWOPI;	
+		if(curphase < 0.0) 
+			curphase += TWOPI;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return lastval;
+}
+
+inline double fastlfo::tritick(void)
+{
+	double val;
+	double thisphase = curphase + offset;	
+	while(thisphase >= TWOPI)
+		thisphase -= TWOPI;
+	if(kcount==ksamps){
+		if(!b_sync){
+			if(curfreq != param.freq){
+				curfreq = param.freq;
+				incr = twopiovrsr * param.freq;		
+			}
+		}
+		if(thisphase <= PI)	{
+			val =   (4.0 * (thisphase * (1.0 / TWOPI) )) - 1.0;
+		}
+		else {
+			val =  3.0 - 4.0 * (thisphase * (1.0 / TWOPI) );
+		}
+		curphase += incr;
+		OSC_WRAPPHASE;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+	
+}
+
+inline double fastlfo::squaretick(void)
+{
+	double val;
+	double thisphase = curphase + offset;	
+	while(thisphase >= TWOPI)
+		thisphase -= TWOPI;
+	if(kcount==ksamps){
+		if(!b_sync){
+			if(curfreq != param.freq){
+				curfreq = param.freq;
+				incr = twopiovrsr * param.freq;		
+			}
+		}
+		if(thisphase <= PI)
+			val = 1.0;
+		else
+			val = -1;
+		curphase += incr;
+		OSC_WRAPPHASE;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+	
+}
+
+double fastlfo::sawuptick(void)
+{
+	double val;
+	double thisphase = curphase + offset;	
+	while(thisphase >= TWOPI)
+		thisphase -= TWOPI;
+
+	if(kcount==ksamps){
+		if(!b_sync){
+			if(curfreq != param.freq){
+				curfreq = param.freq;
+				incr = twopiovrsr * param.freq;		
+			}
+		}
+		val =  (2.0 * (thisphase * (1.0 / TWOPI) )) - 1.0;
+		curphase += incr;
+		OSC_WRAPPHASE;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+	
+}
+
+inline double fastlfo::sawdowntick(void)
+{
+	double val;
+	double thisphase = curphase + offset;	
+	while(thisphase >= TWOPI)
+		thisphase -= TWOPI;
+
+	if(kcount==ksamps){
+		if(!b_sync){
+			if(curfreq != param.freq){
+				curfreq = param.freq;
+				incr = twopiovrsr * param.freq;		
+			}
+		}
+		val =  1.0 - 2.0 * (thisphase * (1.0 / TWOPI) );
+		curphase += incr;
+		OSC_WRAPPHASE;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+
+// some redundant code with phs and curphs here, but keep both for now!
+
+
+inline double fastlfo::randomtick(void)
+{
+	double val;
+	double dval = 0.0;
+    int i;
+
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1 +(double)(curphs * dfdmax);
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r =  randint31(rand);
+				if(tpdf){
+					long rr = randint31(r);
+                    long ri;
+                    unsigned long sum = r + rr;
+                    ri = (long)(sum/2);                       
+                    r =  ri - TPDFSHIFT;
+				}
+                else if(gauss){
+                    int nrands = 16;
+                    long ri = 0;
+                    long rr[16];
+                    rr[0] = rand;
+                    for(i = 1; i < nrands; i++)
+                        rr[i] = randint31(rr[i-1]);
+                    for(i=0;i < nrands;i++)
+                        ri += rr[i]>>4;                        
+                    r = ri - BISHIFT;    
+                }
+                else if(biexp){
+                    long rr = r;
+                    double dbiexp = 0.0;
+                                    
+                    dbiexp = 2.0 * (double)rr * dv2_31;
+                    while(dbiexp==0.0 || dbiexp == 2.0){                    
+                        rr = randint31(rr);                                            
+                    }                
+                    if(dbiexp > 1.0)
+                        dval = -log(2.0-dbiexp) / LAMBDA;
+                    if(dbiexp < 1.0)
+                        dval = log(dbiexp) / LAMBDA;
+                    if( dval > 0.9)
+                        dval = 0.9;
+                    if(dval < -0.9)
+                        dval = -0.9;                                        
+                    r =   ((unsigned long)(dval/dv2_31) +BIPOLAR)>>1;                                       
+                }
+				curphs &= PHMASK;
+				rand = r;
+				num1 = num2;
+				num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+				dfdmax = (num2-num1) / FMAXLEN;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else{           
+            val = num1 +(double)(phs * dfdmax);
+            phs +=(long) (param.freq * kicvt);
+            if(phs >= MAXLEN){
+                long r =  randint31(rand);
+                assert(r>0);
+                if(tpdf){
+                    long rr = randint31(r);
+                    long ri;
+                    unsigned long sum = r + rr;
+                    ri = (long)(sum/2);                   
+                    r =  ri - TPDFSHIFT;                     
+                }
+                else if(gauss){
+                    int nrands = 16;
+                    long ri = 0;
+                    long rr[16];
+                    rr[0] = rand;
+                    for(i = 1;i < nrands;i++)
+                        rr[i]= randint31(rr[i-1]);
+                    for(i=0;i < nrands;i++)
+                        ri += rr[i]>>4;                        
+                    r = ri - BISHIFT;                        
+                }
+                else if(biexp){
+                    long rr = r;
+                    double dbiexp = 0.0;
+                                    
+                    dbiexp = 2.0 * (double)rr * dv2_31;
+                    while(dbiexp==0.0 || dbiexp == 2.0){                    
+                        rr = randint31(rr);                                            
+                    }                
+                    if(dbiexp > 1.0)
+                        dval = -log(2.0-dbiexp) / LAMBDA;
+                    if(dbiexp < 1.0)
+                        dval = log(dbiexp) / LAMBDA;
+                    if( dval > 0.9)
+                        dval = 0.9;
+                    if(dval < -0.9)
+                        dval = -0.9;                                        
+                    r =   ((unsigned long)(dval/dv2_31) +BIPOLAR)>>1;
+                    /* assert(r>0);
+                      assert(r < RANDINT_MAX);
+                    */
+                }            
+                phs &= PHMASK;
+                rand = r;
+                num1 = num2;
+                num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+                /* NB makes uniform rand not really uniform, more a sort of rounded distr
+                 * so maybe not ideal for strict plain uniform distr - still while though!
+                */
+                dfdmax = (num2-num1) / FMAXLEN;                            
+            }
+		}
+        if(gauss)
+            val *= 1.85;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+inline double fastlfo::rand_tpdf_tick(void)
+{
+	double val;
+
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1 +(double)(curphs * dfdmax);
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r =  randint31(rand);
+				
+				long rr = randint31(r);
+                long ri;
+                long sum = r + rr;
+                ri = (long)(sum/2);                       
+                r =  ri - TPDFSHIFT;
+				                              
+				curphs &= PHMASK;
+				rand = r;
+				num1 = num2;
+				num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+				dfdmax = (num2-num1) / FMAXLEN;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else{           
+            val = num1 +(double)(phs * dfdmax);
+            /* need this test for audiorate noise; but is it the right test for everywhere? */
+            /* time-varying noise rate? */
+            if(ksamps > 1)
+                phs +=(long) (param.freq * kicvt + 0.5);
+            else
+                phs = MAXLEN;
+            if(phs >= MAXLEN){
+                long r =  randint31(rand);                                
+                long rr = randint31(r);
+                long rsum;
+#ifdef _DEBUG
+                double d1,d2,dtpdf;
+                d1 = ((2.0 *(double)r)  / (double) RANDINT_MAX) - 1.0;
+                d2 = ((2.0 *(double)rr) / (double) RANDINT_MAX) - 1.0;
+                dtpdf = d1 + d2;
+                dtpdf *= 0.5;
+#endif
+                rand = rr;
+                /* bipolar value  =  2*r - 1  or 2* (r - 0.5)*/
+                long bipolard2 = 0x40000000;
+                r  = (r - bipolard2);
+                rr = (rr- bipolard2);
+                rsum = r + rr;
+                double dsum = (double)rsum  / (double) RANDINT_MAX ;                                                                                          
+                phs &= PHMASK; 
+                num1 = num2;
+                num2 = dsum;
+                dfdmax = (num2-num1) / FMAXLEN;                            
+            }
+		}        
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+inline double fastlfo::rand_gauss_tick(void)
+{
+	double val;
+    int i;
+
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1 +(double)(curphs * dfdmax);
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r =  randint31(rand);				                
+                int nrands = 16;
+                long ri = 0;
+                long rr[16];
+
+                rr[0] = rand;
+                for(i = 1; i < nrands; i++)
+                    rr[i] = randint31(rr[i-1]);
+                for(i=0;i < nrands;i++)
+                    ri += rr[i]>>4;                        
+                r = ri - BISHIFT;    
+                                
+				curphs &= PHMASK;
+				rand = r;
+				num1 = num2;
+				num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+				dfdmax = (num2-num1) / FMAXLEN;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else{           
+            val = num1 +(double)(phs * dfdmax);
+            if(ksamps > 1)
+                phs +=(long) (param.freq * kicvt + 0.5);
+            else
+                phs = MAXLEN;
+            
+            if(phs >= MAXLEN){
+                long r =  randint31(rand);                                                
+                int nrands = 16;
+                long ri = 0;
+                long rr[16];
+
+                rr[0] = rand;
+                for(i = 1;i < nrands;i++)
+                    rr[i]= randint31(rr[i-1]);
+                for(i=0;i < nrands;i++)
+                    ri += rr[i]>>4;                        
+                r = ri - BISHIFT;                        
+                                         
+                phs &= PHMASK;
+                rand = r;
+                num1 = num2;
+                num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;                
+                dfdmax = (num2-num1) / FMAXLEN;                            
+            }
+		}
+        
+        val *= 1.85;
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+inline double fastlfo::rand_exp_tick(void)
+{
+	double val;
+	double dval = 0.0;
+
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1 +(double)(curphs * dfdmax);
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r =  randint31(rand);				                
+                long rr = r;
+                double dbiexp = 0.0;
+                                    
+                dbiexp = 2.0 * (double)rr * dv2_31;
+                while(dbiexp==0.0 || dbiexp == 2.0){                    
+                    rr = randint31(rr);                                            
+                }                
+                if(dbiexp > 1.0)
+                    dval = -log(2.0-dbiexp) / LAMBDA;
+                if(dbiexp < 1.0)
+                    dval = log(dbiexp) / LAMBDA;
+                if( dval > 0.9)
+                    dval = 0.9;
+                if(dval < -0.9)
+                    dval = -0.9;                                        
+                r =   ((unsigned long)(dval/dv2_31) +BIPOLAR)>>1;                                       
+                
+				curphs &= PHMASK;
+				rand = r;
+				num1 = num2;
+				num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+				dfdmax = (num2-num1) / FMAXLEN;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else{           
+            val = num1 +(double)(phs * dfdmax);
+            if(ksamps > 1)
+                phs +=(long) (param.freq * kicvt + 0.5);
+            else
+                phs = MAXLEN;            
+            if(phs >= MAXLEN){
+                long r =  randint31(rand);                                
+                long rr = r;
+                double dbiexp = 0.0;
+                                    
+                dbiexp = 2.0 * (double)rr * dv2_31;
+                while(dbiexp==0.0 || dbiexp == 2.0){                    
+                    rr = randint31(rr);                                            
+                }                
+                if(dbiexp > 1.0)
+                    dval = -log(2.0-dbiexp) / LAMBDA;
+                if(dbiexp < 1.0)
+                    dval = log(dbiexp) / LAMBDA;
+                if( dval > 0.9)
+                    dval = 0.9;
+                if(dval < -0.9)
+                    dval = -0.9;                                        
+                r =   ((unsigned long)(dval/dv2_31) +BIPOLAR)>>1;
+                                                                  
+                phs &= PHMASK;
+                rand = r;
+                num1 = num2;
+                num2 = (double)((long)((unsigned)r<<1)-BIPOLAR) * dv2_31;
+                
+                dfdmax = (num2-num1) / FMAXLEN;                            
+            }
+		}        
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+
+inline double fastlfo::randhtick(void)
+{
+	double val;
+	
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1;
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r = randint31(rand);
+				if(tpdf){
+					long rr = randint31(r);
+                    long ri;
+                    unsigned long sum = r + rr;
+                    ri = (long)(sum/2);                       
+                    r =  ri - TPDFSHIFT;
+				}
+				rand = r;
+				num1 = (double)((long)((unsigned)r<<1) - BIPOLAR) * dv2_31;
+				curphs &= PHMASK;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else {
+			val = num1;
+            if(ksamps > 1)
+                phs +=(long) (param.freq * kicvt + 0.5);
+            else
+                phs = MAXLEN;
+			
+			if(phs >= MAXLEN){
+				long r = randint31(rand);
+				if(tpdf){
+					long rr = randint31(r);
+                    long ri;
+                    unsigned long sum = r + rr;
+                    ri = (long)(sum/2);                       
+                    r =  ri - TPDFSHIFT;
+				}
+				phs &= PHMASK;
+				rand = r;
+				num1 = (double)((long)((unsigned)r<<1) - BIPOLAR) * dv2_31;
+			}
+		}
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+
+inline double fastlfo::randh_tpdf_tick(void)
+{
+	double val;
+	
+	if(kcount==ksamps){
+		if(b_sync){
+			val = num1;
+			phs += phs_incr;
+			curphs += phs_incr;
+			if(curphs >= MAXLEN){
+				long r = randint31(rand);
+				
+				long rr = randint31(r);
+                long ri;
+                unsigned long sum = r + rr;
+                ri = (long)(sum/2);                       
+                r =  ri - TPDFSHIFT;
+				
+				rand = r;
+				num1 = (double)((long)((unsigned)r<<1) - BIPOLAR) * dv2_31;
+				curphs &= PHMASK;
+			}
+			if(phs >= MAXLEN)
+				phs &= PHMASK;
+		}
+		else {
+			val = num1;			
+			if(ksamps > 1)
+                phs +=(long) (param.freq * kicvt + 0.5);
+            else
+                phs = MAXLEN;
+			if(phs >= MAXLEN){
+				long r = randint31(rand);
+				
+				long rr = randint31(r);
+                long ri;
+                unsigned long sum = r + rr;
+                ri = (long)(sum/2);                       
+                r =  ri - TPDFSHIFT;
+				
+				phs &= PHMASK;
+				rand = r;
+				num1 = (double)((long)((unsigned)r<<1) - BIPOLAR) * dv2_31;
+			}
+		}
+		lastval = val * param.modrange;
+		kcount = 1;
+	}
+	else
+		kcount++;
+	return 	lastval;
+}
+
+/* return positive value between 0 and RANDINT_MAX */
+inline long fastlfo::randint31(long seed31)
+{
+	unsigned long rilo, rihi;
+
+    rilo = RIA * (long)(seed31 & 0xFFFF);
+    rihi = RIA * (long)((unsigned long)seed31 >> 16);
+    rilo += (rihi & 0x7FFF) << 16;
+    if (rilo > (unsigned long) RIM) {
+      rilo &= RIM;
+      ++rilo;
+    }
+    rilo += rihi >> 15;
+    if (rilo > (unsigned long) RIM) {
+      rilo &= RIM;
+      ++rilo;
+    }
+    return (long)rilo;
+}
+
+bool fastlfo::set_WaveType(LfoWaveType type)		
+{	
+	switch(type){
+	case(LFO_SINE):
+            tf = &fastlfo::sinetick;
+		break;
+	case(LFO_TRIANGLE):
+		tf = &fastlfo::tritick;
+		break;
+	case(LFO_SQUARE):
+		tf = &fastlfo::squaretick;
+		break;
+	case(LFO_SAWUP):
+		tf = &fastlfo::sawuptick;
+		break;
+	case(LFO_SAWDOWN):
+		tf = &fastlfo::sawdowntick;
+		break;
+	case(LFO_RANDOM):
+		tf = &fastlfo::randomtick;
+		break;
+       case(LFO_RAND_TPDF):
+		tf = &fastlfo::rand_tpdf_tick;
+		break;
+       case(LFO_RAND_GAUSS):
+		tf = &fastlfo::rand_gauss_tick;
+		break;
+       case(LFO_RAND_EXP):
+		tf = &fastlfo::rand_exp_tick;
+		break;
+	case(LFO_RND_SH):
+		tf = &fastlfo::randhtick;
+		break;
+       case(LFO_RANDH_TPDF):
+		tf = &fastlfo::randh_tpdf_tick;
+		break;
+	default:
+		return false;
+	}
+	m_cur_WaveType = type;	
+	return true;
+}
+

+ 126 - 0
dev/externals/reverb/wavetable.h

@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1983-2013 Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * 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
+ *
+ */
+ 
+
+// wavetable.h: interface for the fast_lfo class.
+// Feb 2009 added gaussian option to random
+//FWD 3.20: eliminate warning of unused private var (phsmask)
+
+//////////////////////////////////////////////////////////////////////
+
+
+#if !defined(AFX_WAVETABLE_H__2F961225_3FF4_11D2_96D4_444553540000__INCLUDED_)
+#define AFX_WAVETABLE_H__2F961225_3FF4_11D2_96D4_444553540000__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+typedef enum lfowavetype {LFO_SINE,LFO_TRIANGLE,LFO_SAWUP,LFO_SAWDOWN,LFO_SQUARE,LFO_RANDOM,LFO_RND_SH,
+LFO_RAND_TPDF,LFO_RAND_GAUSS,LFO_RAND_EXP, LFO_RANDH_TPDF,
+/*LFO_RANDH_GAUSS,LFO_RANDH_EXP, */
+LFO_NUM_WAVES } LfoWaveType;
+
+enum {PVS_LFO_FREQ_LORANGE, PVS_LFO_FREQ_HIRANGE};
+
+
+typedef struct lfoparam
+{
+	double freq;
+	double modrange;
+} LfoParam;
+
+
+
+class fastlfo {
+	typedef double (fastlfo:: *tickfunc)(void);
+public:
+
+	fastlfo();
+	virtual ~fastlfo() {}
+	long init(double srate, double normphase = 0.0,long seedval = 0,unsigned long ksmps = 1);
+	double		tick(void)							{ return (this->*tf)(); }
+	tickfunc	tf;
+	void		set_freq(double freq)				{ param.freq = freq;	}
+	double		get_freq(void)				const	{ return param.freq;	}
+	void		set_mod(double mod)					{ param.modrange = mod;	}
+	double		get_mod(void)				const	{ return param.modrange;}
+	void		reset(double phase);
+	bool		set_WaveType(LfoWaveType type);
+	void		set_tpdf()              		    { tpdf = true; gauss = biexp = false;}
+    void        set_white()                         {  tpdf = gauss = biexp = false;}
+    void        set_biexp()                         {  tpdf = gauss = false; biexp = true;}
+    void        set_flat()                          { tpdf = gauss = biexp = false;}
+    void        set_gaussian()                      { tpdf = biexp = false; gauss = true;}
+	void		sync_phase(double phase, double offset, double phaseincr);
+	void		set_sync(bool onoff)				{ b_sync = onoff;}
+    double      csrand();                   
+private:
+	double	sinetick(void);
+	double	tritick(void);
+	double	squaretick(void);
+	double	sawuptick(void);
+	double	sawdowntick(void);
+	double	randomtick(void);
+	double	randhtick(void);
+    double  rand_tpdf_tick(void);
+    double  rand_gauss_tick(void);
+    double  rand_exp_tick(void);
+    double  randh_tpdf_tick(void);
+ //   double  randh_gauss_tick(void);
+ //   double  randh_exp_tick(void);
+
+	long	randint31(long seed31);
+
+	double	m_srate, m_inv_srate;
+	double twopiovrsr;
+	double curfreq;
+	double curphase;
+	double incr;
+	LfoParam	param;
+	LfoWaveType  m_cur_WaveType;  
+	/* vars for random oscs, with a little help from Csound!*/
+	long		phs;
+	long		kicvt;
+//	long		phsmask;   // currently not used
+	long		rand;
+	double		num1,num2;
+	double		dfdmax;
+	/* for krate output */
+	unsigned long		ksamps;
+	unsigned long		kcount;
+	double		lastval;
+	bool	tpdf;
+    bool gauss;
+    bool biexp;
+	// for lfo sync
+	bool b_sync;
+	double		offset;
+	// for random oscs
+	long curphs;
+	long phs_incr;
+	long phs_offset;
+    /* for csrand */
+    long csseed;
+};
+
+#endif // !defined(AFX_WAVETABLE_H__2F961225_3FF4_11D2_96D4_444553540000__INCLUDED_)
+
+