Browse Source

initial commit

richarddobson 3 years ago
parent
commit
39fc6297cd

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