瀏覽代碼

initial commit

richarddobson 3 年之前
父節點
當前提交
8f95598170
共有 11 個文件被更改,包括 4510 次插入0 次删除
  1. 20 0
      dev/pv/CMakeLists.txt
  2. 32 0
      dev/pv/Makefiled.osx
  3. 450 0
      dev/pv/ap_pvoc.c
  4. 236 0
      dev/pv/main.c
  5. 804 0
      dev/pv/mxfft.c
  6. 1019 0
      dev/pv/pvoc.c
  7. 20 0
      dev/pview/CMakeLists.txt
  8. 342 0
      dev/pview/pview.c
  9. 37 0
      dev/pvxio2/Makefiled.osx
  10. 66 0
      dev/pvxio2/pvdefs.h
  11. 1484 0
      dev/pvxio2/pvfileio.c

+ 20 - 0
dev/pv/CMakeLists.txt

@@ -0,0 +1,20 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -Dunix")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O2 -Wall -DWIN32")
+  else()
+    set(CMAKE_C_FLAGS "-O2 -Wall -Dlinux -Dunix")
+  endif()
+endif()
+
+link_directories(../cdp2k ../sfsys)
+
+include_directories(../../include)
+
+add_executable(pvoc main.c ap_pvoc.c mxfft.c pvoc.c)
+
+target_link_libraries(pvoc cdp2k sfsys ${EXTRA_LIBRARIES})
+
+my_install(pvoc)
+

+ 32 - 0
dev/pv/Makefiled.osx

@@ -0,0 +1,32 @@
+#
+#	OSX debug makefile for pvoc � Richard Dobson, CDP Ltd 2014
+#
+#
+CC=gcc
+CFLAGS= -g -Wall -mmacosx-version-min=10.5 -Dunix -I ../include -I ../../include
+SFSYS= ../../lib/libsfsysd.a
+CDP2K= ../../lib/libcdp2kd.a
+PROG=pvocd
+BINS= main.o ap_pvoc.o mxfft.o pvoc.o 
+ 
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+#
+#	targets
+#
+all:	$(PROG)
+
+$(PROG):	$(BINS)
+	$(CC) $(CFLAGS) $(BINS) -o $(PROG) $(CDP2K) $(SFSYS)
+
+clean:
+	rm -f $(PROG)
+	
+veryclean:	clean
+	rm -f *.o
+
+install:	$(PROG)
+	cp $(PROG) ../Release
+#
+#	dependencies
+#

+ 450 - 0
dev/pv/ap_pvoc.c

@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
+ * http://www.trevorwishart.co.uk
+ * 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
+ *
+ */
+
+
+
+/* May 2011 rebuilt with fixed mainfuncs.c for preserving sample type etc */
+#include <stdio.h>
+#include <stdlib.h>
+#include <structures.h>
+#include <cdpmain.h>
+#include <tkglobals.h>
+#include <pnames.h>
+#include <pvoc.h>
+#include <processno.h>
+#include <modeno.h>
+#include <globcon.h>
+#include <logic.h>
+#include <filetype.h>
+#include <mixxcon.h>
+#include <speccon.h>
+#include <flags.h>
+#include <arrays.h>
+#include <formants.h>
+#include <sfsys.h>
+#include <osbind.h>
+#include <string.h>
+#include <math.h>
+
+/***************************************************************************************/
+/****************************** FORMERLY IN aplinit.c **********************************/
+/***************************************************************************************/
+
+/***************************** ESTABLISH_BUFPTRS_AND_EXTRA_BUFFERS **************************/
+
+int establish_bufptrs_and_extra_buffers(dataptr dz)
+{
+	dz->extra_bufcnt = -1;	/* ENSURE EVERY CASE HAS A PAIR OF ENTRIES !! */
+	dz->bptrcnt = 0;
+	dz->bufcnt  = 0;
+	switch(dz->process) {
+	case(PVOC_ANAL):			dz->extra_bufcnt = 0;	dz->bufcnt = 0;		break;
+	case(PVOC_SYNTH):			dz->extra_bufcnt = 0;	dz->bufcnt = 0;		break;
+	case(PVOC_EXTRACT):			dz->extra_bufcnt = 0;	dz->bufcnt = 0;		break;
+	default:
+		sprintf(errstr,"Unknown program type [%d] in establish_bufptrs_and_extra_buffers()\n",dz->process);
+		return(PROGRAM_ERROR);
+	}
+
+	if(dz->extra_bufcnt < 0) {
+		sprintf(errstr,"bufcnts have not been set: establish_bufptrs_and_extra_buffers()\n");
+		return(PROGRAM_ERROR);
+	}
+	return establish_groucho_bufptrs_and_extra_buffers(dz);
+}
+
+/***************************** SETUP_INTERNAL_ARRAYS_AND_ARRAY_POINTERS **************************/
+
+int setup_internal_arrays_and_array_pointers(dataptr dz)
+{
+	int n;		 
+	dz->ptr_cnt    = -1;		/* base constructor...process */
+	dz->array_cnt  = -1;
+	dz->iarray_cnt = -1;
+	dz->larray_cnt = -1;
+	switch(dz->process) {
+	case(PVOC_ANAL):    dz->array_cnt=0; dz->iarray_cnt=0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
+	case(PVOC_SYNTH):   dz->array_cnt=0; dz->iarray_cnt=0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
+	case(PVOC_EXTRACT): dz->array_cnt=0; dz->iarray_cnt=0; dz->larray_cnt = 0; dz->ptr_cnt = 0; dz->fptr_cnt = 0; break;
+	}
+
+/*** WARNING ***
+ANY APPLICATION DEALING WITH A NUMLIST INPUT: MUST establish AT LEAST 1 double array: i.e. dz->array_cnt = at least 1
+**** WARNING ***/
+
+
+	if(dz->array_cnt < 0 || dz->iarray_cnt < 0 || dz->larray_cnt < 0 || dz->ptr_cnt < 0 || dz->fptr_cnt < 0) {
+		sprintf(errstr,"array_cnt not set in setup_internal_arrays_and_array_pointers()\n");	   
+		return(PROGRAM_ERROR);
+	}
+
+	if(dz->array_cnt > 0) {  
+		if((dz->parray  = (double **)malloc(dz->array_cnt * sizeof(double *)))==NULL) {
+			sprintf(errstr,"INSUFFICIENT MEMORY for internal double arrays.\n");
+			return(MEMORY_ERROR);
+		}
+		for(n=0;n<dz->array_cnt;n++)
+			dz->parray[n] = NULL;
+	}
+	if(dz->iarray_cnt > 0) {
+		if((dz->iparray = (int     **)malloc(dz->iarray_cnt * sizeof(int *)))==NULL) {
+			sprintf(errstr,"INSUFFICIENT MEMORY for internal int arrays.\n");
+			return(MEMORY_ERROR);
+		}
+		for(n=0;n<dz->iarray_cnt;n++)
+			dz->iparray[n] = NULL;
+	}
+	if(dz->larray_cnt > 0) {	  
+		if((dz->lparray = (int    **)malloc(dz->larray_cnt * sizeof(int *)))==NULL) {
+			sprintf(errstr,"INSUFFICIENT MEMORY for internal long arrays.\n");
+			return(MEMORY_ERROR);
+		}
+		for(n=0;n<dz->larray_cnt;n++)
+			dz->lparray[n] = NULL;
+	}
+	if(dz->ptr_cnt > 0)   {  	  
+		if((dz->ptr    	= (double  **)malloc(dz->ptr_cnt  * sizeof(double *)))==NULL) {
+			sprintf(errstr,"INSUFFICIENT MEMORY for internal pointer arrays.\n");
+			return(MEMORY_ERROR);
+		}
+		for(n=0;n<dz->ptr_cnt;n++)
+			dz->ptr[n] = NULL;
+	}
+	if(dz->fptr_cnt > 0)   {  	  
+		if((dz->fptr = (float  **)malloc(dz->fptr_cnt * sizeof(float *)))==NULL) {
+			sprintf(errstr,"INSUFFICIENT MEMORY for internal float-pointer arrays.\n");
+			return(MEMORY_ERROR);
+		}
+		for(n=0;n<dz->fptr_cnt;n++)
+			dz->fptr[n] = NULL;
+	}
+	return(FINISHED);
+}
+
+/****************************** ASSIGN_PROCESS_LOGIC *********************************/
+
+int assign_process_logic(dataptr dz)
+{						 
+	switch(dz->process) {
+	case(PVOC_ANAL):	  setup_process_logic(SNDFILES_ONLY,		BIG_ANALFILE,		ANALFILE_OUT,	dz);	break;
+	case(PVOC_SYNTH):	  setup_process_logic(ANALFILE_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
+	case(PVOC_EXTRACT):	  setup_process_logic(SNDFILES_ONLY,		UNEQUAL_SNDFILE,	SNDFILE_OUT,	dz);	break;
+	default:
+		sprintf(errstr,"Unknown process: assign_process_logic()\n");
+		return(PROGRAM_ERROR);
+		break;
+	}
+	if(dz->has_otherfile) {
+		switch(dz->input_data_type) {
+		case(ALL_FILES):
+		case(TWO_SNDFILES):
+		case(SNDFILE_AND_ENVFILE):
+		case(SNDFILE_AND_BRKFILE):
+		case(SNDFILE_AND_UNRANGED_BRKFILE):
+		case(SNDFILE_AND_DB_BRKFILE):
+			break;
+		case(MANY_SNDFILES):
+			if(dz->process==INFO_TIMELIST)
+				break;
+			/* fall thro */
+		default:
+			sprintf(errstr,"Most processes accepting files with different properties\n"
+						   "can only take 2 sound infiles.\n");
+			return(PROGRAM_ERROR);
+		}
+	}
+	return(FINISHED);
+}
+
+/***************************** SET_LEGAL_INFILE_STRUCTURE **************************
+ *
+ * Allows 2nd infile to have different props to first infile.
+ */
+
+void set_legal_infile_structure(dataptr dz)
+{
+	switch(dz->process) {
+	default:
+		dz->has_otherfile = FALSE;
+		break;
+	}
+}
+
+/***************************************************************************************/
+/****************************** FORMERLY IN internal.c *********************************/
+/***************************************************************************************/
+
+/****************************** SET_LEGAL_INTERNALPARAM_STRUCTURE *********************************/
+
+int set_legal_internalparam_structure(int process,int mode,aplptr ap)
+{
+	int exit_status = FINISHED;
+	switch(process) {
+		case(PVOC_ANAL):	exit_status = set_internalparam_data("000iiiiiiiiiiii",ap);	break;
+		case(PVOC_SYNTH):	exit_status = set_internalparam_data("000iiiiiiiiiiii",ap);	break;
+		case(PVOC_EXTRACT):	exit_status = set_internalparam_data(   "iiiiiiiiiiii",ap);	break;
+	default:
+		sprintf(errstr,"Unknown process in set_legal_internalparam_structure()\n");
+		return(PROGRAM_ERROR);
+	}
+	return(exit_status);		
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN specialin.c *********************************/
+/********************************************************************************************/
+
+/********************** READ_SPECIAL_DATA ************************/
+
+int read_special_data(char *str,dataptr dz)	   
+{
+	aplptr ap = dz->application;
+
+	switch(ap->special_data) {
+	default:
+		sprintf(errstr,"Unknown special_data type: read_special_data()\n");
+		return(PROGRAM_ERROR);
+	}
+	return(FINISHED);	/* NOTREACHED */
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN preprocess.c ********************************/
+/********************************************************************************************/
+
+/****************************** PARAM_PREPROCESS *********************************/
+
+int param_preprocess(dataptr dz)	
+{
+	switch(dz->process) {
+	case(PVOC_ANAL):			
+	case(PVOC_SYNTH):			
+	case(PVOC_EXTRACT):			
+		return pvoc_preprocess(dz);
+	default:
+		sprintf(errstr,"PROGRAMMING PROBLEM: Unknown process in param_preprocess()\n");
+		return(PROGRAM_ERROR);
+	}
+	return(FINISHED);	/* NOTREACHED */
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN procgrou.c **********************************/
+/********************************************************************************************/
+
+
+/**************************** GROUCHO_PROCESS_FILE ****************************/
+
+int groucho_process_file(dataptr dz)   /* FUNCTIONS FOUND IN PROCESS.C */
+{	
+	switch(dz->process) {
+	case(PVOC_ANAL):
+	case(PVOC_SYNTH):
+	case(PVOC_EXTRACT):	return pvoc_process(dz);
+	default:
+		sprintf(errstr,"Unknown case in process_file()\n");
+		return(PROGRAM_ERROR);
+	}
+	return(FINISHED);	/* NOTREACHED */
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN buffers.c ***********************************/
+/********************************************************************************************/
+
+/**************************** ALLOCATE_LARGE_BUFFERS ******************************/
+
+int allocate_large_buffers(dataptr dz)
+{
+	switch(dz->process) {
+	case(PVOC_ANAL):			
+	case(PVOC_SYNTH):			
+	case(PVOC_EXTRACT):			
+		return FINISHED;
+	default:
+		sprintf(errstr,"Unknown program no. in allocate_large_buffers()\n");
+		return(PROGRAM_ERROR);
+	}
+	return(FINISHED);	/* NOTREACHED */
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN cmdline.c ***********************************/
+/********************************************************************************************/
+
+int get_process_no(char *prog_identifier_from_cmdline,dataptr dz)
+{
+	if     (!strcmp(prog_identifier_from_cmdline,"anal"))			dz->process = PVOC_ANAL;
+	else if(!strcmp(prog_identifier_from_cmdline,"synth"))			dz->process = PVOC_SYNTH;
+	else if(!strcmp(prog_identifier_from_cmdline,"extract"))		dz->process = PVOC_EXTRACT;
+	else {
+		sprintf(errstr,"Unknown program identification string '%s'\n",prog_identifier_from_cmdline);
+		return(USAGE_ONLY);
+	}
+	return FINISHED;
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN usage.c *************************************/
+/********************************************************************************************/
+
+/******************************** USAGE1 ********************************/
+
+int usage1(void)
+{
+	sprintf(errstr,
+	"USAGE: pvoc NAME (mode) infile outfile (parameters)\n"
+	"\n"
+	"where NAME can be any one of\n"
+	"\n"
+	"anal   synth 	extract\n"
+	"\n"
+	"Type 'pvoc anal'  for more info on pvoc anal option... ETC.\n");
+	return(USAGE_ONLY);
+}
+
+/******************************** USAGE2 ********************************/
+
+int usage2(char *str)
+{
+	if(!strcmp(str,"anal")) {		 
+		sprintf(errstr,
+	    "CONVERT SOUNDFILE TO SPECTRAL FILE\n\n"
+		"USAGE: pvoc anal  mode infile outfile [-cpoints] [-ooverlap]\n\n"
+		"MODES ARE....\n"
+		"1) STANDARD ANALYSIS\n"
+		"2) OUTPUT SPECTRAL ENVELOPE VALS ONLY\n"
+		"3) OUTPUT SPECTRAL MAGNITUDE VALS ONLY\n"
+		"POINTS   No of analysis points (2-32768 (power of 2)): default 1024\n"
+		"         More points give better freq resolution\n"
+		"         but worse time-resolution (e.g. rapidly changing spectrum).\n"
+		"OVERLAP  Filter overlap factor (1-4): default 3\n");
+	} else if(!strcmp(str,"synth")) {		 
+		sprintf(errstr,
+	    "CONVERT SPECTRAL FILE TO SOUNDFILE\n\n"
+		"USAGE: pvoc synth infile outfile\n");
+	} else if(!strcmp(str,"extract")) {		 
+		sprintf(errstr,
+	    "ANALYSE THEN RESYNTHESIZE SOUND WITH VARIOUS OPTIONS\n\n"
+		"USAGE: pvoc extract infile outfile\n"
+		"       [-cpoints] [-ooverlap] [-ddochans] [-llochan] [-hhichan]\n\n"
+		"POINTS   No of analysis points (2-32768 (power of 2)): default 1024\n"
+		"         More points give better freq resolution\n"
+		"         but worse time-resolution (e.g. rapidly changing spectrum).\n"
+		"OVERLAP  Filter overlap factor (1-4): default 3\n"
+		"DOCHANS  resynthesize odd (1) or even (2) channels only.\n"
+		"LOCHAN   ignore analysis channels below this in resynth (default: 0)\n"
+		"HICHAN   ignore analysis channels above this in resynth (dflt: highest channel)\n"
+		"LOCHAN and HICHAN refer to channels rather than analysis points.\n"
+		"         There is 1 channel for every 2 points.\n"
+		"         HICHAN should therefore not be > ANALYSIS POINTS/2\n"
+		"         To default to topmost chan, set at ZERO.\n\n"
+		"NB If no flags are set, the output sound will be the same as the input.\n");
+	} else
+		sprintf(errstr,"Unknown option '%s'\n",str);
+	return(USAGE_ONLY);
+}
+
+/******************************** USAGE3 ********************************/
+
+int usage3(char *str1,char *str2)
+{
+	sprintf(errstr,"Insufficient parameters on command line.\n");
+	return(USAGE_ONLY);
+}
+
+/********************************************************************************************/
+/********************************** FORMERLY IN pconsistency.c ******************************/
+/********************************************************************************************/
+
+/******************** CHECK_PARAM_VALIDITY_AND_CONSISTENCY (redundant) *************************/
+
+int check_param_validity_and_consistency(dataptr dz)
+{
+	int exit_status, chans;
+	int chancnt, Nchans;
+	int M, D, win_overlap;
+	float arate;
+	int srate;
+
+	switch(dz->process) {
+	case(PVOC_ANAL):
+		chans = dz->infile->channels;
+		srate = dz->infile->srate;
+		win_overlap = dz->iparam[PVOC_WINOVLP_INPUT]-1;
+		chancnt = dz->iparam[PVOC_CHANS_INPUT];
+		chancnt = chancnt + (chancnt%2);
+		switch(win_overlap) {
+			case 0:	M = 4*chancnt;		break;
+			case 1:	M = 2*chancnt;		break;
+			case 2: M = chancnt;		break;
+			case 3: M = chancnt / 2;	break;
+			default:
+				sprintf(errstr,"pvoc: Invalid window overlap factor.\n");
+				return(PROGRAM_ERROR);
+		}
+		if((D = (int)(M/PVOC_CONSTANT_A)) == 0){
+			fprintf(stdout,"WARNING: Decimation too low: adjusted.\n");
+			fflush(stdout);
+			D = 1;
+		}
+		Nchans = chancnt + 2;
+		if(dz->mode == ENVEL_ONLY || dz->mode == MAG_ONLY) {
+			Nchans /= 2;
+			arate = (float)(Nchans * dz->infile->srate)/(float)D;
+		} else
+			arate = (float)dz->infile->srate/(float)D;
+
+		dz->infile->srate    = (int)arate;
+		dz->infile->channels = chancnt + 2;
+		if((exit_status = create_sized_outfile(dz->wordstor[0],dz))<0)
+			return(exit_status);
+		dz->infile->channels = chans;
+		dz->infile->srate    = srate;
+		break;
+	case(PVOC_SYNTH):
+		chans = dz->infile->channels;
+		srate = dz->infile->srate;
+		dz->infile->channels = 1;
+		dz->infile->srate = dz->infile->origrate; 
+		/*RWD OCT 05 set outfile stype to infile origstype */
+        /* RWD Apr 2011 NB: will get changed if -f flag used */
+		dz->outfile->stype = dz->infile->origstype;
+		if((exit_status = create_sized_outfile(dz->wordstor[0],dz))<0)
+			return(exit_status);
+		dz->infile->channels = chans;
+		dz->infile->srate = srate;
+		break;
+	}
+	return (FINISHED);
+}
+
+/***************************** INNER_LOOP (redundant) **************************/
+
+int inner_loop
+(int *peakscore,int *descnt,int *in_start_portion,int *least,int *pitchcnt,int windows_in_buf,dataptr dz)
+{
+	return(FINISHED);
+}
+

+ 236 - 0
dev/pv/main.c

@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
+ * http://www.trevorwishart.co.uk
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <structures.h>
+#include <tkglobals.h>
+#include <pvoc.h>
+#include <filetype.h>
+#include <processno.h>
+#include <modeno.h>
+#include <formants.h>
+#include <cdpmain.h>
+#include <special.h>
+#include <logic.h>
+#include <globcon.h>
+#include <cdpmain.h>
+#include <sfsys.h>
+#include <ctype.h>
+#include <string.h>
+
+char errstr[2400];
+
+/*extern*/ int sloom = 0;
+/*extern*/ int sloombatch = 0;
+/*extern*/ int anal_infiles = 0;
+/*extern*/ int is_converted_to_stereo = -1;
+const char* cdp_version = "7.1.0";
+
+/**************************************** MAIN *********************************************/
+
+int main(int argc,char *argv[])
+{
+	int exit_status;
+	//FILE *fp   = NULL;
+	dataptr dz = NULL;
+	//char *special_data_string = NULL;
+	char **cmdline;
+	int  cmdlinecnt;
+	aplptr ap;
+	int *valid = NULL;
+	int is_launched = FALSE;
+	int  validcnt;
+
+	if(argc==2 && (strcmp(argv[1],"--version") == 0)) {
+		fprintf(stdout,"%s\n",cdp_version);
+		fflush(stdout);
+		return 0;
+	}
+						/* CHECK FOR SOUNDLOOM */
+/* TW May 2001 */
+	if((sloom = sound_loom_in_use(&argc,&argv)) > 1) {
+		sloom = 0;
+		sloombatch = 1;
+	}
+
+	if(!sloom) {
+		if((exit_status = allocate_and_initialise_validity_flags(&valid,&validcnt))<0) {
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+			return(FAILED);
+		}
+	}
+
+	if(sflinit("cdp")){
+		sfperror("cdp: initialisation\n");
+		return(FAILED);
+	}
+
+						  /* SET UP THE PRINCIPLE DATASTRUCTURE */
+	if((exit_status = establish_datastructure(&dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+	if(!sloom) {
+							  /* INITIAL CHECK OF CMDLINE DATA */
+		if((exit_status = make_initial_cmdline_check(&argc,&argv))<0) {
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+			return(FAILED);
+		}
+		cmdline    = argv;	/* GET PRE_DATA, ALLOCATE THE APPLICATION, CHECK FOR EXTRA INFILES */
+		cmdlinecnt = argc;
+		if((exit_status = get_process_and_mode_from_cmdline(&cmdlinecnt,&cmdline,dz))<0) {
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+			return(FAILED);
+		}		
+		if((exit_status = setup_particular_application(dz))<0) {
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+			return(FAILED);
+		}
+		if((exit_status = count_and_allocate_for_infiles(cmdlinecnt,cmdline,dz))<0) {
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+			return(FAILED);
+		}
+	} else {
+		if((exit_status = parse_tk_data(argc,argv,&cmdline,&cmdlinecnt,dz))<0) {  	/* includes setup_particular_application()      */
+			exit_status = print_messages_and_close_sndfiles(exit_status,is_launched,dz);/* and cmdlinelength check = sees extra-infiles */
+			return(exit_status);		 
+		}
+	}
+
+	ap = dz->application;
+
+/*********************************************************************************************************************
+	   cmdline[0]				 		  2 vals					   		  ACTIVE		 
+TK 		(infile) (more-infiles) (outfile) (flag val) (formantsqksrch) (special) params  options   variant-params  flags
+CMDLINE	(infile) (more-infiles) (outfile) (formants) (formantsqksrch) (special) params  POSSIBLY  POSSIBLY	  	POSSIBLY
+								 		  1 val
+*********************************************************************************************************************/
+
+	if((exit_status = parse_infile_and_hone_type(cmdline[0],valid,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+	if((exit_status = setup_param_ranges_and_defaults(dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+					/* OPEN FIRST INFILE AND STORE DATA, AND INFORMATION, APPROPRIATELY */
+
+	if(dz->input_data_type!=NO_FILE_AT_ALL) {
+		if((exit_status = open_first_infile(cmdline[0],dz))<0) {	
+			print_messages_and_close_sndfiles(exit_status,is_launched,dz);	
+			return(FAILED);
+		}
+	}
+	cmdlinecnt--;
+	cmdline++;
+
+/*********************************************************************************************************************
+		cmdline[0]				   2 vals				   			   ACTIVE		 
+TK 		(more-infiles) (outfile) (flag val) (formantsqksrch) (special) params  options   variant-params  flags
+CMDLINE	(more-infiles) (outfile) (formants) (formantsqksrch) (special) params  POSSIBLY  POSSIBLY		  POSSIBLY
+								   1 val
+*********************************************************************************************************************/
+
+	if((exit_status = handle_extra_infiles(&cmdline,&cmdlinecnt,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);		
+		return(FAILED);
+	}
+
+/*********************************************************************************************************************
+		cmdline[0]	  2					   			    ACTIVE		 
+TK 		(outfile) (flag val) (formantsqksrch) (special) params  options   variant-params  flags
+CMDLINE	(outfile) (formants) (formantsqksrch) (special) params  POSSIBLY  POSSIBLY		   POSSIBLY
+					  1
+*********************************************************************************************************************/
+
+	if((exit_status = handle_outfile(&cmdlinecnt,&cmdline,is_launched,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+/****************************************************************************************
+		cmdline[0]	  		   			       ACTIVE		 
+TK 		(flag val) (formantsqksrch) (special) params  options   variant-params  flags
+CMDLINE	(formants) (formantsqksrch) (special) params  POSSIBLY  POSSIBLY		POSSIBLY
+*****************************************************************************************/
+
+	if((exit_status = handle_formants(&cmdlinecnt,&cmdline,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	if((exit_status = handle_formant_quiksearch(&cmdlinecnt,&cmdline,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	if((exit_status = handle_special_data(&cmdlinecnt,&cmdline,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+ 
+/****************************************************************************************
+		cmdline[0]	  		   			    
+TK 		active_params  	options   		variant-params  flags
+CMDLINE	active_params  	POSSIBLY  		POSSIBLY		POSSIBLY
+*****************************************************************************************/
+
+	if((exit_status = read_parameters_and_flags(&cmdline,&cmdlinecnt,dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+	if((exit_status = check_param_validity_and_consistency(dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+
+ 	is_launched = TRUE;
+
+	if((exit_status = allocate_large_buffers(dz))<0){
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	if((exit_status = param_preprocess(dz))<0){
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	if((exit_status = groucho_process_file(dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	if((exit_status = complete_output(dz))<0) {
+		print_messages_and_close_sndfiles(exit_status,is_launched,dz);
+		return(FAILED);
+	}
+	exit_status = print_messages_and_close_sndfiles(FINISHED,is_launched,dz);
+	free(dz);
+	return(SUCCEEDED);
+}
+

+ 804 - 0
dev/pv/mxfft.c

@@ -0,0 +1,804 @@
+/*
+ * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
+ * http://www.trevorwishart.co.uk
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <structures.h>
+#include <globcon.h>
+#include <pvoc.h>
+#include <math.h>			/*RWD*/
+
+int 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);
+ */
+
+int fft_(float *a, float *b, int nseg, int n, int nspn, int isn)
+		/*  a: pointer to array 'anal'  */
+		/*  b: pointer to array 'banal' */
+{
+	int exit_status;
+	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=0;
+
+/* 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(FINISHED);
+
+	nspn=abs(nf*nspn);
+	ntot=abs(nspn*nseg);
+
+	if(isn*ntot == 0){
+		sprintf(errstr,"zero in FFT parameters %d %d %d %d",nseg, n, nspn, isn);
+		return(DATA_ERROR);
+	}
+	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) {
+			sprintf(errstr,"FFT parameter n has more than 15 factors : %d", n);
+	    	return(DATA_ERROR);
+	}
+      	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 */
+
+	if((exit_status = fftmx(a,b,ntot,nf,nspn,isn,m,&kt,at,ck,bt,sk,np,nfac))<0)
+		return(exit_status);
+
+/* 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(FINISHED);
+}
+
+/*
+ *-----------------------------------------------------------------------
+ * subroutine:  fftmx
+ * called by subroutine 'fft' to compute mixed-radix fourier transform
+ *-----------------------------------------------------------------------
+ */
+int fftmx(float *a,float *b,int ntot,int n,int nspan,int isn,int m,int *kt,
+			float *at,float *ck,float *bt,float *sk,int *np,int nfac[])
+{	
+	int	i,inc,
+		j,jc,jf, jj,
+		k, k1, k2, k3=0, 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=0.0, c3=0.0, c72, cd,
+		dr,
+		rad, 
+		sd, s1, s2=0.0, s3=0.0, 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] = (float)(a[j] * ak);
+	        	b[j] = (float)(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;
+		do{	do{	k2 = kk + kspan;
+		      	ak = a[k2];
+		      	bk = b[k2];
+		      	a[k2] = (float)((a[kk]) - ak);
+		      	b[k2] = (float)((b[kk]) - bk);
+		      	a[kk] = (float)((a[kk]) + ak);
+		      	b[kk] = (float)((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] = (float)((c1 * ak) - (s1 * bk));
+		      	b[k2] = (float)((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] = (float)(ak + aj);
+      	b[kk] = (float)(bk + bj);
+      	ak += (-0.5 * aj);
+      	bk += (-0.5 * bj);
+      	aj = (a[k1] - a[k2]) * s120;
+      	bj = (b[k1] - b[k2]) * s120;
+      	a[k1] = (float)(ak - bj);
+      	b[k1] = (float)(bk + aj);
+      	a[k2] = (float)(ak + bj);
+      	b[k2] = (float)(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] = (float)(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] = (float)(bkp + bjp);
+      	bjp = (float)(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] = (float)((akp*c1) - (bkp*s1));
+      	b[k1] = (float)((akp*s1) + (bkp*c1));
+      	a[k2] = (float)((ajp*c2) - (bjp*s2));
+      	b[k2] = (float)((ajp*s2) + (bjp*c2));
+      	a[k3] = (float)((akm*c3) - (bkm*s3));
+      	b[k3] = (float)((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] = (float)akp;
+      	b[k1] = (float)bkp;
+      	a[k2] = (float)ajp;
+      	b[k2] = (float)bjp;
+      	a[k3] = (float)akm;
+      	b[k3] = (float)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] = (float)(aa + akp + ajp);
+      	b[kk] = (float)(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] = (float)(ak - bj);
+      	a[k4] = (float)(ak + bj);
+      	b[k1] = (float)(bk + aj);
+      	b[k4] = (float)(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] = (float)(ak - bj);
+      	a[k3] = (float)(ak + bj);
+      	b[k2] = (float)(bk + aj);
+      	b[k3] = (float)(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.0f;
+	sk[jf] = 0.0f;
+	for(j=1; j<k ; j++){
+		ck[j] = (float)((ck[k])*c1 + (sk[k])*s1);
+	      	sk[j] = (float)((ck[k])*s1 - (sk[k])*c1);
+	      	k--;
+	      	ck[k] = ck[j];
+	      	sk[k] = -(sk[j]);
+	}
+lbl250:	k1 = kk;
+      	k2 = kk + kspnn;
+	aa = a[kk];
+	bb = b[kk];
+      	ak = aa;
+      	bk = bb;
+      	j = 1;
+      	k1 += kspan;
+		do{	k2 -= kspan;
+	      	j++;
+	      	at[j] = a[k1] + a[k2];
+	      	ak = at[j] + ak;	
+	      	bt[j] = b[k1] + b[k2];
+	      	bk = bt[j] + bk;	
+	      	j++;
+	      	at[j] = a[k1] - a[k2];
+	      	bt[j] = b[k1] - b[k2];
+	      	k1 += kspan;
+	}while(k1 < k2);
+      	a[kk] = (float)ak;
+      	b[kk] = (float)bk;
+      	k1 = kk;
+      	k2 = kk + kspnn;
+      	j = 1;
+lbl270:	k1 += kspan;
+      	k2 -= kspan;
+      	jj = j;
+      	ak = aa;
+      	bk = bb;
+      	aj = 0.0;
+      	bj = 0.0;
+      	k = 1;
+		do{	k++;
+	      	ak = (at[k] * ck[jj]) + ak;
+	      	bk = (bt[k] * ck[jj]) + bk;	
+	      	k++;
+	      	aj = (at[k] * sk[jj]) + aj;
+	      	bj = (bt[k] * sk[jj]) + bj;
+	      	jj += j;
+	      	if (jj > jf) 
+			jj -= jf;
+	}while(k < jf);
+      	k = jf - j;
+      	a[k1] = (float)(ak - bj);
+      	b[k1] = (float)(bk + aj);
+      	a[k2] = (float)(ak + bj);
+      	b[k2] = (float)(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] = (float)((c2*ak) - (s2 * b[kk]));
+      	b[kk] = (float)((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;
+		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] = (float)ak;
+	      	bk = b[kk];
+	      	b[kk] = b[k2];
+	      	b[k2] = (float)bk;
+	      	kk += inc;
+	      	k2 += kspan;
+	}while(k2 < ks);
+lbl380:	do{	k2 -= np[j++];
+	      	k2 += np[j+1];
+	}while(k2 > np[j]);
+      	j = 1;
+lbl390:	if(kk < k2){
+		goto lbl370;
+	}
+      	kk += inc;
+      	k2 += kspan;
+      	if(k2 < ks) goto lbl390;
+      	if(kk < ks) goto lbl380;
+      	jc = k3;
+      	goto lbl440;
+/*
+ * permutation for multivariate transform
+ */
+lbl400:	do{	do{	k = kk + jc;
+				do{	ak = a[kk];
+			      	a[kk] = a[k2];
+			      	a[k2] = (float)ak;
+			      	bk = b[kk];
+			      	b[kk] = b[k2];
+			      	b[k2] = (float)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(FINISHED);
+
+      	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(FINISHED);; 
+}
+
+
+/*
+ *-----------------------------------------------------------------------
+ * 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);
+ */
+
+int reals_(float *a, float *b, int n, int isn)
+
+			/* a refers to an array of floats 'anal'   */
+			/* b refers to an array of floats 'banal'  */
+/* See IEEE book for a long comment here on usage */
+
+{	int	inc,
+		j,
+		k,
+		lim,
+		mm,ml,
+		nf,nk,nh;
+ 
+	double	aa,ab,
+		ba,bb,
+		cd,cn,
+		dr,
+		em,
+		rad,re,
+		sd,sn;
+	double	xx;	/******* ADDED APRIL 1991 ******/
+/* adjust  input array pointers (called from C) */
+	a--;	b--;
+	inc=abs(isn);
+	nf=abs(n);
+      	if(nf*isn==0){
+			sprintf(errstr,"zero in reals parameters in FFT : %d : %d ",n,isn);
+	       	return(DATA_ERROR);;
+		}
+	 	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;
+	}
+	 	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] = (float)((em-bb)*0.5);
+        	b[j] = (float)((em+bb)*0.5);
+        	a[k] = (float)((aa-re)*0.5);
+		a[j] = (float)((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(FINISHED);
+}

+ 1019 - 0
dev/pv/pvoc.c

@@ -0,0 +1,1019 @@
+/*
+ * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
+ * http://www.trevorwishart.co.uk
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <structures.h>
+#include <globcon.h>
+#include <tkglobals.h>
+#include <pnames.h>
+#include <processno.h>
+#include <modeno.h>
+#include <logic.h>
+#include <cdpmain.h>
+#include <pvoc.h>
+#include <string.h>
+#include <sfsys.h>
+#include <pvoc.h>
+
+// NEW 2014 -->
+static int  outfloats(float *nextOut, float *maxsampl,float *minsample,float *local_maxsample,int *num_overflows,int todo, int finished, dataptr dz);
+// <-- NEW 2014
+static int  pvoc_float_array(int nnn,float **ptr);
+static int  sndwrite_header(int N2,int *Nchans,float *arate,float R,int D,int origsize,int *isr,int M,dataptr dz);
+static void hamming(float *win,int winLen,int even);
+static int  pvoc_time_display(int nI,int srate,int *samptime,dataptr dz);
+static int write_samps_pvoc(float *bbuf,int samps_to_write,dataptr dz);
+
+/****************************** HAMMING ******************************/
+
+void hamming(float *win,int winLen,int even)
+{
+	float Pi,ftmp;
+	int i;
+
+/***********************************************************
+					Pi = (float)((double)4.*atan((double)1.));
+***********************************************************/
+	Pi = (float)PI;
+	ftmp = Pi/winLen;
+
+	if (even) {
+		for (i=0; i<winLen; i++)
+		*(win+i) = (float)((double).54 + (double).46*cos((double)(ftmp*((float)i+.5))));
+		*(win+winLen) = 0.0f;}
+	else{	*(win) = 1.0f;
+		for (i=1; i<=winLen; i++)
+		*(win+i) =(float)((double).54 + (double).46*cos((double)(ftmp*(float)i)));
+	}
+	return;
+}
+
+/****************************** FLOAT_ARRAY ******************************/
+
+int pvoc_float_array(int nnn,float **ptr)
+{	/* set up a floating point array length nnn. */
+	*ptr = (float *) calloc(nnn,sizeof(float));
+	if(*ptr==NULL){
+		sprintf(errstr,"pvoc: insufficient memory\n");
+		return(MEMORY_ERROR);
+	}
+	return(FINISHED);
+}
+
+/****************************** PVOC_PROCESS ******************************/
+
+int pvoc_process(dataptr dz)
+{
+	int exit_status;
+	int finished = 0;
+	int num_overflows = 0;
+	int samptime = SAMP_TIME_STEP;
+	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 */
+			maxsample = 0.0, minsample = 0.0, biggest;
+
+	int		M = 0,			/* length of analWindow impulse response */
+			D = 0,			/* decimatin factor */
+			I = 0,			/* interpolation factor (default will be I=D)*/
+			/* RWD */
+			/*F = 0,*/			/* fundamental frequency (determines dz->iparam[PVOC_CHANS]) */
+			analWinLen,		/* half-length of analysis window */
+			synWinLen;		/* half-length of synthesis window */
+
+	int	sampsize,		/* sample size for output file */
+			outCount,		/* number of samples written to output */
+			ibuflen,		/* length of input buffer */
+			obuflen,		/* length of output buffer */
+			nI = 0,			/* current input (analysis) sample */
+			nO,				/* current output (synthesis) sample */
+			nMaxOut;		/* last output (synthesis) sample */
+	int	origsize = 0,	/* sample type of file analysed */
+			isr,			/* sampling rate */
+			Nchans,			/* no of chans */
+			endsamp = VERY_BIG_INT;
+
+	float	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 = 0.0f,	/* scale factor for calculating statistics */
+			rIn,			/* decimated sampling rate */
+			rOut,			/* pre-interpolated sampling rate */
+			R,				/* input sampling rate */
+// NEW 2014 -->
+			local_maxsample = 0.0;
+// <-- NEW 2014
+
+	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,			/* dz->iparam[PVOC_CHANS]/2 */
+			NO,			/* synthesis NO = dz->iparam[PVOC_CHANS] / P */
+			NO2,		/* NO/2 */
+			IO,			/* synthesis IO = I / P */
+			IOi,		/* synthesis IOi = Ii / P */
+			Mf = 0,		/* flag for even M */
+#ifdef SINGLE_SAMP
+			rv,			/* return value from fgetfloat */
+#endif
+			flag = 0;	/* end-of-input flag */
+	float	arate = 1.0 /* TW: arbitrary initialisation */;		/* sample rate for header on stdout if -A */
+	/*RWD */
+	float F = 0.0f;
+
+	if(sndgetprop(dz->ifd[0],"sample type", (char *) &sampsize, sizeof(int)) < 0){
+		sprintf(errstr,"pvoc: failure to get sample type\n");
+		return(MEMORY_ERROR);
+	}
+
+	if (dz->process==PVOC_SYNTH) {
+		isr      = dz->infile->origrate;
+		sampsize = dz->infile->origstype;							   		
+		arate    = dz->infile->arate;
+		Nchans   = dz->infile->channels;
+		dz->iparam[PVOC_CHANS] = Nchans - 2;
+		M		 = dz->infile->Mlen;
+		D        = dz->infile->Dfac;
+		R    	 = ((float) D * arate);
+	} else {
+		isr 	 = dz->infile->srate;
+		R        = (float)isr;
+		Nchans   = dz->infile->channels;
+		/*RWD OCT 05 need this to preserved hires infile formats */
+		origsize = dz->infile->stype;	
+	}
+	if(flteq(R,0.0)) {
+		sprintf(errstr,"Problem: zero sampling rate\n");
+		return(DATA_ERROR);
+	}
+	
+	sampsize = ((dz->iparam[PVOC_ANAL_ONLY]) ? SAMP_FLOAT:dz->infile->stype);
+	N2 = dz->iparam[PVOC_CHANS] / 2;
+
+	F = /*(int)*/(float)(R /(float)dz->iparam[PVOC_CHANS]);	  /*RWD*/
+
+	if(dz->process!=PVOC_SYNTH) {
+		switch(dz->iparam[PVOC_WIN_OVERLAP]){
+			case 0:	M = 4*dz->iparam[PVOC_CHANS];	break;
+			case 1:	M = 2*dz->iparam[PVOC_CHANS];	break;
+			case 2: M = dz->iparam[PVOC_CHANS];		break;
+			case 3: M = N2;							break;
+			default:
+				sprintf(errstr,"pvoc: Invalid window overlap factor.\n");
+				return(PROGRAM_ERROR);
+		}
+	}
+
+	if(!sloom) {
+		if(dz->iparam[PVOC_ANAL_ONLY] && (dz->process==PVOC_SYNTH)) {
+			sprintf(errstr,"both -A and -S specified: Impossible!\n");
+			return(PROGRAM_ERROR);
+		}
+	}
+
+	Mf = 1 - M%2;
+
+	if (M < 7) {
+		fprintf(stdout,"WARNING: analWindow impulse response is too small\n");
+ 		fflush(stdout);
+	}
+	ibuflen = 4 * M;
+	obuflen = 4 * M;
+
+	if(dz->process!=PVOC_SYNTH) {
+		if((D = (int)(M/PVOC_CONSTANT_A)) == 0){
+			fprintf(stdout,"WARNING: Decimation too low: adjusted.\n");
+			fflush(stdout);
+			D = 1;
+		}
+	}
+
+	switch(dz->process) {
+	case(PVOC_ANAL):
+	case(PVOC_EXTRACT):
+		arate = (float)(R/D);	/* Needed to write to outheader */
+		dz->wanted = dz->iparam[PVOC_CHANS] + 2;	
+
+		/* fall thro */
+	case(PVOC_SYNTH):
+		dz->frametime = (float)(1.0/arate);
+		break;
+	}
+
+	if(sloom) {
+		if(dz->process==PVOC_SYNTH) {
+			dz->tempsize = (dz->insams[0]/dz->infile->channels) * dz->infile->Dfac;	
+				/*length of output in samps: needed for time-display in TK mode */
+		} else {
+			dz->tempsize = dz->insams[0];
+		}
+	}
+
+	I   = D;
+	NO  = dz->iparam[PVOC_CHANS];	/* synthesis transform will be NO points */
+	NO2 = NO/2;
+	IO  = I;
+
+	if((exit_status = sndwrite_header(N2,&Nchans,&arate,R,D,origsize,&isr,M,dz))<0)
+		return(exit_status);
+
+//TW 
+//	if(!sloom) {
+	if(!sloom && !sloombatch) {
+		fprintf(stdout,"analysis/synthesis beginning\n");	
+		fflush(stdout);
+	}
+	/* set up analysis window: The window is assumed to be symmetric
+		with M total points.  After the initial memory allocation,
+		analWindow always points to the midpoint of the window
+		(or one half sample to the right, if M is even); analWinLen
+		is half the true window length (rounded down). 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. */
+
+	if((exit_status = pvoc_float_array(M+Mf,&analWindow))<0)
+		return(exit_status);
+	analWindow += (analWinLen = M/2);
+
+	hamming(analWindow,analWinLen,Mf);
+
+	for (i = 1; i <= analWinLen; i++)
+		*(analWindow - i) = *(analWindow + i - Mf);
+
+	if (M > dz->iparam[PVOC_CHANS]) {
+		if (Mf)
+		*analWindow *=(float)
+		((double)dz->iparam[PVOC_CHANS]*sin((double)PI*.5/dz->iparam[PVOC_CHANS])/(double)(PI*.5));
+		for (i = 1; i <= analWinLen; i++) 
+			*(analWindow + i) *=(float)
+			((double)dz->iparam[PVOC_CHANS] * sin((double) (PI*(i+.5*Mf)/dz->iparam[PVOC_CHANS])) / (PI*(i+.5*Mf))); /* D.Timis */
+		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. */
+
+	if((exit_status = pvoc_float_array(M+Mf,&synWindow))<0)
+		return(exit_status);
+	synWindow += (synWinLen = M/2);
+
+	if (M <= dz->iparam[PVOC_CHANS]){
+		hamming(synWindow,synWinLen,Mf);
+		for (i = 1; i <= synWinLen; i++)
+			*(synWindow - i) = *(synWindow + i - Mf);
+
+		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);
+
+		for (i = -synWinLen; i <= synWinLen; i++)
+			*(synWindow + i) *= sum;
+	} else {	
+		hamming(synWindow,synWinLen,Mf);
+		for (i = 1; i <= synWinLen; i++)
+			*(synWindow - i) = *(synWindow + i - Mf);
+
+		if (Mf)
+			*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*Mf)/IO)) /(double) (PI*(i+.5*Mf)));
+		for (i = 1; i <= synWinLen; i++)
+			*(synWindow - i) = *(synWindow + i - Mf);
+
+		sum = (float)(1.0/sum);
+
+		for (i = -synWinLen; i <= synWinLen; i++)
+			*(synWindow + i) *= sum;
+	}
+      
+	/* set up input buffer:  nextIn always points to the next empty
+		word in the input buffer (i.e., the sample following
+		sample number (n + analWinLen)).  If the buffer is full,
+		then nextIn jumps back to the beginning, and the old
+		values are written over. */
+
+	if((exit_status = pvoc_float_array(ibuflen,&input))<0)
+		return(exit_status);
+
+	nextIn = input;
+
+	/* set up output buffer:  nextOut always points to the next word
+		to be shifted out.  The shift is simulated by writing the
+		value to the standard output and then setting that word
+		of the buffer to zero.  When nextOut reaches the end of
+		the buffer, it jumps back to the beginning.  */
+
+	if((exit_status = pvoc_float_array(obuflen,&output))<0)
+		return(exit_status);
+
+	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. */
+
+	if((exit_status = pvoc_float_array(dz->iparam[PVOC_CHANS]+2,&anal))<0)
+		return(exit_status);
+	banal = anal + 1;
+
+	if((exit_status = pvoc_float_array(N2+1,&oldInPhase))<0)
+		return(exit_status);
+	if((exit_status = pvoc_float_array(N2+1,&maxAmp))<0)
+		return(exit_status);
+	if((exit_status = pvoc_float_array(N2+1,&avgAmp))<0)
+		return(exit_status);
+	if((exit_status = pvoc_float_array(N2+1,&avgFrq))<0)
+		return(exit_status);
+	if((exit_status = pvoc_float_array(N2+1,&env))<0)
+		return(exit_status);
+
+	/* set up synthesis buffer for (dz->iparam[PVOC_CHANS]/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). */
+
+	if((exit_status = pvoc_float_array(NO+2,&syn))<0)
+		return(exit_status);
+	bsyn = syn + 1;
+
+	if((exit_status = pvoc_float_array(NO2+1,&oldOutPhase))<0)
+		return(exit_status);
+
+	/* initialization: input time starts negative so that the rightmost
+		edge of the analysis filter just catches the first non-zero
+		input samples; output time is always T times input time. */
+
+	outCount = 0;
+	rIn  = (float)(R/(float)D);
+	rOut = (float)(R/(float)I);
+	RoverTwoPi = (float)(rIn/TWOPI);
+	TwoPioverR = (float)(TWOPI/rOut);
+	nI = -(analWinLen / D) * D;	/* input time (in samples) */
+	nO = 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;
+
+	/* main loop:  If endsamp is not specified it is assumed to be very large
+		and then readjusted when fgetfloat detects the end of input. */
+
+	display_virtual_time(0L,dz);	
+
+	while(nI < (endsamp + analWinLen)){
+		if (dz->process==PVOC_SYNTH){
+#ifdef SINGLE_SAMP
+			for (i = 0; i < dz->iparam[PVOC_CHANS]+2; i++){		/* synthesis only */
+				if ((rv = fgetfloat((anal+i),dz->ifd[0])) <= 0){
+					goto epilog;
+				}
+			}
+#else
+// NEW 2014 -->
+			memset((char *)anal,0,(dz->iparam[PVOC_CHANS]+2) * sizeof(float));	/* Ensure buffer is wiped, esp for empty buffers at process end */
+// <-- NEW 2014
+			if((i = fgetfbufEx(anal, dz->iparam[PVOC_CHANS]+2, dz->ifd[0], 0)) < 0) {
+				sfperror("pvoc: read error: ");
+				return(SYSTEM_ERROR);
+			}
+// NEW 2014 -->
+			if(i < dz->iparam[PVOC_CHANS]+2) {
+				finished = 1;				//	Finished reading input
+				if(local_maxsample == 0.0)	//	Finished writing output
+					goto epilog;
+			}
+// <-- NEW 2014
+#endif
+		} else {		/* prepare for analysis: read next Dd input values */
+#ifdef SINGLE_SAMP
+			for (i = 0; i < Dd; i++){
+				if (fgetfloat(nextIn++,dz->ifd[0]) <= 0)
+					Dd = i;			/* EOF ? */
+				if (nextIn >= (input + ibuflen))
+					nextIn -= ibuflen;
+			}
+#else
+			{
+				static float *sbuf = 0;
+				static int sblen = 0;
+				int got, tocp;
+				float *sp;
+
+				if(sblen < Dd) {
+					if(sbuf != 0)
+						free(sbuf);
+
+					if((sbuf = (float *)malloc(Dd*sizeof(float))) == 0) {
+						sprintf(errstr, "pvoc: can't allocate short buffer\n");
+						return(MEMORY_ERROR);
+					}
+					sblen = Dd;
+				}
+				if((got = fgetfbufEx(sbuf, Dd, dz->ifd[0],0)) < 0) {
+					sfperror("pvoc: read error");
+					return(SYSTEM_ERROR);
+				}
+				if(got < Dd)
+					Dd = got;
+				sp = sbuf;
+
+				tocp = min(got, input+ibuflen-nextIn);
+				got -= tocp;
+				while(tocp-- > 0)
+					*nextIn++ = *sp++;
+
+				if(got > 0) {
+					nextIn -= ibuflen;
+					while(got-- > 0)
+						*nextIn++ = *sp++;
+				}
+				if (nextIn >= (input + ibuflen))
+					nextIn -= ibuflen;
+			}
+#endif
+			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 (dz->iparam[PVOC_CHANS]/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 (dz->iparam[PVOC_CHANS]/2 + 1)
+		channels.   The subroutines fft and reals together implement
+		one efficient FFT call for a real input sequence.  */
+
+
+			for (i = 0; i < dz->iparam[PVOC_CHANS]+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 += dz->iparam[PVOC_CHANS];
+			k = k % dz->iparam[PVOC_CHANS];
+			for (i = -analWinLen; i <= analWinLen; i++) {
+				if (++j >= ibuflen)
+					j -= ibuflen;
+				if (++k >= dz->iparam[PVOC_CHANS])
+					k -= dz->iparam[PVOC_CHANS];
+				*(anal + k) += *(analWindow + i) * *(input + j);
+			}
+			if((exit_status = fft_(anal,banal,1,N2,1,-2))<0)
+				return(exit_status);
+			if((exit_status = reals_(anal,banal,N2,-2))<0)
+				return(exit_status);
+
+	/* 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. */
+
+			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.)
+					angleDif = 0.0f;
+	
+				else {
+					rratio = atan((double)imag/(double)real);
+					if(real<0.0) {
+						if(imag<0.0)
+							rratio -= PI;
+						else
+							rratio += PI;
+					}
+					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);
+			}
+		}
+
+	/* if analysis only, write out interleaved instantaneous amplitudes
+		and frequencies; otherwise perform resynthesis */
+
+    	if (dz->iparam[PVOC_ENVOUT_ONLY]) {
+#ifdef SINGLE_SAMP
+			for (i=0; i <= N2; i++)
+				fputfloat((env+i),dz->ofd);
+#else
+			write_samps_pvoc(env, N2+1, dz);
+#endif
+    	} else if (dz->iparam[PVOC_MAGOUT_ONLY]) {
+#ifdef SINGLE_SAMP
+			for (i=0; i <= N2; i++)
+				fputfloat((anal + 2*i),dz->ofd);
+#else
+			float *fp = anal;
+			for (i=0; i <= N2; i++) {
+				fputfloat(fp,dz->ofd);
+				fp += 2;
+			}
+#endif
+		} else if (dz->iparam[PVOC_ANAL_ONLY]) {
+#ifdef SINGLE_SAMP
+			for (i=0; i < dz->iparam[PVOC_CHANS]+2; i++)
+				fputfloat((anal+i),dz->ofd);
+#else
+			write_samps_pvoc(anal, dz->iparam[PVOC_CHANS]+2, dz);
+#endif
+		} else { /* synthesis */
+				
+
+			if (dz->iparam[PVOC_PARTIAL_RESYNTH]){		 /* zero out non-selected channels */
+				for (i = 0; i < dz->iparam[PVOC_AF_PAIR_LO]; i++)
+					*(anal+2*i) = 0.0f;
+				for (i = dz->iparam[PVOC_AF_PAIR_HI]+1; i <= N2; i++)
+					*(anal+2*i) = 0.0f;
+				if (dz->iparam[PVOC_SELECTED_CHAN] == 1) {
+					for (i = dz->iparam[PVOC_AF_PAIR_LO]; i <= dz->iparam[PVOC_AF_PAIR_HI]; i++) {
+						if (i%2 == 0)
+							*(anal+2*i) = 0.0f;
+					}
+				}
+				if (dz->iparam[PVOC_SELECTED_CHAN] == 2) {
+					for (i = dz->iparam[PVOC_AF_PAIR_LO]; i <= dz->iparam[PVOC_AF_PAIR_HI]; i++) {
+						if (i%2 != 0)
+							*(anal+2*i) = 0.0f;
+					}
+				}
+			}
+
+	/* 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 <= dz->iparam[PVOC_CHANS]){
+				for (i = 0; i < NO+2; i++)
+					*(syn+i) = *(anal+i);
+			}
+			else {
+				for (i = 0; i <= dz->iparam[PVOC_CHANS]+1; i++) 
+					*(syn+i) = *(anal+i);
+				for (i = dz->iparam[PVOC_CHANS]+2; i < NO+2; i++) 
+					*(syn+i) = 0.0f;
+			}
+
+			for(i=0, i0=syn, i1=syn+1; i<= NO2; i++,i0+=2,i1+=2){
+				mag = *i0;
+				*(oldOutPhase + i) += *i1 - ((float) i * F);
+				phase = *(oldOutPhase + i) * TwoPioverR;
+				*i0 = (float)((double)mag * cos((double)phase));
+				*i1 = (float)((double)mag * sin((double)phase));
+			}
+
+	/* synthesis: The synthesis subroutine uses the Weighted Overlap-Add
+		technique to reconstruct the time-domain signal.  The (dz->iparam[PVOC_CHANS]/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.  */
+
+			if((exit_status = reals_(syn,bsyn,NO2,2))<0)
+				return(exit_status);
+			if((exit_status = fft_(syn,bsyn,1,NO2,1,2))<0)
+				return(exit_status);
+
+			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);
+			}
+
+#ifdef SINGLE_SAMP
+			for (i = 0; i < IOi; i++){	/* shift out next IOi values */
+				fputfloat(nextOut,dz->ofd);
+				*(nextOut++) = 0.;
+				if (nextOut >= (output + obuflen))
+					nextOut -= obuflen;
+				outCount++;
+			}
+#else
+			for (i = 0; i < IOi;){	/* shift out next IOi values */
+				int j;
+				int todo = min(IOi-i, output+obuflen-nextOut);
+
+// NEW 2014 -->
+				if((exit_status = outfloats(nextOut,&maxsample,&minsample,&local_maxsample,&num_overflows,todo,finished,dz))<0)
+					return(exit_status);
+// <-- NEW 2014
+				i += todo;
+				outCount += todo;
+				for(j = 0; j < todo; j++)
+					*nextOut++ = 0.0f;
+				if (nextOut >= (output + obuflen))
+					nextOut -= obuflen;
+
+			}
+#endif
+		}
+					
+		if(flag 							/* flag means do this operation only once */
+		&& (nI > 0) && (Dd < D)){			/* EOF detected */
+			flag = 0;
+			endsamp = nI + analWinLen - (D - Dd);
+		}
+
+		nI += D;				/* increment time */
+		nO += IO;
+								/* Dd = D except when the end of the sample stream intervenes */
+		Dd = min(D, max(0, D+endsamp-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 = Ii;
+
+
+		if(nI > samptime && (exit_status = pvoc_time_display(nI,isr,&samptime,dz))<0)
+			return(exit_status);
+	}	/* End of main while loop */
+
+	if(!dz->iparam[PVOC_ANAL_ONLY]) {
+		nMaxOut = endsamp;
+		while (outCount <= nMaxOut){
+#ifdef SINGLE_SAMP
+			outCount++;
+			fputfloat(nextOut++,dz->ofd);
+			if (nextOut >= (output + obuflen))
+				nextOut -= obuflen;
+#else
+			int todo = min(nMaxOut-outCount, output+obuflen-nextOut);
+			if(todo == 0)
+				break;
+// NEW 2014 -->
+			if((exit_status = outfloats(nextOut,&maxsample,&minsample,&local_maxsample,&num_overflows,todo,finished,dz))<0)
+				return(exit_status);
+// <-- NEW 2014
+			outCount += todo;
+			nextOut += todo;
+			if (nextOut >= (output + obuflen))
+				nextOut -= obuflen;
+
+#endif
+		}
+		if((exit_status = pvoc_time_display((int)endsamp,isr,&samptime,dz))<0)
+			return(exit_status);
+	}
+
+epilog:
+
+#ifndef NOOVERCHK
+	if(num_overflows > 0) {
+		biggest =  maxsample;
+		if(-minsample > maxsample)
+			biggest = -minsample;
+		fprintf(stdout, "WARNING: %d samples overflowed, and were clipped\n",num_overflows);
+		fprintf(stdout, "WARNING: maximum sample was %f  :  minimum sample was %f\n",maxsample,minsample);
+		fprintf(stdout, "WARNING: You should reduce source level to avoid clipping: use gain of <= %lf\n",1.0/biggest);
+		fflush(stdout);
+	}
+
+#endif
+	return(FINISHED);
+}
+
+/*MCA
+ *	Convert floats -> shorts explicitly, since we are compiled with
+ *	hardware FP(probably), and the sound filing system is not!
+ *	(even without this, it should be more efficient!)
+ */
+
+// NEW 2014 -->
+int outfloats(float *nextOut,float *maxsample,float *minsample,float *local_maxsample,int *num_overflows,int todo,int finished,dataptr dz)
+{
+	static float *sbuf = 0;
+	static int sblen = 0;
+	float *sp;
+	int cnt;
+	float val;
+	float local_minsample = 0.0;
+	*local_maxsample = 0.0;
+	if(sblen < todo) {
+		if(sbuf != 0)
+			free(sbuf); 
+		if((sbuf = (float *)malloc(todo*sizeof(float))) == 0) {
+			sprintf(errstr, "pvoc: can't allocate output buffer\n");
+			return(MEMORY_ERROR);
+		}
+		sblen = todo;
+	}
+
+	sp = sbuf;
+#ifdef NOOVERCHK
+	for(cnt = 0; cnt < todo; cnt++)
+		*sp++ = *nextOut++;	
+#else
+	for(cnt = 0; cnt < todo; cnt++) {
+		val = *nextOut++;
+		if(val >= 1.0 || val <= -1.0) {
+			(*num_overflows)++;
+			if(val > 0.0f) {
+				if(val > *maxsample)
+					*maxsample = val;
+				val = 1.0f;
+			}
+			if(val < 0.0f) {
+				if(val < *minsample)
+					*minsample = val;
+				val = -1.0f;
+			}
+		}
+		*sp = val;
+		if(*sp > *local_maxsample)
+			*local_maxsample = val;
+		if(*sp < local_minsample)
+			local_minsample = val;
+		sp++;
+	}
+#endif
+	*local_maxsample = max(-local_minsample,*local_maxsample);
+	if(finished && (*local_maxsample == 0.0))	//	Input read finished && Output buffer empty
+		todo = 0;
+	if(todo > 0) {
+		if(write_samps_pvoc(sbuf, todo, dz) < 0) {
+			sfperror("pvoc: write error");
+			return(SYSTEM_ERROR);
+		}
+	}
+	return(FINISHED);
+// <-- NEW 2014
+}
+
+/************************************ SNDWRITE_HEADER ************************************/
+
+int sndwrite_header(int N2,int *Nchans,float *arate,float R,int D,int origsize,int *isr,int M,dataptr dz)
+{
+	if (dz->iparam[PVOC_ANAL_ONLY]){
+
+		if (dz->iparam[PVOC_ENVOUT_ONLY] || dz->iparam[PVOC_MAGOUT_ONLY]){
+			*Nchans = N2 + 1;
+			if(sndputprop(dz->ofd,"blocksize",(char *)Nchans,sizeof(int)) < 0 )
+				fprintf(stdout,"WARNING: pvoc failed to write bloksize data\n");
+			*arate = (float)((float)(*Nchans)*R/(float)D);
+		} else {
+			*Nchans = dz->iparam[PVOC_CHANS] + 2;
+			dz->outfile->channels = *Nchans;
+			*arate = (float)(R/(float)D);
+		}
+		dz->outfile->srate = (int)(*arate);
+		dz->outfile->arate = *arate;
+		dz->outfile->origstype = origsize;
+		dz->outfile->origrate = *isr;
+		dz->outfile->Mlen = M;
+		dz->outfile->Dfac = D;
+
+	} else {	/* Synthesis */
+	    *isr = (int)R;
+		dz->outfile->srate = *isr;
+	    *Nchans = 1;
+		dz->outfile->channels = *Nchans;
+	}
+	fflush(stdout);
+	return(FINISHED);
+}
+
+/****************************** PVOC_PREPROCESS ******************************/
+
+int pvoc_preprocess(dataptr dz)
+{
+	int ampfrqpair_cnt;
+	dz->iparam[PVOC_ENVOUT_ONLY] = FALSE;
+	dz->iparam[PVOC_MAGOUT_ONLY] = FALSE;
+	dz->iparam[PVOC_ANAL_ONLY]   = FALSE;
+	switch(dz->process) {
+	case(PVOC_ANAL):
+		dz->iparam[PVOC_CHANS]       = dz->iparam[PVOC_CHANS_INPUT];
+		dz->iparam[PVOC_WIN_OVERLAP] = dz->iparam[PVOC_WINOVLP_INPUT]-1;
+		switch(dz->mode) {
+		case(STANDARD_ANAL):	dz->iparam[PVOC_ANAL_ONLY] = TRUE;										break;
+		case(ENVEL_ONLY):		dz->iparam[PVOC_ANAL_ONLY] = TRUE; dz->iparam[PVOC_ENVOUT_ONLY] = TRUE; break;
+		case(MAG_ONLY):			dz->iparam[PVOC_ANAL_ONLY] = TRUE; dz->iparam[PVOC_MAGOUT_ONLY] = TRUE; break;
+		default:
+			sprintf(errstr,"Unknown mode in pvoc_preprocess()\n");
+			return(PROGRAM_ERROR);
+		}
+		break;
+	case(PVOC_SYNTH):		
+		dz->iparam[PVOC_CHANS] = dz->infile->channels - 2;
+		/*RWD need this! */
+		dz->iparam[PVOC_PARTIAL_RESYNTH] = FALSE;
+		break;
+	case(PVOC_EXTRACT):		
+		dz->iparam[PVOC_CHANS] = dz->iparam[PVOC_CHANS_INPUT];
+  		ampfrqpair_cnt = dz->iparam[PVOC_CHANS]/2;
+		dz->iparam[PVOC_WIN_OVERLAP] = dz->iparam[PVOC_WINOVLP_INPUT]-1;
+		if(dz->iparam[PVOC_CHANSLCT_INPUT]) {
+			dz->iparam[PVOC_SELECTED_CHAN]  = dz->iparam[PVOC_CHANSLCT_INPUT];
+			dz->iparam[PVOC_PARTIAL_RESYNTH] = TRUE;
+		}
+		dz->iparam[PVOC_AF_PAIR_LO] = 0;
+		if(dz->iparam[PVOC_LOCHAN_INPUT] > 0) {
+			dz->iparam[PVOC_AF_PAIR_LO] = min(dz->iparam[PVOC_LOCHAN_INPUT],ampfrqpair_cnt);
+			dz->iparam[PVOC_PARTIAL_RESYNTH] = TRUE;
+		}
+		dz->iparam[PVOC_AF_PAIR_HI] = ampfrqpair_cnt;
+		if(dz->iparam[PVOC_HICHAN_INPUT] > 0) {
+			dz->iparam[PVOC_AF_PAIR_HI] = min(dz->iparam[PVOC_HICHAN_INPUT],ampfrqpair_cnt);
+			dz->iparam[PVOC_PARTIAL_RESYNTH] = TRUE;
+		}
+		if((dz->iparam[PVOC_CHANSLCT_INPUT] == 0) && (dz->iparam[PVOC_LOCHAN_INPUT] == 0)
+		&& (dz->iparam[PVOC_HICHAN_INPUT] == 0)) {
+			sprintf(errstr,"As no channels have been selected, output sound would be the same as the input.\n");
+			return(DATA_ERROR);
+		}
+		break;
+	default:
+		sprintf(errstr,"Unknown process in pvoc_preprocess()\n");
+		return(PROGRAM_ERROR);
+	}
+	/* Force PVOC_CHANS to be even */
+	dz->iparam[PVOC_CHANS] = dz->iparam[PVOC_CHANS] + (dz->iparam[PVOC_CHANS]%2);
+	return(FINISHED);
+}
+
+/*********************************** PVOC_TIME_DISPLAY ***********************************/
+
+int pvoc_time_display(int nI,int srate,int *samptime,dataptr dz)
+{
+	int true_chans, true_srate;
+	int true_outfiletype;
+	switch(dz->process) {
+	case(PVOC_ANAL):
+		true_outfiletype = dz->outfiletype;
+		dz->outfiletype  = SNDFILE_OUT;	
+	  	display_virtual_time(nI,dz);	
+		dz->outfiletype	 = true_outfiletype;
+	  	break;	 
+	case(PVOC_SYNTH): 	
+		true_chans = dz->infile->channels;
+		true_srate = dz->infile->srate;
+		dz->infile->channels = 1;
+		dz->infile->srate    = srate;	
+	  	display_virtual_time(nI,dz);	
+		dz->infile->channels = true_chans;
+		dz->infile->srate    = true_srate;
+		break;	 
+	case(PVOC_EXTRACT): 	
+		display_virtual_time(nI,dz);	
+		break;	 
+	default:
+		sprintf(errstr,"Unknown process in pvoc_time_display()\n");
+		return(PROGRAM_ERROR);
+	}
+	*samptime += SAMP_TIME_STEP;
+	return(FINISHED);
+}
+
+/******************************* WRITE_SAMPS_PVOC ********************************/
+
+int write_samps_pvoc(float *bbuf,int samps_to_write,dataptr dz)
+{
+	
+	int samps_written;
+	int i,j;
+   	int granularity = 22100;
+   	int this_granularity = 0;
+	float val;
+
+	if(dz->needpeaks){
+		for(i=0;i < samps_to_write; i += dz->outchans){
+			for(j = 0;j < dz->outchans;j++){
+				val = (float)fabs(bbuf[i+j]);
+				/* this way, posiiton of first peak value is stored */
+				if(val > dz->outpeaks[j].value){
+					dz->outpeaks[j].value = val;
+					dz->outpeaks[j].position = dz->outpeakpos[j];
+				}
+			}
+			/* count framepos */
+			for(j=0;j < dz->outchans;j++)
+				dz->outpeakpos[j]++;
+		}
+	}
+	if((samps_written = fputfbufEx(bbuf,samps_to_write,dz->ofd))<=0) {
+		sprintf(errstr,"Can't write to output soundfile: %s\n",sferrstr());
+		return(SYSTEM_ERROR);
+	}
+	dz->total_samps_written += samps_written;	
+    this_granularity += samps_to_write;
+    if(this_granularity > granularity){
+        display_virtual_time(dz->total_samps_written,dz);
+        this_granularity -= granularity;
+    }
+	return(FINISHED);
+}

+ 20 - 0
dev/pview/CMakeLists.txt

@@ -0,0 +1,20 @@
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -Wall -mmacosx-version-min=10.5 -Dunix")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O2 -Wall -DWIN32")
+  else()
+    set(CMAKE_C_FLAGS "-O2 -Wall -Dlinux -Dunix")
+  endif()
+endif()
+
+link_directories(../cdp2k ../sfsys)
+
+include_directories(../../include)
+
+add_executable(pview pview.c)
+
+target_link_libraries(pview cdp2k sfsys ${EXTRA_LIBRARIES})
+
+my_install(pview)
+

+ 342 - 0
dev/pview/pview.c

@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1983-2013 Trevor Wishart and Composers Desktop Project Ltd
+ * http://www.trevorwishart.co.uk
+ * 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
+ *
+ */
+
+
+
+/**************************************************************************
+ *
+ * Convert snd data to text format suitable for Pview in Sound Loom
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <osbind.h>
+#include <math.h>
+#include <float.h>
+#include <sfsys.h>
+#include <cdplib.h>
+
+
+/*RWD: changed all fprintf tests to < 0 */
+
+
+#define SHORTMONOSCALE      256.0       /* Scales shorts in range 0-32768 to display in range 0 128 */
+#define SHORTSTEREOSCALE    512.0       /* Scales shorts in range 0-32768 to display in stereorange 0 64 */
+#define FLOATMONOSCALE      128.0       /* Scales shorts in range 0-32768 to display in range 0 128 */
+#define FLOATSTEREOSCALE    64.0        /* Scales shorts in range 0-32768 to display in stereorange 0 64 */
+#define VIEW_WIDTH          900.0       /* width of sloom display */
+
+#define F_SECSIZE (512.0)
+/*  Functions   */
+
+static  int     tidy_up(int,unsigned int);
+static  int     open_in(char*);
+static  int     get_big_buf(void);
+static  int     make_textfile(char *filename);
+
+
+/*short *bigbuf;    */  /* buffer used to read samples from soundfile   */
+float *bigbuf;
+
+size_t buflen;              /* buffer length in samps           */
+int  ifd;                   /* input soundfile descriptor       */
+int srate;                  /* sampling rate of input           */
+int  channels;              /* number of channels of input      */
+int startsamp, endsamp;
+const char* cdp_version = "7.1.0";
+
+int main(int argc,char *argv[])
+{
+    unsigned int start = hz1000();
+    char filename[200];
+
+    if(argc==2 && (strcmp(argv[1],"--version") == 0)) {
+        fprintf(stdout,"%s\n",cdp_version);
+        fflush(stdout);
+        return 0;
+    }
+    if(argc!=4)  {
+        fprintf(stdout,"ERROR: Wrong number of arguments.\n");
+        fflush(stdout);
+        return tidy_up(2,start);
+    }
+    if(sscanf(argv[2],"%d",&startsamp)!=1) {
+        fprintf(stdout,"ERROR: cannot read start sample number.\n");
+        fflush(stdout);
+        return tidy_up(2,start);
+    }
+    if(sscanf(argv[3],"%d",&endsamp)!=1) {
+        fprintf(stdout,"ERROR: cannot read end sample number.\n");
+        fflush(stdout);
+        return tidy_up(2,start);
+    }
+    /* open input file */
+    if(open_in(argv[1]) < 0)
+        return tidy_up(2,start);
+
+    strcpy(filename,argv[1]);
+
+    /* get biggest buffer */
+    if(get_big_buf() == 0)
+        return tidy_up(1,start);
+        
+    /* max soundfiles */
+    if(make_textfile(filename)<0)
+        return tidy_up(1,start);
+
+    /* tidy up */
+    return tidy_up(0,start);
+}
+
+/**************************** OPEN_IN ****************************
+ *
+ * opens input soundfile and gets header 
+ */
+
+int open_in(char *name) /* opens input soundfile and gets header */
+{
+
+    if( (ifd = sndopenEx(name,0,CDP_OPEN_RDONLY)) < 0 ) {
+        fprintf(stdout,"INFO: Cannot open file: %s\n\t",name);
+        fflush(stdout);
+        return(-1);
+    }
+
+    /*  get sampling rate   */
+
+    if(sndgetprop(ifd,"sample rate", (char *)&srate,sizeof(int)) < 0) {
+        srate = 44100;
+        fprintf(stdout,"WARNING: Cannot get sampling rate of %s : Default of 44100 assumed\n",name);
+        fflush(stdout);
+    }
+
+    /*  get channels        */
+
+    if(sndgetprop(ifd, "channels", (char*)&channels, sizeof(int)) < 0) {
+        channels = 2;
+        fprintf(stdout,"WARNING: Cannot get channels of %s Default of STEREO assumed\n",name);
+        fflush(stdout);
+    }
+    return 0;
+}
+
+/**************************** GET_BIG_BUF ****************************
+ *
+ * allocates memory for the biggest possible buffer 
+ */
+
+int get_big_buf()
+{
+    size_t i;
+
+    buflen = (size_t) Malloc(-1)-sizeof(float); /* allow for alignment */
+
+    /* if less than one sector available */
+    if( buflen < (F_SECSIZE * sizeof(float)) || (bigbuf=(float*)Malloc(buflen+sizeof(float))) == NULL) {
+        fprintf(stdout,"ERROR: Failed to allocate float buffer.\n");
+        fflush(stdout);
+        return 0;
+    }
+    i = ((long)bigbuf+sizeof(float)-1)/sizeof(float)*sizeof(float); /* align bigbuf to word boundary */
+    bigbuf = (float*)i;
+    buflen = (size_t)((buflen/F_SECSIZE)*F_SECSIZE);
+    buflen /= sizeof(float);
+    buflen /= channels;
+    buflen *= channels;
+    return 1;
+}
+
+/**************************** MAKE TEXTFILE *****************************/
+
+int make_textfile(char *filename)
+{
+    int sampsgot, n, m, zoomfact;
+    double gpsample0, gpsample1;
+    int total_samps_got;
+    int gpcnt, sampout, sampouttrue;
+
+    /*RWD*/
+    int linecount = 0;
+
+    /* check header first */
+    FILE *fp;
+
+    /* read and find maximum */
+
+    if(channels > 2) {
+        fprintf(stdout,"ERROR: Process only works with mono or stereo files\n");
+        fflush(stdout);
+        return -1;
+    }
+    if((fp = fopen("cdptest00.txt","w"))==NULL) {
+        fprintf(stdout, "ERROR: Failed to open the Sound Loom display file 'cdptest00.txt'\n");
+        fflush(stdout);
+        return -1;
+    }
+    zoomfact = max(1,(int)ceil((double)(endsamp - startsamp)/VIEW_WIDTH));
+    if(fprintf(fp,"%d\n",zoomfact) < 1) {
+        fclose(fp);
+        fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+        fflush(stdout);
+        return -1;
+    }
+    gpcnt = 0;
+    gpsample0 = 0.0;
+    gpsample1 = 0.0;
+    total_samps_got = 0;
+    while((sampsgot = fgetfbufEx(bigbuf,buflen,ifd,1)) > 0 ) {
+        switch(channels) {
+        case(1):
+            for(n=0;n<sampsgot;n++) {
+                total_samps_got++;
+                if(total_samps_got <= startsamp)
+                    continue;
+                else if(total_samps_got > endsamp)
+                    break;
+                if(zoomfact == 1)
+                    gpsample0 = bigbuf[n];
+                else
+                    gpsample0 += fabs(bigbuf[n]);
+                gpcnt++;
+                if(gpcnt >= zoomfact) {
+                    gpsample0 /= (double)zoomfact;
+                    sampout = (short)round(gpsample0 * FLOATMONOSCALE); 
+                    if(fprintf(fp,"%lf %d\n",gpsample0,sampout) < 0) {
+                        fclose(fp);
+                        fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                        fflush(stdout);
+                        return -1;
+                    }
+                    gpsample0 = 0.0;
+                    gpcnt = 0;
+                }
+            }
+            if(gpcnt > 0) {
+                gpsample0 /= (double)gpcnt;
+                sampout = (short)round(gpsample0 * FLOATMONOSCALE); 
+                    if(fprintf(fp,"%lf %d\n",gpsample0,sampout) < 0) {
+                    fclose(fp);
+                    fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                    fflush(stdout);
+                    return -1;
+                }
+            }
+            break;
+        case(2):
+            for(n=0,m=1;m<sampsgot;n+=2,m+=2) {      /*RWD test was using n */
+                total_samps_got++;
+                if(total_samps_got <= startsamp)
+                    continue;
+                else if(total_samps_got > endsamp)
+                    break;
+                if(zoomfact == 1) {
+                    gpsample0 = bigbuf[n];
+                    gpsample1 = bigbuf[m];
+                } else {
+                    gpsample0 += fabs(bigbuf[n]);
+                    gpsample1 += fabs(bigbuf[m]);
+                }
+                gpcnt++;
+                if(gpcnt >= zoomfact) {
+                    gpsample0 /= (double)zoomfact;
+                    gpsample1 /= (double)zoomfact;
+                    sampout = (short)round(gpsample0 * FLOATSTEREOSCALE); 
+                    
+                    linecount++;
+                    if(fprintf(fp,"%lf %d\n",gpsample0,sampout) < 0) {
+                        fclose(fp);
+                        fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                        fflush(stdout);
+                        return -1;
+                    }
+                    sampout = (short)round(gpsample1 * FLOATSTEREOSCALE);
+                    
+                    linecount++;
+                    if(fprintf(fp,"%lf %d\n",gpsample1,sampout) < 0) {
+                        fclose(fp);
+                        fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                        fflush(stdout);
+                        return -1;
+                    }
+                    gpsample0 = 0.0;
+                    gpsample1 = 0.0;
+                    gpcnt = 0;
+                }
+            }
+            if(gpcnt > 0) {
+                gpsample0 /= (double)gpcnt;
+                gpsample1 /= (double)gpcnt;
+                sampout = (short)round(gpsample0 * FLOATSTEREOSCALE);
+                
+                linecount++;
+                if(fprintf(fp,"%lf %d\n",gpsample0,sampout) < 0) {
+                    fclose(fp);
+                    fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                    fflush(stdout);
+                    return -1;
+                }
+                sampouttrue = (short)round(gpsample1);
+                sampout = (short)round(gpsample1 * FLOATSTEREOSCALE);
+                
+                linecount++;
+                if(fprintf(fp,"%lf %d\n",gpsample1,sampout) < 0) {
+                    fclose(fp);
+                    fprintf(stdout, "ERROR: Failed to complete data write to Sound Loom display file 'cdptest00.txt'\n");
+                    fflush(stdout);
+                    return -1;
+                }
+            }
+            break;
+        default:      /*RWD*/
+            break;
+        }
+/*      break; */
+    }
+    fclose(fp);
+    return(0);
+}
+
+/**************************** TIDY_UP ****************************
+ *
+ * Exit, freeing buffers and closing files where necessary.
+ */
+
+int tidy_up(int where,unsigned int start)
+{
+    switch(where) {
+    case 0:
+        Mfree(bigbuf);
+    case 1:
+        sndcloseEx(ifd);
+    case 2:
+//      sffinish();
+    default:
+        break;
+    }
+    while(!(hz1000() - start))
+        ;
+    return(1);
+}
+    

+ 37 - 0
dev/pvxio2/Makefiled.osx

@@ -0,0 +1,37 @@
+# Mingw debug osx Makefile for pvxio2../ 
+
+CC = cc
+#OPT = -O3
+OPT = -g
+
+CFLAGS = $(OPT) -mmacosx-version-min=10.8  -Dunix -D__MAC__ -Wall -Werror=strict-aliasing -I../newinclude
+OBJS=pvfileio.o
+TARGET=libpvxio2d.a
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+all:	$(TARGET)
+
+	 
+$(TARGET): $(OBJS)
+#	ar cr  -o $(TARGET) $(OBJS)
+	libtool -static -o $(TARGET) $(OBJS)
+	ranlib $(TARGET)
+
+clean:
+	rm  -f $(OBJS)
+
+veryclean:	clean
+	rm -f $(TARGET)
+
+install:	$(TARGET)
+	cp $(TARGET) ../../lib
+    
+# dependencies
+
+pvfileio.o:	../newinclude/pvfileio.h ../newinclude/pvdefs.h
+
+
+
+

+ 66 - 0
dev/pvxio2/pvdefs.h

@@ -0,0 +1,66 @@
+/* pvdefs.h */
+/*
+ * Copyright (c) 2000,2022 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
+ *
+ */
+#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

+ 1484 - 0
dev/pvxio2/pvfileio.c

@@ -0,0 +1,1484 @@
+
+/* 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 union {
+    int32_t lsamp;
+    float fsamp;
+    unsigned char bytes[4];
+} SND_SAMP;
+
+typedef struct pvoc_file {
+    WAVEFORMATEX fmtdata;
+    PVOCDATA pvdata;
+    /* RWD 2022 to help CDP pvocex extension, requires two-stage file creation/update */
+    int32_t propsoffset;
+    int32_t needsupdate;    //for two-stage creation/update CDP-style
+    int32_t fmtchunkoffset;
+    int32_t datachunkoffset;
+    int32_t nFrames;   /* no of frames in file */
+    int32_t FramePos;  /* where we are in file */
+    int32_t curpos;
+    int32_t fd;
+    int32_t to_delete;
+    int32_t readonly;
+    int32_t do_byte_reverse;
+    char *name;
+    float *customWindow;
+    
+} PVOCFILE;
+
+
+
+static PVOCFILE *files[MAXFILES];
+
+static int32_t pvoc_writeheader(int32_t ofd);
+static int32_t pvoc_readheader(int32_t ifd,WAVEFORMATPVOCEX *pWfpx);
+
+int32_t pvoc_getdatasize_bytes(int32_t fd)
+{
+    int32_t datachunksize, framesize;
+    
+    if(files[fd]==NULL)
+        return -1;
+    if(files[fd]->nFrames == 0)
+        return 0;
+    framesize = files[fd]->pvdata.nAnalysisBins * 2 * sizeof(float);
+    datachunksize = files[fd]->nFrames * framesize * files[fd]->fmtdata.nChannels;
+    return datachunksize;
+}
+
+static int32_t write_guid(int32_t fd,int32_t byterev,const GUID *pGuid)
+{
+    int32_t 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 int32_t 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 int32_t write_pvocdata(int32_t fd,int32_t byterev,const PVOCDATA *pData)
+{
+    int32_t written;
+ //   int32_t dwval;
+#ifdef _DEBUG
+    assert(fd >= 0);
+    assert(pData);
+#endif
+
+    if(byterev){
+        int32_t revdwval;
+        PVOCDATA data;
+        SND_SAMP ssamp;
+        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);
+        
+        ssamp.fsamp = pData->fAnalysisRate;
+        //dwval = * (int32_t *) &(pData->fAnalysisRate);
+        ssamp.lsamp = REVDWBYTES(/*dwval*/ ssamp.lsamp);
+        //data.fAnalysisRate = * (float *) &dwval;
+        data.fAnalysisRate = ssamp.fsamp;
+        
+        ssamp.fsamp = pData->fWindowParam;
+        //dwval = * ( int32_t *) &(pData->fWindowParam);
+        revdwval = REVDWBYTES(ssamp.lsamp);
+        //dwval = REVDWBYTES(dwval);
+        //data.fWindowParam = * (float *) &dwval;
+        data.fWindowParam = ssamp.fsamp;
+        
+        written = write(fd,(char *) &data,sizeof(PVOCDATA));
+    }
+    else
+        written = write(fd,(char *) pData,sizeof(PVOCDATA));
+
+    return written;
+
+}
+
+static int32_t 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);
+    assert(pfmt->cbSize == 62);
+#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 int32_t pvoc_writeWindow(int32_t fd,int32_t byterev,float *window,DWORD length)
+{
+    if(byterev){
+        /* don't corrupt source array! */
+        DWORD i;
+        int32_t lval, *lp = (int32_t *) window;
+
+        for(i=0;i < length; i++){
+            lval = *lp++;
+            lval = REVDWBYTES(lval);
+            if(write(fd,(char *)&lval,sizeof(int32_t)) != sizeof(int32_t))
+                return 0;
+        }
+    }
+    else{
+        if(write(fd,(char *) window,length * sizeof(float)) != (int32_t)(length*sizeof(float)))
+            return 0;
+    }
+
+
+    return (int32_t)(length * sizeof(float));
+}
+
+static int32_t pvoc_readWindow(int fd, int byterev, float *window,DWORD length)
+{
+    if(byterev){
+        DWORD i;
+        int32_t got,oval,lval, *lp = (int32_t *) window;
+#ifdef SINGLE_FLOAT 
+        for(i=0;i < length;i++){
+            if(read(fd,(char *)&lval,sizeof(int32_t)) != sizeof(int32_t))
+                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 int32_t byte_order()
+{
+  int32_t   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.
+
+ *******************************************/
+
+int32_t init_pvsys(void)
+{
+    int32_t 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,DWORD chans, DWORD 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... */
+int32_t  pvoc_createfile(const char *filename,
+                     DWORD fftlen,DWORD overlap,DWORD chans,
+                     DWORD format,int32_t srate,
+                     pv_stype stype,pv_wtype wtype,
+                     float wparam,float *fWindow,DWORD dwWinlen)
+{
+    
+    int32_t 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;
+    pfile->needsupdate = 0;   //could be overruled later, but only once!
+    
+    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->propsoffset = 0;
+    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;
+}
+
+int32_t pvoc_getpvxprops(int32_t ifd, PVOCDATA *data)
+{
+    if(files[ifd]==NULL)
+        return 0;
+#ifdef _DEBUG
+    assert(data != NULL);
+#endif
+    memcpy((char*) data,(char*) &files[ifd]->pvdata,sizeof(PVOCDATA));
+    return 1;
+}
+/* tell pvsys to update pvocdata on close; return 0 for success */
+/* only other way to do this is to test initial params != 0 */
+/* I want to control this explicitly, for now */
+int32_t pvoc_set_needsupdate(int32_t ifd)
+{
+    if(files[ifd]==NULL)
+        return -1;
+    if(files[ifd]->readonly)
+        return -1;
+    files[ifd]->needsupdate = 1;
+    return 0;
+}
+
+//returns 0 for success
+int32_t pvoc_canupdate(int32_t ifd)
+{
+    if(files[ifd]==NULL || files[ifd]->readonly)
+        return -1;
+    if(files[ifd]->needsupdate)
+        return 0;
+    else
+        return -1;
+}
+
+//ONLY when creating new file using 2-stages.called last thing before file close.
+#if 0
+int32_t pvoc_updateprops(int32_t fd, const PVOCDATA *data)
+{
+    DWORD pos;
+    
+    if(files[fd]==NULL)
+        return -1;
+    if(files[fd]->readonly)
+        return -1;
+    if(files[fd]->needsupdate==0)
+        return -1;
+#ifdef _DEBUG
+    // the only field that MUST be set, needed for read/write frames
+    assert(files[fd]->pvdata.nAnalysisBins == data->nAnalysisBins);
+#endif
+    // need to seek to propsoffset, then we can call write_pvocdata
+    pos = lseek(files[fd]->fd,files[fd]->propsoffset,SEEK_SET);
+    if(pos != files[fd]->propsoffset){
+        pv_errstr = "\npvsys: error updating pvoc props";
+        return 0;
+    }
+    return 1;
+}
+#endif
+
+int32_t pvoc_openfile(const char *filename,PVOCDATA *data,WAVEFORMATEX *fmt)
+{
+    int32_t 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;
+    pfile->needsupdate = 0;   // just to state it explicitly!
+    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;
+
+}
+
+static int32_t pvoc_readfmt(int32_t fd,int32_t byterev,WAVEFORMATPVOCEX *pWfpx)
+{
+    DWORD dword;
+    WORD 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){
+        SND_SAMP ssamp;
+        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);
+
+        ssamp.fsamp = pWfpx->data.fAnalysisRate;
+        //dword = * (DWORD *)&(pWfpx->data.fAnalysisRate);  /* RWD TODO: use union */
+        //dword = REVDWBYTES(dword);
+        ssamp.lsamp = REVDWBYTES(ssamp.lsamp);
+        //pWfpx->data.fAnalysisRate = *(float *)&dword;
+        pWfpx->data.fAnalysisRate = ssamp.fsamp;
+        
+        ssamp.fsamp = pWfpx->data.fWindowParam;
+        //dword = * (DWORD *)&(pWfpx->data.fWindowParam);
+        //dword = REVDWBYTES(dword);
+        ssamp.lsamp = REVDWBYTES(ssamp.lsamp);
+        //pWfpx->data.fWindowParam = *(float *)&dword;
+        pWfpx->data.fWindowParam = ssamp.fsamp;
+
+    }
+    if(pWfpx->dwVersion != PVX_VERSION){
+        pv_errstr = "\npvsys: unknown pvocex Version";
+        return 0;
+    }
+
+    return 1;
+
+
+}
+
+
+static int32_t pvoc_readheader(int32_t ifd,WAVEFORMATPVOCEX *pWfpx)
+{
+    DWORD tag, size,riffsize;
+    int32_t 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 int32_t pvoc_writeheader(int ofd)
+{
+    int32_t 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(int32_t)) != sizeof(int32_t)
+        || write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)) {
+        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(int32_t)) != sizeof(int32_t)){
+        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(int32_t)) != sizeof(int32_t)
+        || write(files[ofd]->fd,(char *)&size,sizeof(int32_t)) != sizeof(int32_t)) {
+        pv_errstr = "\npvsys: error writing header";
+        return 0;
+    }
+    /* we need to record where we are, as we may have to update fmt data before file close */
+    files[ofd]->fmtchunkoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
+    
+    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(DWORD)) != 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(int32_t)) != sizeof(int32_t)
+        || write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)){
+        pv_errstr = "\npvsys: error writing fmt chunk";
+        return 0;
+    }
+/* RWD new 2022 so we can update all pvocex props on closing new file */
+    files[ofd]->propsoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
+    
+    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(int32_t)) != sizeof(int32_t)
+            || write(files[ofd]->fd,(char *)&size,sizeof(int32_t)) != sizeof(int32_t)) {
+            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(int32_t)) != sizeof(int32_t)){
+        pv_errstr = "\npvsys: write error writing header";
+        return 0;
+    }
+
+    /* we need to update size later on...*/
+
+    size = 0;
+    if(write(files[ofd]->fd,&size,sizeof(int32_t)) != sizeof(int32_t)){
+        pv_errstr = "\npvsys: write error writing header";
+        return 0;
+    }
+    files[ofd]->datachunkoffset = lseek(files[ofd]->fd,0,SEEK_CUR);
+
+    files[ofd]->curpos = files[ofd]->datachunkoffset;
+    return 1;
+}
+
+/* called by pvoc_closefile() */
+/* which checks "readonly", if not set, file is newly created */
+static int32_t pvoc_updateheader(int ofd)
+{
+    int32_t riffsize,datasize;
+    DWORD pos;
+
+#ifdef _DEBUG   
+    assert(files[ofd]);
+    assert(files[ofd]->fd >= 0);
+    //assert(files[ofd]->curpos == lseek(files[ofd]->fd,0,SEEK_CUR));
+    assert(!files[ofd]->readonly);
+#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: seek 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: write 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: seek error updating riff chunk";
+        return 0;
+    }
+    if(write(files[ofd]->fd,(char *) &riffsize,sizeof(DWORD)) != sizeof(DWORD)){
+        pv_errstr = "\npvsys: write error updating riff chunk";
+        return 0;
+    }
+ 
+    // update PVOCDATA and WAVEFORMATEX fields if required
+    if(files[ofd]->needsupdate){
+#ifdef _DEBUG
+        fprintf(stderr,"updating in pvoc_update_header()\n");
+#endif
+        pos = lseek(files[ofd]->fd,files[ofd]->fmtchunkoffset,SEEK_SET);
+        if(pos != files[ofd]->fmtchunkoffset){
+            pv_errstr = "\npvsys: seek error updating fmt data";
+            return 0;
+        }
+        pos = write_fmt(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->fmtdata));
+        if(pos != SIZEOF_WFMTEX){
+            pv_errstr = "\npvsys: write error updating fmt data";
+            return 0;
+        }
+        
+        pos = lseek(files[ofd]->fd,files[ofd]->propsoffset,SEEK_SET);
+        if(pos != files[ofd]->propsoffset){
+            pv_errstr = "\npvsys: seek error updating pvx data";
+            return 0;
+        }
+        //int32_t write_pvocdata(int32_t fd,int32_t byterev,const PVOCDATA *pData)
+        pos = write_pvocdata(files[ofd]->fd,files[ofd]->do_byte_reverse,&(files[ofd]->pvdata));
+        if(pos != sizeof(PVOCDATA)){
+            pv_errstr = "\npvsys: write error updating pvx data";
+            return 0;
+        }
+    }
+    pos = lseek(files[ofd]->fd,0,SEEK_END);
+    if(pos < 0){
+        pv_errstr = "\npvsys: seek error seeking to end of file";
+        return 0;
+    }
+    
+    return 1;
+}
+
+int32_t pvoc_update_closefile(int ofd, const PVOCDATA *data, const WAVEFORMATEXTENSIBLE *wfx)
+{
+    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) {
+        pv_errstr = "\ncannot update readonly file"; // probably an input file
+    }
+    else {
+        memcpy((char*) &files[ofd]->fmtdata, (char*) &wfx->Format, sizeof(WAVEFORMATEX));
+        memcpy((char*) &files[ofd]->pvdata, (char*) data, sizeof(PVOCDATA));
+    }
+    return pvoc_closefile(ofd);
+}
+
+
+int32_t 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] = 0;
+
+    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....
+
+
+ */
+int32_t pvoc_putframes(int ofd,const float *frame,int32_t numframes)
+{
+    DWORD i;
+    DWORD towrite;  /* count in 'words' */
+    int32_t 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 = (int32_t *) frame;
+        for(i=0;i < towrite; i++){
+            temp = *lfp++;
+            temp = REVDWBYTES(temp);
+            if(write(files[ofd]->fd,(char *) &temp,sizeof(int32_t)) != sizeof(int32_t)){
+                pv_errstr = "\npvsys: error writing data";
+                return 0;
+            }
+
+        }
+    }
+    else {
+        if(write(files[ofd]->fd,(char *) frame,towrite * sizeof(float)) != (int32_t)(towrite * sizeof(float))){
+            pv_errstr = "\npvsys: error writing data";
+            return 0;
+        }
+    }
+
+    files[ofd]->FramePos += numframes;
+    files[ofd]->nFrames += 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
+ */
+int32_t pvoc_getframes(int32_t ifd,float *frames,DWORD nframes)
+{
+    int32_t i;
+    int32_t toread;
+    int32_t oval,temp,*lfp;
+    int32_t got;
+    int32_t 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 = (int32_t *) frames;
+#ifdef SINGLE_FLOAT
+        for(i=0;i < toread;i++){
+            if((got=read(files[ifd]->fd,(char *) &temp,sizeof(int32_t))) <0){
+                pv_errstr = "\npvsys: error reading data";
+                return rc;
+            }
+            if(got < sizeof(int32_t)){
+                /* 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))) < (int32_t)(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;
+}
+// return 0 for success, -1 for error
+int32_t pvoc_rewind(int32_t ifd,int32_t skip_first_frame)
+{
+    int32_t rc = -1;
+    int32_t fd;
+    int32_t pos;
+    int32_t skipsize = 0;
+    int32_t 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;
+    }
+    skipframes = files[ifd]->fmtdata.nChannels;
+    //skipsize =  files[ifd]->pvdata.dwFrameAlign * skipframes;
+    
+    
+    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 */ pos;
+    files[ifd]->FramePos = skipframes;
+
+    return 0;
+
+}
+
+/* may be more to do in here later on */
+int32_t pvsys_release(void)
+{
+    int32_t i;
+
+    for(i=0;i < MAXFILES;i++){
+        if(files[i]) {
+#ifdef _DEBUG
+            fprintf(stderr,"\nDEBUG WARNING: file %d still open!\n",i);
+#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 */
+int32_t pvoc_framecount(int32_t ifd)
+{
+    if(files[ifd]==NULL)
+        return -1;
+
+    return files[ifd]->nFrames;
+}
+/* RWD Jan 2014   */
+// return -1 for error, else framepos
+int32_t pvoc_framepos(int32_t ifd)
+{
+    if(files[ifd]==NULL)
+        return -1;
+        
+    return files[ifd]->FramePos;
+}
+// return -1 for error, 0 for success
+//NB framecount always disregards chans. But offset represents m/c block: offset*2 to get frames
+int32_t pvoc_seek_mcframe(int32_t ifd, int32_t offset, int32_t mode)
+{
+ //   DWORD mcframealign;
+    DWORD rawoffset;
+    int32_t rc = -1;
+    int32_t mcframealign;
+    
+    if(files[ifd]==NULL)
+        return -1;
+    mcframealign =  files[ifd]->pvdata.dwFrameAlign * files[ifd]->fmtdata.nChannels;
+    rawoffset = offset * mcframealign;
+    switch(mode){
+    case SEEK_SET:
+            // offset is m/c quantity, e.g. 2 * frame for stereo
+        if(offset >= (files[ifd]->nFrames / files[ifd]->fmtdata.nChannels)){
+            pv_errstr = "\npvsys: seek target beyond end of file";
+            break;
+        } 
+        rawoffset += files[ifd]->datachunkoffset;
+        if(lseek(files[ifd]->fd,rawoffset,SEEK_SET) != rawoffset ) {
+            pv_errstr = "\npvsys: seek error, SEEK_SET";
+            return -1;
+        }
+        files[ifd]->curpos = rawoffset;
+        files[ifd]->FramePos = offset * files[ifd]->fmtdata.nChannels;
+        rc = 0;
+        break;
+    case SEEK_END:
+    // go to end of file + offset, offset <= 0
+        if(offset > 0){
+            pv_errstr = "\npvsys: seek target before start of file, offset must be <= 0";
+            break;
+        }
+#ifdef _DEBUG
+        fprintf(stderr,"pvoc_seek_mcframe: fd = %d\n",ifd);
+#endif
+        //NB not relative to datachunkoffset in this case
+        if(lseek(files[ifd]->fd,rawoffset,SEEK_END) < 0){
+            pv_errstr = "\npvsys: seek error, SEEK_END";
+            return -1;
+        }
+#ifdef _DEBUG
+        fprintf(stderr,"pvoc_seek_mcframe: files[%d]->nFrames = %d\n",ifd,files[ifd]->nFrames);
+#endif
+        files[ifd]->FramePos = files[ifd]->nFrames - (offset * files[ifd]->fmtdata.nChannels);
+        files[ifd]->curpos = files[ifd]->FramePos * files[ifd]->pvdata.dwFrameAlign;
+#ifdef _DEBUG
+        fprintf(stderr,"pvoc_seek_mcframe: got curpos = %d\n",files[ifd]->curpos);
+#endif
+        rc = 0;  //success!
+        break;
+    case SEEK_CUR:
+        // covered by pvoc_framepos(), equiv to a pvoc_tell() function.
+        pv_errstr = "\npvsys: SEEK_CUR mode not supported yet!";
+        break;
+    }
+    return rc;
+}