richarddobson 3 anni fa
parent
commit
b657be6e56

+ 20 - 0
dev/newsfsys/CMakeLists.txt

@@ -0,0 +1,20 @@
+# CMakeLists.txt for newsfsys (Release 8) RWD 2022
+if(APPLE)
+  set(CMAKE_C_FLAGS "-O2 -mmacosx-version-min=10.8  -Dunix -D__MAC__ -DMAC -DENABLE_PVX")
+else()
+  if(MINGW)
+    set(CMAKE_C_FLAGS "-O2 -D_X86_ -DWIN32 -D_WIN32 -D__GNUWIN32__ -DENABLE_PVX")
+  else()
+    set(CMAKE_C_FLAGS "-O2 -Dunix -Dlinux -D_X86_ -DENABLE_PVX")
+  endif()
+endif()
+
+include_directories(../newinclude)
+
+if(MINGW)
+   set(SFSYS_SRCS sfsys.c snd.c sfdir.c osbind.c props.c shortcuts.c scandir.c alias.c ieee80.c)
+else()
+   set(SFSYS_SRCS sfsys.c snd.c sfdir.c osbind.c props.c ieee80.c pvfileio.c)
+endif()
+
+add_library(newsfsys ${SFSYS_SRCS})

+ 49 - 0
dev/newsfsys/Makefiled.osx

@@ -0,0 +1,49 @@
+#
+#	OSX debug makefile for newsfsys (with PVOCEX support) (Release 8)  � Richard Dobson, CDP Ltd 2022
+#
+
+CC=cc
+# add -DVERBOSE to get diagnostic reports to stdout
+# for basic debugging, define DEBUG_MAC
+CDPROOT=..
+CFLAGS= -g -mmacosx-version-min=10.8  -Dunix -D__MAC__  -D_DEBUG -DENABLE_PVX -I$(CDPROOT)/newinclude -I../include
+
+HDRS=	$(CDPROOT)/newinclude/sfsys.h $(CDPROOT)/newinclude/osbind.h ./sffuncs.h
+OBJS=	sfsys.o snd.o sfdir.o osbind.o props.o ieee80.o
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+#
+#	targets
+#
+all:	libsfsysd.a
+
+
+libsfsysd.a:	$(OBJS)
+#	ar cr sfsys.a $(OBJS)
+	libtool -static -o libsfsysd.a $(OBJS)
+	ranlib libsfsys.a
+
+clean:
+	rm -f $(OBJS)
+
+veryclean:	clean
+	rm  -f libsfsysd.a
+
+install:	libsfsysd.a 	
+	cp libsfsysd.a ../../lib/libsfsysd.a
+
+
+test:	pvxtest.o libsfsys.a
+	$(CC) $(CFLAGS) pvxtest.o -o pvxtest -L. -L../../lib  -lsfsysd -lpvxio2d
+
+#specnu:	specnu.o
+#	$(CC) $(CFLAGS) specnu.o -o specnu -L. -L../../lib -lsfsysd -lcdp2kd 
+
+#
+#	dependencies
+#
+sfsys.o:	$(HDRS)
+sfdir.o:	$(HDRS)
+snd.o:		$(HDRS)
+osbind.o:	$(HDRS)

+ 109 - 0
dev/newsfsys/alias.c

@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+
+
+
+/*alias.c: WIN32 only */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alias.h"
+#ifndef _MAX_PATH
+#define _MAX_PATH (255)
+#endif
+
+
+#ifdef TESTING
+int main(int argc, char **argv)
+{
+
+    char linkfile[_MAX_PATH];
+    char path[_MAX_PATH];
+    char desc[_MAX_PATH];
+    char tmp[_MAX_PATH];
+    char *dotp = NULL, *targetname;
+
+    if(argc < 2){
+        printf("\nusage: alias file.lnk");
+        return 1;
+    }
+    linkfile[0] = path[0] = desc[0] = tmp[0] = '\0';
+
+    strcpy(linkfile,argv[1]);
+    targetname = linkfile;
+    /*does name have .lnk extension?*/
+    dotp = strrchr(linkfile, '.');
+    if(dotp == NULL || (dotp != NULL && _stricmp(dotp, ".lnk") != 0)) {
+        //add .lnk extension
+        strcpy(tmp,linkfile);
+        strcat(tmp,".lnk");                     /*TODO: check strlen(linkfile) !*/
+        targetname = tmp;
+    }
+    /* check if the file exists             */
+    if(!fileExists(targetname)) {
+        printf("\ncannot find a link file with that name!");
+        return 1;
+    }
+    /*and get the target    */
+    if(!getAliasInfo(targetname,path,desc)){
+        printf("\nfailed to get link info: is it a real shortcut?");
+        return 1;
+    }
+
+    printf("\nPath = %s\nDescr = %s\n",path,desc);
+
+    return 0;
+}
+#endif
+
+/*return 0 for failure, 1 for success*/
+int getAliasName(const char *name, char *newname)
+{
+    char *dotp = NULL, *targetname =  (char *) name;
+    char tmp[_MAX_PATH],desc[_MAX_PATH];
+    if(name==NULL || newname==NULL)
+        return 0;
+    tmp[0] = '\0';
+    dotp = strrchr(targetname, '.');
+    if(dotp == NULL || (dotp != NULL && _stricmp(dotp, ".lnk") != 0)) {
+        /*add .lnk extension    */
+        strcpy(tmp,name);
+        strcat(tmp,".lnk");                     /*TODO: check strlen(linkfile) !*/
+        targetname = tmp;
+    }
+    /* check if the file exists             */
+    if(!fileExists(targetname)) {
+        newname=NULL;
+        return 0;
+    }
+    /*and get the target    */
+    if(!getAliasInfo(targetname,newname,desc)){
+        newname[0] = '\0';
+        return 0;
+    }
+
+    return 1;
+}

+ 36 - 0
dev/newsfsys/alias.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+/*alias.h*/
+
+
+
+int COMinit(void);
+void COMclose(void);
+int getAliasInfo(const char *linkfilename, char *path, char *desc);
+int fileExists(const char *name);
+int getAliasName(const char *name, char *newname);
+

+ 149 - 0
dev/newsfsys/ieee80.c

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

+ 17 - 0
dev/newsfsys/ieee80.h

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

+ 341 - 0
dev/newsfsys/osbind.c

@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+/*
+ *  emulation of Misc Atari OS routines
+ */
+
+/*
+ * Revision 1.2  1995/02/01  10:54:37  martin
+ * Added CDP_MEMORY_BBSIZE to control Malloc(-1) result
+ *
+ * Revision 1.1  1994/10/31  15:46:36  martin
+ * Initial revision
+ *
+ */
+ /*RWD 16/1/96 changed hz200 to GetSystemTime() from GetLocalTime() */
+ /*RWD 7/9/96 fixed bug in getdrivefreespace() */
+#ifdef _WIN32
+#include <windows.h>
+#include <time.h>
+#endif
+#include <stdlib.h>
+#include <osbind.h>
+#if defined __GNUWIN32__ || defined unix
+#include <ctype.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#ifdef __GNUWIN32__
+#include <io.h>
+#endif
+
+#ifdef unix
+#include <string.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+#ifdef linux
+#include <stdint.h>
+#include <sys/vfs.h>
+#endif
+
+/*RWD May 2005: has to be a signed value sadly!*/
+/* RWD Jan 2014 not any longer... */
+#define BIGFILESIZE (0xFFFFFFFFLU)
+
+
+void *
+Malloc(long size)
+{
+    char *bbs;
+    unsigned long res;
+
+    if(size != -1)
+        return malloc(size);
+    /* this is a request for the maximum block size */
+
+    if((bbs = getenv("CDP_MEMORY_BBSIZE")) == NULL)
+        return (void *)(1024*1024);
+    res = atol(bbs);
+    if(res < 10)
+        res = 100;
+    else if(res > 20*1024)
+        res = 20 * 1024;
+    res *= 1024;
+    return (void *)res;
+}
+
+void
+Mfree(void *ptr)
+{
+    free(ptr);
+}
+
+/************** WINDOWS ************************/
+
+#ifdef _WIN32
+unsigned int
+hz200(void)
+{
+    unsigned long secs, ticks;
+    SYSTEMTIME st;
+
+    GetLocalTime(&st);
+    ticks = st.wMilliseconds/(1000/200);
+    secs = (st.wHour*60 + st.wMinute) * 60 + st.wSecond;
+
+    return (unsigned int)(secs*200 + ticks);
+}
+/*TW*/
+unsigned int
+hz1000(void)
+{
+    unsigned long secs, ticks;
+    SYSTEMTIME st;
+
+    GetLocalTime(&st);
+    ticks = st.wMilliseconds;
+    secs = (st.wHour*60 + st.wMinute) * 60 + st.wSecond;
+    return (unsigned int)( secs*1000 + ticks);
+}
+
+unsigned int
+getdrivefreespace(const char *path)
+{
+    DWORD sectorspercluster;
+    DWORD bytespersector;
+    DWORD freeclusters;
+    DWORD totclusters;
+    __int64 freesectors;    /*RWD Jan 2014 */
+
+    char pbuf[4];
+
+    if(path[0] != '\0' && path[1] == ':') {
+        pbuf[0] = path[0];
+        pbuf[1] = ':';
+        pbuf[2] = '\\';
+        pbuf[3] = '\0';     /*RWD*/
+        path = pbuf;
+    } else
+        path = 0;
+
+    if(!GetDiskFreeSpace(path, &sectorspercluster, &bytespersector, &freeclusters, &totclusters))
+        return 0;
+
+    freesectors = (__int64)freeclusters * sectorspercluster;
+    if(freesectors > BIGFILESIZE/bytespersector)
+        return BIGFILESIZE; /* next line would overflow!! */
+    return (unsigned int)(freesectors * bytespersector);
+}
+
+char *
+legalfilename(char *filename)
+{
+    char *lastdot = strrchr(filename, '.');
+    char *lastfs = strrchr(filename, '\\');
+    DWORD maxcl;
+    char path[4];
+    char fstname[10];
+
+    if(lastdot == 0 || lastfs == 0 || lastdot <= lastfs || filename[1] != ':')
+        return "Illegal filename: internal error";
+    path[0] = filename[0];
+    path[1] = ':';
+    path[2] = '\\';
+    path[3] = '\0';
+
+    if(!GetVolumeInformation(path,
+                    (LPTSTR)0, 0,   /* volume name */
+                    (LPDWORD)0, /* volume serial number */
+                    &maxcl,     /* maximum component length */
+                    (LPDWORD)0, /* filesystem flags */
+                    fstname, 10 /* file system type name */
+            ))
+        return "Illegal filename: can't get volume information";
+
+    if(strcmp(fstname, "FAT") == 0 && maxcl == 12) {/* don't know what vfat under windows4 will look like! */
+        if(strchr(lastfs+1, '.') != lastdot)
+            return "Illegal filename: FAT filenames cannot have more than 1 dot";
+        if(strlen(lastdot+1) > 3)
+            return "Illegal filename: FAT filenames cannot have more than 3 letters after dot";
+        if(lastdot - (lastfs+1) > 8)
+            return "Illegal filename: FAT filenames cannot have more than 8 letters before dot";
+        return 0;
+    }
+    if(strlen(lastfs+1) > maxcl)
+        return "Illegal filename: filename component is too long";
+    return 0;
+}
+
+#endif
+
+/***************************** OS X (at least)******************/
+
+#ifdef unix
+unsigned int
+hz200(void)
+{
+    struct timeval tv;
+
+    if(gettimeofday(&tv, (struct timezone *)0) < 0)
+        abort();
+
+    return (unsigned int)(tv.tv_sec*200 + tv.tv_usec/(1000000/200));
+}
+unsigned int
+hz1000(void)
+{
+    struct timeval tv;
+
+    if(gettimeofday(&tv, (struct timezone *)0) < 0)
+        abort();
+
+    return (unsigned int)(tv.tv_sec*1000 + tv.tv_usec/(1000000/1000));
+
+}
+
+unsigned int
+getdrivefreespace(const char *path)
+{
+    int ret;
+    uint64_t avail = 0; /*RWD Jan 2014 */
+    struct statfs diskstat = {0};
+    /* RWD we ignore path, and use current dir; cannot use non-existent file path anyway */
+    ret = statfs(".",&diskstat);
+    if(ret==0){
+        if(diskstat.f_bfree > (uint64_t) BIGFILESIZE / diskstat.f_bsize)
+            avail = BIGFILESIZE;
+        else
+            avail = diskstat.f_bsize * diskstat.f_bfree;
+    }   
+    return (unsigned int) avail;
+}
+
+int
+_stricmp(const char *a, const char *b)
+{
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+
+int
+_strnicmp(const char *a, const char *b, size_t length)
+{
+    size_t len = length;
+
+    while(*a != '\0' && *b != '\0') {
+        int ca = islower(*a) ? toupper(*a) : *a;
+        int cb = islower(*b) ? toupper(*b) : *b;
+
+        if(len-- < 1)
+            return 0;
+
+        if(ca < cb)
+            return -1;
+        if(ca > cb)
+            return 1;
+
+        a++;
+        b++;
+    }
+    if(*a == '\0' && *b == '\0')
+        return 0;
+    if(*a != '\0')
+        return 1;
+    return -1;
+}
+
+
+char *
+_fullpath(char *buf, const char *filename, size_t maxlen)
+{
+    int mybuf = 0;
+
+    if(filename[0] == '/') {
+        if((buf = malloc(strlen(filename) + 1)) == 0)
+            return (char *)filename;
+        strcpy(buf, filename);
+        return buf;
+    }
+    if(buf == 0) {
+        mybuf++;
+        maxlen = 2 * /*PATH_MAX*/ MAXPATHLEN;
+        if((buf = malloc(maxlen)) == 0)
+            return NULL;
+    }
+
+    if(getcwd(buf, maxlen) == NULL)
+        return (char *)filename;
+
+    if(strlen(buf) + strlen(filename) + 2 >= maxlen)
+        return (char *)filename;
+
+    strcat(buf, "/");
+    strcat(buf, filename);
+
+    if(mybuf)
+        buf = realloc(buf, strlen(buf)+1);
+    return buf;
+}
+
+char *
+legalfilename(char *filename)
+{
+    return 0;
+}
+#endif
+
+
+/* for both WIN32 and unix! */
+
+void
+initrand48(void)
+{
+    srand(time((time_t *)0));
+}
+
+double
+drand48(void)
+{
+    return (double)rand()/(double)RAND_MAX;
+}
+

+ 467 - 0
dev/newsfsys/props.c

@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+
+
+
+/*props.c*/
+/*builtin- C version of props.cpp*/
+/*RWD.5.99 private version to support WAVE_EX*/
+/*RWD.5.99 NB NO SUPPORT FOR LONG INT FORMATS YET!*/
+/*RWD Feb 2014 release 7, added 6.1 speaker format */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sfsys.h>
+#include "sffuncs.h"
+#include "chanmask.h"
+
+char *props_errstr;
+
+int sf_headread(int fd,SFPROPS *props)
+{
+    int srate,chans,samptype,origsize = 0,origrate=0,origchans = 0,dummy;
+    float arate = (float)0.0;
+    int wlen=0,dfac=0,specenvcnt = 0,checksum=0;
+    channelformat chformat;
+
+    props->window_size = 0.0f;
+    props_errstr = NULL;
+    if(props==NULL)
+        return 0;
+    if(fd <0){
+        props_errstr = "Cannot read Soundfile: Bad Handle";
+        return 0;
+    }
+    if(sfgetprop(fd,"sample rate", (char *)&srate, sizeof(int)) < 0) {
+        props_errstr = "Failure to read sample rate";
+        return 0;
+    }
+    if(sfgetprop(fd,"channels", (char *)&chans, sizeof(int)) < 0) {
+        props_errstr ="Failure to read channel data";
+        return 0;
+    }
+    if(sfgetprop(fd,"sample type", (char *)&samptype, sizeof(int)) < 0){
+        props_errstr = "Failure to read sample size";
+        return 0;
+    }
+
+    props->srate = srate;
+    props->chans = chans;
+    props->type = wt_wave;
+
+    if(sf_getchanformat(fd,&chformat) < 0){
+        props_errstr = "cannot find channel format information";
+        return 0;
+    }
+    props->chformat = chformat;
+
+    switch(samptype){
+    case(SAMP_SHORT):
+        props->samptype = SHORT16;
+        break;
+    case(SAMP_FLOAT):
+        props->samptype = FLOAT32;
+        break;
+    case(SAMP_LONG):
+        props->samptype = INT_32;
+        break;
+    case(SAMP_2432):
+        props->samptype = INT2432;
+        break;
+    case(SAMP_2424):
+        props->samptype = INT2424;
+        break;
+    case(SAMP_2024):
+        props->samptype = INT2024;
+        break;
+    case(SAMP_MASKED):
+        props->samptype = INT_MASKED;
+        break;
+    default:
+        props_errstr = "unrecognised integer sample format";
+        return 0;
+        break;
+    }
+
+    /*now elaborate the format...*/
+    if(chformat > STDWAVE && chformat != MC_BFMT){
+        long chmask = sfgetchanmask(fd);
+
+        /*we want to know the channel config, if any, and the brand of file*/
+        if(chmask < 0) {
+            props_errstr = "internal error: cannot read channel mask";
+            return 0;
+        }
+        /*RWD Jan 2007 accept chancount > numbits set in speakermask */
+        switch(chmask){
+            /*check all cross-platform formats first...*/
+        case(SPKRS_UNASSIGNED):
+            props->chformat = MC_STD;
+            break;
+        case(SPKRS_MONO):
+            if(props->chans==1)
+                props->chformat = MC_MONO;
+            else
+                props->chformat = MC_WAVE_EX;   /*something weird...*/
+            break;
+        case(SPKRS_STEREO):
+            if(props->chans==2)
+                props->chformat = MC_STEREO;
+            else
+                props->chformat = MC_WAVE_EX;   /*something weird...*/
+            break;
+        case(SPKRS_GENERIC_QUAD):
+            if(props->chans==4)
+                props->chformat = MC_QUAD;
+            else
+                props->chformat = MC_WAVE_EX;   /*something weird...*/
+            break;
+        case(SPKRS_SURROUND_LCRS):
+            if(props->chans==4)
+                props->chformat = MC_LCRS;
+            else
+                props->chformat = MC_WAVE_EX;   /*something weird...*/
+            break;
+        case(SPKRS_DOLBY5_1):
+            if(props->chans==6)
+                props->chformat = MC_DOLBY_5_1;
+            else
+                props->chformat = MC_WAVE_EX;   /*something weird...*/
+            break;
+        case(SPKRS_SURR_6_1):
+            props->chformat = MC_SURR_6_1;
+            break;
+        case(SPKRS_SURR_7_1):
+            props->chformat = MC_SURR_7_1;
+            break;
+        case(SPKRS_CUBE):
+            props->chformat = MC_CUBE;
+            break;
+
+        default:
+            props->chformat = MC_WAVE_EX;   /*something weird...    */
+            break;
+        }
+    }
+
+    if(props->samptype==FLOAT32) {
+        /*we know we have floats: is it spectral file?*/
+        if(sfgetprop(fd,"original sampsize",(char *)&origsize, sizeof(int))<0){
+            props_errstr = "Failure to read original sample size";
+        }
+        if(sfgetprop(fd,"original sample rate",(char *)&origrate,sizeof(int))<0){
+            props_errstr = "Failure to read original sample rate";
+        }
+        if(sfgetprop(fd,"arate",(char *)&arate,sizeof(float)) < 0){
+            props_errstr = "Failure to read analysis sample rate";
+        }
+        if(sfgetprop(fd,"analwinlen",(char *)&wlen,sizeof(int)) < 0){
+            props_errstr = "Failure to read analysis window length";
+        }
+        if(sfgetprop(fd,"decfactor",(char *)&dfac,sizeof(int)) < 0){
+            props_errstr = "Failure to read decimation factor";
+        }
+        checksum = origsize + origrate + wlen + dfac + (int)arate;
+        if(checksum==0) {       /*its a wave file, or an envelope file*/
+            int isenv = 0;
+
+            float winsize;
+            if(sfgetprop(fd,"is an envelope",(char *) &isenv,sizeof(int)) < 0){
+                props_errstr = "Failure to read envelope property";
+            }
+            if(isenv){
+                /*its an envelope file, get window size*/
+                if(sfgetprop(fd,"window size",(char *)&winsize,sizeof(/*double*/float)) < 0) {
+                    props_errstr = "Error reading window size in envelope file";
+                    return 0;
+                }
+                props->window_size = /*(float)*/ winsize;
+                props->type = wt_binenv;
+            }
+
+
+            /*return 1;*/
+            /*tell props what brand of file this is - e.g WAVE_EX*/
+            return (sfformat(fd,&props->format) == 0);
+
+        }
+        else {
+            if(props_errstr==NULL){ /*its a good spectral file*/
+                props->origsize = origsize;
+                props->origrate = origrate;
+                props->arate = arate;
+                props->winlen = wlen;    /*better be wlen+2 ?*/
+                props->decfac = dfac;
+                /*props.chans = (wlen+2)/2;             //?? */
+                if(sfgetprop(fd,"is a pitch file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_pitch;
+                else if(sfgetprop(fd,"is a transpos file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_transposition;
+                else if(sfgetprop(fd,"is a formant file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_formant;
+                else
+                    props->type = wt_analysis;
+            } else
+                return 0;       /*somehow, got a bad analysis file...*/
+        }
+        /* get any auxiliary stuff for control file*/
+        /*adapted from TW's function in speclibg.cpp*/
+        switch(props->type){
+        case(wt_formant):
+            if(sfgetprop(fd,"specenvcnt",(char *)&specenvcnt,sizeof(int)) < 0){
+                props_errstr = "Failure to read formant size in formant file";
+                return 0;
+            }
+            props->specenvcnt = specenvcnt;
+
+        case(wt_pitch):
+        case(wt_transposition):
+            if(props->chans != 1){ /*RWD: this makes old-style files illegal!*/
+                props_errstr =  "Channel count does not equal to 1 formant,pitch or transposition file";
+                return 0;
+            }
+            if(sfgetprop(fd,"orig channels", (char *)&origchans, sizeof(int)) < 0) {
+                props_errstr =  "Failure to read original channel data in formant,pitch or transposition file";
+                return 0;
+            }
+            props->origchans = origchans;
+            break;
+        default:
+            break;
+        }
+        /*for a spectral or binenv file, we say standard channel format*/
+        props->chformat = STDWAVE;      /*might be MC_STD in due course!*/
+
+    }
+    /*return 1;*/
+    /*tell props what brand of file this is - e.g WAVE_EX*/
+    return (sfformat(fd,&props->format) == 0);
+}
+
+
+
+int snd_headread(int fd,SFPROPS *props)
+{
+    int srate,chans,samptype,origsize = 0,origrate = 0, origchans = 0,dummy;
+    float arate = (float)0.0;
+    int wlen=0,dfac=0,specenvcnt = 0,checksum=0;
+    channelformat chformat;
+    props->window_size = 0.0f;
+
+    props_errstr = NULL;
+    if(props==NULL)
+        return 0;
+    if(fd <0){
+        props_errstr = "Bad Soundfile Handle";
+        return 0;
+    }
+    if(sndgetprop(fd,"sample rate", (char *)&srate, sizeof(int)) < 0) {
+        props_errstr = "Failure to read sample rate";
+        return 0;
+    }
+    if(sndgetprop(fd,"channels", (char *)&chans, sizeof(int)) < 0) {
+        props_errstr ="Failure to read channel data";
+        return 0;
+    }
+    if(sndgetprop(fd,"sample type", (char *)&samptype, sizeof(int)) < 0){
+        props_errstr = "Failure to read sample size";
+        return 0;
+    }
+
+    props->srate = srate;
+    props->chans = chans;
+    props->type = wt_wave;
+
+    if(snd_getchanformat(fd,&chformat) < 0){
+        props_errstr = "cannot find channel format information";
+        return 0;
+    }
+    props->chformat = chformat;
+
+    switch(samptype){
+    case(SAMP_SHORT):
+        props->samptype = SHORT16;
+        break;
+    case(SAMP_FLOAT):
+        props->samptype = FLOAT32;
+        break;
+    case(SAMP_LONG):
+        props->samptype = INT_32;
+        break;
+    case(SAMP_2432):
+        props->samptype = INT2432;
+        break;
+    case(SAMP_2424):
+        props->samptype = INT2424;
+        break;
+    case(SAMP_2024):
+        props->samptype = INT2024;
+        break;
+    case(SAMP_MASKED):
+        props->samptype = INT_MASKED;
+        break;
+    default:
+        props_errstr = "unrecognised integer sample format";
+        return 0;
+        break;
+    }
+
+
+
+    /*now elaborate the format..*/
+    if(chformat > STDWAVE && chformat != MC_BFMT){
+        int chmask = sndgetchanmask(fd);
+
+        /*we want to know the channel config, if any, and the brand of file     */
+        if(chmask < 0)
+            return 0;
+        switch(chmask){
+            /*check all cross-platform formats first...*/
+        case(SPKRS_UNASSIGNED):
+            props->chformat = MC_STD;
+            break;
+        case(SPKRS_MONO):
+            props->chformat = MC_MONO;
+            break;
+        case(SPKRS_STEREO):
+            props->chformat = MC_STEREO;
+            break;
+        case(SPKRS_GENERIC_QUAD):
+            props->chformat = MC_QUAD;
+            break;
+        case(SPKRS_SURROUND_LCRS):
+            props->chformat = MC_LCRS;
+            break;
+        case(SPKRS_DOLBY5_1):
+            props->chformat = MC_DOLBY_5_1;
+            break;
+        case(SPKRS_SURR_6_1):
+            props->chformat = MC_SURR_6_1;
+            break;
+        case(SPKRS_SURR_7_1):
+            props->chformat = MC_SURR_7_1;
+            break;
+        case(SPKRS_CUBE):
+            props->chformat = MC_CUBE;
+            break;
+        default:
+            props->chformat = MC_WAVE_EX;   /*could be something weird from WAVE-EX...*/
+            break;
+        }
+    }
+
+    if(props->samptype==FLOAT32 || props->samptype== SAMP_LONG) {
+        /*we know we have floats: is it spectral file?*/
+        if(sndgetprop(fd,"original sampsize",(char *)&origsize, sizeof(int))<0){
+            props_errstr = "Failure to read original sample size";
+        }
+        if(sndgetprop(fd,"original sample rate",(char *)&origrate,sizeof(int))<0){
+            props_errstr = "Failure to read original sample rate";
+        }
+        if(sndgetprop(fd,"arate",(char *)&arate,sizeof(float)) < 0){
+            props_errstr = "Failure to read analysis sample rate";
+        }
+        if(sndgetprop(fd,"analwinlen",(char *)&wlen,sizeof(int)) < 0){
+            props_errstr = "Failure to read analysis window length";
+        }
+        if(sndgetprop(fd,"decfactor",(char *)&dfac,sizeof(int)) < 0){
+            props_errstr = "Failure to read decimation factor";
+        }
+        /*TODO: find a way to guarantee unique checksums...*/
+        checksum = origsize + origrate + wlen + dfac + (int)arate;
+        if(checksum==0) {       /*its a wave file*/
+            int isenv = 0;
+            float winsize;
+            if(sndgetprop(fd,"is an envelope",(char *) &isenv,sizeof(int)) < 0){
+                props_errstr = "Error looking for envelope property";
+            }
+            if(isenv){
+                /*its an envelope file, get window size*/
+
+                if(sndgetprop(fd,"window size",(char *)&winsize,sizeof(float)) < 0) {
+                    props_errstr = "Error reading window size in envelope file";
+                    return 0;
+                }
+                props->window_size = /*(float)*/ winsize;
+
+                props->type = wt_binenv;
+                /*must be floatsams, in this case*/
+                props->samptype = /*SAMP_FLOAT;*/ FLOAT32; /* RWD July 2004 */
+                /*and it won't (yet) be anything other than a standard file*/
+                props->chformat = STDWAVE;
+            }
+            /*tell props what brand of file this is - e.g WAVE_EX*/
+            return (snd_fileformat(fd,&props->format) == 0);
+        }
+        else {
+            if(props_errstr==NULL){ /*its a good spectral file*/
+                /*must be floatsams*/
+                props->samptype= FLOAT32;
+
+                props->origsize = origsize;
+                props->origrate = origrate;
+                props->arate = arate;
+                props->winlen = wlen;
+                props->decfac = dfac;
+
+                if(sndgetprop(fd,"is a pitch file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_pitch;
+                else if(sndgetprop(fd,"is a transpos file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_transposition;
+                else if(sndgetprop(fd,"is a formant file", (char *)&dummy, sizeof(int))>=0)
+                    props->type = wt_formant;
+                else
+                    props->type = wt_analysis;
+            } else
+                return 0;       /*somehow, got a bad analysis file...*/
+        }
+        /* get any auxiliary stuff for control file */
+        /* adapted from TW's function in speclibg.cpp */
+        switch(props->type){
+        case(wt_formant):
+            if(sndgetprop(fd,"specenvcnt",(char *)&specenvcnt,sizeof(int)) < 0){
+                props_errstr = "Failure to read formant size in formant file";
+                return 0;
+            }
+            props->specenvcnt = specenvcnt;
+            /*      break; */ /*RWD July 2004*/
+        case(wt_pitch):
+        case(wt_transposition):
+            if(props->chans != 1){
+                props_errstr =  "Channel count not equal to 1 in transposition file";
+                return 0;
+            }
+            if(sndgetprop(fd,"orig channels", (char *)&origchans, sizeof(int)) < 0) {
+                props_errstr =  "Failure to read original channel data from transposition file";
+                return 0;
+            }
+            props->origchans = origchans;
+            break;
+        default:
+            break;
+        }
+        props->chformat = STDWAVE;
+    }
+    /*tell props what brand of file this is - e.g WAVE_EX*/
+    return (snd_fileformat(fd,&props->format) == 0);
+}

+ 353 - 0
dev/newsfsys/props.cpp

@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+ 
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ 
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+/*	props.cpp: adapted from speclib3.c
+ *	NB this is now real C++ code, not C!	
+ *	
+ */
+//LATEST VERSION OCT97: supercedes all previous!
+//can I make this fully portable to both graphic and console apps?
+
+//TODO: Feb 2014: replace sizeof(long) with sizeof(int) everywhere!
+
+#include "stdafx.h"		   //for MFC: def of BOOL, TRUE, FALSE, etc
+//#include <propobjs.h>
+//use multi-threaded version of sfsys.lib
+extern "C" {
+#include <sfsys.h>
+}
+
+#include "props.h"
+char *props_errstr = NULL;
+//RWD TODO: also need a copy constructor...
+/***************************** HEADREAD *******************************
+ *
+ * Read input analysis file header data.
+ */
+//RWD TODO: see Holub: props MUST be a pointer, not ref
+BOOL sfheadread(int fd,SFPROPS &props)
+{
+	long srate,chans,samptype,origsize = 0,origrate=0,origchans = 0,dummy;
+	float arate = (float)0.0;
+	int wlen=0,dfac=0,specenvcnt = 0,checksum=0;
+
+	props_errstr = NULL;
+	if(fd <0){
+		props_errstr = "Cannot read Soundfile: Bad Handle";
+		return FALSE;
+	}
+   	if(sfgetprop(fd,"sample rate", (char *)&srate, sizeof(long)) < 0) {
+		props_errstr = "Failure to read sample rate";
+		return FALSE;
+    }
+    if(sfgetprop(fd,"channels", (char *)&chans, sizeof(long)) < 0) {
+		props_errstr ="Failure to read channel data";
+		return FALSE;
+    }
+    if(sfgetprop(fd,"sample type", (char *)&samptype, sizeof(long)) < 0){
+		props_errstr = "Failure to read sample size";
+		return FALSE;
+    }
+	if(!(samptype==SAMP_SHORT  || samptype== SAMP_FLOAT)){
+			props_errstr = "unsupported sample type";
+			return FALSE;
+	}
+	props.srate = srate;
+	props.chans = chans;
+    if(samptype == SAMP_SHORT || samptype == SAMP_FLOAT){	
+			props.type = wt_wave;
+			props.samptype = (samptype == SAMP_SHORT ? SHORT16 : FLOAT32);
+	}
+	
+	if(props.samptype==FLOAT32) {
+		//we know we have floats: is it spectral file?
+    	if(sfgetprop(fd,"original sampsize",(char *)&origsize, sizeof(long))<0){
+			props_errstr = "Failure to read original sample size";		
+    	}
+    	if(sfgetprop(fd,"original sample rate",(char *)&origrate,sizeof(long))<0){
+			props_errstr = "Failure to read original sample rate";		
+    	}
+    	if(sfgetprop(fd,"arate",(char *)&arate,sizeof(float)) < 0){
+			props_errstr = "Failure to read analysis sample rate";		
+    	}
+    	if(sfgetprop(fd,"analwinlen",(char *)&wlen,sizeof(int)) < 0){
+			props_errstr = "Failure to read analysis window length";		
+    	}
+    	if(sfgetprop(fd,"decfactor",(char *)&dfac,sizeof(int)) < 0){
+			props_errstr = "Failure to read decimation factor";		
+    	}
+		checksum = origsize + origrate + wlen + dfac + (int)arate;
+		if(checksum==0)		//its a wave file
+			return TRUE;		
+		else {
+			if(props_errstr==NULL){	//its a good spectral file
+				props.origsize = origsize;
+				props.origrate = origrate;
+				props.arate = arate;
+				props.winlen = wlen;	 //better be wlen+2 ?
+				props.decfac = dfac;
+			//props.chans = (wlen+2)/2;		//??
+				if(sfgetprop(fd,"is a pitch file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_pitch;
+				else if(sfgetprop(fd,"is a transpos file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_transposition;
+				else if(sfgetprop(fd,"is a formant file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_formant;
+				else
+					props.type = wt_analysis;
+			} else
+				return FALSE;	//somehow, got a bad analysis file...
+		}
+		// get any auxiliary stuff for control file
+		//adapted from TW's function in speclibg.cpp
+		switch(props.type){
+		case(wt_formant):
+			if(sfgetprop(fd,"specenvcnt",(char *)&specenvcnt,sizeof(int)) < 0){
+				props_errstr = "Failure to read formant size in formant file";
+				return FALSE;
+			}
+			props.specenvcnt = specenvcnt;
+			break;
+		case(wt_pitch):
+		case(wt_transposition):
+			if(props.chans != 1){ //RWD: this makes old-style files illegal!
+				props_errstr = 	"Channel count does not equal to 1 in transposition file";
+    			return FALSE;
+			}
+			if(sfgetprop(fd,"orig channels", (char *)&origchans, sizeof(long)) < 0) {
+				props_errstr = 	"Failure to read original channel data from transposition file";
+				return FALSE;
+			}
+			props.origchans = origchans;
+			break;
+		default:
+			break;
+		}
+	}			
+    return TRUE;
+}	    
+
+
+
+BOOL sndheadread(int fd,SFPROPS &props)
+{
+	long srate,chans,samptype,origsize = 0,origrate = 0, origchans = 0,dummy;
+	float arate = (float)0.0;
+	int wlen=0,dfac=0,specenvcnt = 0,checksum=0;
+
+	props_errstr = NULL;
+	if(fd <0){
+		props_errstr = "Bad Soundfile Handle";
+		return FALSE;
+	}
+   	if(sndgetprop(fd,"sample rate", (char *)&srate, sizeof(long)) < 0) {
+		props_errstr = "Failure to read sample rate";
+		return FALSE;
+    }
+    if(sndgetprop(fd,"channels", (char *)&chans, sizeof(long)) < 0) {
+		props_errstr ="Failure to read channel data";
+		return FALSE;
+    }
+    if(sndgetprop(fd,"sample type", (char *)&samptype, sizeof(long)) < 0){
+		props_errstr = "Failure to read sample size";
+		return FALSE;
+    }
+	if(!(samptype==SAMP_SHORT || samptype== SAMP_FLOAT)){
+			props_errstr = "unsupported sample type";
+			return FALSE;
+	}
+
+	props.srate = srate;
+	props.chans = chans;
+    if(samptype == SAMP_SHORT || samptype== SAMP_FLOAT){	
+		props.type = wt_wave;
+		props.samptype = (samptype == SAMP_SHORT ? SHORT16 : FLOAT32);
+	}
+	if(props.samptype==FLOAT32) {
+		//we know we have floats: is it spectral file?
+    	if(sndgetprop(fd,"original sampsize",(char *)&origsize, sizeof(long))<0){
+			props_errstr = "Failure to read original sample size";
+    	}
+    	if(sndgetprop(fd,"original sample rate",(char *)&origrate,sizeof(long))<0){
+			props_errstr = "Failure to read original sample rate";
+    	}
+    	if(sndgetprop(fd,"arate",(char *)&arate,sizeof(float)) < 0){
+			props_errstr = "Failure to read analysis sample rate";
+    	}
+    	if(sndgetprop(fd,"analwinlen",(char *)&wlen,sizeof(int)) < 0){
+			props_errstr = "Failure to read analysis window length";
+    	}
+    	if(sndgetprop(fd,"decfactor",(char *)&dfac,sizeof(int)) < 0){
+			props_errstr = "Failure to read decimation factor";
+    	}
+		//TODO: find a way to guarantee unique checksums...
+		checksum = origsize + origrate + wlen + dfac + (int)arate;
+		if(checksum==0)		//its a wave file
+			return TRUE;		
+		else {
+			if(props_errstr==NULL){	//its a good spectral file				
+				props.origsize = origsize;
+				props.origrate = origrate;
+				props.arate = arate;
+				props.winlen = wlen;
+				props.decfac = dfac;
+				//props.chans = (wlen+2)/2;		//??
+
+				if(sndgetprop(fd,"is a pitch file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_pitch;
+				else if(sndgetprop(fd,"is a transpos file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_transposition;
+				else if(sndgetprop(fd,"is a formant file", (char *)&dummy, sizeof(long))>=0)
+					props.type = wt_formant;
+				else
+					props.type = wt_analysis;
+			} else
+				return FALSE;	//somehow, got a bad analysis file...
+		}
+		// get any auxiliary stuff for control file
+		//adapted from TW's function in speclibg.cpp
+		switch(props.type){
+		case(wt_formant):
+			if(sndgetprop(fd,"specenvcnt",(char *)&specenvcnt,sizeof(int)) < 0){
+				props_errstr = "Failure to read formant size in formant file";
+				return FALSE;
+			}
+			props.specenvcnt = specenvcnt;
+			break;
+		case(wt_pitch):
+		case(wt_transposition):
+			if(props.chans != 1){
+				props_errstr = 	"Channel count not equal to 1 in transposition file";
+    			return FALSE;
+			}
+			if(sndgetprop(fd,"orig channels", (char *)&origchans, sizeof(long)) < 0) {
+				props_errstr = 	"Failure to read original channel data from transposition file";
+				return FALSE;
+			}
+			props.origchans = origchans;
+			break;
+		default:
+			break;
+		}
+
+    }
+    return TRUE;
+}	    
+// next is for SOUND files only...
+//TODO: full support for analysis and control files!
+BOOL sfwave_headwrite(int fd,const SFPROPS &props)
+{
+	
+#ifdef _DEBUG
+     ASSERT(fd >= 0);
+	 ASSERT(props.samptype==SHORT16 || props.samptype==FLOAT32);
+	 ASSERT(props.srate > 0);
+	 ASSERT(props.chans > 0);
+#endif
+	int srate = props.srate;
+	int chans = props.chans;
+	int samptype;
+
+	if(fd <0){
+		props_errstr = "Cannot write soundfile: bad Handle";
+		return FALSE;
+	}
+	if(props.type == wt_wave)
+		samptype = (props.samptype == SHORT16 ? SAMP_SHORT : SAMP_FLOAT);  //within sfsys,mostly safe assumption...
+	else {
+		props_errstr = "Not a wave file";
+		return FALSE;
+	}
+		
+   	if(sfputprop(fd,"sample rate", (char *)&srate, sizeof(long)) < 0) {
+		props_errstr = "Failure to write sample rate";
+		return FALSE;
+    	}
+    	if(sfputprop(fd,"channels", (char *)&chans, sizeof(long)) < 0) {
+		props_errstr ="Failure to write channel data";
+		return FALSE;
+    	}
+    	if(sfputprop(fd,"sample type", (char *)&samptype, sizeof(long)) < 0){
+		props_errstr = "Failure to write sample size";
+		return FALSE;
+    	}
+      	return TRUE;
+}
+
+fileprops::~fileprops()
+{
+}
+
+fileprops::fileprops()
+{
+	srate 	   = 0L;		 		
+	stype 	   = -1L;
+	filetype   = UNKNOWN;
+	channels   = 0L;
+	origstype  = -1L;
+	origrate   = 0L;
+	origchans  = 0L;
+	specenvcnt = 0;
+	Mlen 	   = 0;
+	Dfac 	   = 0;
+	arate 	   = 0.0f;
+}
+
+//TODO: when I have unique checksums, can just compare them
+const fileprops& fileprops::operator=(const fileprops &rhs)
+{
+	if(!(rhs == *this)){		 //use  my operator==; TODO: define operator!= as well...
+			srate 		= rhs.srate;
+			channels 	= rhs.channels;
+			stype 		= rhs.stype;
+			origstype 	= rhs.origstype;
+			origrate 	= rhs.origrate;
+			Mlen 		= rhs.Mlen;
+			Dfac 		= rhs.Dfac;
+			arate 		= rhs.arate;
+			origchans 	= rhs.origchans;
+			specenvcnt 	= rhs.specenvcnt;	//RWD: comes from?
+	}
+	return *this;
+}
+
+int fileprops::operator==(const fileprops &rhs) const
+{
+		  return (srate	== rhs.srate
+			 && channels 	== rhs.channels
+			&&	stype 		== rhs.stype
+			&&	origstype 	== rhs.origstype
+			&&	origrate 	== rhs.origrate
+			&&	Mlen 		== rhs.Mlen
+			&&	Dfac 		== rhs.Dfac
+			&&	arate 		== rhs.arate
+			&&	origchans 	== rhs.origchans
+			&&	specenvcnt 	== rhs.specenvcnt);
+}
+
+
+
+
+
+
+

+ 1486 - 0
dev/newsfsys/pvfileio.c

@@ -0,0 +1,1486 @@
+
+/* 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... */
+
+/* POSSIBLE TODO: expand arglist to enforce O_EXCL mode? */
+/* currently handled externally in CDP sfsys using file_exists() */
+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;
+}

+ 159 - 0
dev/newsfsys/pvxtest.c

@@ -0,0 +1,159 @@
+//
+//  pvxtest.c
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "sfsys.h"
+
+float ampmax(float* buffer, int samps)
+{
+    double amp = 0.0;
+    int iamp;
+    for (iamp=0; iamp < samps ;iamp += 2) {
+        float thisamp = buffer[iamp];
+        amp =  max(amp, thisamp);
+    }
+    return amp;
+}
+
+int main(int argc, char **argv)
+{
+    SFPROPS props;
+    int ifd, ofd, i, bufcount, sampswanted, got,put;
+    int numsamps,numframes, pos;
+    float *buf = NULL;
+    float *framesbuf = NULL;
+    float *framebufptr = NULL;
+    int frames_per_buf = 10;
+    int framecount = 0;
+    int filesize = 0;
+    int bytedatasize;
+    
+    if(argc< 3){
+        fprintf(stderr, "usage: pvxtest sfile.pvx outfile.ana\n");
+        return 1;
+    }
+    if(sflinit("pvxtest")){
+        fprintf(stderr,"pvxtest: unable to initialize CDP Sound Filing System\n");
+        return 1;
+    }
+    if((ifd = sndopenEx(argv[1],0,CDP_OPEN_RDONLY)) < 0){
+        fprintf(stderr,"pvxtest: unable to open infile %s: %s\n",argv[1], sferrstr());
+        return 1;
+    }
+    else {
+        fprintf(stderr,"pvxtest: file opened OK, ifd = %d\n",ifd );
+    }
+    //read file size if various ways...
+    filesize = sndsizeEx(ifd);
+    fprintf(stderr,"got filesize = %d\n",filesize);
+    
+    
+    if(!snd_headread(ifd,&props)){
+        fprintf(stderr,"pvxtest: unable to read infile header: %s\n",sferrstr());
+        return 1;
+    }
+    //read file size if various ways...
+    filesize = sndsizeEx(ifd);
+    fprintf(stderr,"got filesize = %d\n",filesize);
+    /* do we have correct props? */
+    fprintf(stderr, "props.srate: = %d\nprops.chans: %d\n",
+            props.srate,props.chans);
+    fprintf(stderr, "props.origsize: %d\nprops.origrate:%d\nprops.origchans:%d (pitch/fmt/trans only\n",
+            props.origsize, props.origrate,props.origchans);
+    fprintf(stderr,"props.arate:%f\nprops.winlen:%d\nprops.decfac :%d\n",
+            props.arate,props.winlen,props.decfac);
+    
+    framesbuf = malloc(props.chans * frames_per_buf);
+    if(framesbuf == NULL){
+        fprintf(stderr, "alloc failure\n");
+        return 1;
+    }
+
+    props.type = wt_analysis;
+    props.format = WAVE;
+    props.samptype = FLOAT32;
+    fprintf(stderr,"setting output chans to %d\n",props.chans);
+    //ofd = sndcreat_ex(argv[2],-1,&props,SFILE_CDP,CDP_CREATE_NORMAL);
+    ofd = sndcreat_formatted(argv[2],-1,SAMP_FLOAT,props.chans,props.srate,CDP_CREATE_NORMAL);
+    if(ofd < 0){
+        fprintf(stderr,"pvxtest: unable to open outfile %s: %s\n",argv[2], sferrstr());
+        return 1;
+    }
+#ifndef SINGLEFRAME
+    sampswanted = props.chans;
+    buf = (float *) malloc(sizeof(float) * sampswanted);
+    if(buf == NULL){
+        fprintf(stderr,"failed to allocate buf of %d samps\n",sampswanted);
+        return -1;
+    }
+    bufcount = 0;
+    //for(i=0;i < 20;i++){
+    for(i=0; /* i < numframes */; i++){
+        got = fgetfbufEx(buf, sampswanted,ifd,0);
+        if(got <= 0)
+            break;
+        if(i==0 & got == sampswanted){
+            fprintf(stderr,"%d: read %d samps, max = %f\n",bufcount,got, ampmax(buf,got));
+        }
+        put = fputfbufEx(buf,sampswanted,ofd);
+        if(put < 0){
+            fprintf(stderr,"error writing to oputfile, block %d\n",i);
+        }
+        bufcount++;
+    }
+    fprintf(stderr,"written %d frames\n",bufcount);
+#else
+    sampswanted = props.chans * frames_per_buf;
+    for(i=0;;i++){
+        got = fgetfbufEx(framesbuf,sampswanted,ifd,0);
+        if(got <= 0)
+            break;
+        put = fputfbufEx(framesbuf,got,ifd);
+        if(put < 0){
+            fprintf(stderr,"error writing to oputfile, block %d\n",i);
+        }
+        framecount += got / props.chans;
+    }
+    fprintf(stderr, "processed %d frames\n",framecount);
+#endif
+    
+    //must set all CDP analysis props before closing
+    fprintf(stderr, "adding prop orig sampsize: %d\n",props.origsize);
+    if(sndputprop(ofd,"original sampsize",(char *) &(props.origsize),sizeof(int)) < 0){
+        fprintf(stderr,"Failure to write original sample size %s\n", (char*)&(props.origsize));
+    }
+    fprintf(stderr, "adding prop orig srate: %d\n",props.origrate);
+    if(sndputprop(ofd,"original sample rate",(char *) &(props.origrate),sizeof(int)) < 0){
+        fprintf(stderr,"Failure to write original sample size\n");
+    }
+    if(sndputprop(ofd,"arate",(char*) &props.arate,sizeof(float)) < 0){
+        fprintf(stderr,"Failure to write original sample size\n");
+    }
+    if(sndputprop(ofd,"analwinlen",(char*)  &props.winlen,sizeof(int)) < 0){
+        fprintf(stderr,"Failure to write original sample size\n");
+    }
+    if(sndputprop(ofd,"decfactor",(char*) &props.decfac,sizeof(int)) < 0){
+        fprintf(stderr,"Failure to write original sample size\n");
+    }
+#ifdef _DEBUG
+    {
+        // just check a couple...
+        int got_origsize = 0,got_origrate = 0;
+        if(sndgetprop(ofd,"original sampsize",(char*) &got_origsize,sizeof(int)) < 0)
+            fprintf(stderr,"failed to recapture orig sampsize\n");
+        fprintf(stderr,"got_origsize = %d\n",got_origsize);
+        
+        if(sndgetprop(ofd,"original sample rate",(char*) &got_origrate,sizeof(int)) < 0)
+            fprintf(stderr,"failed to recapture orig samprate\n");
+        fprintf(stderr,"got_origrate = %d\n",got_origrate);
+    }
+#endif
+
+    fprintf(stderr,"pvxtest: calling sndcloseEx(infile) (%d)\n",ifd);
+    sndcloseEx(ifd);
+    fprintf(stderr,"pvxtest: calling sndcloseEx(outfile) (%d)\n",ofd);
+    sndcloseEx(ofd);
+    return 0;
+}

+ 165 - 0
dev/newsfsys/scandir.c

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+
+
+
+/*
+ *      portable sfsys - NT version of scandir
+ */
+
+static char *rcsid = "$Id: scandir.c%v 1.2 1994/10/31 15:56:04 martin Exp $";
+/*
+ *      $Log: scandir.c%v $
+ * Revision 1.2  1994/10/31  15:56:04  martin
+ * Fix local copy of rcsid string
+ *
+ * Revision 1.1  1994/10/31  15:46:36  martin
+ * Initial revision
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include "scandir.h"
+static char *scandir_h_rcsid = SCANDIR_H_RCSID;
+
+#define ARRAY_INCR      (10)
+
+#ifdef _WIN32
+
+int
+scandir(const char *dir,
+        struct dirent ***namelist,
+        int (*selfn)(const struct dirent *d),
+        int (*srtfn)(const struct dirent * const *d1,
+                     const struct dirent * const *d2))
+{
+    struct dirent **names = (struct dirent **)malloc(1);
+    int names_size = 0;
+    int names_seen = 0;
+    HANDLE shndl;
+    struct dirent f;
+    WIN32_FIND_DATA filedata;
+    char pattern[_MAX_PATH];
+
+    if(names == 0)
+        return -1;
+    strcpy(pattern, dir);
+    strcat(pattern, "\\*.*");
+
+    if((shndl = FindFirstFile(pattern, &filedata)) == INVALID_HANDLE_VALUE) {
+        if(GetLastError() == ERROR_FILE_NOT_FOUND)
+            return 0;
+        return -1;
+    }
+
+    for(;;) {
+        strcpy(f.d_name, filedata.cFileName);
+        if(selfn(&f)) {
+            if(names_seen >= names_size) {
+                names_size += ARRAY_INCR;
+                if((names = (struct dirent **) realloc(names, names_size*sizeof(struct dirent *))) == 0) {
+                    FindClose(shndl);
+                    return -1;
+                }
+            }
+            if((names[names_seen] = (struct dirent *)malloc(sizeof(struct dirent))) == 0) {
+                FindClose(shndl);
+                return -1;
+            }
+            *names[names_seen++] = f;
+        }
+
+        if(!FindNextFile(shndl, &filedata)) {
+            if(GetLastError() != ERROR_NO_MORE_FILES) {
+                FindClose(shndl);
+                return -1;
+            }
+            break;
+        }
+    }
+
+    FindClose(shndl);
+    qsort(names, names_seen, sizeof(struct dirent *),
+          (int (*)(const void *, const void *))srtfn);
+    *namelist = names;
+    return names_seen;
+}
+#endif
+
+#ifdef unix
+int
+scandir(const char *dir,
+        struct dirent ***namelist,
+        int (*selfn)(const struct dirent *d),
+        int (*srtfn)(const struct dirent * const *d1,
+                     const struct dirent * const *d2))
+{
+    DIR *dirfd;
+    struct dirent *f;
+    struct dirent **names = (struct dirent **)malloc(1);
+    int names_size = 0;
+    int names_seen = 0;
+
+    if(names == 0)
+        return -1;
+    if((dirfd = opendir(".")) == NULL)
+        return -1;
+
+    while((f = readdir(dirfd)) != NULL) {
+        if(!selfn(f))
+            continue;
+        if(names_seen >= names_size) {
+            names_size += ARRAY_INCR;
+            if((names = realloc(names, names_size*sizeof(struct dirent *))) == 0) {
+                closedir(dirfd);
+                return -1;
+            }
+        }
+        if((names[names_seen] = (struct dirent *)malloc(sizeof(struct dirent))) == 0) {
+            closedir(dirfd);
+            return -1;
+        }
+        *names[names_seen++] = *f;
+    }
+    closedir(dirfd);
+    qsort(names, names_seen, sizeof(struct dirent *),
+          (int (*)(const void *, const void *))srtfn);
+    *namelist = names;
+    return names_seen;
+}
+
+#endif
+
+
+
+int
+alphasort(const struct dirent * const *d1,
+          const struct dirent * const *d2)
+{
+    return strcmp((*d1)->d_name, (*d2)->d_name);
+}

+ 64 - 0
dev/newsfsys/scandir.h

@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+/*
+ *      Portable scandir for systems that don't have it!
+ *
+ *      These definitions should be safe if the routines
+ *      are already declared
+ */
+
+#define SCANDIR_H_RCSID "$Id: scandir.h%v 1.1 1994/10/31 15:30:08 martin Exp $"
+/*
+ *      $Log: scandir.h%v $
+ * Revision 1.1  1994/10/31  15:30:08  martin
+ * Initial revision
+ *
+ */
+#if defined(__MAC__)
+#include <machine/types.h>
+#endif
+#if defined(unix)
+#include <dirent.h>
+#elif defined(_WIN32) || defined(__SC__)
+
+struct dirent {
+        char d_name[_MAX_PATH];
+};
+
+#else
+#error "Unknown system"
+#endif
+#ifndef __MAC__
+int scandir(const char *dir,
+            struct dirent ***namelist,
+            int (*selfn)(const struct dirent *d),
+            int (*srtfn)(const struct dirent * const *d1,
+                         const struct dirent * const *d2));
+
+int alphasort(const struct dirent * const *d1,
+              const struct dirent * const *d2);
+#endif

+ 176 - 0
dev/newsfsys/sfdir.c

@@ -0,0 +1,176 @@
+/* RWD small mods for newsfsys, no need for FILE_AMB_SUPPORT symbol */
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+/*
+ *      portable sfsys - Unix version of sfdir, etc
+ */
+
+/*
+ *      $Log: sfdir.c%v $
+ * Revision 1.1  1994/10/31  15:41:38  martin
+ * Initial revision
+ *
+ */
+/*RWD OCT97: rebuild under CDP97 to recognise *.ana as analysis file*/
+/* RWD Dec 2009 restored fix to fmt info code! NB some sf_direct fields now int or unsigned int*/
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <osbind.h>
+#include <sfsys.h>      /*RWD: don't want local copies of this!*/
+#include "sffuncs.h"
+#if defined(_SGI_SOURCE) || defined unix
+#include <dirent.h>
+#else
+#include "scandir.h"
+#endif
+
+#if defined WIN32 || defined linux
+static int
+selfn(const struct dirent *d)
+#else
+    static int
+    selfn(struct dirent *d)
+#endif
+{
+    char *dotp = strrchr(d->d_name, '.');
+
+    if(dotp == 0)
+        return 0;
+    if(_stricmp(dotp, ".aif") == 0
+       ||_stricmp(dotp, ".aiff") == 0
+       ||_stricmp(dotp, ".wav") == 0
+       /*RWD.1.99 add AIFC - just how many xtensions can there be?*/
+       || _stricmp(dotp,".afc") == 0                   /*official Apple*/
+       || _stricmp(dotp,".aifc") ==0                   /* unix?*/
+       || _stricmp(dotp,".aic") == 0                   /* lots of other people...*/
+       ||_stricmp(dotp, ".amb") == 0
+       ||_stricmp(dotp, ".wxyz") == 0
+       /*RWD OCT97;JULY98: recognise TW filetypes*/
+       || _stricmp(dotp,".ana") == 0
+#ifdef ENABLE_PVX
+       || _stricmp(dotp,".pvx") == 0
+#endif
+       || _stricmp(dotp,".frq") == 0
+       || _stricmp(dotp,".fmt") == 0          /* lose this in time */
+       || _stricmp(dotp,".for") == 0
+       || _stricmp(dotp,".env") == 0          /* lose this in time */
+       || _stricmp(dotp,".evl") == 0
+       || _stricmp(dotp,".trn") == 0
+       || _stricmp(dotp,".lnk") == 0    /*RWD98 VERY experimental ; may be wrong place to do it, or wrong way...*/
+       )
+
+        return 1;
+    return 0;
+}
+
+int
+sfdir(int SFCALLS (*func)(struct sf_direct *filedetails), int flags)
+{
+    struct dirent **filelist = 0;
+    int numfiles, file;
+    int fdes;
+    int rc = SFDIR_NOTFOUND;
+    struct sf_direct dir;
+
+    if((numfiles = scandir(".", &filelist, selfn, alphasort)) < 0)
+        return SFDIR_ERROR;
+
+    if(numfiles == 0)
+        return SFDIR_NOTFOUND;
+    if(filelist == 0)
+        abort();           /*RWD: ouch!*/
+
+    for(file = 0; file < numfiles; file++) {
+        struct dirent *f = filelist[file];
+        if((fdes = sfopenEx(f->d_name,CDP_OPEN_RDONLY)) >= 0){
+            dir.flags = 0;
+            dir.length = (unsigned int) sfsize(fdes);    /*RWD 2007 */
+            dir.index = 0;
+            dir.seclen = dir.length>>LOGSECSIZE;
+#if defined _WIN32
+            dir.is_shortcut = sf_is_shortcut(fdes,dir.targetname);
+            if(!dir.is_shortcut)
+                dir.targetname[0] = '\0';
+#endif
+            sfformat(fdes,&(dir.fmt));
+            strncpy(dir.name, f->d_name, MAXSFNAME-1);
+            dir.name[MAXSFNAME-1] = '\0';
+            if(sfgetprop(fdes, "sample type", (char *)&dir.samptype, sizeof(int)) != sizeof(int))
+                dir.samptype = -1;
+            if(sfgetprop(fdes, "sample rate", (char *)&dir.samprate, sizeof(int)) != sizeof(int))
+                dir.samprate = -1;
+            if(sfgetprop(fdes, "channels", (char *)&dir.nochans, sizeof(int)) != sizeof(int))
+                dir.nochans = -1;
+            dir.origwordsize = sfgetwordsize(fdes);
+            if(dir.origwordsize==8)
+                dir.length /= 2;
+            sfclose(fdes);
+
+            if(func(&dir)) {
+                rc = SFDIR_FOUND;
+                break;
+            }
+        } else {
+            if(sferrno() != ESFNOTFOUND) {
+                rc = SFDIR_ERROR;
+                /*RWD.9.98      for dirsf*/
+                dir.fmt = FMT_UNKNOWN;
+                /*break;*/
+
+            }
+            dir.flags = SFFLG_IOERROR;
+            dir.length = 0xffffffff; /* RWD Aug 2009: unsigned equiv of -1 . need to improve on this system ere long */
+            dir.index = 0;
+            dir.seclen = -1;
+            strncpy(dir.name, f->d_name, MAXSFNAME-1);
+            dir.name[MAXSFNAME-1] = '\0';
+            dir.samptype = -1;
+            dir.samprate = -1;
+            dir.nochans = -1;
+
+            if(func(&dir)) {
+                rc = SFDIR_FOUND;
+                break;
+            }
+        }
+    }
+
+    for(file = 0; file < numfiles; file++)
+        free(filelist[file]);
+    free(filelist);
+    return rc;
+}
+
+int
+rdiskcfg(struct rdskcfg *cfg)
+{
+    cfg->disksize = 1024*1024;      /* we lie! */
+    cfg->npart = 1;
+    cfg->pinfo[0].unit_no = 0;
+    cfg->pinfo[0].phys_start = 0;
+    cfg->pinfo[0].sf_start = 0;
+    cfg->pinfo[0].sf_end = 1024*1024-1;
+    return 0;
+}

+ 223 - 0
dev/newsfsys/sffuncs.h

@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+ */
+
+
+
+/* sffuncs.h : private routines and structures */
+/*RWD Dec 2009 changed various fields in sf_direct to int or unsigned int */
+#ifndef __CDP_SFFUNCS_INCLUDED__
+#define __CDP_SFFUNCS_INCLUDED__
+/*
+ *      Various dummy definitions which did things on the Atari, but must be
+ *      removed for other platforms
+ */
+#define STDARGS /**/
+#define SFCALLS /**/
+
+/* RWD.5.99: are these now too small for WIN32? */
+#define MAXSFNAME       (255)   /* maximum length of SFfile's name */
+#define MAXPREFIX       (50)    /* maximum length of a filename prefix */
+
+#ifndef WIN32
+typedef long long __int64;
+#endif
+
+/*
+ *      Structure definitions
+ */
+/*RWD.5.99 alignment problems with targetname[] ? */
+struct sf_direct {
+    int     flags;                  /* flags about the file (see below) */
+    unsigned int length;            /* size of file in bytes */
+    long    index;                  /* index of header sector on disk */
+    long    seclen;                 /* number of sectors (inc headers) */
+    char    name[MAXSFNAME+1];      /* the name of the file */
+    int     samptype;               /* the type of samples */
+    int     nochans;                /* the number of channels */
+    int     samprate;               /* the sampling rate */
+
+/* sfsysEx */
+    int     origwordsize;   /*recognize 8bit sfiles            */
+    int     is_shortcut;
+    char    targetname[256];        /*_MAX_PATH      = 255*/
+    fileformat      fmt;
+};
+
+/*
+ *      structure for various information about the soundfiling system
+ */
+struct sfdata {
+    long version;
+    long base_sf_fd;        /* lowest possible sf filedescriptor */
+    long limit_sf_fd;       /* limit of sf filedescriptors */
+    long base_snd_fd;       /* lowest possible snd filedescriptor */
+    long limit_snd_fd;      /* limit of snd filedescriptors */
+};
+
+/*
+ *      the format of a raw disk configuration block - mostly a fudge!
+ */
+#define MAXPARTS        (64)    /* maximum number of SFS partitions */
+
+struct partinfo {               /* information about a partition */
+    short   unit_no;        /* SCSI unit number (log|ctrl) */
+    long    phys_start;     /* phys. address of first sector(ie sf_start) */
+    long    sf_start;       /* logical address of first sector */
+    long    sf_end;         /* logical address of last valid sec in part. */
+};
+
+struct rdskcfg {
+    long    disksize;       /* number of sectors available for the sfs */
+    short   npart;          /* number of partitions */
+    struct partinfo pinfo[MAXPARTS];        /* information on the partitions */
+};
+
+/*
+ *      First special routines
+ */
+
+extern char *sfgetbigbuf(int *secsize); /* create a big memory buffer */
+
+extern int sfdata(struct sfdata *, int size);   /* get information about sfsys */
+
+/*
+ *      The dummy prefix stuff
+ */
+extern int sfsetprefix(char *path);
+extern void sfgetprefix(char *path);
+extern int rdiskcfg(struct rdskcfg *);
+/*
+ *      The main (DEPRECATED) sf functions
+ */
+
+extern int sfdir(int SFCALLS (*func)(struct sf_direct *filedetails), int flags);
+extern int sfopen(const char *name);
+extern int sfcreat(const char *name, int sizein, int *sizeout);
+extern int sfclose(int sfd);
+extern int sfunlink(int sfd);
+//extern long sfrename(int sfd, const char *newname);
+//extern long sf_makepath(char path[], const char* sfname);
+
+
+extern int SFCALLS STDARGS sfread(int sfd, char *buf, int cnt);
+extern int SFCALLS STDARGS sfwrite(int sfd, char *buf, int cnt);
+extern int sfseek(int sfd, int dist, int whence);
+extern __int64 sfsize(int sfd);
+extern int sfadjust(int sfd, __int64 delta);
+extern int sfgetprop(int sfd, const char *propname, char *dest, int lim);
+extern int sfputprop(int sfd, char *propname, char *src, int size);
+extern int sfrmprop(int sfd, char *propname);
+extern int sfdirprop(int sfd, int (*func)(char *propname, int propsize));
+
+/*RWD OCT97 temp fudge to deal with 8bit sffiles...not to be used in applications! */
+
+extern int      sfgetwordsize(int sfd);
+
+/*recognise shortcuts (aliasses) in WIN32*/
+/* this ~should~ be portable...*/
+/* return -1 for error, 0 for false, 1 for true */
+int sf_is_shortcut(int sfd,char *name);
+/* this is portable */
+/* return -1 for error, 0 otherwise */
+int sfformat(int sfd,fileformat *pfmt);
+
+/*support for mixed int formats*/
+int sf_getcontainersize(int sfd);
+int sf_getvalidbits(int sfd);
+
+/********** SFSYS 98 extended routines to support live streaming, header adjstments *******/
+/* creates full header; format cannot be changed later! */
+/* return -1 for error, else sfd*/
+#ifdef FILE64_WIN
+int sfcreat_formatted(const char *name, __int64 size, __int64 *outsize,int channels,
+                      int srate, int stype,cdp_create_mode mode);
+
+#else
+int sfcreat_formatted(const char *name, __int64 size, __int64 *outsize,int channels,
+                      int srate, int stype,cdp_create_mode mode);
+#endif
+/* re-entrant version of above, for GUI apps. Used in GrainMill. */
+/* keeps the input sfd; returns 1 for success, 0 for error */
+/* NB a snd~ version will be created asap! */
+
+int sfrecreat_formatted(int sfd, __int64 size, __int64 *outsize,int channels,int srate, int stype);
+
+/*return -1 for error, 0 for no peak info, 1 for success*/
+/* returns raw ANSI peaktime from header: use ctime() etc to decode */
+int sfreadpeaks(int sfd,int channels,CHPEAK peakdata[],int *peaktime);
+/* currently, we have to track PEAK data explicitly, and update header at the end.
+ * eventually, there will be a frame-append function that updates PEAK info automatcally */
+/* return -1 for error, 0 for success */
+int sfputpeaks(int sfd,int channels,const CHPEAK peakdata[]);
+
+/* new version of sfopen, controlling access */
+int sfopenEx(const char *name,unsigned int access);
+const char* sf_getfilename(int ifd);
+extern int SFCALLS STDARGS sfread_buffered(int sfd, char *buf, int cnt);
+extern int SFCALLS STDARGS sfwrite_buffered(int sfd, char *buf, int cnt);
+extern __int64 sfseek_buffered(int sfd, __int64 dist, int whence);
+int
+sfcreat_ex(const char *name, __int64 size, __int64 *outsize,
+           SFPROPS *props,int min_header,cdp_create_mode mode);
+
+int sfgetchanmask(int sfd);
+
+int sf_getchanformat(int sfd, channelformat *chformat);
+
+/* read sfile props into new structure, all in one go */
+extern int sf_headread(int fd,SFPROPS *props);
+
+#define SF_MAGIC        (0x15927624)    /* value of _sfmagic() */
+#define SF_CMAGIC       (0x27182835)    /* magic number for configuration */
+#define SF_UNLKMAGIC    (0x46689275)    /* magic value for rdiskunlck() */
+
+#define MSBFIRST        (1)
+#define LSBFIRST        (1)
+/* RWD extended set of symbols ! Now works for Mac universal Binary build */
+#if defined(__I86__) || defined(_X86_) || defined(__x86_64) || defined(__i386__) || defined(__i486__) || defined(_IBMR2)
+#undef MSBFIRST
+#elif defined(M68000) || defined(__sgi) || defined (__POWERPC__)
+#undef LSBFIRST
+#else
+#error  "Unknown byte order for this processor"
+#endif
+
+#if defined(MSBFIRST) && defined(LSBFIRST)
+#error  "Internal: can't be both MSB and LSB"
+#endif
+
+#define REVDWBYTES(t)   ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
+#define REVWBYTES(t)    ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
+
+/*RWD.6.99 REV3BYTES is a function*/
+//static char * REV3BYTES(char *samp_24);
+extern int sampsize[];
+
+#ifdef MSBFIRST
+#define REVDATAINFILE(f)        ((f)->filetype == riffwav || (f)->filetype == wave_ex)
+#else
+#define REVDATAINFILE(f)        (((f)->filetype == eaaiff) || ((f)->filetype==aiffc))
+#endif
+
+#endif

+ 7059 - 0
dev/newsfsys/sfsys.c

@@ -0,0 +1,7059 @@
+/* RWD version  2022 to support PVOCEX format for Release 8*/
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, 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
+ *
+ */
+
+
+
+/* sfsys 64bit (sfsys2010):  handle  files up to 4GB : fpos_t/fread/POS64 version */
+/* PC requires FILE64_WIN  defined at compiler level */
+/*
+ *      portable sfsys replacement
+ */
+/*RWD.6.5.99 test version for PEAK chunk, etc...
+*RWD.7.99 delete created file on error in format, etc
+*RWD Jan 2004: fixed WAVE_EX GUID reading on big-endian
+*/
+
+/* Nov 2005:  allow any channel count for B-Format files */
+/* April 2006: correct bug recoignising .ambi extension in gettypefrom name98() */
+/* but we don't really need to preserve this anyway...could just remove */
+
+/*April 2006: revise rdwavhdr to scan all chunks, so can pick up PEAK chunk after data chunk,
+   as done by SoundForge! */
+
+
+/* Oct 2006: changed defs of WORD and DWORD to unsigned, can we support 4GB files???? */
+/* RWD 2009: first version of 64bit file handling for 4GB files.*/
+/* RWD May 2011: hacked rdwavhdr to accept dastardly Wavelab files with 20byte fmt chunk */
+/* PS: and also even more dastardly PT plain wave files with 40byte fmt chunk! */
+/* RWD Jan 2013 fixed bug in getsfsysadtl, return correct padded size */
+/* RWD Nov 2013 RELEASE 7: added MC_SURR_6_1 */
+/* still TODO: replace tmpnam with mkstemp for CDP temporary files for GUI progs (see below)
+ * Or: leave it to the GUI progs to sort out! */
+/* RWD Feb 2014: converted to ints for x64 building */
+/* RWD MAR 2015 finished (?) adoption of default value for CDP_SOUND_EXT, eliminated various compiler warnings */
+#ifdef __GNUC__
+# if (defined(__LP64__) || defined(_LP64))
+# define CDPLONG64
+# endif
+#endif
+#ifdef CDPLONG64
+# define CDPLONG int
+#else
+# define CDPLONG long
+#endif
+
+
+//static char *rcsid = "$Id: sfsys.c%v 4.0 1998/17/02 00:47:05 martin Exp $";
+
+/*pstring for AIFC      - includes the pad byte*/
+static const char aifc_floatstring[10] = { 0x08,'F','l','o','a','t',0x20,'3','2',0x00};
+static const char aifc_notcompressed[16] = {0x0e,'n','o','t',0x20,'c','o','m','p','r','e','s','s','e','d',0x00};
+/*
+ *      global state
+ */
+int _sfverno = 0x801;                     //RWD: remember to update this!
+
+/* NB: private flag! */
+#define SFILE_ANAL  (3)     /* RWD Nov 2009: no PEAK,CLUE chunk for analysis files */
+
+#ifdef DEBUG_MAC
+#include <errno.h>
+static void parse_errno(int err)
+{
+
+    switch(err){
+    case(EBADF):
+        printf("fd is not valid descriptor\n");
+        break;
+    case(EINVAL):
+        printf("fd is socket, not a file\n");
+        printf("or: file not open for writing\n");
+        break;
+    default:
+        printf("unrecognised value for err: %d\n",err);
+        break;
+    }
+
+}
+#endif
+
+/*
+ *      $Log: sfsys.c%v $
+ * Revision 2.2  1994/12/13  00:47:05  martin
+ * Add declaration for Unix
+ *
+ * Revision 2.1  1994/10/31  15:49:10  martin
+ * New version number to differentiate from versions already shipped
+ *
+ * Revision 1.1  1994/10/31  15:41:38  martin
+ * Initial revision
+ *
+ */
+/*  RWD July 1997: Revision 2.3: add support for new WAV float format
+ RWD OCT 97: IMPORTANT FIX: DEAL WITH EXTRA 2 BYTES IN NEW WAVEFORMATEX CHUNK
+ We MUST READ wave files correctly; it is not wrong, as such, to continue to write them
+ the old way, (at least, for straight PCM format), but it is nevertheless inconsistent
+ with modern practice.
+ also: completed support for 8bit infiles       (read,seek,dirsf)
+        added initial support of minimal 'fact' chunk in WAVE files for non-PCM formats
+ to use: #define CDP97
+ to use Win32 file functions, define CDP99
+ */
+#include <stdio.h>
+#ifdef unix
+#include <unistd.h>
+# ifdef __MAC__
+# include <sys/syslimits.h>
+# endif
+#endif
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdint.h>
+
+#if defined _WIN32
+#include <windows.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if defined(_WIN32) || defined(__SC__) || defined  (__GNUWIN32__)
+#include <io.h>
+extern char*   _fullpath (char*, const char*, size_t);
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#ifdef _DEBUG
+#include <assert.h>
+#endif
+#include <sfsys.h>              /*RWD: don't want local copies of this!*/
+/*RWD April 2005 */
+#include "sffuncs.h"
+
+#include <osbind.h>
+#ifdef _WIN32
+#include "alias.h"
+#endif
+
+int CDP_COM_READY = 0;   /*global flag, ah well...(define in alias.h will access func??)*/
+/* RWD see line ~431; this is basically a mess; difficult to manage order of includes etc in WIN32 - blame windows.h! */
+#ifdef unix
+# ifdef ENABLE_PVX
+# include "pvfileio.h"
+# endif
+#endif
+
+#include <chanmask.h>
+/* RWD oct 2022 to eliminate pointer aliasing in AIFF srate handling */
+#include "ieee80.h"
+
+//static char *sfsys_h_rcsid = SFSYS_H_RCSID;
+
+
+
+
+/*
+ *      The portability definitions come first
+ */
+#ifdef unix
+#define _O_BINARY       (0)
+#define _O_RDWR         O_RDWR
+#define _O_RDONLY       O_RDONLY
+#define _O_CREAT        O_CREAT
+#define _O_TRUNC        O_TRUNC
+#define _O_EXCL         O_EXCL
+#define _S_IWRITE       S_IWRITE
+#define _S_IREAD        S_IREAD
+
+#define chsize  ftruncate
+#endif
+
+
+
+#if !defined(_WIN32) && !defined(__GNUC__)
+#define __inline        /**/
+#endif
+
+#if defined __MAC__
+#define _MAX_PATH (PATH_MAX)
+#endif
+
+#if defined MAC || defined __MAC__ || defined linux
+int getAliasName(char *filename,char *newpath)
+{
+    return 1;
+}
+#endif
+#ifndef min
+#define min(a,b)        (((a) < (b)) ? (a) : (b) )
+#endif
+
+
+typedef unsigned short WORD;
+#if defined CDPLONG64 || defined unix
+typedef unsigned int DWORD;
+#else
+/* WIN32, so 4 bytes anyway */
+typedef unsigned long DWORD;
+#endif
+
+
+#ifdef NOTDEF
+// moved to sffuncs.h
+#define MSBFIRST        (1)
+#define LSBFIRST        (1)
+/*RWD May 2007:  revise defines to recognise both forms of MAC (__PPC__) */
+#if defined(__I86__) || defined(_X86_) || defined(__i386__) || defined(__i486__) || defined(_IBMR2) || defined(__LITTLE_ENDIAN__)
+#undef MSBFIRST
+#elif defined(M68000) || defined(__sgi) || defined (__ppc__) || defined(__BIG_ENDIAN__)
+#undef LSBFIRST
+#else
+#error  "Unknown byte order for this processor"
+#endif
+
+#if defined(MSBFIRST) && defined(LSBFIRST)
+#error  "Internal: can't be both MSB and LSB"
+#endif
+
+#define REVDWBYTES(t)   ( (((t)&0xff) << 24) | (((t)&0xff00) << 8) | (((t)&0xff0000) >> 8) | (((t)>>24) & 0xff) )
+#define REVWBYTES(t)    ( (((t)&0xff) << 8) | (((t)>>8) &0xff) )
+
+/*RWD.6.99 REV3BYTES is a function*/
+static char * REV3BYTES(char *samp_24);
+extern int sampsize[];
+
+#ifdef MSBFIRST
+#define REVDATAINFILE(f)        ((f)->filetype == riffwav || (f)->filetype == wave_ex)
+#else
+#define REVDATAINFILE(f)        (((f)->filetype == eaaiff) || ((f)->filetype==aiffc))
+#endif
+
+#endif
+/*
+ *      Sfsys-related definitions
+ */
+#define SFDBASE (1000)
+#define ALLOC(s)        ((s *)malloc(sizeof(s)))
+
+#define TAG(a,b,c,d)    ( ((a)<<24) | ((b)<<16) | ((c)<<8) | (d) )
+
+/*
+ *      Wave format-specific stuff
+ */
+#ifndef WAVE_FORMAT_PCM
+#define WAVE_FORMAT_PCM         (0x0001)
+#endif
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT  (0x0003)
+#endif
+#ifndef WAVE_FORMAT_EXTENSIBLE
+#define WAVE_FORMAT_EXTENSIBLE (65534)
+#endif
+
+#define CURRENT_PEAK_VERSION (1)
+#define sizeof_WFMTEX (40)
+
+
+#ifdef linux
+#define POS64(x) (x.__pos)
+#else
+#define POS64(x) (x)
+#endif
+
+typedef union {
+    DWORD lsamp;
+    float fsamp;
+    unsigned char bytes[4];
+} SND_SAMP;
+
+
+struct fmtchunk {
+    WORD    formattag;
+    WORD    channels;
+    DWORD   samplespersec;
+    DWORD   avgbytespersec;
+    WORD    blockalign;
+};
+
+#if 0
+// now use wavdefs.h
+# ifndef _WIN32
+typedef struct _GUID
+{
+    DWORD      Data1;
+    WORD       Data2;
+    WORD       Data3;
+    unsigned char Data4[8];
+} GUID;
+
+typedef struct {
+    WORD  wFormatTag;
+    WORD  nChannels;
+    DWORD nSamplesPerSec;
+    DWORD nAvgBytesPerSec;
+    WORD  nBlockAlign;
+    WORD  wBitsPerSample;
+    WORD  cbSize;
+} WAVEFORMATEX;
+# endif
+#endif
+
+#ifdef _WIN32
+typedef struct {
+    WAVEFORMATEX    Format;
+    union {
+        WORD wValidBitsPerSample;       /* bits of precision  */
+        WORD wSamplesPerBlock;          /* valid if wBitsPerSample==0 */
+        WORD wReserved;                 /* If neither applies, set to */
+                                        /* zero. */
+    } Samples;
+    DWORD           dwChannelMask;      /* which channels are */
+                                        /* present in stream  */
+    GUID            SubFormat;
+} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
+#endif
+
+
+const static GUID  KSDATAFORMAT_SUBTYPE_PCM = {0x00000001,0x0000,0x0010,
+                                                            {0x80,
+                                                            0x00,
+                                                            0x00,
+                                                            0xaa,
+                                                            0x00,
+                                                            0x38,
+                                                            0x9b,
+                                                            0x71}};
+
+const static GUID  KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003,0x0000,0x0010,
+                                                            {0x80,
+                                                            0x00,
+                                                            0x00,
+                                                            0xaa,
+                                                            0x00,
+                                                            0x38,
+                                                            0x9b,
+                                                            0x71}};
+
+//B-FORMAT!
+// {00000001-0721-11d3-8644-C8C1CA000000}
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_PCM = { 0x00000001, 0x0721, 0x11d3,
+                                                            { 0x86,
+                                                            0x44,
+                                                            0xc8,
+                                                            0xc1,
+                                                            0xca,
+                                                            0x0,
+                                                            0x0,
+                                                            0x0 } };
+
+
+static const GUID SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT = { 0x00000003, 0x0721, 0x11d3,
+                                                            { 0x86,
+                                                            0x44,
+                                                            0xc8,
+                                                            0xc1,
+                                                            0xca,
+                                                            0x0,
+                                                            0x0,
+                                                            0x0 } };
+struct cuepoint {
+    DWORD   name;
+    DWORD   position;
+    DWORD   incchunkid;
+    DWORD   chunkoffset;
+    DWORD   blockstart;
+    DWORD   sampleoffset;
+};
+
+/*
+ *      aiff format-specific stuff
+ */
+
+struct aiffchunk {
+    DWORD tag;
+    DWORD size;
+    fpos_t offset;
+    char *buf;
+    struct aiffchunk *next;
+};
+
+/*
+ *      Property storage structures
+ */
+#define PROPCNKSIZE     (2000)
+
+struct property {
+    char *name;
+    char *data;
+    int size;
+    struct property *next;
+};
+
+/*
+ *      Common declarations
+ */
+enum sndfiletype {
+    unknown_wave,
+    riffwav,
+    eaaiff,
+    aiffc,                                   //RWD sfsys98
+    wave_ex,                                //RWD.5.99
+#ifdef ENABLE_PVX
+    pvxfile,
+#endif
+    cdpfile                                 //RWD sfsys98
+};
+
+#ifdef _WIN32
+# ifdef ENABLE_PVX
+#  include "wavdefs.h"
+#  include "pvfileio.h"
+# endif
+#endif
+
+//RWD.6.99 NOTE: because of the slight possibility of 32bit int formats, from both file formats,
+// we need an explicit indicator of sample type. We choose to use the fmtchunk.formattag WAVE-style
+//for this, even for AIFF formats. Eventually, AIF-C will be used for all float AIFF files
+
+
+struct sf_file {
+    char *filename;
+    enum sndfiletype filetype;
+    int refcnt;
+#if defined _WIN32
+    HANDLE fileno;
+#else
+    FILE* fileno;
+#endif
+    int infochanged;
+    int todelete;
+    int readonly;
+    DWORD mainchunksize;
+    fpos_t fmtchunkoffset;
+    WAVEFORMATEXTENSIBLE fmtchunkEx;   //RWD NB: (for pvocex) includes WAVEFORMATEX as 'Format'
+    int bitmask;
+    fpos_t datachunkoffset;
+/* typedef from long long */
+    __int64 datachunksize;
+    __int64 sizerequested;
+    int extrachunksizes;
+    struct aiffchunk *aiffchunks;
+    int proplim;
+    fpos_t propoffset;
+    int propschanged;
+    int curpropsize;
+    struct property *props;
+#ifdef ENABLE_PVX
+    PVOCDATA *pvxprops;
+    int pvxfileno;
+#endif
+    DWORD curpos;
+    fpos_t factchunkoffset;
+    int header_set;                 //streaming: disallow header updates
+    int is_shortcut;
+        //RWD.6.5.99
+    time_t peaktime;
+    fpos_t peakchunkoffset;
+    CHPEAK *peaks;
+    channelformat chformat;
+    int min_header;
+};
+
+/*
+ *      for later!!
+ *      I think all we have to do is keep an fd open for each file open, and share
+ *      the other information - we don't have to worry about being thread-safe, etc!!!
+ */
+struct sf_openfile {
+    struct sf_file *file;
+#if defined _WIN32
+    HANDLE fileno;
+#else
+    FILE* fileno;
+#endif
+    DWORD curpos;
+};
+
+/*
+ *      Values for sizerequested
+ */
+#define ES_EXIST        (-2)
+
+#define LEAVESPACE      (10*1024)               /* file space that must be left */ //RWD? align to cluster size?
+
+
+
+/*
+ *      internal state
+ */
+int rsferrno = 0;
+char *rsferrstr = "no previous error";
+static struct sf_file *findfile(int sfd);
+static struct sf_file *sf_files[SF_MAXFILES];
+//static enum sndfiletype gettypefromname(const char *path);              //RWD98: lets declare this here
+static enum sndfiletype gettypefromname98(const char *path);
+static enum sndfiletype gettypefromfile(struct sf_file *f);
+static int wrwavhdr98(struct sf_file *f, int channels, int srate, int stype);
+static int wraiffhdr98(struct sf_file *f, int channels, int srate, int stype);
+static int wavupdate98(time_t thistime,struct sf_file *f);
+static int      aiffupdate(time_t thistime,struct sf_file *f);
+static int aiffupdate98(time_t thistime,struct sf_file *f);
+
+//private, but used by snd routines
+unsigned int _rsf_getmaxpeak(int sfd,float *peak);
+/*RWD 3:2000  to add analysis properties internally */
+static int  addprop(struct sf_file *f, char *propname, char *src, int size);
+#ifdef ENABLE_PVX
+static int writefirstprop(struct sf_file *f, char *propname, char *src, int size);
+static int pvx_createprops(struct sf_file *f);
+#endif
+static int compare_guids(const GUID *gleft, const GUID *gright)
+{
+    const char *left = (const char *) gleft, *right = (const char *) gright;
+    return !memcmp(left,right,sizeof(GUID));
+}
+static int file_exists(const char * fname);
+
+
+static int check_guid(struct sf_file *f)
+{
+    //expects a GUID to be loaded already,
+    //at present, we only need to know if this is b-format or not.
+    //but might as well validate floatsam format against nBits , jic
+    if(f->filetype != wave_ex)                      //should be an assert, but this is NOT a console lib!
+        return 1;
+    f->chformat = MC_STD;
+    //we elabotate on possible std assignments later...
+    if(compare_guids(&(f->fmtchunkEx.SubFormat),&(KSDATAFORMAT_SUBTYPE_PCM)))
+        return 0;
+    
+    if(compare_guids(&(f->fmtchunkEx.SubFormat),&(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
+        if(f->fmtchunkEx.Format.wBitsPerSample == 32)
+            return 0;
+    
+    if(compare_guids(&(f->fmtchunkEx.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_PCM))) {
+        f->chformat = MC_BFMT;
+        return 0;
+    }
+    
+    if(compare_guids(&(f->fmtchunkEx.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT)))  {
+        if(f->fmtchunkEx.Format.wBitsPerSample == 32){
+            f->chformat = MC_BFMT;
+            return 0;
+        }
+    }
+    
+    return 1;
+}
+
+/* RWD note: if not Windows, we use std C ftruncate() */
+#if defined _WIN32
+
+static int w_ch_size(HANDLE fh,__int64 size)
+{
+//#ifdef FILE64_WIN
+    LARGE_INTEGER li;
+    li.QuadPart =  size;
+    li.LowPart = SetFilePointer(fh,li.LowPart,&li.HighPart,FILE_BEGIN);
+    if(li.LowPart != 0xFFFFFFFF && GetLastError() == NO_ERROR){
+        if (!SetEndOfFile(fh))
+            return -1;
+    }
+    else
+       return -1;
+// #else
+//        if(SetFilePointer(fh,(long) size,NULL,FILE_BEGIN)== 0xFFFFFFFF
+//                || !SetEndOfFile(fh))
+//                return -1;
+// #endif
+    return 0;
+}
+#endif
+
+/*
+ *      read/write routines
+ */
+
+/* RWD 2007 executive decision not to allow cnt >= 2GB! */
+
+#if defined _WIN32
+static __inline int
+doread(struct sf_file *f, char *buf, int cnt)
+{
+    DWORD done = 0, count = (DWORD) cnt;
+
+    if(f->fileno == INVALID_HANDLE_VALUE)
+        return 1;
+
+    if(!ReadFile(f->fileno, buf, count,&done,NULL)){
+# ifdef _DEBUG
+        LPVOID lpMsgBuf;
+        FormatMessage(
+            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+            NULL,
+            GetLastError(),
+            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+            (LPTSTR) &lpMsgBuf,
+            0,
+            NULL
+        );
+#  ifndef WINDOWS
+        fprintf(stderr,(const char *)lpMsgBuf);
+#  else
+        MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );
+#  endif
+        LocalFree( lpMsgBuf );
+# endif
+        return 1;
+    }
+    return done != count;
+}
+
+#else
+//RWD NB returns ERROR if requested byte-count not read
+static __inline int
+doread(struct sf_file *f, char *buf, int cnt)
+{
+    int done = fread(buf,sizeof(char),cnt,f->fileno);
+    return done != cnt;
+}
+#endif
+
+
+static int
+read_w_lsf(WORD *wp, struct sf_file *f)
+{
+    WORD w;
+    if(doread(f, (char *)&w, sizeof(WORD)))
+        return 1;
+#ifdef MSBFIRST
+    *wp = REVWBYTES(w);
+#else
+    *wp = w;
+#endif
+    return 0;
+}
+
+static int
+read_w_msf(WORD *wp, struct sf_file *f)
+{
+    WORD w;
+    if(doread(f, (char *)&w, sizeof(WORD)))
+        return 1;
+#ifdef LSBFIRST
+    *wp = REVWBYTES(w);
+#else
+    *wp = w;
+#endif
+    return 0;
+}
+
+static int
+read_dw_lsf(DWORD *dwp, struct sf_file *f)
+{
+    DWORD dw;
+    if(doread(f, (char *)&dw, sizeof(DWORD)))
+        return 1;
+#ifdef MSBFIRST
+    *dwp = REVDWBYTES(dw);
+#else
+    *dwp = dw;
+#endif
+    return 0;
+}
+
+static int
+read_dw_msf(DWORD *dwp, struct sf_file *f)
+{
+    DWORD dw;
+    if(doread(f, (char *)&dw, sizeof(DWORD)))
+        return 1;
+#ifdef LSBFIRST
+    *dwp = REVDWBYTES(dw);
+#else
+    *dwp = dw;
+#endif
+    return 0;
+}
+
+
+#if defined _WIN32
+static __inline int
+dowrite(struct sf_file *f, char *buf, int cnt)
+{
+    DWORD done = 0, count = (DWORD)cnt;
+    if(!WriteFile(f->fileno, buf, count,&done,NULL))
+        return 1;
+    return done != count;
+}
+
+
+#else
+
+static __inline int
+dowrite(struct sf_file *f, char *buf, int cnt)
+{
+    int done = fwrite(buf,sizeof(char),cnt,f->fileno);
+    return done != cnt;
+}
+#endif
+
+static int
+write_w_lsf(WORD w, struct sf_file *f)
+{
+#ifdef MSBFIRST
+    w = REVWBYTES(w);
+#endif
+    return dowrite(f, (char *)&w, sizeof(WORD));
+}
+
+static int
+write_w_msf(WORD w, struct sf_file *f)
+{
+#ifdef LSBFIRST
+    w = REVWBYTES(w);
+#endif
+    return dowrite(f, (char *)&w, sizeof(WORD));
+}
+
+static int
+write_dw_lsf(DWORD dw, struct sf_file *f)
+{
+#ifdef MSBFIRST
+    dw = REVDWBYTES(dw);
+#endif
+    return dowrite(f, (char *)&dw, sizeof(DWORD));
+}
+
+static int
+write_dw_msf(DWORD dw, struct sf_file *f)
+{
+#ifdef LSBFIRST
+    dw = REVDWBYTES(dw);
+#endif
+    return dowrite(f, (char *)&dw, sizeof(DWORD));
+}
+
+//RWD.6.5.99 write peak data
+/* NB position values are of frames, so int good for 2GB anyway */
+static int write_peak_lsf(int channels, struct sf_file *f)
+{
+    int i;
+    DWORD peak[2];
+    SND_SAMP ssamp;
+    
+#ifdef _DEBUG
+    if(f->peaks==NULL){
+        printf("\nerror: attempt to write uninitialized peak data");
+        return 1;
+    }
+#endif
+    for(i=0; i < channels; i++){
+        // /*long*/ DWORD  *pdw;
+        // pdw = (/*long*/DWORD *) &(f->peaks[i].value);    /* RWD replaced with union */
+        ssamp.fsamp = f->peaks[i].value;
+        //peak[0] = *pdw;
+        peak[0] = ssamp.lsamp;
+        peak[1] = f->peaks[i].position;
+#ifdef MSBFIRST
+        peak[0] = REVDWBYTES(peak[0]);
+        peak[1]  = REVDWBYTES(peak[1]);
+#endif
+        if(dowrite(f,(char *) peak, 2 * sizeof(DWORD)))
+            return 1;
+    }
+    return 0;
+}
+
+static int read_peak_lsf(int channels, struct sf_file *f)
+{
+    int i;
+    DWORD peak[2];
+    SND_SAMP ssamp;
+#ifdef _DEBUG
+    if(f->peaks==NULL){
+        printf("\nerror: attempt to write uninitialized peak data");
+        return 1;
+    }
+#endif
+    for(i=0;i < channels; i++){
+        if(doread(f,(char *)peak,2 * sizeof(DWORD)))
+            return 1;
+#ifdef MSBFIRST
+        peak[0] = REVDWBYTES(peak[0]);
+        peak[1]  = REVDWBYTES(peak[1]);
+#endif
+        ssamp.lsamp = peak[0];
+        //f->peaks[i].value = *(float *) &(peak[0]);          /* RWD TODO: replaced with union */
+        f->peaks[i].value = ssamp.fsamp;
+        f->peaks[i].position = peak[1];
+    }
+    return 0;
+}
+
+static int write_peak_msf(int channels, struct sf_file *f)
+{
+    int i;
+    DWORD peak[2];
+    SND_SAMP ssamp;
+    for(i=0; i < channels; i++){
+        // /*long*/DWORD  *pdw;
+        //pdw = (/*long*/DWORD *) &(f->peaks[i].value);      /* RWD replaced with union */
+        ssamp.fsamp = f->peaks[i].value;
+        // peak[0] = *pdw;
+        peak[0] = ssamp.lsamp;
+        peak[1] = f->peaks[i].position;
+#ifdef LSBFIRST
+        peak[0] = REVDWBYTES(peak[0]);
+        peak[1]  = REVDWBYTES(peak[1]);
+#endif
+        if(dowrite(f,(char *) peak, 2 * sizeof(DWORD)))
+            return 1;
+        
+    }
+    return 0;
+    
+}
+
+static int read_peak_msf(int channels, struct sf_file *f)
+{
+    int i;
+    DWORD peak[2];
+    SND_SAMP ssamp;
+    for(i=0;i < channels; i++){
+        if(doread(f,(char *)peak,2 * sizeof(DWORD)))
+            return 1;
+#ifdef LSBFIRST
+        peak[0] = REVDWBYTES(peak[0]);
+        peak[1]  = REVDWBYTES(peak[1]);
+#endif
+        ssamp.lsamp = peak[0];
+        //f->peaks[i].value = *(float *) &(peak[0]);        /* RWD replaced with union */
+        f->peaks[i].value = ssamp.fsamp;
+        f->peaks[i].position = peak[1];
+    }
+    return 0;
+}
+/*
+ *      Fudge Apple extended format, for sample rates
+ */
+/* RWD Oct 2022 removed old "TABLE_IEEE754" code */
+/* now using compact Csound code */
+
+
+
+#ifdef DW_EX_OLDCODE
+static int
+read_ex_todw(DWORD *dwp, struct sf_file *f)
+{
+        double neg = 1.0;
+        WORD exp;
+        /*unsigned long*/DWORD ms_sig;
+        double res;
+        char buf[10];   /* for 80-bit extended float */
+
+        if(doread(f, buf, 10))
+                return 1;
+# ifdef LSBFIRST
+        exp = REVWBYTES(*(WORD *)&buf[0]);
+        ms_sig = (/*unsigned long*/DWORD)REVDWBYTES(*(DWORD *)&buf[2]);   /* RWD TODO replace with union */
+# else
+        exp = *(WORD *)&buf[0];
+        ms_sig = (/*unsigned long*/DWORD)*(DWORD *)&buf[2];               /* RWD TODO replace with union */
+# endif
+        if(exp & 0x8000) {
+                exp &= ~0x8000;
+                neg = -1.0;
+        }
+        exp -= 16382;
+        res = (double)ms_sig/TWOPOW32;
+        res = neg*ldexp(res, exp);
+        *dwp = (DWORD)(res+0.5);
+        return 0;
+}
+
+static int
+write_dw_toex(DWORD dw, struct sf_file *f)
+{
+    double val = (double)dw;
+    int neg = 0;
+    /*unsigned long*/DWORD mant;
+    int exp;
+    char buf[10];
+    
+    if(val < 0.0) {
+        val = -val;
+        neg++;
+    }
+    mant = (/*unsigned long*/DWORD)(frexp(val, &exp) * TWOPOW32 + 0.5);
+    exp += 16382;
+    if(neg)
+        exp |= 0x8000;
+# ifdef LSBFIRST
+    *(WORD *)&buf[0] = REVWBYTES(exp);              /* RWD TODO replace all with union? */
+    *(DWORD *)&buf[2] = REVDWBYTES((DWORD)mant);
+# else
+    *(WORD *)&buf[0] = exp;
+    *(DWORD *)&buf[2] = mant;
+# endif
+    *(DWORD *)&buf[6] = 0;
+    return dowrite(f, buf, 10);
+}
+#else
+/* use Csound funcs */
+static int
+read_ex_todw(DWORD *dwp, struct sf_file *f)
+{
+    double Csound_res = 0.0;
+    char buf[10];   /* for 80-bit extended float */
+
+    if(doread(f, buf, 10))
+        return 1;
+    Csound_res = ieee_80_to_double((unsigned char *) buf);
+    *dwp = (DWORD) Csound_res;
+    return 0;
+}
+
+static int
+write_dw_toex(DWORD dw, struct sf_file *f)
+{
+    double val = (double)dw;
+    char buf[10];
+    double_to_ieee_80(val,(unsigned char *) buf);
+    return dowrite(f,buf,10);
+}
+
+
+#endif  // DW_EX_OLDCODE
+
+
+/*
+ *      wave file-format specific routines
+ */
+
+static int
+getsfsyscue(struct sf_file *f)
+{
+    DWORD cnt;
+    int rc = 0;
+    struct cuepoint cue;
+    
+    if(read_dw_lsf(&cnt, f))
+        return -1;
+    while(cnt-- > 0) {
+        if(read_dw_msf(&cue.name, f)
+           ||read_dw_lsf(&cue.position, f)
+           ||read_dw_msf(&cue.incchunkid, f)
+           ||read_dw_lsf(&cue.chunkoffset, f)
+           ||read_dw_lsf(&cue.blockstart, f)
+           ||read_dw_lsf(&cue.sampleoffset, f))
+            return -1;
+        if(cue.name == TAG('s','f','i','f'))
+            rc = 1;
+    }
+    return rc;
+}
+
+/*
+ *      extended properties are as follows:
+ *
+ *      property name '\n'
+ *      property value '\n'
+ *      ...
+ *      '\n'
+ */
+static int
+xtoi(int ch)
+{
+    if(ch >= '0' && ch <= '9')
+        return ch - '0';
+    if(ch >= 'A' && ch <= 'F')
+        return ch - 'A' + 10;
+    if(ch >= 'a' && ch <= 'f')
+        return ch - 'a' + 10;
+    return 0;
+}
+
+static int
+itox(int i)
+{
+    static char trans[] = "0123456789ABCDEF";
+    return trans[i&0x0f];
+}
+
+static void
+parseprops(struct sf_file *f, char *data)
+{
+    char *cp = data;
+    char *ep, *evp;
+    int cnt;
+    struct property **ppp = &f->props;
+    struct property *np;
+
+    f->curpropsize = 0;
+
+    while(cp-data < f->proplim && *cp != '\n') {
+        if((ep = strchr(cp, '\n')) == 0
+           ||(evp = strchr(ep+1, '\n')) == 0
+           ||((evp-ep-1)&1))
+            return;
+        if((np = ALLOC(struct property)) == 0
+           ||(np->name = (char *) malloc(ep-cp+1)) == 0
+           ||(np->data = (char *) malloc((evp-ep-1)/2)) == 0)
+            return;
+        np->size = (evp-ep-1)/2;
+        np->next = 0;
+        memcpy(np->name, cp, ep-cp);
+        np->name[ep-cp] = '\0';
+        for(cnt = 0; cnt < np->size; cnt++)
+            np->data[cnt] = (xtoi((ep+1)[2*cnt])<<4) + xtoi((ep+1)[2*cnt+1]);
+        *ppp = np;
+        ppp = &np->next;
+        f->curpropsize += strlen(np->name) + 1 + np->size + 1;
+
+        cp = evp+1;
+    }
+}
+
+static int
+writeprops(struct sf_file *f)
+{
+    char *obuf, *op;
+    int cnt;
+    struct property *p = f->props;
+    
+    if((obuf = (char *) malloc(f->proplim)) == 0) {
+        rsferrno = ESFNOMEM;
+        rsferrstr = "No memory to write properties with";
+        return -1;
+    }
+    op = obuf;
+    
+    while(p != 0) {
+        strcpy(op, p->name);
+        op += strlen(p->name);
+        *op++ = '\n';
+        for(cnt = 0; cnt < p->size; cnt++) {
+            *op++ = itox(p->data[cnt]>>4);
+            *op++ = itox(p->data[cnt]);
+        }
+        *op++ = '\n';
+        if(op-obuf >= f->proplim)                 //RWD.1.99 this is really an assert test...
+            abort();
+        p = p->next;
+    }
+    while(op < &obuf[f->proplim])
+        *op++ = '\n';
+    /*RWD 2007: we rely on all props being within first 2GB of file! */
+#if defined _WIN32
+    if (SetFilePointer(f->fileno,(LONG)f->propoffset,NULL,FILE_BEGIN) == 0xFFFFFFFF
+#else
+    if(fseeko(f->fileno, POS64(f->propoffset), SEEK_SET)
+#endif
+        ||dowrite(f, obuf, f->proplim)) {
+        rsferrno = ESFWRERR;
+        rsferrstr = "Write error writing new property values";
+        return -1;
+    }
+    free(obuf);
+    return 0;
+}
+
+static int
+getsfsysadtl(struct sf_file *f, int adtllen)
+{
+    DWORD tag, size;
+    DWORD name;
+    fpos_t bytepos;
+    char *propspace;
+    char buf[1];
+
+    while(adtllen > 0) {
+        if(read_dw_msf(&tag, f)
+                ||read_dw_lsf(&size, f))
+            return -1;
+        switch(tag) {
+        case TAG('n','o','t','e'):
+            if(read_dw_msf(&name, f))
+                return -1;
+            if(name != TAG('s','f','i','f')
+                    ||(int)(POS64(f->propoffset)) >= 0
+                    ||(propspace = (char *) malloc(size-sizeof(DWORD))) == 0) {
+
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,(LONG)((size-sizeof(DWORD)+1)&~1),NULL,FILE_CURRENT)== 0xFFFFFFFF)
+#else
+                if(fseek(f->fileno, (size-sizeof(DWORD)+1)&~1, SEEK_CUR))
+#endif
+                    return -1;
+                break;
+            }
+            f->proplim = size-sizeof(DWORD);
+#if defined _WIN32
+            if((f->propoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF
+                    ||doread(f, propspace, size-sizeof(DWORD)))
+                return -1;
+#else
+            if(fgetpos(f->fileno,&bytepos)
+                    ||doread(f, propspace, size-sizeof(DWORD)))
+                return -1;
+            f->propoffset = bytepos;
+#endif
+            parseprops(f, propspace);
+            free(propspace);
+            if(size&1)
+                doread(f, buf, 1);
+            break;
+        default:
+#if defined _WIN32
+            if(SetFilePointer(f->fileno, (size+1)&~1,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+                return -1;
+#else
+            if(fseek(f->fileno, (size+1)&~1, SEEK_CUR) < 0)
+                return -1;
+#endif
+            break;
+        }
+        adtllen -= 2*sizeof(DWORD) + ((size+1)&~1);     /* RWD Jan 2013 */
+    }
+    return 0;
+}
+
+//RWD.7.99 TODO: set f->min_header here?
+
+/*RWD 2007: MUST use DWORD to enure we get sizes up to 4GB */
+/* BUT: if we seek to end of data chunk, need 64bit seek */
+static int
+rdwavhdr(struct sf_file *f)
+{
+    DWORD tag, size;
+    int fmtseen = 0;
+    int dataseen  = 0;      /*RWD April 2006: try to read PEAK chunk after data (boo hiss Sony! )*/
+    int err = 0;            /*  "" */
+    int gotsfsyscue = 0;
+    DWORD factsize = 0;
+    DWORD peak_version;
+#if defined _WIN32
+    LARGE_INTEGER pos64;   /* pos64.QuadPart is __int64 */
+#endif
+    fpos_t bytepos;
+
+    //WAVEFORMATEXTENSIBLE *pFmtEx;
+    if(read_dw_msf(&tag, f)
+         ||read_dw_lsf(&size, f)
+         ||tag != TAG('R','I','F','F')) {
+        rsferrno = ESFNOTFOUND;
+        rsferrstr = "File is not a RIFF file";
+        return 1;
+    }
+    if(size < 4) {
+        rsferrno = ESFNOTFOUND;
+        rsferrstr = "File data size is too small";
+        return 1;
+    }
+    if(read_dw_msf(&tag, f)
+         ||tag != TAG('W','A','V','E')) {
+        rsferrno = ESFNOTFOUND;
+        rsferrstr = "File is not a wave RIFF file";
+        return 1;
+    }
+    f->filetype = riffwav;          //might be wave_ex
+    f->mainchunksize = size;
+    f->extrachunksizes = 0;
+    f->proplim = 0;
+    f->props = 0;
+    POS64(f->propoffset) = (unsigned) -1;
+    POS64(f->factchunkoffset) = (unsigned) -1;
+        //datachunkoffset now initialized in allocsffile
+
+        //RWD.6.99 do I need to do the AIFF getout for bad sizes here too?
+    for(;;) {
+        if(read_dw_msf(&tag, f)
+            ||read_dw_lsf(&size,f)){
+        /*RWD April 2006 TODO: detect EOF! */
+            err++;
+            goto ioerror;
+        }
+        switch(tag) {
+            case TAG('f','m','t',' '):
+                        //RWD: must deal with possibility of WAVEFORMATEX extra cbSize word
+#if defined _WIN32
+                if((f->fmtchunkoffset = SetFilePointer(f->fileno, 0L,NULL, FILE_CURRENT))== 0xFFFFFFFF
+                   ||read_w_lsf(&f->fmtchunkEx.Format.wFormatTag, f)
+                   ||read_w_lsf(&f->fmtchunkEx.Format.nChannels, f)
+                   ||read_dw_lsf(&f->fmtchunkEx.Format.nSamplesPerSec, f)
+                   ||read_dw_lsf(&f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+                   ||read_w_lsf(&f->fmtchunkEx.Format.nBlockAlign, f) ) {
+                    err++;
+                    goto ioerror;
+                }
+#else
+                if(fgetpos(f->fileno, &bytepos)
+                        ||read_w_lsf(&f->fmtchunkEx.Format.wFormatTag, f)
+                        ||read_w_lsf(&f->fmtchunkEx.Format.nChannels, f)
+                        ||read_dw_lsf(&f->fmtchunkEx.Format.nSamplesPerSec, f)
+                        ||read_dw_lsf(&f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+                        ||read_w_lsf(&f->fmtchunkEx.Format.nBlockAlign, f) ) {
+                    err++;
+                    goto ioerror;
+                }
+                f->fmtchunkoffset = bytepos;
+#endif
+                switch(f->fmtchunkEx.Format.wFormatTag) {
+                    case WAVE_FORMAT_PCM:
+                    case WAVE_FORMAT_IEEE_FLOAT:                    //RWD 07:97
+                    case WAVE_FORMAT_EXTENSIBLE:                    //RWD.5.99
+                        if(read_w_lsf(&f->fmtchunkEx.Format.wBitsPerSample, f)) {
+                            err++;
+                            goto ioerror;
+                        }
+                        //RWD.6.99 set f->fmtchunkEx.Samples.wValidBitsPerSample to this as default
+                        // will only be changed by a WAVE_EX header
+                        f->fmtchunkEx.Samples.wValidBitsPerSample = f->fmtchunkEx.Format.wBitsPerSample;
+                        //deal with things such as 20bits per sample...
+                        //this covers standard WAVE, WAVE-EX may adjust again
+                        if(f->fmtchunkEx.Format.wBitsPerSample !=
+                                ((f->fmtchunkEx.Format.nBlockAlign / f->fmtchunkEx.Format.nChannels) * 8)){
+                        //deduce the container size
+                            f->fmtchunkEx.Format.wBitsPerSample =
+                            ((f->fmtchunkEx.Format.nBlockAlign / f->fmtchunkEx.Format.nChannels) * 8);
+
+                        }
+                        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+                            case 32:                /* floats or longs*/
+                                break;
+                            case(24):
+                            case 16:                /* shorts */
+                            case 8:                 /* byte -> short mappping */
+/*RWD 07:97*/                   if(!(f->fmtchunkEx.Format.wFormatTag == WAVE_FORMAT_PCM  ||
+                                        f->fmtchunkEx.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)){
+                                    rsferrno = ESFNOTFOUND;
+                                    rsferrstr = "can't open Sfile : Format mismatch";
+                                    return 1;
+                                }
+                                break;
+                            default:
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "can't open Sfile - unsupported format";
+                                return 1;
+                        }
+                        //now catch any extra bytes, and parse WAVE_EX if we have it...
+                        if(size > 16){
+                            unsigned short cbSize;
+                            if(read_w_lsf(&cbSize,f))
+                                goto ioerror;
+                    /* RWD May 2011. Effing Steinberg Wavelab writes a 20byte fmt chunk! */
+                    /* Feb 2012: AND Pro Tools writes a 40byte fmt chunk! */
+                            if(cbSize==0){
+                                int wordstoskip = (size-18) / sizeof(WORD);
+                                int skip;
+                                unsigned short dummy;
+                                for(skip = 0; skip < wordstoskip; skip++){
+                                    if(read_w_lsf(&dummy,f))
+                                        goto ioerror;
+                                }
+                            }
+                            else{
+                                if(f->fmtchunkEx.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE){
+                                    rsferrno = ESFNOTFOUND;
+                                    rsferrstr = "unexpected extra format information - not a wave file";
+                                    return 1;
+                                }
+                                if(cbSize != 22){
+                                    rsferrno = ESFNOTFOUND;
+                                    rsferrstr = "unexpected extra format information in WAVE_EX file!";
+                                    return 1;
+                                }
+                                f->filetype = wave_ex;
+                                    if(read_w_lsf(&(f->fmtchunkEx.Samples.wValidBitsPerSample),f)
+                                            ||
+                                            read_dw_lsf(&(f->fmtchunkEx.dwChannelMask),f)){
+                                        err++;
+                                        goto ioerror;
+                                    }
+                                    //get the GUID
+                                    if(doread(f,(char *) &(f->fmtchunkEx.SubFormat),sizeof(GUID))) {
+                                        err++;
+                                        goto ioerror;
+                                    }
+#ifdef MSBFIRST
+                                    f->fmtchunkEx.SubFormat.Data1 = REVDWBYTES(f->fmtchunkEx.SubFormat.Data1);
+                                    f->fmtchunkEx.SubFormat.Data2 = REVWBYTES(f->fmtchunkEx.SubFormat.Data2);
+                                    f->fmtchunkEx.SubFormat.Data3 = REVWBYTES(f->fmtchunkEx.SubFormat.Data3);
+#endif
+                                    if(check_guid(f)){
+                                        rsferrno = ESFNOTFOUND;
+                                        rsferrstr = "unrecognized WAV_EX subformat";
+                                        return 1;
+                                    }
+                                //we don't try to ID the spkr-config here: do that in the sf/snd open fucntions
+                            }
+                        }
+                        break;
+                    default:
+                        rsferrno = ESFNOTFOUND;
+                        rsferrstr = "can't open Sfile - unsupported format";
+                        return 1;
+                    }
+                    //set the bitmask here - covers plain WAVE too
+                    if(f->fmtchunkEx.Samples.wValidBitsPerSample
+                            < f->fmtchunkEx.Format.wBitsPerSample){
+                        int mask = 0xffffffff;
+                        int shift = 32 - f->fmtchunkEx.Format.wBitsPerSample;
+                        f->bitmask = mask << (shift + (f->fmtchunkEx.Format.wBitsPerSample
+                                        - f->fmtchunkEx.Samples.wValidBitsPerSample));
+                    }
+                    fmtseen++;
+                    break;
+                case TAG('L','I','S','T'):
+                    if(read_dw_msf(&tag, f))
+                        goto ioerror;
+                    switch(tag) {
+                        case TAG('w','a','v','l'):
+                            rsferrno = ESFNOTFOUND;
+                            rsferrstr = "Can't open SFfile - no support for list chunks yet!";
+                            return 1;
+                        case TAG('a','d','t','l'):
+                            if(getsfsysadtl(f, size-sizeof(DWORD)) < 0)     {
+                                err++;
+                                goto ioerror;
+                            }
+                            break;
+                        default:
+#if defined _WIN32
+                            if(SetFilePointer(f->fileno, size-sizeof(DWORD),NULL, FILE_CURRENT) == 0xFFFFFFFF)
+#else
+                            POS64(bytepos) = size-sizeof(DWORD);
+                            if(fseeko(f->fileno, /*size-sizeof(DWORD)*/POS64(bytepos), SEEK_CUR) < 0)
+#endif
+                            {
+                                err++;
+                                goto ioerror;
+                            }
+                            break;
+                    }
+                    break;
+
+//read fact chunk (not needed for std PCM files...but we use it anyway in sfsys97!
+                case TAG('f','a','c','t'):
+#if defined _WIN32
+                    if((f->factchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
+#else
+                    if(fgetpos(f->fileno,&f->factchunkoffset))
+#endif
+                    {
+                        err++;
+                        goto ioerror;
+                    }
+                    if(read_dw_lsf(&factsize,f))  {
+                        err++;
+                        goto ioerror;
+                    }
+                    break;                                         //RWD: need to update extrachunksizes?
+                    //RWD.5.99 read PEAK chunk
+                case TAG('P','E','A','K'):
+                    f->peaks = (CHPEAK *) calloc(f->fmtchunkEx.Format.nChannels,sizeof(CHPEAK));
+                    if(f->peaks == NULL){
+                        rsferrno = ESFNOMEM;
+                        rsferrstr = "No memory for peak data";
+                        return 1;
+                    }
+                    if(read_dw_lsf(&peak_version,f)) {
+                        err++;
+                        goto ioerror;
+                    }
+                    switch(peak_version){
+                        case(CURRENT_PEAK_VERSION):
+                            if(read_dw_lsf((DWORD*) &(f->peaktime),f)){
+                                err++;
+                                goto ioerror;
+                            }
+/* RWD 2007:  PEAK chunk is after data chunk in some naff but otherwise legal files,
+  so ordinary lseek no good */
+
+#if defined _WIN32
+                            pos64.QuadPart = 0;
+                            pos64.LowPart = SetFilePointer(f->fileno, 0L, &pos64.HighPart, FILE_CURRENT);
+                            if(pos64.LowPart == 0xFFFFFFFF  && GetLastError() != NO_ERROR){
+                                err++;
+                                goto ioerror;
+                            }
+                            else {
+                                /* WAVE and AIFF have to fit inside unsigned int */
+                                f->peakchunkoffset = (unsigned int) pos64.QuadPart;
+                                if(read_peak_lsf(f->fmtchunkEx.Format.nChannels,f))     {
+                                    err++;
+                                    goto ioerror;
+                                }
+                            }
+
+#else
+                            if(fgetpos(f->fileno,&f->peakchunkoffset)
+                                    || read_peak_lsf(f->fmtchunkEx.Format.nChannels,f))     {
+                                err++;
+                                goto ioerror;
+                            }
+#endif
+                            break;
+                        default:
+#ifdef _DEBUG
+                            fprintf(stderr,"\nunknown PEAK version!");
+#endif
+                            free(f->peaks);
+                            f->peaks = NULL;
+                            break;
+                    }
+                    break;
+
+                case TAG('d','a','t','a'):
+#if defined _WIN32
+/*  datachunk MUST be within 2GB! */
+                    if((f->datachunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF){
+                        err++;
+                        goto ioerror;
+                    }
+                    f->datachunksize = size;
+                    if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                        f->datachunksize *= 2;
+#else
+                    if(fgetpos(f->fileno,&f->datachunkoffset) ){
+                        err++;
+                        goto ioerror;
+                    }
+                    f->datachunksize = size;
+                    if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                        f->datachunksize *= 2;
+#endif
+                        /* skip over data chunk; to try to read later chunks */
+
+                    size = (size+1)&~1;
+#if defined _WIN32
+                    pos64.QuadPart = (__int64) size;
+                    pos64.LowPart = SetFilePointer(f->fileno, pos64.LowPart,&pos64.HighPart,FILE_CURRENT);
+                    if(pos64.LowPart == 0xFFFFFFFF && GetLastError() != NO_ERROR){
+                        err++;
+                        goto ioerror;
+                    }
+#else
+                    /* expect >2GB data chunk, so must use fseeko etc */
+                    POS64(bytepos) = POS64(f->datachunkoffset) + size;
+                    if(fsetpos(f->fileno, &bytepos)) {
+                        err++;
+                        goto ioerror;
+                    }
+#endif
+                    dataseen++;
+                    break;
+                case TAG('c','u','e',' '):
+                    if((gotsfsyscue = getsfsyscue(f)) < 0) {
+                        err++;
+                        goto ioerror;
+                    }
+                    break;
+                default:
+                    size = (size+1)&~1;
+                    /* we trust that only the data chunk will be huge! */
+#if defined _WIN32
+                    if(SetFilePointer(f->fileno, size, NULL,FILE_CURRENT) == 0xFFFFFFFF) {
+                        err++;
+                        goto ioerror;
+                    }
+#else
+                    if(fseek(f->fileno, size, SEEK_CUR) < 0) {
+                        err++;
+                        goto ioerror;
+                    }
+#endif
+                    f->extrachunksizes += size + 2*sizeof(DWORD);      //RWD: anonymous chunks - we will copy these one day...
+                    break;
+            }
+        }
+        /* NOTREACHED */
+
+ioerror:
+    if(fmtseen && dataseen)
+        return 0;
+
+    rsferrno = ESFRDERR;
+    rsferrstr = "read error (or file too short) reading wav header";
+    return 1;
+}
+
+/********** SFSYS98 version ****************
+ *  this RECEIVES format data from calling function,
+ * to create the required header
+ */
+static int
+wrwavhdr98(struct sf_file *f, int channels, int srate, int stype)
+{
+    struct cuepoint cue;
+    int extra = 0;
+    int wordsize;
+#ifdef linux
+    fpos_t bytepos = {0};
+#else
+    fpos_t bytepos = 0;
+#endif
+    WORD cbSize = 0x0000;    //RWD for WAVEFORMATEX, FLOAT FORMAT ONLY
+    f->mainchunksize = 0;   /* we don't know the full size yet! */
+    f->extrachunksizes = 0;
+
+        //we will not use sffuncs for 24bit files!
+    if(stype >= SAMP_MASKED){
+        rsferrstr = "this verson cannot write files in requested format";
+        return 1;
+    }
+
+    //wordsize = (stype == SAMP_FLOAT ?  sizeof(float) : sizeof(short));
+    wordsize = sampsize[stype];
+    if(stype== SAMP_FLOAT)
+        extra =  sizeof(WORD);
+
+    if(write_dw_msf(TAG('R','I','F','F'), f)
+         ||write_dw_lsf(0, f)
+         ||write_dw_msf(TAG('W','A','V','E'), f))
+        goto ioerror;
+
+    f->fmtchunkEx.Format.wFormatTag = stype == SAMP_FLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
+    f->fmtchunkEx.Format.nChannels = (unsigned short)channels;
+    f->fmtchunkEx.Format.nSamplesPerSec = srate;
+    f->fmtchunkEx.Format.nAvgBytesPerSec = wordsize * channels * srate;
+    f->fmtchunkEx.Format.nBlockAlign = (unsigned short) ( wordsize * channels);
+
+    //for standard WAVE, we set wBitsPerSample to = validbits
+    if(stype==SAMP_2024)
+        f->fmtchunkEx.Format.wBitsPerSample = 20;
+    else if(stype==SAMP_2432)
+        f->fmtchunkEx.Format.wBitsPerSample = 24;
+    else
+        f->fmtchunkEx.Format.wBitsPerSample = (short)(8 * wordsize);
+        //need this for wavupdate98()
+    f->fmtchunkEx.Samples.wValidBitsPerSample = f->fmtchunkEx.Format.wBitsPerSample;
+    if(write_dw_msf(TAG('f','m','t',' '), f)
+            ||write_dw_lsf(16 + extra, f)                          //RWD CDP97: size = 18 to include cbSize field
+#if defined _WIN32
+            ||((f->fmtchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
+            ||write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+            ||write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+            ||write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)
+            )
+        goto ioerror;
+#else
+            ||fgetpos(f->fileno,&bytepos)
+            ||write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+            ||write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+            ||write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+            ||write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)
+            )
+        goto ioerror;
+    f->fmtchunkoffset = bytepos;
+#endif
+    if(stype == SAMP_FLOAT) {
+        if(write_w_lsf(cbSize,f))
+            goto ioerror;
+    }
+
+    //RWD.6.5.99 ADD the PEAK chunk
+    if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+        int i,size;
+        DWORD now = 0;
+        for(i=0;i < channels; i++){
+            f->peaks[i].value = 0.0f;
+            f->peaks[i].position = 0;
+        }
+        size = 2 * sizeof(DWORD) + channels * sizeof(CHPEAK);
+        if(write_dw_msf(TAG('P','E','A','K'),f)
+                || write_dw_lsf(size,f)
+#if defined _WIN32
+                || ((f->peakchunkoffset = SetFilePointer(f->fileno,0L,NULL, FILE_CURRENT))== 0xFFFFFFFF)
+                || write_dw_lsf(CURRENT_PEAK_VERSION,f)
+                || write_dw_lsf(now,f)
+                || write_peak_lsf(channels,f))
+            goto ioerror;
+#else
+                || fgetpos(f->fileno,&bytepos)
+                || write_dw_lsf(CURRENT_PEAK_VERSION,f)
+                || write_dw_lsf(now,f)
+                || write_peak_lsf(channels,f))
+
+            goto ioerror;
+
+        f->peakchunkoffset = bytepos;
+#endif
+    }
+
+
+    if(f->min_header >= SFILE_CDP){
+        /*
+         *      add the cue point/note chunk for properties
+         */
+        /* RWD Nov 2009: don't need cue for analysis files */
+        if(f->min_header==SFILE_CDP){
+            //RWD TODO: add switch to skip writing this extra stuff!
+            if(write_dw_msf(TAG('c','u','e',' '), f)
+                ||write_dw_lsf(sizeof(struct cuepoint) + sizeof(DWORD), f) )
+                goto ioerror;
+
+            cue.name = TAG('s','f','i','f');
+            cue.position = 0;
+            cue.incchunkid = TAG('d','a','t','a');
+            cue.chunkoffset = 0;
+            cue.blockstart = 0;
+            cue.sampleoffset = 0;
+            if(write_dw_lsf(1, f)       /* one cue point */
+                    ||write_dw_msf(cue.name, f)
+                    ||write_dw_lsf(cue.position, f)
+                    ||write_dw_msf(cue.incchunkid, f)
+                    ||write_dw_lsf(cue.chunkoffset, f)
+                    ||write_dw_lsf(cue.blockstart, f)
+                    ||write_dw_lsf(cue.sampleoffset, f) )
+                goto ioerror;
+        }
+        /*... add a LIST chunk of type 'adtl'... */
+        if(write_dw_msf(TAG('L','I','S','T'), f)
+                    ||write_dw_lsf(sizeof(DWORD) + 3*sizeof(DWORD) + PROPCNKSIZE, f)
+                    ||write_dw_msf(TAG('a','d','t','l'), f) )
+            goto ioerror;
+
+        /*      add the property-space note chunk  */
+        if(write_dw_msf(TAG('n','o','t','e'), f)
+                    ||write_dw_lsf(sizeof(DWORD) + PROPCNKSIZE, f)
+                    ||write_dw_msf(TAG('s','f','i','f'), f)
+#if defined _WIN32
+                    ||((f->propoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
+                    ||SetFilePointer(f->fileno, (long)PROPCNKSIZE,NULL, FILE_CURRENT) == 0xFFFFFFFF )
+            goto ioerror;
+#else
+                    ||fgetpos(f->fileno, &bytepos)
+                    ||fseek(f->fileno, (long)PROPCNKSIZE, SEEK_CUR) < 0)
+            goto ioerror;
+        f->propoffset = bytepos;
+#endif
+
+        f->propschanged = 1;
+        f->proplim = PROPCNKSIZE;
+    }
+        /*
+         *      and add the data chunk
+         */
+    f->datachunksize = 0;
+
+    if(write_dw_msf(TAG('d','a','t','a'), f)
+          ||write_dw_lsf(0, f)
+#if defined _WIN32
+          ||((f->datachunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF))
+        goto ioerror;
+#else
+          ||fgetpos(f->fileno, &bytepos))
+        goto ioerror;
+    f->datachunkoffset = bytepos;
+#endif
+
+    f->header_set = 1;                      //later, may allow update prior to first write ?
+    return 0;
+    /* NOTREACHED */
+
+ioerror:
+    rsferrno = ESFWRERR;
+    rsferrstr = "write error writing formatted wav header";
+    return 1;
+}
+
+
+//wave-ex special
+static int
+wrwavex(struct sf_file *f, SFPROPS *props)
+{
+    struct cuepoint cue;
+    //int extra = 0;
+    int wordsize;
+    WORD validbits,cbSize = 22;
+    GUID guid;
+    GUID *pGuid = NULL;
+    //int fmtsize = sizeof_WFMTEX;
+    fpos_t bytepos;
+    POS64(bytepos) = 0;
+    
+    f->mainchunksize = 0;   /* we don't know the full size yet! */
+    f->extrachunksizes = 0;
+    POS64(f->propoffset) = 0;
+    if(props->chformat==STDWAVE){
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "std wave format requested for WAVE-EX file!";
+        return 1;
+    }
+    
+    if(props->samptype == FLOAT32){
+        pGuid = (GUID *)  &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+        
+    }
+    else{
+        pGuid =(GUID *) &KSDATAFORMAT_SUBTYPE_PCM;
+    }
+    //NB AIFF and AIFF-C don't support masked formats - just write the required larger container size
+    switch(props->samptype){
+        case(INT_32):
+        case(FLOAT32):
+        case(INT2432):
+            wordsize = sizeof(/*long*/int);
+            break;
+        case(INT2424):
+        case(INT2024):
+            wordsize = 3;
+            break;
+        case(SHORT16):
+            wordsize= sizeof(short);
+            break;
+        default:
+            //don't accept SHORT8, can't deal with INT_MASKED yet - no info in SFPROPS yet!
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "unsupported sample format requested for WAVE-EX file";
+            return 1;
+    }
+    if(props->samptype==INT2432)
+        validbits = (WORD)24;
+    else if(props->samptype==INT2024)
+        validbits = (WORD)20;
+    else
+        validbits = (WORD)( 8 * wordsize);              //deal with more masks in due course...
+    
+    if(write_dw_msf(TAG('R','I','F','F'), f)
+       ||write_dw_lsf(0, f)
+       ||write_dw_msf(TAG('W','A','V','E'), f))
+        goto ioerror;
+    /* MC_STD,MC_GENERIC,MC_LCRS,MC_BFMT,MC_DOLBY_5_1,
+     MC_SURR_5_0,MC_SURR_7_1,MC_CUBE,MC_WAVE_EX
+     */
+    
+    f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    f->fmtchunkEx.Format.nChannels = (unsigned short) props->chans;
+    f->fmtchunkEx.Format.nSamplesPerSec = props->srate;
+    f->fmtchunkEx.Format.nAvgBytesPerSec = wordsize * props->chans * props->srate;
+    f->fmtchunkEx.Format.nBlockAlign = (unsigned short) ( wordsize * props->chans);
+    f->fmtchunkEx.Format.wBitsPerSample = (WORD)(wordsize * 8);
+    f->fmtchunkEx.Samples.wValidBitsPerSample = validbits;
+    /*RWD Jan 30 2007: permit mask bits < nChans */
+    switch(props->chformat){
+        case(MC_STD):
+            f->fmtchunkEx.dwChannelMask = SPKRS_UNASSIGNED;
+            break;
+        case(MC_MONO):
+            if(props->chans /*!=*/ < 1){     /*RWD Jan 30 2007 */
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            f->fmtchunkEx.dwChannelMask = SPKRS_MONO;
+            break;
+        case(MC_STEREO):
+            if(props->chans /*!=*/ < 2){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            f->fmtchunkEx.dwChannelMask = SPKRS_STEREO;
+            break;
+        case(MC_QUAD):
+            if(props->chans /*!=*/ < 4){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_GENERIC_QUAD;
+            break;
+        case(MC_BFMT):
+            // Nov 2005: now supporting many channel counts for B-Format!
+#ifdef NOTDEF
+            if(props->chans != 4){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+#endif
+            f->fmtchunkEx.dwChannelMask = SPKRS_UNASSIGNED;
+            pGuid = props->samptype==FLOAT32 ?(GUID *) &SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT :(GUID *) &SUBTYPE_AMBISONIC_B_FORMAT_PCM;
+            break;
+        case(MC_LCRS):
+            if(props->chans /*!=*/< 4){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_SURROUND_LCRS;
+            break;
+        case(MC_DOLBY_5_1):
+            if(props->chans /*!=*/ < 6){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_DOLBY5_1;
+            break;
+            /*March 2008 */
+        case(MC_SURR_5_0):
+            if(props->chans /*!=*/ < 5){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_SURR_5_0;
+            break;
+            /* Nov 2013 */
+            
+        case(MC_SURR_6_1):
+            if(props->chans < 7){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            f->fmtchunkEx.dwChannelMask = SPKRS_SURR_6_1;
+            break;
+            /* OCT 2009 */
+        case(MC_SURR_7_1):
+            if(props->chans /*!=*/ < 8){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_SURR_7_1;
+            break;
+        case(MC_CUBE):
+            if(props->chans /*!=*/ < 8){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "conflicting channel configuration for WAVE-EX file";
+                return 1;
+            }
+            
+            f->fmtchunkEx.dwChannelMask = SPKRS_CUBE;
+            break;
+        default:
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "unsupported channel configuration for WAVE-EX file";
+            return 1;
+            break;
+            
+            
+    }
+    
+    
+    if(write_dw_msf(TAG('f','m','t',' '), f)
+       ||write_dw_lsf(sizeof_WFMTEX, f)                               //RWD CDP97: size = 18 to include cbSize field
+#if defined _WIN32
+       ||((f->fmtchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
+       ||write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+       ||write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+       ||write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+       ||write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+       ||write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+       ||write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)
+       ||write_w_lsf(cbSize,f)
+       )
+        goto ioerror;
+#else
+    ||fgetpos(f->fileno, &bytepos)
+    ||write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+    ||write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+    ||write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+    ||write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+    ||write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+    ||write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)
+    ||write_w_lsf(cbSize,f)
+    )
+    goto ioerror;
+    f->fmtchunkoffset = bytepos;
+    
+#endif
+    //don't need fact chunk unless actually a compressed format...
+    
+    guid = *pGuid;
+#ifdef MSBFIRST
+    guid.Data1 = REVDWBYTES(guid.Data1);
+    guid.Data2 = REVWBYTES(guid.Data2);
+    guid.Data3 = REVWBYTES(guid.Data3);
+#endif
+    if(write_w_lsf(f->fmtchunkEx.Samples.wValidBitsPerSample,f)
+       ||      write_dw_lsf(f->fmtchunkEx.dwChannelMask,f)
+       || dowrite(f,(char *) &guid,sizeof(GUID))                 /* RWD NB deal with byte-reversal sometime! */
+       )
+        goto ioerror;
+    
+    // ADD the PEAK chunk
+    if((f->min_header>=SFILE_PEAKONLY) && f->peaks){
+        int i,size;
+        DWORD now = 0;
+        for(i=0;i < props->chans; i++){
+            f->peaks[i].value = 0.0f;
+            f->peaks[i].position = 0;
+        }
+        size = 2 * sizeof(DWORD) + props->chans * sizeof(CHPEAK);
+        if(write_dw_msf(TAG('P','E','A','K'),f)
+           || write_dw_lsf(size,f)
+#if defined _WIN32
+           || ((f->peakchunkoffset = SetFilePointer(f->fileno,0L,NULL, FILE_CURRENT))== 0xFFFFFFFF)
+           || write_dw_lsf(CURRENT_PEAK_VERSION,f)
+           || write_dw_lsf(now,f)
+           || write_peak_lsf(props->chans,f))
+            
+            goto ioerror;
+#else
+        || fgetpos(f->fileno, &bytepos)
+        || write_dw_lsf(CURRENT_PEAK_VERSION,f)
+        || write_dw_lsf(now,f)
+        || write_peak_lsf(props->chans,f))
+        
+        goto ioerror;
+        f->peakchunkoffset = bytepos;
+#endif
+    }
+    
+    
+    if(f->min_header>=SFILE_CDP){
+        /*
+         *      add the cue point/note chunk for properties
+         */
+        //RWD TODO: add switch to skip writing this extra stuff!
+        if(write_dw_msf(TAG('c','u','e',' '), f)
+           ||write_dw_lsf(sizeof(struct cuepoint) + sizeof(DWORD), f) )
+            goto ioerror;
+        
+        cue.name = TAG('s','f','i','f');
+        cue.position = 0;
+        cue.incchunkid = TAG('d','a','t','a');
+        cue.chunkoffset = 0;
+        cue.blockstart = 0;
+        cue.sampleoffset = 0;
+        if(write_dw_lsf(1, f)   /* one cue point */
+           ||write_dw_msf(cue.name, f)
+           ||write_dw_lsf(cue.position, f)
+           ||write_dw_msf(cue.incchunkid, f)
+           ||write_dw_lsf(cue.chunkoffset, f)
+           ||write_dw_lsf(cue.blockstart, f)
+           ||write_dw_lsf(cue.sampleoffset, f) )
+            goto ioerror;
+        
+        /*... add a LIST chunk of type 'adtl'... */
+        
+        if(write_dw_msf(TAG('L','I','S','T'), f)
+           ||write_dw_lsf(sizeof(DWORD) + 3*sizeof(DWORD) + PROPCNKSIZE, f)
+           ||write_dw_msf(TAG('a','d','t','l'), f) )
+            goto ioerror;
+        
+        /*      add the property-space note chunk  */
+        if(write_dw_msf(TAG('n','o','t','e'), f)
+           ||write_dw_lsf(sizeof(DWORD) + PROPCNKSIZE, f)
+           ||write_dw_msf(TAG('s','f','i','f'), f)
+#if defined _WIN32
+           ||((f->propoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF)
+           ||SetFilePointer(f->fileno, (long)PROPCNKSIZE,NULL, FILE_CURRENT) == 0xFFFFFFFF )
+            goto ioerror;
+#else
+        ||fgetpos(f->fileno, &bytepos)
+        ||fseek(f->fileno, (long)PROPCNKSIZE, SEEK_CUR) < 0)
+        goto ioerror;
+        f->propoffset = bytepos;
+        
+#endif
+        f->propschanged = 1;
+        f->proplim = PROPCNKSIZE;
+    }
+    
+    /*
+     *      and add the data chunk
+     */
+    f->datachunksize = 0;
+    
+    if(write_dw_msf(TAG('d','a','t','a'), f)
+       ||write_dw_lsf(0, f)
+#if defined _WIN32
+       ||((f->datachunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF))
+        goto ioerror;
+#else
+    ||fgetpos(f->fileno, &bytepos))
+    
+    goto ioerror;
+    f->datachunkoffset = bytepos;
+#endif
+    f->header_set = 1;                      //later, may allow update prior to first write ?
+    return 0;
+    /* NOTREACHED */
+    
+ioerror:
+    rsferrno = ESFWRERR;
+    rsferrstr = "error writing wav_ex header";
+    return 1;
+}
+
+
+
+
+
+/*
+ *      aiff routines
+ */
+
+//RWD98 BUG, SOMEWHERE: THIS READS THE SSND CHUNK, AND GETS SIZE = REMAIN; BUT REMAIN SHOULD BE SIZE + 8
+// SO, EITHER BUG IN THIS CODE, OR IN WRAIFFHDR... SO CANNOT USE WINDOWS DWORD 'COS UNSIGNED....
+//RWD.6.99 NOTE: some tests require the Fomat field to be set, even though this is aiff
+//RWD.7.99: accept masked AIFF formats: container size is always next intergral number of bytes
+static int
+rdaiffhdr(struct sf_file *f)
+{
+        DWORD /*long */ tag, size = 0, remain = 0, appltag; //RWD.04.98 can't be unsigned until the size anomalies are resolved
+        DWORD peaktime; /* RWD Jan 2005 */
+    int commseen = 0, ssndseen = 0;
+        DWORD numsampleframes;
+        DWORD ssnd_offset, ssnd_blocksize;
+        fpos_t bytepos;
+        POS64(bytepos) = 0;
+        char *propspace;
+        DWORD peak_version;
+        // WARNING! The file can have the wrong size (e.g. from sox)
+        if(read_dw_msf(&tag, f)
+         ||read_dw_msf(&remain, f)
+         ||tag != TAG('F','O','R','M')) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File is not an AIFF file";
+                return 1;
+        }
+        if(remain < 3*sizeof(DWORD)) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File data size is too small";
+                return 1;
+        }
+        if(read_dw_msf(&tag, f)
+         ||tag != TAG('A','I','F','F')) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File does not include an AIFF form";
+                return 1;
+        }
+        f->mainchunksize = remain;
+        f->extrachunksizes = 0;
+        POS64(f->propoffset) = -1;
+        f->aiffchunks = 0;
+        remain -= sizeof(DWORD);
+
+        while(remain > 0) {
+                if(read_dw_msf(&tag, f)
+                        ||read_dw_msf(&size,f)){
+                        if(ssndseen && commseen){               //RWD accept the file anyway if we have enough
+                                remain = 0;
+                                break;
+                        }
+                        else
+                                goto ioerror;
+                }
+                remain -= 2*sizeof(DWORD);
+                switch(tag) {
+                case TAG('C','O','M','M'):
+                        if(size != 18) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "AIFF COMM chunk of incorrect size";
+                                return 1;
+                        }
+#if defined _WIN32
+                        if((f->fmtchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF
+              ||read_w_msf(&f->fmtchunkEx.Format.nChannels, f)
+              ||read_dw_msf(&numsampleframes, f)
+              ||read_w_msf(&f->fmtchunkEx.Format.wBitsPerSample, f)
+              ||read_ex_todw(&f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                                goto ioerror;
+#else
+                        if(fgetpos(f->fileno, &bytepos)
+                          ||read_w_msf(&f->fmtchunkEx.Format.nChannels, f)
+                          ||read_dw_msf(&numsampleframes, f)
+                          ||read_w_msf(&f->fmtchunkEx.Format.wBitsPerSample, f)
+                          ||read_ex_todw(&f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                                goto ioerror;
+            f->fmtchunkoffset = bytepos;
+#endif
+            /*RWD Trevor uses srate of zero for envel files! */
+            /* nSamples... is unsigned anyway, so dont bother with this one any more... */
+#ifdef NOTDEF
+                        if(f->fmtchunkEx.Format.nSamplesPerSec < 0) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Unknown AIFF sample rate";
+                                return 1;
+                        }
+#endif
+                        /*RWD.7.99 we now read 32bit in standard AIFF as LONGS
+                        * we rely on the extra properties to tell if it's an analysis file */
+                        f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_PCM;
+                        //fill in other info
+                        f->fmtchunkEx.Samples.wValidBitsPerSample = f->fmtchunkEx.Format.wBitsPerSample;
+                        //we have to deduce blockalign, and hence containersize
+                        switch(f->fmtchunkEx.Samples.wValidBitsPerSample){
+                        case(32):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(/*long*/int);
+                                break;
+                        case(20):
+                        case(24):
+                                f->fmtchunkEx.Format.nBlockAlign = 3;
+                                break;
+                        case(16):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(short);
+                                break;
+                        case(8):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(char);
+                                break;
+                        default:
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "unsupported sample size in aiff file";
+                                return 1;
+                        }
+                        f->fmtchunkEx.Format.nBlockAlign *= f->fmtchunkEx.Format.nChannels;
+                        //should do avgBytesPerSec too...
+                        f->fmtchunkEx.dwChannelMask = 0;
+                        remain -= 18;
+                        commseen++;
+                        break;
+
+                        //RWD.5.99 read PEAK chunk
+                case TAG('P','E','A','K'):
+                        f->peaks = (CHPEAK *) calloc(f->fmtchunkEx.Format.nChannels,sizeof(CHPEAK));
+                        if(f->peaks == NULL){
+                                rsferrno = ESFNOMEM;
+                                rsferrstr = "No memory for peak data";
+                                return 1;
+                        }
+                        if(read_dw_msf(&peak_version,f))
+                                goto ioerror;
+                        switch(peak_version){
+                        case(CURRENT_PEAK_VERSION):
+                                if(read_dw_msf(&peaktime,f)) /* RWD Jan 2005 for DevCPP */
+                                        goto ioerror;
+                                f->peaktime = (time_t) peaktime;
+#if defined _WIN32
+                                if((f->peakchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF
+                   || read_peak_msf(f->fmtchunkEx.Format.nChannels,f))
+                                        goto ioerror;
+#else
+                                if(fgetpos(f->fileno, &bytepos)
+                                || read_peak_msf(f->fmtchunkEx.Format.nChannels,f))
+                                        goto ioerror;
+                f->peakchunkoffset = bytepos;
+#endif
+                                break;
+                        default:
+#ifdef _DEBUG
+                                fprintf(stderr,"\nunknown PEAK version!");
+#endif
+                                free(f->peaks);
+                                f->peaks = NULL;
+                                break;
+                        }
+                        remain -= 2 * sizeof(DWORD) + (sizeof(CHPEAK) * f->fmtchunkEx.Format.nChannels);
+                        break;
+
+
+                case TAG('S','S','N','D'):
+                        if(read_dw_msf(&ssnd_offset, f)
+                         ||read_dw_msf(&ssnd_blocksize, f)
+#if defined _WIN32
+                         ||(f->datachunkoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT)) ==0xFFFFFFFF
+                         ||     SetFilePointer(f->fileno, ((size+1)&~1) - 2 *sizeof(DWORD),NULL,FILE_CURRENT) ==0xFFFFFFFF)
+                goto ioerror;
+#else
+             || fgetpos(f->fileno, &bytepos))
+                 goto ioerror;
+            f->datachunkoffset = bytepos;
+            POS64(bytepos) += ((size+1)&~1) - 2 *sizeof(DWORD);
+                        /*fseek(f->fileno, ((size+1)&~1) - 2 *sizeof(DWORD), SEEK_CUR) < 0)     */
+            if(fsetpos(f->fileno,&bytepos))
+                                goto ioerror;
+
+#endif
+            remain -= 2 * sizeof(DWORD); /* RWD Apr 2011 need this */
+            /* RWD MAR 2015 eliminate warning, ssnd_offset is unsigned */
+                        if(/* ssnd_offset < 0 || */ ssnd_offset > ssnd_blocksize) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Funny offset in AIFF SSND chunk";
+                                return 1;
+                        }
+                        POS64(f->datachunkoffset) += ssnd_offset;
+                        ssndseen++;
+                        remain -= (size+1)&~1;          //RWD98 BUG!!! remain can get less than size...
+                        break;
+
+                case 0:
+                        rsferrno = ESFNOTFOUND;
+                        rsferrstr = "Illegal zero tag in aiff chunk";
+                        return 1;
+
+                case TAG('A','P','P','L'):
+                        if(size < sizeof(DWORD)
+                         ||read_dw_msf(&appltag,f) )
+                                goto ioerror;
+                        if(appltag == TAG('s','f','i','f')) {
+                                if(POS64(f->propoffset) >= 0
+                                 ||(propspace = (char *) malloc(size - sizeof(DWORD))) == 0
+#if defined _WIN32
+                                 ||(f->propoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+                 ||doread(f, propspace, size-sizeof(DWORD)) )
+                                        goto ioerror;
+#else
+                                 ||fgetpos(f->fileno, &bytepos)
+                                 ||doread(f, propspace, size-sizeof(DWORD)) )
+                                        goto ioerror;
+                POS64(f->propoffset) = POS64(bytepos);
+#endif
+                                f->proplim = size - sizeof(DWORD);
+                                parseprops(f, propspace);
+                                if(size&1)
+                                        doread(f, propspace, 1);
+                                free(propspace);
+                                break;
+                        } else {
+#if defined _WIN32
+                                if(SetFilePointer(f->fileno,-4L,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+#else
+                                if(fseek(f->fileno, -4L, SEEK_CUR) < 0)
+#endif
+                                        goto ioerror;
+                        }
+                        /* FALLTHROUGH */
+
+                default:
+/* RWD MAR 2015: size is unsigned, eliminate warning! */
+#ifdef NOTDEF
+                        if(size < 0  /* || size > 100*1024 */) {  /* RWD Apr 2011 */
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Silly size for unknown AIFF chunk";
+                                return 1;
+                        }
+#endif
+                        if(ssndseen) {
+                                struct aiffchunk **cpp, *cp;
+
+                                for(cpp = &f->aiffchunks; *cpp != 0; cpp = &(*cpp)->next)
+                                        ;
+                                if((*cpp = cp = ALLOC(struct aiffchunk)) == 0
+                                 ||(cp->buf = (char *) malloc((size+1)&~1)) == 0) {
+                                        rsferrno = ESFNOMEM;
+                                        rsferrstr = "No memory for aiff chunk storage";
+                                        return 1;
+                                }
+                                cp->tag = tag;
+                                cp->size = size;
+                                cp->next = 0;
+#if defined _WIN32
+                                if((cp->offset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT)) == 0xFFFFFFFF
+                   ||doread(f, cp->buf, (size+1)&~1))
+                                        goto ioerror;
+#else
+                                if(fgetpos(f->fileno,&bytepos)
+                                 ||doread(f, cp->buf, (size+1)&~1))
+                                        goto ioerror;
+                cp->offset = bytepos;
+#endif
+                        } else {
+#if defined _WIN32
+                                if(SetFilePointer(f->fileno,(long)((size+1)&~1),NULL,FILE_CURRENT) == 0xFFFFFFFF)
+#else
+                                if(fseeko(f->fileno, (size+1)&~1, SEEK_CUR) < 0)
+#endif
+                                        goto ioerror;
+                        }
+
+                        f->extrachunksizes += ((size+1)&~1) + 2*sizeof(DWORD);
+                        remain -= (size+1)&~1;
+                        break;
+
+                }
+        }
+
+        if(!commseen) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "AIFF format error: no COMM chunk found";
+                return 1;
+        }
+        if(!ssndseen) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "AIFF format error: no SSND chunk found";
+                return 1;
+        }
+
+        f->datachunksize = numsampleframes * f->fmtchunkEx.Format.nChannels;
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+        case 32:                /* floats */
+                f->datachunksize *= 4;
+                break;
+        case 20:
+        case 24:
+                f->datachunksize *= 3;
+                break;
+        case 16:                /* shorts */
+                f->datachunksize *= 2;
+                break;
+        case 8:                 /* byte -> short mappping */
+                f->datachunksize *= 2;  /* looks like short samples! */
+                break;
+        default:
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "can't open aiff file - unsupported wordsize";
+                return 1;
+        }
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,f->datachunkoffset,NULL,FILE_BEGIN)==0xFFFFFFFF)
+#else
+    bytepos = f->datachunkoffset;
+        if(fsetpos(f->fileno, &bytepos) < 0)
+#endif
+                goto ioerror;
+        return 0;
+        /* NOTREACHED */
+
+ioerror:
+        rsferrno = ESFRDERR;
+        rsferrstr = "read error (or file too short) reading AIFF header";
+        return 1;
+}
+
+
+/*AIF-C*/
+
+#define AIFC_VERSION_1 (0xA2805140)
+static int
+rdaifchdr(struct sf_file *f)
+{
+        DWORD tag, size = 0, remain = 0, appltag;
+        int commseen = 0, ssndseen = 0,fmtverseen = 0;
+        DWORD numsampleframes, aifcver,ID_compression;
+        DWORD ssnd_offset, ssnd_blocksize;
+        char *propspace;
+        DWORD peak_version;
+    DWORD peaktime;
+    fpos_t bytepos;
+
+        if(read_dw_msf(&tag, f)
+         ||read_dw_msf(&remain, f)
+         ||tag != TAG('F','O','R','M')) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File is not an AIFF file";
+                return 1;
+        }
+        if(remain < 3*sizeof(DWORD)) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File data size is too small";
+                return 1;
+        }
+        if(read_dw_msf(&tag, f)
+         ||tag != TAG('A','I','F','C')) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "File does not include an AIFC form";
+                return 1;
+        }
+        f->mainchunksize = remain;
+        f->extrachunksizes = 0;
+        POS64(f->propoffset) = -1;
+        f->aiffchunks = 0;
+
+        //start by assuming integer format:
+        f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_PCM;
+
+
+        remain -= sizeof(DWORD);
+
+        while(remain > 0) {
+                if(read_dw_msf(&tag, f)
+                 ||read_dw_msf(&size,f)){
+                        if(ssndseen && commseen){               //RWD accept the file anyway if we have enough
+                                remain = 0;
+                                break;
+                        }
+                        else
+                                goto ioerror;
+                }
+                remain -= 2*sizeof(DWORD);
+                switch(tag) {
+                case TAG('F','V','E','R'):
+                        if(size != 4){
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "bad aif-c FVER chunk";
+                                return 1;
+                        }
+
+                        if(read_dw_msf(&aifcver,f) || aifcver != AIFC_VERSION_1){
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "bad aif-c Version";
+                                return 1;
+                        }
+                        remain -= sizeof(DWORD);
+                        fmtverseen++;
+                        break;
+                case TAG('C','O','M','M'):
+                        if(size < 22) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "AIFC COMM chunk of incorrect size";
+                                return 1;
+                        }
+#if defined _WIN32
+                        if((f->fmtchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF
+               ||read_w_msf(&f->fmtchunkEx.Format.nChannels, f)
+               ||read_dw_msf(&numsampleframes, f)
+               ||read_w_msf(&f->fmtchunkEx.Format.wBitsPerSample, f)
+               ||read_ex_todw(&f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                                goto ioerror;
+#else
+                        if(fgetpos(f->fileno, &bytepos)
+                         ||read_w_msf(&f->fmtchunkEx.Format.nChannels, f)
+                         ||read_dw_msf(&numsampleframes, f)
+                         ||read_w_msf(&f->fmtchunkEx.Format.wBitsPerSample, f)
+                         ||read_ex_todw(&f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                                goto ioerror;
+            f->fmtchunkoffset = bytepos;
+#endif
+            /*RWD: Trevor uses srate of zero for envel files! */
+/* RWD MAR 2015: so eliminate code to avoid warning, as above */
+#ifdef NOTDEF
+                        if(f->fmtchunkEx.Format.nSamplesPerSec < 0) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Unknown AIFC sample rate";
+                                return 1;
+                        }
+#endif
+                        if(read_dw_msf(&ID_compression,f))
+                                goto ioerror;
+                        if( !(
+                                (ID_compression == TAG('N','O','N','E'))
+                                  || (ID_compression == TAG('F','L','3','2'))   //Csound
+                                  || (ID_compression == TAG('f','l','3','2'))   //Apple
+/*  used in Steinberg 24bit SDIR files */
+                  || (ID_compression == TAG('i','n','2','4'))
+                                                  )){
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Unknown AIFC compression type";
+                                return 1;
+                        }
+                        //set sample type in sfinfo
+                        if((ID_compression== TAG('F','L','3','2'))
+                                || ID_compression == TAG('f','l','3','2')){
+
+/*Nov 2001: F***** Quicktime writes size = 16, for floats! */
+                                        if(f->fmtchunkEx.Format.wBitsPerSample != 32){
+                                                if(f->fmtchunkEx.Format.wBitsPerSample != 16){
+                                                        rsferrno = ESFNOTFOUND;
+                                                        rsferrstr = "error in AIFC header: samples not 32bit in floats file ";
+                                                        return 1;
+                                                }
+                                                else
+                                                        f->fmtchunkEx.Format.wBitsPerSample = 32;
+                                        }
+                                        f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+                        }
+
+
+            /* RWD 06/01/09  precautionary, to validate 'in24'  24bit files  */
+            if(ID_compression == TAG('i','n','2','4')) {
+                                if(f->fmtchunkEx.Format.wBitsPerSample != 24){
+                                        rsferrstr = "error in AIFC header: sample size not 24bit in <in24> file ";
+                                        return 1;
+                                }
+                        }
+
+
+                        //no ambiguity here with 32bit format.
+
+                        //fill in other info
+                        f->fmtchunkEx.Samples.wValidBitsPerSample = f->fmtchunkEx.Format.wBitsPerSample;
+                        f->fmtchunkEx.dwChannelMask = 0;
+                        //we have to deduce blockalign, and hence containersize
+                        switch(f->fmtchunkEx.Samples.wValidBitsPerSample){
+                        case(32):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(/*long*/int);
+                                break;
+                        case(20):
+                        case(24):
+                                f->fmtchunkEx.Format.nBlockAlign = 3;
+                                break;
+                        case(16):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(short);
+                                break;
+                        case(8):
+                                f->fmtchunkEx.Format.nBlockAlign = sizeof(char);
+                                break;
+                        default:
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "unsupported sample size in aiff file";
+                                return 1;
+                        }
+                        f->fmtchunkEx.Format.nBlockAlign *= f->fmtchunkEx.Format.nChannels;
+                        //should do avgBytesPerSec too...
+
+                        /*RWD 23:10:2000 can get odd sizes... */
+
+                        //skip past pascal string
+#if defined _WIN32
+                        if(SetFilePointer(f->fileno, ((size+1)&~1) - 22,NULL,FILE_CURRENT) ==0xFFFFFFFF)
+#else
+                        if(fseek(f->fileno,((size+1)&~1)        - 22,SEEK_CUR) < 0)
+#endif
+                                goto ioerror;
+                        remain -= (size+1)&~1;
+                        commseen++;
+                        break;
+
+                        //RWD.5.99 read PEAK chunk
+                case TAG('P','E','A','K'):
+                        f->peaks = (CHPEAK *) calloc(f->fmtchunkEx.Format.nChannels,sizeof(CHPEAK));
+                        if(f->peaks == NULL){
+                                rsferrno = ESFNOMEM;
+                                rsferrstr = "No memory for peak data";
+                                return 1;
+                        }
+                        if(read_dw_msf(&peak_version,f))
+                                goto ioerror;
+                        switch(peak_version){
+                        case(CURRENT_PEAK_VERSION):
+                                if(read_dw_msf(&peaktime,f))
+                                        goto ioerror;
+                                f->peaktime = (time_t) peaktime;
+#if defined _WIN32
+                                if((f->peakchunkoffset = SetFilePointer(f->fileno, 0L, NULL, FILE_CURRENT)) == 0xFFFFFFFF
+                  || read_peak_msf(f->fmtchunkEx.Format.nChannels,f))
+                                        goto ioerror;
+#else
+                                if(fgetpos(f->fileno, &bytepos)
+                                  || read_peak_msf(f->fmtchunkEx.Format.nChannels,f))
+                                        goto ioerror;
+                f->peakchunkoffset = bytepos;
+#endif
+                                break;
+                        default:
+#ifdef _DEBUG
+                                fprintf(stderr,"\nunknown PEAK version!");
+#endif
+                                free(f->peaks);
+                                f->peaks = NULL;
+                                break;
+                        }
+                        remain -= 2 * sizeof(DWORD) + (sizeof(CHPEAK) * f->fmtchunkEx.Format.nChannels);
+                        break;
+
+                case TAG('S','S','N','D'):
+                        if(read_dw_msf(&ssnd_offset, f)
+                         ||read_dw_msf(&ssnd_blocksize, f)
+#if defined _WIN32
+                         ||(f->datachunkoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT)) ==0xFFFFFFFF
+                         ||     SetFilePointer(f->fileno, ((size+1)&~1) - 2 *sizeof(DWORD),NULL,FILE_CURRENT) ==0xFFFFFFFF)
+               goto ioerror;
+            if(ssnd_offset < 0 || ssnd_offset > ssnd_blocksize) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "Funny offset in AIFC SSND chunk";
+                return 1;
+            }
+            f->datachunkoffset += ssnd_offset;
+#else
+                         ||fgetpos(f->fileno, &bytepos) )
+                goto ioerror;
+            f->datachunkoffset = bytepos;
+            POS64(bytepos) = ((size+1)&~1) - 2 *sizeof(DWORD);
+            if(fseeko(f->fileno,POS64(bytepos), SEEK_CUR) < 0)
+                                goto ioerror;
+                        /* RWD MAR 2015 ssnd_offset unsigned, eliminate compiler warning */
+                        if(/* ssnd_offset < 0 || */ ssnd_offset > ssnd_blocksize) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Funny offset in AIFC SSND chunk";
+                                return 1;
+                        }
+                        POS64(f->datachunkoffset) += ssnd_offset;
+#endif
+                        ssndseen++;
+                        remain -= (size+1)&~1;          //RWD98 BUG!!! remain can get less than size...
+                        break;
+
+                case 0:
+                        rsferrno = ESFNOTFOUND;
+                        rsferrstr = "Illegal zero tag in aifc chunk";
+                        return 1;
+
+                case TAG('A','P','P','L'):
+                        if(size < sizeof(DWORD)
+                         ||read_dw_msf(&appltag,f) )
+                                goto ioerror;
+                        if(appltag == TAG('s','f','i','f')) {
+                                if(POS64(f->propoffset) >= 0
+                                 ||(propspace = (char *) malloc(size - sizeof(DWORD))) == 0
+#if defined _WIN32
+                                 ||(f->propoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+                 ||doread(f, propspace, size-sizeof(DWORD)) )
+                                        goto ioerror;
+#else
+                                 ||fgetpos(f->fileno, &bytepos)
+                                 ||doread(f, propspace, size-sizeof(DWORD)) )
+                                        goto ioerror;
+                f->propoffset = bytepos;
+#endif
+                                f->proplim = size - sizeof(DWORD);
+                                parseprops(f, propspace);
+                                if(size&1)
+                                        doread(f, propspace, 1);
+                                free(propspace);
+                                break;
+                        } else {
+#if defined _WIN32
+                                if(SetFilePointer(f->fileno,-4L,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+#else
+                                if(fseek(f->fileno, -4L, SEEK_CUR) < 0)
+#endif
+                                        goto ioerror;
+                        }
+                        /* FALLTHROUGH */
+
+                default:
+            /* RWD MAR 2015 eliminate compiler warning; bit of an arbitrary exclusion anyway? */
+                        if(/* size < 0 || */ size > 100*1024) {
+                                rsferrno = ESFNOTFOUND;
+                                rsferrstr = "Silly size for unknown AIFC chunk";
+                                return 1;
+                        }
+
+                        if(ssndseen) {
+                                struct aiffchunk **cpp, *cp;
+
+                                for(cpp = &f->aiffchunks; *cpp != 0; cpp = &(*cpp)->next)
+                                        ;
+                                if((*cpp = cp = ALLOC(struct aiffchunk)) == 0
+                                 ||(cp->buf = (char *) malloc((size+1)&~1)) == 0) {
+                                        rsferrno = ESFNOMEM;
+                                        rsferrstr = "No memory for aifc chunk storage";
+                                        return 1;
+                                }
+                                cp->tag = tag;
+                                cp->size = size;
+                                cp->next = 0;
+#if defined _WIN32
+                                if((cp->offset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT)) == 0xFFFFFFFF
+                  ||doread(f, cp->buf, (size+1)&~1))
+                                        goto ioerror;
+#else
+                                if(fgetpos(f->fileno, &bytepos)
+                                  ||doread(f, cp->buf, (size+1)&~1))
+                                        goto ioerror;
+                cp->offset = bytepos;
+#endif
+                        } else {
+#if defined _WIN32
+                                if(SetFilePointer(f->fileno,(long)((size+1)&~1),NULL,FILE_CURRENT) == 0xFFFFFFFF)
+                    goto ioerror;
+#else
+                                if(fgetpos(f->fileno, &bytepos))
+                    goto ioerror;
+                POS64(bytepos) += (size+1)&~1;
+                if(fsetpos(f->fileno, &bytepos))
+                                        goto ioerror;
+#endif
+                        }
+
+                        f->extrachunksizes += ((size+1)&~1) + 2*sizeof(DWORD);
+                        remain -= (size+1)&~1;
+                        break;
+
+                }
+        }
+
+        if(!commseen) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "AIFC format error: no COMM chunk found";
+                return 1;
+        }
+        if(!ssndseen) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "AIFC format error: no SSND chunk found";
+                return 1;
+        }
+        if(!fmtverseen) {
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "AIFC format error: no FVER chunk found";
+                return 1;
+        }
+
+        f->datachunksize = numsampleframes * f->fmtchunkEx.Format.nChannels;
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+        case 32:                /* floats */
+                f->datachunksize *= 4;
+                break;
+        case 16:                /* shorts */
+                f->datachunksize *= 2;
+                break;
+        case 20:
+        case 24:
+                f->datachunksize *= 3;
+                break;
+        case 8:                 /* byte -> short mappping */
+                f->datachunksize *= 2;  /* looks like short samples! */
+                break;
+        default:
+                rsferrno = ESFNOTFOUND;
+                rsferrstr = "can't open Sfile - unsupported wordsize";
+                return 1;
+        }
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,f->datachunkoffset,NULL,FILE_BEGIN)==0xFFFFFFFF)
+#else
+    POS64(bytepos) = POS64(f->datachunkoffset);
+        if(fsetpos(f->fileno, &bytepos) < 0)
+#endif
+                goto ioerror;
+        return 0;
+        /* NOTREACHED */
+
+ioerror:
+        rsferrno = ESFRDERR;
+        rsferrstr = "read error (or file too short) reading AIFC header";
+        return 1;
+}
+
+
+#if 0
+static int
+wraiffhdr(struct sf_file *f)
+{
+    fpos_t bytepos;
+
+        f->mainchunksize = 0;
+        if(write_dw_msf(TAG('F','O','R','M'), f)
+         ||write_dw_msf(0, f)
+         ||write_dw_msf(TAG('A','I','F','F'), f) )
+                goto ioerror;
+
+        f->fmtchunkEx.Format.nChannels = 1;
+        f->fmtchunkEx.Format.nSamplesPerSec = 44100;
+        f->fmtchunkEx.Format.wBitsPerSample = 16;
+        f->aiffchunks = 0;
+
+        if(write_dw_msf(TAG('C','O','M','M'), f)
+         || write_dw_msf(18, f)
+#if  defined _WIN32
+         || (f->fmtchunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+     || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+     || write_dw_msf(0, f)                      /* num sample frames */
+     || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+     || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+        goto ioerror;
+#else
+         ||fgetpos(f->fileno, &bytepos)
+         || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+         || write_dw_msf(0, f)                  /* num sample frames */
+         || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+         || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                goto ioerror;
+    f->fmtchunkoffset = bytepos;
+#endif
+        if(write_dw_msf(TAG('A','P','P','L'), f)
+         || write_dw_msf(sizeof(DWORD) + PROPCNKSIZE, f)
+         || write_dw_msf(TAG('s','f','i','f'), f)
+#if  defined _WIN32
+         || (f->propoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+         || SetFilePointer(f->fileno,(long)PROPCNKSIZE,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+        goto ioerror;
+#else
+         || fgetpos(f->fileno, &bytepos)
+         || fseek(f->fileno, (long)PROPCNKSIZE, SEEK_CUR) < 0)
+                goto ioerror;
+    f->propoffset = bytepos;
+#endif
+        if(write_dw_msf(TAG('S','S','N','D'), f)
+         || write_dw_msf(0, f)
+         || write_dw_msf(0, f)          /* offset */
+         || write_dw_msf(0, f)          /* blocksize */
+#if defined _WIN32
+         || (f->datachunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT)) == 0xFFFFFFFF)
+        goto ioerror;
+#else
+         || fgetpos(f->fileno, &bytepos) )
+                goto ioerror;
+    f->datachunkoffset = bytepos;
+#endif
+        f->propschanged = 1;
+        f->proplim = PROPCNKSIZE;
+        f->datachunksize = 0;
+        f->extrachunksizes = 0;
+
+        return 0;
+        /* NOTREACHED */
+ioerror:
+        rsferrno = ESFWRERR;
+        rsferrstr = "write error writing aiff header";
+        return 1;
+}
+#endif
+
+/*********** sfsys98 extension **********/
+
+static int
+wraiffhdr98(struct sf_file *f,int channels,int srate,int stype)
+{
+    fpos_t bytepos;
+
+        if(stype >= SAMP_MASKED)
+                return 1;
+        rsferrstr = NULL;
+        f->mainchunksize = 0;
+        if(write_dw_msf(TAG('F','O','R','M'), f)
+         || write_dw_msf(0, f)
+         || write_dw_msf(TAG('A','I','F','F'), f) )
+                goto ioerror;
+
+        f->fmtchunkEx.Format.nChannels = (short)channels;
+        f->fmtchunkEx.Format.nSamplesPerSec = srate;
+        switch(stype){
+        case(SAMP_SHORT):
+                f->fmtchunkEx.Format.wBitsPerSample = 16;
+                f->fmtchunkEx.Format.nBlockAlign = sizeof(short) * f->fmtchunkEx.Format.nChannels;
+                break;
+        case(SAMP_FLOAT):         //need to keep this for now...
+        case(SAMP_LONG):
+                f->fmtchunkEx.Format.wBitsPerSample = 32;
+                f->fmtchunkEx.Format.nBlockAlign = sizeof(/*long*/int) * f->fmtchunkEx.Format.nChannels;
+                break;
+        case(SAMP_2024):
+                f->fmtchunkEx.Format.wBitsPerSample = 20;
+                f->fmtchunkEx.Format.nBlockAlign = 3 * f->fmtchunkEx.Format.nChannels;
+                break;
+        case(SAMP_2424):
+                f->fmtchunkEx.Format.wBitsPerSample = 24;
+                f->fmtchunkEx.Format.nBlockAlign = 3 * f->fmtchunkEx.Format.nChannels;
+                break;
+        //NB 24/32 not allowed in AIFF
+        //case SAMP_MASKED: supported by AIFF, inside nearest integral byte-size
+        default:
+                rsferrstr = "sample format not supported in AIFF files";
+                goto ioerror;   //something we don't know about!
+
+
+        }
+        f->aiffchunks = 0;
+
+        if(write_dw_msf(TAG('C','O','M','M'), f)
+         || write_dw_msf(18, f)
+#if  defined _WIN32
+         || (f->fmtchunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+     || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+     || write_dw_msf(0, f)                      /* num sample frames */
+     || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+     || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+       goto ioerror;
+#else
+         || fgetpos(f->fileno, &bytepos)
+         || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+         || write_dw_msf(0, f)                  /* num sample frames */
+         || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+         || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                goto ioerror;
+    f->fmtchunkoffset = bytepos;
+#endif
+        //RWD.6.5.99 ADD the PEAK chunk
+        if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+                int i,size;
+                DWORD now = 0;
+                for(i=0;i < channels; i++){
+                        f->peaks[i].value = 0.0f;
+                        f->peaks[i].position = 0;
+                }
+                size = 2 * sizeof(DWORD) + channels * sizeof(CHPEAK);
+                if(write_dw_msf(TAG('P','E','A','K'),f)
+          || write_dw_msf(size,f)
+#if  defined _WIN32
+                  || ((f->peakchunkoffset = SetFilePointer(f->fileno,0L,NULL, FILE_CURRENT))== 0xFFFFFFFF)
+          || write_dw_msf(CURRENT_PEAK_VERSION,f)
+          || write_dw_msf(now,f)
+          || write_peak_msf(channels,f))
+                        goto ioerror;
+#else
+          || fgetpos(f->fileno, &bytepos)
+                  || write_dw_msf(CURRENT_PEAK_VERSION,f)
+                  || write_dw_msf(now,f)
+                  || write_peak_msf(channels,f))
+                        goto ioerror;
+        f->peakchunkoffset = bytepos;
+#endif
+        }
+
+        if(f->min_header >= SFILE_CDP){
+
+                if(write_dw_msf(TAG('A','P','P','L'), f)
+                  || write_dw_msf(sizeof(DWORD) + PROPCNKSIZE, f)
+                  || write_dw_msf(TAG('s','f','i','f'), f)
+#if defined _WIN32
+                  || (f->propoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+                  || SetFilePointer(f->fileno,(long)PROPCNKSIZE,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+            goto ioerror;
+#else
+                  || fgetpos(f->fileno, &bytepos)
+                  || fseek(f->fileno, (long)PROPCNKSIZE, SEEK_CUR) < 0)
+                        goto ioerror;
+        f->propoffset = bytepos;
+#endif
+        }
+
+        if(write_dw_msf(TAG('S','S','N','D'), f)
+          || write_dw_msf(0, f)
+          || write_dw_msf(0, f)         /* offset */
+          || write_dw_msf(0, f)         /* blocksize */
+#if defined _WIN32
+          || (f->datachunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT)) == 0xFFFFFFFF)
+        goto ioerror;
+#else
+          || fgetpos(f->fileno, &bytepos) )
+                goto ioerror;
+    f->datachunkoffset = bytepos;
+#endif
+        if(f->min_header >= SFILE_CDP){
+                f->propschanged = 1;
+                f->proplim = PROPCNKSIZE;
+        }
+        f->datachunksize = 0;
+        f->extrachunksizes = 0;
+        f->header_set = 1;
+        return 0;
+        /* NOTREACHED */
+ioerror:
+        rsferrno = ESFWRERR;
+        if(rsferrstr == NULL)
+                rsferrstr = "error writing aiff header";
+        return 1;
+}
+
+/*RWD 22:6:2000 now use 'fl32', so the new Cubase will read it!*/
+static int
+wraifchdr(struct sf_file *f,int channels,int srate,int stype)
+{
+        DWORD aifcver = AIFC_VERSION_1;
+        DWORD ID_compression;
+    fpos_t bytepos;
+        //assume 32bit floats, but we may be asked to use aifc for integer formats too
+        char *str_compressed = (char *) aifc_floatstring;
+        int pstring_size = 10;
+        rsferrstr = NULL;
+        if(stype >= SAMP_MASKED)
+                return 1;
+        /*RWD Sept 2000: was "ff32!" .*/
+        if(stype==SAMP_FLOAT)
+                ID_compression = TAG('f','l','3','2');
+        /* RWD 06-01-09 TODO:  add "in24"  type? */
+        else {
+                ID_compression = TAG('N','O','N','E');
+                pstring_size = 16;
+                str_compressed = (char *) aifc_notcompressed;
+        }
+
+        f->mainchunksize = 0;
+        if(write_dw_msf(TAG('F','O','R','M'), f)
+         ||write_dw_msf(0, f)
+         ||write_dw_msf(TAG('A','I','F','C'), f) )
+                goto ioerror;
+
+        f->fmtchunkEx.Format.nChannels = (short)channels;
+        f->fmtchunkEx.Format.nSamplesPerSec = srate;
+        switch(stype){
+        case(SAMP_SHORT):
+                f->fmtchunkEx.Format.wBitsPerSample = 16;
+                f->fmtchunkEx.Format.nBlockAlign = sizeof(short) * f->fmtchunkEx.Format.nChannels;
+
+                break;
+        case(SAMP_FLOAT):
+        case(SAMP_LONG):
+                f->fmtchunkEx.Format.wBitsPerSample = 32;
+                f->fmtchunkEx.Format.nBlockAlign = sizeof(/*long*/int) * f->fmtchunkEx.Format.nChannels;
+
+                break;
+        case(SAMP_2024):
+                f->fmtchunkEx.Format.wBitsPerSample = 20;
+                f->fmtchunkEx.Format.nBlockAlign = 3 * f->fmtchunkEx.Format.nChannels;
+
+        case(SAMP_2424):
+                f->fmtchunkEx.Format.wBitsPerSample = 24;
+                f->fmtchunkEx.Format.nBlockAlign = 3 * f->fmtchunkEx.Format.nChannels;
+
+                break;
+        //NB 2432 format not allowed in AIF file!
+        default:
+                rsferrstr = "requested sample format not supported by AIFF-C";
+                goto ioerror;   //something we don't know about!
+
+
+        }
+        f->aiffchunks = 0;
+        //write FVER chunk
+        if(write_dw_msf(TAG('F','V','E','R'),f)
+                || write_dw_msf(4,f)
+                || write_dw_msf(aifcver,f))
+                goto ioerror;
+
+        //extended COMM chunk...22 bytes plus size of pascal string, rounded
+        if(write_dw_msf(TAG('C','O','M','M'), f)
+          || write_dw_msf(22 + pstring_size, f)
+#if defined _WIN32
+          || (f->fmtchunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+      || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+      || write_dw_msf(0, f)                     /* num sample frames */
+      || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+      || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                goto ioerror;
+#else
+          || fgetpos(f->fileno, &bytepos)
+          || write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+          || write_dw_msf(0, f)                 /* num sample frames */
+          || write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+          || write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) )
+                goto ioerror;
+    f->fmtchunkoffset = bytepos;
+#endif
+        //now the special bits...
+        if(write_dw_msf(ID_compression,f))
+                goto ioerror;
+        //the dreaded pascal string...
+        if(dowrite(f,str_compressed,pstring_size))
+                goto ioerror;
+        //RWD.6.5.99 ADD the PEAK chunk
+        if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+                int i,size;
+                DWORD now = 0;
+                for(i=0;i < channels; i++){
+                        f->peaks[i].value = 0.0f;
+                        f->peaks[i].position = 0;
+                }
+                size = 2 * sizeof(DWORD) + channels * sizeof(CHPEAK);
+                if(write_dw_msf(TAG('P','E','A','K'),f)
+          || write_dw_msf(size,f)
+#if defined _WIN32
+                  || ((f->peakchunkoffset = SetFilePointer(f->fileno,0L,NULL, FILE_CURRENT))== 0xFFFFFFFF)
+          || write_dw_msf(CURRENT_PEAK_VERSION,f)
+          || write_dw_msf(now,f)
+          || write_peak_msf(channels,f))
+                        goto ioerror;
+#else
+                  || fgetpos(f->fileno, &bytepos)
+                  || write_dw_msf(CURRENT_PEAK_VERSION,f)
+                  || write_dw_msf(now,f)
+                  || write_peak_msf(channels,f))
+                        goto ioerror;
+        f->peakchunkoffset = bytepos;
+#endif
+        }
+
+        if(f->min_header >= SFILE_CDP){
+                if(write_dw_msf(TAG('A','P','P','L'), f)
+                  || write_dw_msf(sizeof(DWORD) + PROPCNKSIZE, f)
+                  || write_dw_msf(TAG('s','f','i','f'), f)
+#if defined _WIN32
+                  || (f->propoffset = SetFilePointer(f->fileno, 0L,NULL,FILE_CURRENT))==0xFFFFFFFF
+                  || SetFilePointer(f->fileno,(long)PROPCNKSIZE,NULL,FILE_CURRENT) == 0xFFFFFFFF)
+            goto ioerror;
+#else
+                  || fgetpos(f->fileno, &bytepos)
+                  || fseek(f->fileno, (long)PROPCNKSIZE, SEEK_CUR) < 0)
+                        goto ioerror;
+        f->propoffset = bytepos;
+#endif
+        }
+
+        if(write_dw_msf(TAG('S','S','N','D'), f)
+          ||write_dw_msf(0, f)
+          ||write_dw_msf(0, f)          /* offset */
+          ||write_dw_msf(0, f)          /* blocksize */
+#if defined _WIN32
+          ||(f->datachunkoffset = SetFilePointer(f->fileno,0L,NULL,FILE_CURRENT)) == 0xFFFFFFFF)
+        goto ioerror;
+#else
+          ||fgetpos(f->fileno, &bytepos) )
+                goto ioerror;
+    f->datachunkoffset = bytepos;
+#endif
+        if(f->min_header >= SFILE_CDP){
+                f->propschanged = 1;
+                f->proplim = PROPCNKSIZE;
+        }
+        f->datachunksize = 0;
+        f->extrachunksizes = 0;
+        f->header_set = 1;
+        return 0;
+        /* NOTREACHED */
+ioerror:
+        rsferrno = ESFWRERR;
+        if(rsferrstr==NULL)
+                rsferrstr = "error writing aiff-c header";
+        return 1;
+}
+
+/*
+ *      Initialization routines
+ */
+            
+static void
+rsffinish(void)
+{
+    int i;
+
+    for(i = 0; i < SF_MAXFILES; i++)
+        if(sf_files[i] != 0)
+            sfclose(i+SFDBASE);
+#ifdef ENABLE_PVX
+    pvsys_release();
+#endif
+
+#ifdef _WIN32
+    if(CDP_COM_READY){
+        COMclose();
+        CDP_COM_READY = 0;
+    }
+#endif
+}
+
+int
+sflinit(const char *name)
+{
+        int i;
+#if defined ENABLE_PVX
+    init_pvsys();
+#endif
+    for(i = 0; i < SF_MAXFILES; i++)
+        sf_files[i] = 0;
+    atexit(rsffinish);
+
+    if(sizeof(DWORD) != 4 || sizeof(WORD) != 2) {
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "internal: sizeof(WORD) != 2 or sizeof(DWORD) != 4";
+        return -1;
+    }
+#ifdef _WIN32
+//alternative is to set CDP_COM_READY entirely in shortcuts.c
+#ifdef _DEBUG
+    assert(!CDP_COM_READY);
+#endif
+    CDP_COM_READY = COMinit();         //need COM to read shortcuts
+#endif
+        return 0;
+}
+
+/*
+ *      Misc other stuff
+ */
+#if 0
+void
+sffinish()
+{
+        /* leave everything to atexit! */
+}
+#endif
+            
+char *
+sfgetbigbuf(int *secsize)
+{
+        char *mem = (char *) malloc(100*SECSIZE);
+
+        *secsize = (mem == 0) ? 0 : 100;
+        return mem;
+}
+
+void
+sfperror(const char *s)
+{
+        if(s == 0)
+                s = "sound filing system";
+        if(*s != '\0')
+                fprintf(stderr, "%s: %s\n", s, rsferrstr);
+        else
+                fprintf(stderr, "%s\n", rsferrstr);
+}
+
+char *
+sferrstr(void)
+{
+        return rsferrstr;
+}
+
+int
+sferrno(void)
+{
+        return rsferrno;
+}
+
+int
+sfsetprefix(char *path)
+{
+        /* the set prefix call is simply ignored - for now */
+        return 0;
+}
+
+void
+sfgetprefix(char *path)
+{
+        path[0] = '\0';         /* signal that no prefix is set */
+}
+
+/*
+ *      allocate/de-allocate file numbers
+ */
+static int  allocsffile(char *filename)
+{
+    int i;
+    int first_i = -1;
+    /*#if defined CDP97 && defined _WIN32*/
+    int refcnt98 = 1;                        //RWD incr refcnt for THIS file, if we have previously opened it
+    /*#endif*/
+    for(i = 0; i < SF_MAXFILES; i++)
+        if(sf_files[i] == 0) {
+            if(first_i < 0)
+                first_i = i;
+        }
+    
+    //RWD98 excluding the return ~seems~ to be all thats needed to get multiple opens!
+        else if(_stricmp(sf_files[i]->filename, filename) == 0) {/* not quite right! */
+            sf_files[i]->refcnt++;
+            refcnt98++;                //for THIS file
+            //return i;
+        }
+    
+    if(first_i < 0) {
+        rsferrno = ESFNOSFD;
+        rsferrstr = "Too many Sfiles are open";
+        free(filename);
+        return -1;
+    }
+    if((sf_files[first_i] = ALLOC(struct sf_file)) == 0) {
+        rsferrno = ESFNOMEM;
+        rsferrstr = "No memory for open SFfile";
+        free(filename);
+        return -1;
+    }
+    
+    sf_files[first_i]->refcnt = refcnt98;
+    
+    //sf_files[first_i]->refcnt = 1;          //RWD.6.98 restore this  to restore old behaviour
+    
+    sf_files[first_i]->filename = filename;
+    sf_files[first_i]->props = 0;
+    sf_files[first_i]->proplim = 0;
+    sf_files[first_i]->curpropsize = 0;
+    sf_files[first_i]->propschanged = 0;
+    sf_files[first_i]->aiffchunks = 0;
+    sf_files[first_i]->peaktime = 0;
+    POS64(sf_files[first_i]->peakchunkoffset) = 0;
+    POS64(sf_files[first_i]->factchunkoffset) = 0;
+    POS64(sf_files[first_i]->datachunkoffset) = 0;
+    sf_files[first_i]->peaks = NULL;
+    sf_files[first_i]->bitmask = 0xffffffff;
+    sf_files[first_i]->fmtchunkEx.dwChannelMask = 0;
+    sf_files[first_i]->chformat = STDWAVE;
+    sf_files[first_i]->min_header = SFILE_CDP;
+#ifdef ENABLE_PVX
+    sf_files[first_i]->pvxprops = NULL;
+#endif
+    return first_i;
+}
+
+static void
+freesffile(int i)
+{
+    struct property *pp = sf_files[i]->props;
+    struct aiffchunk *ap = sf_files[i]->aiffchunks;
+
+    while(pp != /* 0 */ NULL) {
+        struct property *pnext = pp->next;
+        free(pp->name);
+        free(pp->data);
+        free(pp);
+        pp = pnext;
+    }
+    while(ap != /* 0 */ NULL) {
+        struct aiffchunk *anext = ap->next;
+        free(ap->buf);
+        free(ap);
+        ap = anext;
+    }
+    free(sf_files[i]->filename);
+    //RWD.6.5.99
+    if(sf_files[i]->peaks != NULL)
+        free(sf_files[i]->peaks);
+#ifdef ENABLE_PVX
+    if(sf_files[i]->pvxprops != NULL)
+        free(sf_files[i]->pvxprops);
+#endif
+    free(sf_files[i]);
+    sf_files[i] = /* 0 */ NULL;
+}
+
+#ifdef unix
+#define PATH_SEP        '/'
+#else
+#define PATH_SEP        '\\'
+#endif
+
+//RWD: the environment var code prevents use of a defined analysis file extension
+//in addition to CDP_SOUND_EXT ...
+
+//RWD98 now declared static at top of file
+
+/*RWD for DevCPp*/
+#ifndef _MAX_PATH
+#define _MAX_PATH (255)
+#endif
+
+static enum sndfiletype
+gettypefromname98(const char *path)
+{
+#ifdef NOTDEF
+        char *eos = &path[strlen(path)];        /* points to the null byte */
+        char *lastsl = strrchr(path, PATH_SEP);
+#else
+        //RWD98: use hackable local copy of path, to check for WIN32 shortcut
+        //this bit general, though
+        char *eos, *lastsl;
+        char copypath[_MAX_PATH];
+        int len;
+        copypath[0] = '\0';
+        strcpy(copypath,path);
+        len = strlen(copypath);
+        eos =  &copypath[len];
+        lastsl = strrchr(copypath,PATH_SEP);
+#endif
+
+      //  if(lastsl == 0)               //RWD 2022 this fails if path is in current directory, no separator present
+                //abort();
+        //        return unknown_wave;             //RWD.1.99
+
+
+#ifdef _WIN32
+        //it it a shortcut?
+        if(_stricmp(eos-4, ".lnk")==0) {
+                copypath[len-4] = '\0'; //cut away link extension
+                eos -= 4;       //step past the ext, we should be left with a kosher sfilename
+        }
+#endif
+
+        if(eos-4 > lastsl && _stricmp(eos-4, ".wav") == 0)
+                return riffwav;
+        else if(eos-4 > lastsl && _stricmp(eos-4, ".aif") == 0)
+                return eaaiff;
+        else if(eos-5 > lastsl && _stricmp(eos-5, ".aiff") == 0)
+                return eaaiff;
+        //Recognize AIF-C files: use separate sndfiletype for this?
+        else if(eos-4 > lastsl && _stricmp(eos-4,".afc") == 0)
+                return aiffc;
+        else if(eos-4 > lastsl && _stricmp(eos-4,".aic") == 0)
+                return aiffc;
+        else if(eos-5 > lastsl && _stricmp(eos-5,".aifc") == 0)
+                return aiffc;
+/* FILE_AMB_SUPPORT */
+        else if(eos-4 > lastsl && _stricmp(eos-4, ".amb") == 0)
+                return riffwav;
+        else if(eos-5 > lastsl && _stricmp(eos-5, ".ambi") == 0) //RWD April 2006 was -4 !
+                return riffwav;
+        else if(eos-5 > lastsl && _stricmp(eos-5, ".wxyz") == 0)
+                return riffwav;
+
+        //CDP97: recognise .ana as signifying analysis file     - find out later whether wav or aiff
+        /* 4:2001 added revised extensions for and evl; lose fmt and env in time */
+        else if(_stricmp(eos-4, ".ana") == 0                      //analysis file
+                        || _stricmp(eos-4,".fmt") == 0                    //formant file
+                        || _stricmp(eos-4,".for") == 0
+                        || _stricmp(eos-4,".frq") == 0                    // pitch file
+                        || _stricmp(eos-4,".env") == 0                    // binary envelope
+                        || _stricmp(eos-4,".evl") == 0
+                        || _stricmp(eos-4,".trn") == 0 )                          // transposition file
+
+                return cdpfile;
+#ifdef ENABLE_PVX
+        else if(_stricmp(eos-4,".pvx") == 0 )      //PVOCEX analysis file
+            return pvxfile;
+#endif
+        return unknown_wave;
+}
+
+//if a cdpfile - what format is it?
+//RWD TODO: rewrite this with error retval
+static enum sndfiletype
+gettypefromfile(struct sf_file *f)
+{
+        DWORD tag1,tag2,size;
+        enum sndfiletype type = unknown_wave;
+
+        if(read_dw_msf(&tag1, f) || read_dw_lsf(&size, f)       || read_dw_msf(&tag2,f)) {
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,0,NULL,FILE_BEGIN)==0xFFFFFFFF)
+#else
+                if(fseek(f->fileno,0,SEEK_SET) < 0)
+#endif
+                        return unknown_wave;
+        }
+        else if(tag1 == TAG('R','I','F','F')  && tag2 == TAG('W','A','V','E')){
+                        type = riffwav;
+        }
+
+        else if(tag1 == TAG('F','O','R','M') && tag2 == TAG('A','I','F','F')){
+                        type = eaaiff;
+        }
+        //RWD.1.99 support aifc files as well
+        else if(tag1 == TAG('F','O','R','M') && tag2 == TAG('A','I','F','C')){
+                        type = aiffc;
+        }
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,0,NULL,FILE_BEGIN)==0xFFFFFFFF)
+#else
+        if(fseek(f->fileno,0,SEEK_SET) < 0)
+#endif
+                        return unknown_wave;
+        return type;
+}
+
+//RWD.6.98 when tested, add shortcuts code...
+static char *
+mksfpath(const char *name)
+{
+    char *errormsg;
+    char *path = _fullpath(NULL, name, 0);
+    enum sndfiletype filetype = unknown_wave; //RWD 2015
+
+    if(path == NULL) {
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "can't find full path for soundfile - bad drive?";
+//#ifdef unix
+//        printf("realpath failed:errno = %d:%s\n",errno,strerror(errno));
+//#endif
+        return NULL;
+    }
+
+#ifdef _WIN32
+        //if its a shortcut, strip off the link extension: sfopen will try normal open first
+    {
+        int len;
+        char *eos;
+        len = strlen(path);
+        eos = &path[len-4];
+        if(_stricmp(eos,".lnk")==0)
+            path[len-4] = '\0';
+    }
+#endif
+/* RWD March 2014 make this optional! */
+    filetype = gettypefromname98(path);
+
+    if( filetype == unknown_wave) {
+        char *newpath;
+        char *ext;
+        char *ext_default = "wav";
+// RWD MAR 2015 we may have unset CDP_SOUND_EXT, but not removed it completely!
+        if((ext = getenv("CDP_SOUND_EXT")) == NULL || strlen(ext) == 0 ) {
+            //rsferrno = ESFBADPARAM;
+            //rsferrstr = "unknown sound file type - extension not set";
+            //free(path);
+            //return NULL;
+            ext = ext_default;
+        }
+        if(_stricmp(ext, "wav") != 0
+                &&_stricmp(ext, "aif") != 0
+                &&_stricmp(ext, "aiff") != 0
+                &&_stricmp(ext,"afc") != 0                        //Apple...
+                &&_stricmp(ext,"aic") != 0                        //Csound uses this form
+                &&_stricmp(ext,"aifc") !=0) {
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "unknown sound file type - bad CDP_SOUND_EXT setting";
+            free(path);
+            return NULL;
+        }
+
+        if((newpath = (char *) malloc(strlen(path) + strlen(ext) + 2)) == 0) {
+            rsferrno = ESFNOMEM;
+            rsferrstr = "can't get memory for full path of soundfile";
+            free(path);
+            return NULL;
+        }
+
+        strcpy(newpath, path);
+        strcat(newpath, ".");
+        strcat(newpath, ext);
+        free(path);
+        path = newpath;
+    }
+    if((errormsg = legalfilename(path)) != 0) {
+        rsferrno = ESFBADPARAM;
+        rsferrstr = errormsg;
+        free(path);
+        return NULL;
+    }
+    return path;
+}
+
+/*
+ *      public sf routines
+ */
+//RWD.6.98 TODO when tested, add all the file-sharing code
+// best to #ifdef the revised function in as a block...
+    /* RWD TOD 2022: get rid of all the ifdefs, make separate whole functions */
+#if 0
+int
+sfopen(const char *name)
+{
+    int i, rc;
+    struct sf_file *f;
+    char *sfpath;
+#if defined _WIN32
+    DWORD access = GENERIC_WRITE |  GENERIC_READ;     //assumeed for first open (eg for maxsamp...)
+                //seems I need to set write sharing so some other process can write...
+    DWORD sharing = FILE_SHARE_READ;           //for first open
+#else
+    char *faccess = "r+";
+#endif
+//#ifdef _WIN32
+    char newpath[_MAX_PATH];
+    newpath[0] = '\0';
+//#endif
+    if((sfpath = mksfpath(name)) == NULL)
+        return -1;
+
+    if((i = allocsffile(sfpath)) < 0)
+        return -1;
+    f = sf_files[i];
+//#ifdef NOTDEF
+    //this may not be needed after all: can't really display a file while is is being written to...
+    if(f->refcnt > 1) {
+# if defined _WIN32
+        access = GENERIC_READ;
+        sharing = FILE_SHARE_WRITE | FILE_SHARE_READ;    //repeat opens MUST allow first open to write!
+# else
+        //faccess = "r";   /*RWD 2010 allow this ?? */
+        rsferrno = ESFNOTOPEN;
+        rsferrstr = "Can't open file more than once - yet!";
+        freesffile(i);
+        return -1;
+# endif
+    }
+//#endif
+    f->readonly = 0;
+#ifdef _WIN32
+    f->is_shortcut = 0;
+#endif
+    //first, try normal open as rd/wr
+#if defined _WIN32
+    if((f->fileno = CreateFile(f->filename,access,sharing,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)) ==  INVALID_HANDLE_VALUE)
+#else
+    if((f->fileno = fopen(f->filename, faccess)) == NULL)
+#endif
+    {
+#if defined _WIN32
+        DWORD w_errno = GetLastError();
+#endif
+        rsferrno = ESFNOTFOUND;
+        
+#if defined _WIN32
+        if(w_errno != ERROR_FILE_NOT_FOUND){
+#else
+        if(errno != ENOENT){    //won't exist if its actually a shortcut
+#endif
+            
+#if defined _WIN32
+            if(w_errno == ERROR_INVALID_NAME) {
+#else
+            if(errno == EINVAL) {
+#endif
+                rsferrstr = "Illegal filename";
+                freesffile(i);
+                return -1;
+            }
+#if defined _WIN32
+            if(w_errno != ERROR_ACCESS_DENIED) {
+#else
+            if(errno != EACCES) {
+#endif
+                rsferrstr = "SFile not found";
+                freesffile(i);
+                return -1;
+            }
+        }
+    }
+
+/* block below is ONLY for Windows */
+#ifdef _WIN32
+    //try a shortcut to rd/wr file...
+    if(f->fileno == INVALID_HANDLE_VALUE){
+        if(
+            (CDP_COM_READY) &&
+                (getAliasName(f->filename,newpath))     &&
+//# ifdef CDP99
+                ((f->fileno = CreateFile(newpath,access,sharing,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)) ==  INVALID_HANDLE_VALUE)
+//# else
+//                        ((f->fileno = open(newpath, _O_BINARY|_O_RDWR) ) < 0)
+//# endif
+        ) {
+                        //good link, but still no open...
+//# ifdef CDP99
+            DWORD w_errno = GetLastError();
+            rsferrno = ESFNOTFOUND;
+            if(w_errno == ERROR_INVALID_NAME) {
+//# else
+//                rsferrno = ESFNOTFOUND;
+//                if(errno == EINVAL) {
+//# endif
+                rsferrstr = "Illegal filename";
+                freesffile(i);
+                return -1;
+            }
+//# ifdef CDP99
+            if(w_errno != ERROR_ACCESS_DENIED) {
+//# else
+//                if(errno != EACCES) {
+//# endif
+                rsferrstr = "SFile not found";
+                freesffile(i);
+                return -1;
+            }
+        }
+    }
+#endif
+
+/* "normal" file open code here */
+        //must be rdonly, try normal open or shortcut
+#if defined _WIN32
+    if(f->fileno== INVALID_HANDLE_VALUE){
+#else
+    if(f->fileno==NULL){
+#endif
+ 
+#ifdef _WIN32
+        if(
+            ((f->fileno = CreateFile(f->filename, GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE)
+           && (!((CDP_COM_READY) || (getAliasName(f->filename,newpath)))
+               
+               || ((f->fileno = CreateFile(newpath, GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE)
+               )
+#else
+        if(
+             ((f->fileno = fopen(f->filename, "r")) == NULL)
+           
+           )
+#endif
+        {
+            rsferrstr = "SFile not found";
+            freesffile(i);
+            return -1;
+        }
+        f->readonly = 1;
+    }
+
+
+#ifdef _WIN32
+    if(strlen(newpath) >0) {
+        f->filename[0] = '\0';
+        f->is_shortcut = 1;
+        strcpy(f->filename,newpath);      //filename will be freed eventually; don't copy pointers
+    }
+#endif
+
+
+    switch(f->filetype = gettypefromfile(f)) {
+    case riffwav:
+        rc = rdwavhdr(f);
+        break;
+    case eaaiff:
+        rc = rdaiffhdr(f);
+        break;
+    case aiffc:
+        rc = rdaifchdr(f);
+        break;
+    default:
+        rsferrno = ESFNOSTYPE;
+        rsferrstr = "Internal error: can't find file type";
+        rc = 1;
+    }
+    if(rc) {
+        freesffile(i);
+        return -1;
+    }
+    f->infochanged = 0;
+    f->todelete = 0;
+    f->sizerequested = ES_EXIST;
+    f->curpos = 0;
+    return i+SFDBASE;
+}
+#endif
+
+#ifdef ENABLE_PVX
+/* return -1 for error in fd, 0 for false (i.e. is sfile or .ana file), 1 for true */
+
+int sf_ispvx(int sfd)
+{
+    struct sf_file *f;
+    
+    if((f = findfile(sfd)) == 0)
+        return -1;
+    return (f->filetype == pvxfile);
+}
+           
+        /* pvx file id can be 0, so that can't be used to flag error */
+int get_pvxfd(int sfd ,PVOCDATA *pvxdata ){
+    struct sf_file *f;
+    int rc = -1;
+    if((f = findfile(sfd)) == 0)
+        return rc;
+    else if((f->filetype == pvxfile) && (f->pvxprops != NULL)) {
+        rc = f->pvxfileno;
+        if(pvxdata != NULL) {
+           //memcpy((char*) pvxdata,(char*) &f->pvxprops,sizeof(PVOCDATA));
+            pvxdata->wWordFormat    = f->pvxprops->wWordFormat;
+            pvxdata->wAnalFormat    = f->pvxprops->wAnalFormat;
+            pvxdata->wSourceFormat  = f->pvxprops->wSourceFormat;
+            pvxdata->wWindowType    = f->pvxprops->wWindowType;
+            pvxdata->nAnalysisBins  = f->pvxprops->nAnalysisBins;
+            pvxdata->dwWinlen       = f->pvxprops->dwWinlen;
+            pvxdata->dwOverlap      = f->pvxprops->dwOverlap;
+            pvxdata->dwFrameAlign   = f->pvxprops->dwFrameAlign;
+            pvxdata->fAnalysisRate  = f->pvxprops->fAnalysisRate;
+            pvxdata->fWindowParam   = f->pvxprops->fWindowParam;
+        }
+    }
+    return rc;
+}
+// this needs to be declared in sffuncs.h - accessible by snd.c, but not durectly to app code
+int getpvxfno(int sfd){
+    struct sf_file *f;
+    int rc = 0;
+    if((f = findfile(sfd)) == 0)
+        rc = -1;
+    else if(f->pvxprops == NULL)   //bad call to getpvxfno
+        rc = -1;
+    else
+        rc = f->pvxfileno;
+    return rc;
+}
+#endif
+//RWD.9.98 new version to control access
+
+int
+sfopenEx(const char *name, unsigned int access)
+{
+    int i, rc;
+    struct sf_file *f;
+    char *sfpath;
+    char newpath[_MAX_PATH];
+    newpath[0] = '\0';
+
+    if((sfpath = mksfpath(name)) == NULL)
+        return -1;
+    if((i = allocsffile(sfpath)) < 0)
+        return -1;
+#ifdef ENABLE_PVX
+# ifdef _DEBUG
+    //fprintf(stderr,"sfopenEx: opened in sf_sfiles[%d]\n",i);
+# endif
+    f = sf_files[i];
+    f->readonly = 0;
+    f->is_shortcut = 0;
+#endif
+    
+#ifdef ENABLE_PVX
+    // RWD just cheat for now, until it's all working...
+    if(gettypefromname98(name) == pvxfile){
+        int rc = 0;
+        PVOCDATA pvxdata;
+        WAVEFORMATEX wftx;
+        // setup property block
+# ifdef _DEBUG
+        assert(f->pvxprops == NULL);
+        assert(f->curpropsize == 0);
+        assert(f->props == NULL);
+# endif
+        f->filetype = pvxfile;
+        f->pvxprops = calloc(1,sizeof(PVOCDATA));
+        rc = pvoc_openfile(name,&pvxdata, &wftx);
+        if(rc >= 0) {
+            memcpy(f->pvxprops,&pvxdata, sizeof(PVOCDATA));
+            memcpy(&(f->fmtchunkEx.Format), &wftx, sizeof(WAVEFORMATEX));
+            /* in PVX, Format contains details of the source soundfile for this analysis file (original sampsize etc
+             * this has to be converted into the relevant SFPROPS fields,
+             * and then we have to reconvert Format into what would be obtained from a CDP .ana file.
+             * e.g. must be 32bit floats, srate is (int) analysis rate
+             */
+            //TODO: get datachunk info from header to complete fields in f...which will help dirsf show good  numbers.
+            f->datachunksize = pvoc_getdatasize_bytes(rc);
+            
+            if(pvx_createprops(f) < 0){
+                free(f->pvxprops);
+                f->pvxprops = NULL;
+                return -1;
+            }
+# ifdef _DEBUG
+//            fprintf(stderr,"sfopenEx: opened pvx file %s - chans = %d\n", name, f->fmtchunkEx.Format.nChannels);
+# endif
+            f->fmtchunkEx.Format.nSamplesPerSec = (int) pvxdata.fAnalysisRate;
+            f->fmtchunkEx.Format.nChannels = pvxdata.nAnalysisBins * 2;
+            f->fmtchunkEx.Format.wBitsPerSample = 32;
+            f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+            f->fmtchunkEx.Format.nBlockAlign = f->fmtchunkEx.Format.nChannels * sizeof(float);
+            f->fmtchunkEx.Format.nAvgBytesPerSec =f->fmtchunkEx.Format.nBlockAlign * f->fmtchunkEx.Format.nSamplesPerSec;
+
+# ifdef _DEBUG
+//            fprintf(stderr,"sfopenEx: opened pvx file %s - chans = %d\n", name, f->fmtchunkEx.Format.nChannels);
+# endif
+            f->min_header = SFILE_ANAL; // RWD: may not be needed in this case...?
+            f->pvxfileno = rc;
+            f->infochanged = 0;
+            f->todelete = 0;
+            f->sizerequested = ES_EXIST;
+            f->curpos = 0;
+            //RWD: can't decide whether to fill in datachunksize too for full sf mimicry ...
+            // don't really want snd funcs to depend on that if it can be avoided
+            
+//            if(access == CDP_OPEN_RDONLY)
+            f->readonly = 1;
+            return i + SFDBASE;    // should be fine, we will always check for pvx file
+        }
+        else {
+# ifdef _DEBUG
+//            fprintf(stderr, "sfopenEx: failed to open pvocex file %s\n", name);
+# endif
+            free(f->pvxprops);
+            f->pvxprops = NULL;
+            rsferrstr = "PVX file not found";
+            freesffile(i);
+            return -1;
+        }
+    }
+#endif
+    
+ /* non-PVX from here */
+    if(access == CDP_OPEN_RDONLY){
+#if  defined _WIN32
+        if(((f->fileno = CreateFile(f->filename, GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE)
+#else
+        if(((f->fileno = fopen(f->filename, "r")) == NULL)
+#endif
+            && ((!CDP_COM_READY)
+            || (!getAliasName(f->filename,newpath))
+#if  defined _WIN32
+            || ((f->fileno = CreateFile(newpath, GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE)
+#else
+            || ((f->fileno = fopen(newpath, "r") ) == NULL)
+#endif
+        )){
+
+            rsferrstr = "SFile not found";
+            freesffile(i);
+            return -1;
+        }
+        f->readonly = 1;
+    }
+    else {
+        // normal open as rd/wr
+#if  defined _WIN32
+        DWORD w_errno;
+        if((f->fileno = CreateFile(f->filename, GENERIC_READ | GENERIC_WRITE,
+            FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE) {
+#else
+        if((f->fileno = fopen(f->filename, "r+")) == NULL )      {
+#endif
+            rsferrno = ESFNOTFOUND;
+#if  defined _WIN32
+            w_errno= GetLastError();
+            if(w_errno != ERROR_FILE_NOT_FOUND){
+#else
+            if(errno != ENOENT){    //won't exist if its actually a shortcut
+#endif
+#if defined _WIN32
+                if(w_errno == ERROR_INVALID_NAME) {
+#else
+                    if(errno == EINVAL) {
+#endif
+                        rsferrstr = "Illegal filename";
+                        freesffile(i);
+                        return -1;
+                }
+#if  defined _WIN32
+                if(w_errno != ERROR_ACCESS_DENIED) {
+#else
+                if(errno != EACCES) {
+#endif
+                    rsferrstr = "SFile not found";
+                    freesffile(i);
+                    return -1;
+                }
+            }
+        }
+
+                //try a shortcut to rd/wr file...
+# ifdef _WIN32
+        if(f->fileno == INVALID_HANDLE_VALUE){
+# else
+        if(f->fileno == NULL){
+# endif
+            if(
+               (CDP_COM_READY) &&
+               (getAliasName(f->filename,newpath))     &&
+#if defined _WIN32
+               ((f->fileno = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE,
+                    FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,NULL)) ==  INVALID_HANDLE_VALUE)
+#else
+               ((f->fileno = fopen(newpath, "r+") ) == NULL)
+#endif
+            ) {
+            //good link, but still no open...
+                rsferrno = ESFNOTFOUND;
+#if  defined _WIN32
+                if(w_errno == ERROR_INVALID_NAME) {
+#else
+                if(errno == EINVAL) {
+#endif
+                    rsferrstr = "Illegal filename";
+                    freesffile(i);
+                    return -1;
+                }
+#if defined _WIN32
+                if(w_errno != ERROR_ACCESS_DENIED) {
+#else
+                if(errno != EACCES) {
+#endif
+                    rsferrstr = "SFile not found";
+                    freesffile(i);
+                    return -1;
+                }
+            }
+        }
+
+
+#if  defined _WIN32
+        if(f->fileno== INVALID_HANDLE_VALUE){
+#else
+        if(f->fileno == NULL){
+#endif
+            rsferrstr = "SFile not found";
+            freesffile(i);
+            return -1;
+        }
+
+    }
+#ifdef  _WIN32
+    if(strlen(newpath) >0) {
+        f->filename[0] = '\0';
+        f->is_shortcut = 1;
+        strcpy(f->filename,newpath);      //filename will be freed eventually; don't copy pointers
+    }
+#endif
+
+    switch(f->filetype = gettypefromfile(f)) {
+        case riffwav:
+                rc = rdwavhdr(f);
+                break;
+        case eaaiff:
+                rc = rdaiffhdr(f);
+                break;
+        case aiffc:
+                rc = rdaifchdr(f);
+                break;
+        default:
+                rsferrno = ESFNOSTYPE;
+                rsferrstr = "Internal error: can't find file type";
+                rc = 1;
+    }
+    if(rc) {
+            freesffile(i);
+            return -1;
+    }
+    f->infochanged = 0;
+    f->todelete = 0;
+    f->sizerequested = ES_EXIST;
+    f->curpos = 0;
+    return i+SFDBASE;
+}
+
+
+
+static struct sf_file *
+findfile(int sfd)
+{
+        sfd -= SFDBASE;
+        if(sfd < 0 || sfd >= SF_MAXFILES || sf_files[sfd] == 0) {
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "soundfile descriptor does not refer to an open soundfile";
+                return 0;
+        }
+        return sf_files[sfd];
+}
+
+
+static int
+comparewithlist(const char *list, const char *name)
+{
+        size_t len = strlen(name);
+        for(;;) {
+                if(_strnicmp(list, name, len) == 0
+                 &&(list[len] == '\0' || list[len] == ','))
+                        return 1;
+                if((list = strchr(list, ',')) == 0)
+                        break;
+                list++;
+        }
+        return 0;
+}
+
+            /* RWD note: used only for create functions */
+#if  defined _WIN32
+static HANDLE doopen(const char *name, const char *origname,cdp_create_mode mode)
+{
+        char *ovrflg;
+        HANDLE rc;
+        DWORD access,sharing,attrib,w_errno;
+        access = GENERIC_READ | GENERIC_WRITE;
+        sharing = FILE_SHARE_READ;
+        attrib = FILE_ATTRIBUTE_NORMAL;
+        if(mode==CDP_CREATE_TEMPORARY){
+                sharing = 0;
+                attrib = FILE_ATTRIBUTE_TEMPORARY
+                        | FILE_ATTRIBUTE_HIDDEN
+                        | FILE_FLAG_DELETE_ON_CLOSE;
+        }
+        if(mode==CDP_CREATE_RDONLY)
+                attrib = FILE_ATTRIBUTE_READONLY;
+
+        if((rc = CreateFile(name, access,sharing,NULL,CREATE_NEW,attrib,NULL)) !=  INVALID_HANDLE_VALUE)
+                return rc;
+
+        w_errno = GetLastError();
+        if(!(w_errno == ERROR_FILE_EXISTS || w_errno==ERROR_ALREADY_EXISTS))
+                return rc;
+        if(mode==CDP_CREATE_NORMAL){
+                if((ovrflg = getenv("CDP_OVERWRITE_FILE")) == 0)
+                        return rc;
+                if(strcmp(ovrflg, "*") != 0
+                        &&!comparewithlist(ovrflg, origname))
+                        return rc;
+                return  CreateFile(name, access,sharing,NULL,CREATE_ALWAYS,attrib,NULL);
+        }
+        else
+                return rc;
+}
+#else
+static FILE* doopen(const char *name, const char *origname,cdp_create_mode mode)
+{
+        char *ovrflg;
+        FILE* fp = NULL;
+        //RWD set modeflags here to allow setting a temporary file in CDP97
+        //int exclmode,truncmode;
+        //exclmode = (_O_BINARY|_O_RDWR|_O_CREAT|_O_EXCL );
+        //truncmode = (_O_BINARY|_O_RDWR|_O_TRUNC);
+        char *fmode = "w+x";
+#ifdef _WIN32
+        if(mode==CDP_CREATE_TEMPORARY){
+                exclmode |=  /*_O_SHORT_LIVED*/_O_TEMPORARY;      //create as temporary, if poss no flush to disk
+        }
+#else
+/* TODO: replace with mkstemp, maybe use origname as part of template? */
+    /* RWD MAR 2015, need to eliminate call to tmpnam,
+     * without having to alloc new memory for modifiable name for mkstemp() */
+    /* only the old GUI programs (GrainMill) ask for a temporary filename, anyway... */
+        //if(mode==CDP_CREATE_TEMPORARY)
+        //      name = tmpnam(NULL);
+
+#endif
+        if(mode==CDP_CREATE_RDONLY){
+          //exclmode = (_O_BINARY|_O_RDONLY|_O_CREAT|_O_EXCL );
+          //truncmode = (_O_BINARY|_O_RDONLY|_O_TRUNC);
+          fmode = "r+x";
+        }
+
+        if((fp = fopen(name,fmode ))!= NULL)
+                return fp;
+    if(errno != EEXIST)
+                return fp;
+        if((ovrflg = getenv("CDP_OVERWRITE_FILE")) == 0)
+                return fp;
+        if(strcmp(ovrflg, "*") != 0
+         &&!comparewithlist(ovrflg, origname))
+                return fp;
+    // allow overwriting (I hope...) */
+        return fopen(name,"w+");
+}
+
+#endif
+
+//RWD 2022 removed old sfcreat()
+
+
+static int file_exists(const char * fname)
+{
+    int rc = 0;
+    struct stat buffer;
+    rc = stat(fname,&buffer);
+    if(rc == 0){
+        errno = EEXIST;
+        rsferrstr = "Can't create SFile, already exists";
+        return 1;
+    }
+    else
+        return 0;
+}
+/********* SFSY98 extension: supply format info for streaming, etc      *******/
+//RWD.6.99 supports all new legal formats, except WAVE_EX (use sfcreat_ex)
+//RWD.1.99 added mode arg to create temporary file in Current Directory
+
+/*RWD 2007: change size params to __int64 */
+
+int sfcreat_formatted(const char *name,  __int64 size,  __int64 *outsize,int channels,
+                                  int srate, int stype,cdp_create_mode mode) {
+    int i, rc;
+    struct sf_file *f;
+    char *sfpath;
+/* RWD March 2014 */
+    char *ext_default = "wav";
+/*RWD 2007 */
+    __int64 freespace = getdrivefreespace(name) - LEAVESPACE;
+
+    if((sfpath = mksfpath(name)) == NULL)
+        return -1;
+
+    if((i = allocsffile(sfpath)) < 0)
+        return -1;
+    f = sf_files[i];
+    
+#ifdef ENABLE_PVX
+    /* a split file creation: create partly-complete header, to be filled in later by host.
+     * we receive channels (which sets FFTsize for pvx).
+     * currently, pvsys create func sets winlen (etc) to default values if arg = 0 : might need to redesign this!
+     * horrible suspicion progs ask for pvoc srate = int analysis rate.
+     * Not sure how to use the size args.
+     * so we will ignore them unless and until this creates a problem!
+     */
+    // CDP code covers this, but pvocsys doesn't
+    if(file_exists(name)){
+#ifdef _DEBUG
+        fprintf(stderr,"file exists - can't create\n");
+#endif
+        freesffile(i);
+        return -1;
+    }
+    if(gettypefromname98(name) == pvxfile){
+        int rc = 0;
+        
+        f->filetype = pvxfile;
+        // setup property block
+# ifdef _DEBUG
+        assert(f->pvxprops == NULL);
+        assert(f->curpropsize == 0);
+        assert(f->props == NULL);
+# endif
+        f->proplim = 2000; /* bytes; arbitrary. each putprop call checks this */
+        f->pvxprops = calloc(1,sizeof(PVOCDATA));
+        // init what we can, rely on file close to complete header via SFPROPS etc
+        rc = pvoc_createfile(name,channels - 2,0,1,PVOC_AMP_FREQ,0,STYPE_IEEE_FLOAT, PVOC_HANN,0.0,NULL,0);
+        if(rc < 0){
+# ifdef _DEBUG
+            fprintf(stderr,"sfsys: pvoc_createfile failed: %s\n", pvoc_errorstr());
+# endif
+            return -1;
+        }
+        else {
+# ifdef _DEBUG
+//            fprintf(stderr,"pvoc_createfile succeeded i= %d,rc = %d\n",i,rc);
+# endif
+        }
+        f->pvxfileno = rc;
+        if(!pvoc_getpvxprops(f->pvxfileno,f->pvxprops)){
+            fprintf(stderr,"sfsys: error from pvoc_getpvxprops\n");
+        }
+        pvoc_set_needsupdate(f->pvxfileno);
+# ifdef _DEBUG
+        assert(f->pvxprops->nAnalysisBins > 0);
+# endif
+        return i+SFDBASE;
+    }
+#endif
+    // from here: just normal CDP files
+    
+    //RWD: this is OK, as it tells us we cannot CREATE more than one file of the same name, or one already opened
+    if(f->refcnt > 1) {
+        rsferrno = ESFNOTOPEN;
+        rsferrstr = "Can't open file more than once - yet!";
+        freesffile(i);
+        return -1;
+    }
+#if defined _WIN32
+    if((f->fileno = doopen(f->filename, name,mode)) == INVALID_HANDLE_VALUE) {
+        DWORD w_errno = GetLastError();
+#else
+    if((f->fileno = doopen(f->filename, name,mode)) == NULL) {
+#endif
+
+#if defined _WIN32
+        switch(w_errno) {
+        case ERROR_INVALID_NAME:
+            rsferrno = ESFNOTOPEN;
+            rsferrstr = "Can't create SFile, Illegal filename";
+            break;
+        case ERROR_FILE_EXISTS:
+        case ERROR_ALREADY_EXISTS:
+            rsferrno = ESFDUPFNAME;
+            rsferrstr = "Can't create SFile, already exists";
+            break;
+        case ERROR_ACCESS_DENIED:
+            rsferrno = ESFNOTOPEN;
+            rsferrstr = "Can't create SFile, permission denied";
+            break;
+        default:
+            rsferrno = ESFNOTOPEN;
+            rsferrstr = "Can't create SFile, Internal error";
+        }
+
+#else
+        switch(errno) {
+        case EINVAL:
+            rsferrno = ESFNOTOPEN;
+            rsferrstr = "Can't create SFile, Illegal filename";
+            break;
+        case EEXIST:
+            rsferrno = ESFDUPFNAME;
+            rsferrstr = "Can't create SFile, already exists";
+            break;
+        case EACCES:
+            rsferrno = ESFNOTOPEN;
+            rsferrstr = "Can't create SFile, permission denied";
+            break;
+        default:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, Internal error";
+        }
+#endif
+        freesffile(i);
+            return -1;
+    }
+
+    if(size < 0)
+        f->sizerequested = freespace;
+    else if(size >= freespace) {
+        rsferrno = ESFNOSPACE;
+        rsferrstr = "Not enough space on Disk to create sound file";
+//RWD.7.99
+#if defined _WIN32
+        CloseHandle(f->fileno);
+        DeleteFile(f->filename);
+#else
+        fclose(f->fileno);
+        remove(f->filename);
+#endif
+        freesffile(i);
+        return -1;
+    } else
+        f->sizerequested = /*size&~1*/ size;     /* RWD NOV 2001 NO ROUNDING! We have 24bit samples now! */
+
+    f->readonly = 0;
+    f->header_set = 0;
+
+        ///RWD.6.5.99 prepare peak storage
+    f->peaks = (CHPEAK *) calloc(channels, sizeof(CHPEAK));
+    if(f->peaks==NULL){
+        rsferrno = ESFNOMEM;
+        rsferrstr = "No memory to create peak data storage";
+//RWD.7.99
+#if  defined _WIN32
+        CloseHandle(f->fileno);
+        DeleteFile(f->filename);
+#else
+        fclose(f->fileno);
+        remove(f->filename);
+#endif
+
+        freesffile(i);
+        return -1;
+    }
+
+    switch(f->filetype = gettypefromname98(f->filename)) {
+        char *ext;
+/******* RWD.7.98 all we have to do to write a requested format is to fill in the data in f->fmtchunk
+ ******* and get wrwavhdr() to read this in! *****/
+        case riffwav:
+            rc = wrwavhdr98(f,channels,srate,stype);
+            break;
+        case eaaiff:
+            //make sure AIFF format is legal!
+            if(stype==SAMP_2432){
+                //reject here, as can't tell caller
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "requested sample type illegal for AIFF files";
+                return -1;
+            }
+            if(stype==SAMP_FLOAT){
+                //we now require AIFC for float formats
+                f->filetype = aiffc;
+                rc = wraifchdr(f,channels,srate,stype);
+            }
+            else
+                rc = wraiffhdr98(f,channels,srate,stype);
+            break;
+                //RWD temporary
+            case aiffc:
+                //make sure AIFF format is legal!
+                if(stype==SAMP_2432){
+                    //reject here, as can't tell caller
+                    rsferrno = ESFBADPARAM;
+                    rsferrstr = "requested sample type illegal for AIFF files";
+                    return -1;
+                }
+
+                rc = wraifchdr(f,channels,srate,stype);
+                break;
+            case cdpfile:
+                /* RWD MAR 2015 as above */
+                if((ext = getenv("CDP_SOUND_EXT")) == NULL || strlen(ext) == 0) {
+                    ext = ext_default;
+                }
+                f->min_header = SFILE_ANAL; /*RWD Nov 2009: but we don't want PEAK, CUE for analysis files! */
+                if(f->peaks){
+                    free(f->peaks);
+                    f->peaks = NULL;
+                }
+                if(_stricmp(ext, "wav") == 0){
+                    rc = wrwavhdr98(f,channels,srate,stype);           /*RWD 5:2003*/
+                    f->filetype = riffwav;
+                }
+                else if(_stricmp(ext, "aif") == 0 || _stricmp(ext, "aiff") == 0){
+                    if(stype==SAMP_FLOAT){
+                    //we now require AIFC for float formats
+                        f->filetype = aiffc;
+                        rc = wraifchdr(f,channels,srate,stype);
+                    }
+                    else{
+                        rc=wraiffhdr98(f,channels,srate,stype);
+                        f->filetype= eaaiff;
+                    }
+                }
+                //RWD.1.99 temporary
+                else if(_stricmp(ext, "aic") == 0
+                                || _stricmp(ext, "afc") == 0
+                                || _stricmp(ext, "aifc") == 0){
+                    rc=wraifchdr(f,channels,srate,stype);
+                    f->filetype= aiffc;
+                }
+                else {
+                    rsferrno = ESFBADPARAM;
+                    rsferrstr = "unknown sound file type - bad CDP_SOUND_EXT setting";
+//RWD.7.99
+#if defined _WIN32
+                    CloseHandle(f->fileno);
+                    DeleteFile(f->filename);
+#else
+                    fclose(f->fileno);
+                    remove(f->filename);
+#endif
+                    return -1;
+                }
+                break;
+            default:
+                rsferrno = ESFNOSTYPE;
+                rsferrstr = "Internal error: can't find filetype";
+                rc = 1;
+    }
+    if(rc) {
+//RWD.7.99
+#if defined _WIN32
+        CloseHandle(f->fileno);
+        DeleteFile(f->filename);
+#else
+        fclose(f->fileno);
+        remove(f->filename);
+#endif
+
+        freesffile(i);
+        return -1;
+    }
+
+    f->datachunksize = 0;
+    f->infochanged = 0;
+    f->todelete = 0;
+    if(outsize != 0)
+        *outsize = (unsigned int) f->sizerequested;
+    f->curpos = 0;
+    return i+SFDBASE;
+}
+
+
+//special version for wave-ex
+//props is both in and out
+
+int sfcreat_ex(const char *name, __int64 size, __int64 *outsize,SFPROPS *props,int min_header,cdp_create_mode mode)
+{
+    int i, rc;
+    int stype = -1;
+    struct sf_file *f;
+    char *sfpath;
+    char *ext_default = "wav"; /* RWD March 2014 */
+    unsigned long freespace = getdrivefreespace(name) - LEAVESPACE;
+
+    if((sfpath = mksfpath(name)) == NULL)
+        return -1;
+    if(min_header < SFILE_MINIMUM || min_header > SFILE_CDP){
+        rsferrno =  ESFBADPARAM;
+        rsferrstr = "bad min_header spec";
+        return -1;
+    }
+
+    if((i = allocsffile(sfpath)) < 0)
+        return -1;
+    f = sf_files[i];
+    //RWD: this is OK, as it tells us we cannot CREATE more than one file of the same name, or one already opened
+    if(f->refcnt > 1) {
+        rsferrno = ESFNOTOPEN;
+        rsferrstr = "Can't open file more than once - yet!";
+        freesffile(i);
+        return -1;
+    }
+    //can only minimise header for wavefile!
+    if(props->type == wt_wave)
+        f->min_header = min_header;
+    //reject analysis formats if not floatsams
+    else{   // must be wt_analysis
+        if(props->samptype != FLOAT32){
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "Analysis data must be floats";
+            freesffile(i);
+            return -1;
+        }
+    }
+
+    switch(props->samptype){
+        case (SHORT16):
+            stype = SAMP_SHORT;
+            break;
+        case(FLOAT32):
+            stype = SAMP_FLOAT;
+            break;
+        case(INT_32):
+            stype = SAMP_LONG;
+               break;
+        case(INT2424):
+            stype = SAMP_2424;
+            break;
+        case(INT2024):
+            stype = SAMP_2024;
+            break;
+        case(INT2432):
+            stype = SAMP_2432;
+            break;
+        default:
+            rsferrno =  ESFBADPARAM;
+            rsferrstr = "unsupported sample type";  //add speaker mask stuff ere long, if WAVE_EX
+            freesffile(i);
+            return -1;
+            break;
+    }
+
+#if defined _WIN32
+    if((f->fileno = doopen(f->filename, name,mode)) == INVALID_HANDLE_VALUE) {
+        DWORD w_errno = GetLastError();
+#else
+    if((f->fileno = doopen(f->filename, name,mode)) == NULL) {
+#endif
+#if defined _WIN32
+        switch(w_errno) {
+            case ERROR_INVALID_NAME:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, Illegal filename";
+                break;
+            case ERROR_FILE_EXISTS:
+            case ERROR_ALREADY_EXISTS:
+                rsferrno = ESFDUPFNAME;
+                rsferrstr = "Can't create SFile, already exists";
+                break;
+            case ERROR_ACCESS_DENIED:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, permission denied";
+                break;
+            default:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, Internal error";
+        }
+                
+#else
+        switch(errno) {
+            case EINVAL:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, Illegal filename";
+                break;
+            case EEXIST:
+                rsferrno = ESFDUPFNAME;
+                rsferrstr = "Can't create SFile, already exists";
+                break;
+            case EACCES:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, permission denied";
+                break;
+            default:
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't create SFile, Internal error";
+        }
+#endif
+        freesffile(i);
+        return -1;
+    }
+
+    if(size < 0)
+        f->sizerequested = freespace;
+    else if(size >= freespace) {
+        rsferrno = ESFNOSPACE;
+        rsferrstr = "Not enough space on Disk to create sound file";
+#if defined _WIN32
+        CloseHandle(f->fileno);
+        DeleteFile(f->filename);
+#else
+        fclose(f->fileno);
+        remove(f->filename);
+#endif
+                
+        freesffile(i);
+        return -1;
+    } else
+        f->sizerequested = (size/* + 1*/)/* &~1*/;      /*RWD use size+1) to get 16bit pad for 24bit files */
+
+    f->readonly = 0;
+    f->header_set = 0;
+
+    ///RWD.6.5.99 prepare peak storage: wave and binary envelope are OK
+    if(props->type == wt_wave || props->type== wt_binenv){
+        f->peaks = (CHPEAK *) calloc(props->chans, sizeof(CHPEAK));
+        if(f->peaks==NULL){
+            rsferrno = ESFNOMEM;
+            rsferrstr = "No memory to create peak data storage";
+
+#if defined _WIN32
+            CloseHandle(f->fileno);
+            DeleteFile(f->filename);
+#else
+            fclose(f->fileno);
+            remove(f->filename);
+#endif
+            freesffile(i);
+            return -1;
+        }
+    }
+
+    switch(f->filetype = gettypefromname98(f->filename)) {
+        char *ext;
+        /******* RWD.7.98 all we have to do to write a requested format is to fill in the data in f->fmtchunk
+            ******* and get wrwavhdr() to read this in! *****/
+        case riffwav:
+            if(props->chformat >= MC_STD){
+                f->filetype = wave_ex;
+                props->format = WAVE_EX;
+                rc = wrwavex(f, props);
+            }
+            else {
+                props->chformat = STDWAVE;
+                props->format = WAVE;
+                rc = wrwavhdr98(f,props->chans,props->srate,stype);
+            }
+            break;
+                    //TODO: get the aiff extended formats sorted!
+        case eaaiff:
+            //for now, we have to IGNORE chformat requests
+            props->chformat = STDWAVE;
+            props->format = AIFF;
+            //make sure AIFF format is legal!
+            if(stype==SAMP_2432){
+                stype = SAMP_2424;
+                props->samptype = INT2424;
+            }
+            if(stype==SAMP_FLOAT){
+                //we now require AIFC for float formats
+                props->format = AIFC;
+                f->filetype = aiffc;
+                rc = wraifchdr(f,props->chans,props->srate,stype);
+            }
+            else
+                rc = wraiffhdr98(f,props->chans,props->srate,stype);
+            break;
+        case aiffc:
+            props->format = AIFC;
+            //make sure AIFF format is legal!
+            if(stype==SAMP_2432){
+                stype = SAMP_2424;
+                props->samptype = INT2424;
+            }
+            rc = wraifchdr(f,props->chans,props->srate,stype);
+            break;
+        case cdpfile:
+            /* RWD MAR 2015 as above */
+            if((ext = getenv("CDP_SOUND_EXT")) == NULL || strlen(ext) == 0) {
+                ext = ext_default;
+            }
+            if(_stricmp(ext, "wav") == 0){
+                if(props->chformat > MC_STD){
+                    f->filetype = wave_ex;
+                    props->format = WAVE_EX;
+                    rc = wrwavex(f, props);
+                }
+                else {
+                    props->chformat = STDWAVE;
+                    props->format = WAVE;
+                    rc = wrwavhdr98(f,props->chans,props->srate,stype);
+                    f->filetype = riffwav;
+                }
+            }
+            else if(_stricmp(ext, "aif") == 0 || _stricmp(ext, "aiff") == 0){
+                props->chformat = STDWAVE;
+                props->format = AIFF;
+                if(stype==SAMP_FLOAT){
+                    //we now require AIFC for float formats
+                    props->format = AIFC;
+                    f->filetype = aiffc;
+                    rc = wraifchdr(f,props->chans,props->srate,stype);
+                }
+                else {
+                    rc = wraiffhdr98(f,props->chans,props->srate,stype);
+                    f->filetype= eaaiff;
+                }
+            }
+            //RWD.1.99 temporary
+            else if(_stricmp(ext, "aic") == 0
+                    || _stricmp(ext, "afc") == 0
+                    || _stricmp(ext, "aifc") == 0){
+                props->format = AIFC;
+                f->filetype = aiffc;
+                rc = wraifchdr(f,props->chans,props->srate,stype);
+            }
+            else {
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "unknown sound file type - bad CDP_SOUND_EXT setting";
+                       //RWD.7.99
+#if  defined _WIN32
+                CloseHandle(f->fileno);
+                DeleteFile(f->filename);
+#else
+                fclose(f->fileno);
+                remove(f->filename);
+#endif
+                return -1;
+            }
+            break;
+                    
+        default:
+            rsferrno = ESFNOSTYPE;
+            rsferrstr = "Internal error: can't find filetype";
+            rc = 1;
+    }
+    if(rc) {
+                //RWD.7.99
+#if defined _WIN32
+        CloseHandle(f->fileno);
+        DeleteFile(f->filename);
+#else
+        fclose(f->fileno);
+        remove(f->filename);
+#endif
+        freesffile(i);
+        return -1;
+    }
+
+    f->datachunksize = 0;
+    f->infochanged = 0;
+    f->todelete = 0;
+    if(outsize != 0)
+           *outsize = f->sizerequested;
+    f->curpos = 0;
+
+    if(props->type==wt_analysis){
+        /*Write all pvoc properties*/
+        if(addprop(f,"original sampsize",(char *)&(props->origsize), sizeof(/*long*/int))<0){
+            rsferrstr = "Failure to write original sample size";
+            return -1;
+        }
+        if(addprop(f,"original sample rate",(char *)&(props->origrate),sizeof(/*long*/int))<0){
+               rsferrstr = "Failure to write original sample rate";
+        }
+        if(addprop(f,"arate",(char *)&(props->arate),sizeof(float)) < 0){
+            rsferrstr = "Failure to write analysis sample rate";
+        }
+        if(addprop(f,"analwinlen",(char *)&(props->winlen),sizeof(int)) < 0){
+            rsferrstr = "Failure to write analysis window length";
+        }
+        if(addprop(f,"decfactor",(char *)&(props->decfac),sizeof(int)) < 0){
+               rsferrstr = "Failure to write decimation factor";
+        }
+    }
+    return i+SFDBASE;
+}
+
+
+//RWD.1.99 new func to enable current sfile (e.g temporary) to be reopened for writing
+//return 1 for success, 0 for error
+//new channel/sample formats are accepted, but the cdp_create_mode cannot be changed, nor the file format
+//(without  alot mre jiggery-pokery...)
+//BIG QUESTION: ALLOW THIS ONLY FOR TEMPORARY FILES?
+//note we do not call freesfile[] here, as the file is owned externally
+//current dependency: GRAINMILL
+//will need sndrecreat_formatted eventually, NB set buffer size for 24bit formats!
+
+int
+sfrecreat_formatted(int sfd, __int64 size, __int64 *outsize,int channels,
+                                  int srate, int stype)
+{
+    int rc;
+    struct sf_file *f;
+    char *ext_default = "wav";
+    __int64 freespace;
+
+    //might as well validate the params
+    if(channels < 1 || srate <= 0)
+        return 0;
+    //ho hum, need stype as a typedef...
+    // we're not interested in 8-bit stuff!
+        if((stype >= SAMP_MASKED) || stype == SAMP_BYTE)
+                return 0;
+        if((f = findfile(sfd)) == 0)
+                return 0;
+        //RWD: this is OK, as it tells us we cannot CREATE more than one file of the same name, or one already opened
+        if(f->refcnt > 1) {
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "Can't (re)create file more than once!";
+                return 0;
+        }
+        if(f->readonly == 1)
+                return 0;
+
+#if defined _WIN32
+        if(w_ch_size(f->fileno, 0L) < 0) {
+#else
+        if(ftruncate(fileno(f->fileno), 0) < 0) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "write error resetting file";
+                return 0;
+        }
+
+
+        freespace = getdrivefreespace(f->filename) - LEAVESPACE;
+        if(size < 0)
+                f->sizerequested = freespace;
+        else if((unsigned long)size >= freespace) {
+                rsferrno = ESFNOSPACE;
+                rsferrstr = "Not enough space on Disk to create sound file";
+                return 0;
+        } else
+                f->sizerequested = /*size&~1*/ size;  /*RWD Nov 2001: accept 24bit samples */
+
+        f->readonly = 0;
+        f->header_set = 0;
+
+        //RWD.6.5.99 : accept PEAKS for now, but may need to forbid unless wave or binenv, as above
+        if(f->peaks){
+                free(f->peaks);
+                f->peaks = (CHPEAK *) calloc(channels,sizeof(CHPEAK));
+                if(f->peaks==NULL){
+                        rsferrno = ESFNOMEM;
+                        rsferrstr = "No memory for peak data";
+                        return 0;
+                }
+
+        }
+
+        //we don't change f->min_header...
+        switch(f->filetype) {
+                char *ext;
+        case riffwav:
+                rc = wrwavhdr98(f,channels,srate,stype);
+                break;
+        case eaaiff:
+                //make sure AIFF format is legal!
+                if(stype==SAMP_2432){
+                        //reject here, as can't tell caller
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "requested sample type illegal for AIFF files";
+                        return -1;
+                }
+                if(stype==SAMP_FLOAT){
+                        //we now require AIFC for float formats
+                        f->filetype = aiffc;
+                        rc = wraifchdr(f,channels,srate,stype);
+                }
+                else
+                        rc = wraiffhdr98(f,channels,srate,stype);
+                break;
+
+        case aiffc:
+                //make sure AIFF format is legal!
+                if(stype==SAMP_2432){
+                        //reject here, as can't tell caller
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "requested sample type illegal for AIFF files";
+                        return -1;
+                }
+
+                rc = wraifchdr(f,channels,srate,stype);
+                break;
+        case cdpfile:
+            /* RWD MAR 2015 as above */
+                if((ext = getenv("CDP_SOUND_EXT")) == NULL || strlen(ext) == 0) {
+//                      rsferrno = ESFBADPARAM;
+//                      rsferrstr = "unknown sound file type - extension not set";
+//                      rc = 1;
+            ext = ext_default;
+                }
+                if(_stricmp(ext, "wav") == 0){
+                        rc = wrwavhdr98(f,channels,srate,stype);
+                        f->filetype = riffwav;
+                }
+                else if(_stricmp(ext, "aif") == 0 || _stricmp(ext, "aiff") == 0){
+                        if(stype==SAMP_FLOAT){
+                                //we now require AIFC for float formats
+                                f->filetype = aiffc;
+                                rc = wraifchdr(f,channels,srate,stype);
+                        }
+                        else {
+                                rc = wraiffhdr98(f,channels,srate,stype);
+                                f->filetype= eaaiff;
+                        }
+                }
+                //RWD.1.99 temporary
+                else if(_stricmp(ext, "aic") == 0
+                        || _stricmp(ext, "afc") == 0
+                        || _stricmp(ext, "aifc") == 0){
+                        f->filetype = aiffc;
+                        rc = wraifchdr(f,channels,srate,stype);
+                }
+                else {
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "unknown sound file type - bad CDP_SOUND_EXT setting";
+                        rc = 1;
+                }
+                break;
+
+        default:
+                rsferrno = ESFNOSTYPE;
+                rsferrstr = "Internal error: can't find filetype";
+                rc = 1;
+        }
+        if(rc) {
+                return 0;
+        }
+
+        f->datachunksize = 0;
+        f->infochanged = 0;
+        f->todelete = 0;
+        if(outsize != 0)
+                *outsize = f->sizerequested;
+        f->curpos = 0;
+        return 1;
+}
+
+
+
+
+//RWD OCT97
+int
+sfgetwordsize(int sfd)
+{
+        struct sf_file *f;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        return f->fmtchunkEx.Format.wBitsPerSample;
+}
+
+
+__int64 sfgetdatasize(int sfd)
+{
+        struct sf_file *f;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        return f->datachunksize;
+}
+
+#if defined _WIN32
+int sf_is_shortcut(int sfd,char *name)
+{
+        struct sf_file *f;
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        if(f->is_shortcut){
+                if(name != NULL)
+                        strcpy(name,f->filename);
+        }
+        return f->is_shortcut;
+}
+#endif
+
+/*RWD: nb cnt arg is seen as count of BYTES to fill (expecting cnt/sizeof(sample) words);
+ f->curpos expected to contain bytes of SHORTS or FLOATS
+*/
+int
+sfread(int sfd, char *buf, int cnt)
+{
+        struct sf_file *f;
+        short *sp;
+        DWORD *dp;
+        __int64 remain;
+        int i;
+        //int got = 0;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        cnt = cnt & ~(SECSIZE-1);
+        /*RWD OCT97: here,  remain IS size-specific, so curpos must be, too*/
+        if((remain = (int)(f->datachunksize - f->curpos)) < 0)
+                remain = 0;
+
+
+        if(cnt > remain)
+                cnt = remain;
+        if(cnt == 0)
+                return 0;
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                cnt /= 2;                                               /*see below...*/
+        if(doread(f, buf, cnt)) {                       /*bytes, bytecnt*/
+                rsferrno = ESFRDERR;
+                rsferrstr = "Read error";
+                return -1;
+        }
+
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+        case 8:
+                if(f->filetype == riffwav || f->filetype ==wave_ex) {
+                        for(i = cnt-1; i >= 0; i--)
+                                ((short *)buf)[i] = (buf[i]-128)<<8;
+                } else if((f->filetype == eaaiff) || (f->filetype==aiffc)) {            //RWD.1.99
+                        for(i = cnt-1; i >= 0; i--)
+                                ((short *)buf)[i] = ((signed char *)buf)[i];
+                } else
+                        abort();                                 //RWD ouch!
+                cnt *= 2;                                        // restored from above
+                break;
+        case 16:
+                if(REVDATAINFILE(f)) {
+                        sp = (short *)buf;
+                        for(i = cnt/sizeof(short); i > 0; i--) {
+                                *sp = REVWBYTES(*sp);
+                                sp++;
+                        }
+                }
+                break;
+        case 32:
+                if(REVDATAINFILE(f)) {
+                        dp = (DWORD *)buf;
+                        for(i = cnt/sizeof(DWORD); i > 0; i--) {
+                                *dp = REVDWBYTES(*dp);
+                                dp++;
+                        }
+                }
+                break;
+        default:
+//              abort();                                                 // ouch again!
+                //RW.6.99
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "cannot read unsupported sample type";
+                return -1;
+
+        }
+        f->curpos += cnt;               //assumes pos in SHORTS or FLOATS buffer
+        return cnt;
+}
+//RWD: the original func - no support for new formats, but must eliminate abort() call!
+/* RWD 2007 NB : these funcs all return -1 for error, so are forced to handle only signed longs */
+/* therefore: NOT ready for 4GB files ! */
+int
+sfwrite(int sfd, char *outbuf, int cnt)
+{
+        struct sf_file *f;
+        int i;
+    __int64 remain;
+        short *ssp, *sdp;
+        DWORD *dsp, *ddp;
+        char *buf = outbuf;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "Can't write to read only file";
+                return -1;
+        }
+
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "Can't write to 8bits/sample files";
+                return -1;
+        }
+
+        cnt = cnt & ~(SECSIZE-1);
+        if(f->sizerequested >= 0) {                     /* creating file - explicit size */
+                if((remain = f->sizerequested - f->curpos) < 0)
+                        remain = 0;
+                if(cnt > (int) remain)
+                        cnt = (int) remain;
+        } else if(f->sizerequested == ES_EXIST) {       /* existing file - can't change size */
+                if((remain = f->datachunksize - f->curpos) < 0)
+                        remain = 0;
+                if(cnt > (int) remain)
+                        cnt = (int) remain;
+        }
+
+        if(cnt == 0)
+                return 0;
+        if(REVDATAINFILE(f)) {
+                if((buf = (char *) malloc(cnt)) == 0) {
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "Write error: can't allocate byte swap buffer";
+                        return -1;
+                }
+                switch(f->fmtchunkEx.Format.wBitsPerSample) {
+                case 16:
+                        ssp = (short *)outbuf;
+                        sdp = (short *)buf;
+                        for(i = cnt/sizeof(short); i > 0; i--) {
+                                *sdp = REVWBYTES(*ssp);
+                                ssp++;
+                                sdp++;
+                        }
+                        break;
+                case 32:
+                        dsp = (DWORD *)outbuf;
+                        ddp = (DWORD *)buf;
+                        for(i = cnt/sizeof(DWORD); i > 0; i--) {
+                                *ddp = REVDWBYTES(*dsp);
+                                dsp++;
+                                ddp++;
+                        }
+                        break;
+                default:
+#ifdef NOTDEF
+                        abort();
+#endif
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "cannot write unsupported sample format";
+                        return -1;
+
+
+
+                }
+        }
+                                                        /* else creating file - max size */
+        if(dowrite(f, buf, cnt)) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error";
+                if(buf != outbuf)
+                        free(buf);
+                return -1;
+        }
+        f->curpos += cnt;
+        if(f->curpos > f->datachunksize) {
+                f->datachunksize = f->curpos;
+                f->infochanged = 1;
+        }
+        if(buf != outbuf)
+                free(buf);
+        return cnt;
+}
+
+/* RWD: OBSOLETE - NOT IN USE NOW! */
+int
+sfseek(int sfd, int dist, int whence)
+{
+    struct sf_file *f;
+    unsigned int newpos = 0u;
+    unsigned int size;
+    fpos_t bytepos;
+
+    if((f = findfile(sfd)) == 0)
+        return -1;
+    /*RWD 2007 added casts to silence compiler! */
+    size = (unsigned int) sfsize(sfd);              /* NB Can't fail! */  //RWD OCT97: NB: assumes file of SHORTS or FLOATS
+    switch(whence) {
+    case 0:
+                newpos = dist;
+                break;
+        case 1:
+                newpos = f->curpos + dist;
+                break;
+        case 2:
+                newpos = size + dist;
+                break;
+        default:
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "illegal whence value in sfseek";
+                break;
+        }
+    /* RWD MAR 2015 just to eliminate compiler warning */
+        //if(newpos < 0)
+        //      newpos = 0;
+        if(newpos > size)
+                newpos = size;
+
+        newpos &= ~(SECSIZE-1);
+        f->curpos = newpos;             //still size-specific here...
+
+//RWD OCT97 must seek correctly in 8bit files
+        if(f->fmtchunkEx.Format.wBitsPerSample==8)
+                newpos /= 2;
+
+
+
+#if defined _WIN32
+    newpos += f->datachunkoffset;
+        if(SetFilePointer(f->fileno, newpos,NULL, FILE_BEGIN) != newpos) {
+#else
+    POS64(bytepos) = newpos + POS64(f->datachunkoffset);
+        if(fsetpos(f->fileno, &bytepos) ) {
+#endif
+                rsferrno = ESFRDERR;                      //RWD CDP97
+                rsferrstr = "Seek error";
+                return -1;
+        }
+        return f->curpos;
+}
+
+static char * REV3BYTES(char *samp_24){
+        //trick here: just exchange the outer bytes!
+        char temp = samp_24[0];
+        *samp_24 = samp_24[2];
+        samp_24[2] = temp;
+        return samp_24;
+}
+/* special buffered sf_routines for new sample sizes */
+int
+sfread_buffered(int sfd, char *buf, int lcnt)
+{
+        struct sf_file *f;
+        short *sp;
+        DWORD *dp;
+#ifdef FILE64_WIN
+        __int64 remain,i;
+        __int64 cnt = lcnt;  /*RWD 2007: lcnt used some places below */
+        long containersize;
+#else
+         __int64 remain;
+     __int64 cnt = lcnt;
+        int i,containersize;
+#endif
+
+        //int got = 0;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        //RWD OCT97: here,  remain IS size-specific, so curpos must be, too
+        if((remain = (__int64) (f->datachunksize - f->curpos)) < 0)
+                remain = 0;
+
+#ifdef FILE64_WIN
+        if((__int64) cnt > remain)
+                cnt = (unsigned int) remain;
+#else
+        if(cnt > remain)
+                cnt =  remain;
+#endif
+        if(cnt == 0)
+                return 0;
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                cnt /= 2;                                               //see below...
+        if(doread(f, buf, (int) cnt)) {                 //bytes, bytecnt
+                rsferrno = ESFRDERR;
+                rsferrstr = "Read error";
+                return -1;
+        }
+        containersize = 8 * (f->fmtchunkEx.Format.nBlockAlign /         f->fmtchunkEx.Format.nChannels);
+        switch(containersize) {
+        case 8:
+                if(f->filetype == riffwav || f->filetype ==wave_ex) {
+                        for(i = cnt-1; i >= 0; i--)
+                                ((short *)buf)[i] = (buf[i]-128)<<8;
+                } else if((f->filetype == eaaiff) || (f->filetype==aiffc)) {            //RWD.1.99
+                        for(i = cnt-1; i >= 0; i--)
+                                ((short *)buf)[i] = ((signed char *)buf)[i];
+                } else
+                        abort();                                 //RWD ouch!
+                cnt *= 2;                                        // restored from above
+                break;
+        case 16:
+                if(REVDATAINFILE(f)) {
+                        sp = (short *)buf;
+                        for(i = cnt/sizeof(short); i > 0; i--) {
+                                *sp = REVWBYTES(*sp);
+                                sp++;
+                        }
+                }
+                break;
+
+        case(24):
+                if(REVDATAINFILE(f)) {
+                        char *p_byte = buf;
+                        for(i = cnt/3; i > 0; i--) {
+                                p_byte = REV3BYTES(p_byte);
+                                p_byte += 3;
+                        }
+                }
+                break;
+
+        case 32:
+                if(REVDATAINFILE(f)) {
+                        dp = (DWORD *)buf;
+                        for(i = cnt/sizeof(DWORD); i > 0; i--) {
+                                *dp = REVDWBYTES(*dp);
+                                dp++;
+                        }
+                }
+                break;
+        default:
+#ifdef NOTDEF
+                abort();                                                 // ouch again!
+#endif
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "unsupported sample format";
+                return -1;
+
+
+        }
+        f->curpos += (DWORD) cnt;               //assumes pos in SHORTS or FLOATS buffer
+        return (int) cnt;
+}
+//RWD.7.99 TODO: use blockalign to decide what size word to write
+int
+sfwrite_buffered(int sfd, char *outbuf, int lcnt)
+{
+        struct sf_file *f;
+        __int64 remain;
+        __int64 cnt = lcnt;
+        short *ssp, *sdp;
+        DWORD *dsp, *ddp;
+        char *buf = outbuf;
+        int containersize;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "Can't write to read only file";
+                return -1;
+        }
+
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "Can't write to 8bits/sample files";
+                return -1;
+        }
+
+        //cnt = cnt & ~(SECSIZE-1);
+        if(f->sizerequested >= 0) {                     /* creating file - explicit size */
+                if((remain = f->sizerequested - f->curpos) < 0)
+                        remain = 0;
+                if(cnt > remain)
+                        cnt = remain;
+        } else if(f->sizerequested == ES_EXIST) {       /* existing file - can't change size */
+                if((remain = f->datachunksize - f->curpos) < 0)
+                        remain = 0;
+                if(cnt > remain)
+                        cnt = remain;
+        }
+
+        if(cnt == 0)
+                return 0;
+
+        containersize = 8 * (f->fmtchunkEx.Format.nBlockAlign /         f->fmtchunkEx.Format.nChannels);
+
+        if(REVDATAINFILE(f)) {
+        int i;  /* RWD Feb 2010: should be OK as signed; too risky unsigned in a loop! */
+                if((buf = (char *) malloc((size_t) cnt)) == 0) {  /*RWD 2007 */
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "Write error: can't allocate byte swap buffer";
+                        return -1;
+                }
+                switch(containersize) {
+                        char *p_byte;
+                        char *p_buf;
+                case 16:
+                        ssp = (short *)outbuf;
+                        sdp = (short *)buf;
+                        for(i = cnt/sizeof(short); i > 0; i--) {
+                                *sdp = REVWBYTES(*ssp);
+                                ssp++;
+                                sdp++;
+                        }
+                        break;
+                case 32:
+                        dsp = (DWORD *)outbuf;
+                        ddp = (DWORD *)buf;
+                        for(i = cnt/sizeof(DWORD); i > 0; i--) {
+                                *ddp = REVDWBYTES(*dsp);
+                                dsp++;
+                                ddp++;
+                        }
+                        break;
+                //case 20:
+                case 24:
+                        p_byte = outbuf;
+                        p_buf = buf;
+                        for(i= cnt/3; i > 0; i--){
+                                p_buf[0] = p_byte[2];
+                                p_buf[1] = p_byte[1];
+                                p_buf[2] = p_byte[0];
+
+                                p_byte += 3;
+                                p_buf += 3;
+                        }
+
+                        break;
+                default:
+                        //abort();
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "unsupported sample format";
+                        if(buf != outbuf)
+                                free(buf);
+                        return -1;
+                        break;
+                }
+        }
+                                                        /* else creating file - max size */
+        if(dowrite(f, buf, (int) cnt)) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error";
+                if(buf != outbuf)
+                        free(buf);
+                return -1;
+        }
+        f->curpos += (DWORD) cnt;
+        if(f->curpos > (DWORD) f->datachunksize) {
+                f->datachunksize = f->curpos;
+                f->infochanged = 1;
+        }
+        if(buf != outbuf)
+                free(buf);
+        return (int) cnt;
+}
+/* RWD PVX... maybe still need FILE64_WIN? */
+//#ifdef FILE64_WIN
+#ifdef _WIN32
+/* RWD 2007 remember dist can be negative... */
+__int64
+sfseek_buffered(int sfd, __int64 dist, int whence)
+{
+        struct sf_file *f;
+        __int64 newpos = 0;
+        __int64 i64size;
+        LARGE_INTEGER pos64;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        i64size = sfsize(sfd);          /* NB Can't fail! */  //RWD OCT97: NB: assumes file of SHORTS or FLOATS
+
+        switch(whence) {
+        case 0:
+                newpos = dist;
+                break;
+        case 1:
+                newpos = (__int64) f->curpos + dist;
+                break;
+        case 2:
+                newpos =  i64size + dist;
+                break;
+        default:
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "illegal whence value in sfseek";
+                break;
+        }
+        if(newpos < 0)
+                newpos = 0;
+        if(newpos > i64size)
+                newpos = i64size;
+
+        f->curpos = (unsigned int) newpos;              //still size-specific here...
+
+//RWD OCT97 must seek correctly in 8bit files
+        if(f->fmtchunkEx.Format.wBitsPerSample==8)
+                newpos /= 2;
+
+        newpos += f->datachunkoffset;
+//#endif
+
+#if defined _WIN32
+        pos64.QuadPart = newpos;
+
+
+        pos64.LowPart = SetFilePointer(f->fileno, pos64.LowPart,&pos64.HighPart, FILE_BEGIN);
+        if(pos64.LowPart==0xFFFFFFFF && GetLastError() != NO_ERROR){
+                 /* != newpos) { */
+                rsferrno = ESFRDERR;                      //RWD CDP97
+                rsferrstr = "Seek error";
+                return -1;
+        }
+        if(pos64.QuadPart != newpos){
+                rsferrno = ESFRDERR;
+                rsferrstr = "Seek error";
+                return -1;
+        }
+#else
+        if(lseek(f->fileno, newpos, SEEK_SET) != newpos) {
+                rsferrno = ESFRDERR;                      //RWD CDP97
+                rsferrstr = "Seek error";
+                return -1;
+        }
+#endif
+        return f->curpos;
+}
+
+
+#else
+__int64
+sfseek_buffered(int sfd, __int64 dist, int whence)
+{
+    struct sf_file *f;
+    __int64 newpos = 0;    // allow max seek distance 2GB
+    __int64 size;
+    fpos_t bytepos;
+
+    if((f = findfile(sfd)) == 0)
+        //return -1;
+        return 0xFFFFFFFF;
+    size = sfsize(sfd);             /* NB Can't fail! */  //RWD OCT97: NB: assumes file of SHORTS or FLOATS
+    switch(whence) {
+    case 0:
+        newpos = dist;
+        break;
+    case 1:
+        newpos = f->curpos + dist;
+        break;
+    case 2:
+        newpos = size + dist;
+        break;
+    default:
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "illegal whence value in sfseek";
+        break;
+    }
+    if(newpos < 0)
+        newpos = 0;
+    if(newpos > size)
+        newpos = size;
+
+    f->curpos = (DWORD) newpos;             //still size-specific here...
+
+//RWD OCT97 must seek correctly in 8bit files
+    if(f->fmtchunkEx.Format.wBitsPerSample==8)
+        newpos /= 2;
+
+        //
+
+//#endif
+
+#if defined _WIN32
+    newpos += f->datachunkoffset;
+    if(SetFilePointer(f->fileno, newpos,NULL, FILE_BEGIN) != newpos) {
+#else
+    POS64(bytepos) = (DWORD) newpos + POS64(f->datachunkoffset);
+    if(fsetpos(f->fileno, &bytepos)) {
+#endif
+        rsferrno = ESFRDERR;                      //RWD CDP97
+        rsferrstr = "Seek error";
+        return -1;
+
+    }
+    return (__int64) f->curpos;
+}
+
+#endif
+
+
+//RWD: this may not be sufficient: we may want to distinguish betweeen
+// float sfiles and analfiles....
+static int
+wavupdate(struct sf_file *f)
+{
+    unsigned long seekdist;
+    fpos_t bytepos;
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+        case 8:
+                f->fmtchunkEx.Format.nAvgBytesPerSec = f->fmtchunkEx.Format.nBlockAlign = f->fmtchunkEx.Format.nChannels;
+                f->datachunksize /= 2;
+                break;
+        case 16:
+                f->fmtchunkEx.Format.nAvgBytesPerSec = f->fmtchunkEx.Format.nBlockAlign = f->fmtchunkEx.Format.nChannels * 2;
+                break;
+        case 32:
+                f->fmtchunkEx.Format.nAvgBytesPerSec = f->fmtchunkEx.Format.nBlockAlign = f->fmtchunkEx.Format.nChannels * 4;
+                f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;               //RWD 07:97
+                //RWD TODO: update 'fact' chunk with num-samples
+                break;
+        default:
+                abort();
+        }
+        f->fmtchunkEx.Format.nAvgBytesPerSec *= f->fmtchunkEx.Format.nSamplesPerSec;
+
+        //add space for fact chunk ?
+        //WARNING: THIS HAS NOT BEEN FULLY TESTED!
+        // maxsamp needed this...writes directly to existing header on disk
+        //f->extrachunksizes = 3*sizeof(DWORD);
+
+        f->mainchunksize = POS64(f->datachunkoffset) + (DWORD) f->datachunksize - 2*sizeof(DWORD) + f->extrachunksizes;
+#if defined _WIN32
+        if(SetFilePointer(f->fileno, 4L,NULL, FILE_BEGIN)== 0xFFFFFFFF
+#else
+        if(fseek(f->fileno, 4L, SEEK_SET) < 0
+#endif
+         || write_dw_lsf(f->mainchunksize, f)
+#if defined _WIN32
+         || SetFilePointer(f->fileno, f->fmtchunkoffset,NULL, FILE_BEGIN)== 0xFFFFFFFF
+     || write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+     || write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+     || write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+     || write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+     || write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+     || write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)){
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update format data";
+                return -1;
+        }
+#else
+         || fsetpos(f->fileno, &f->fmtchunkoffset)
+         || write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+         || write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+         || write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+         || write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+         || write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+         || write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)){
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update format data";
+                return -1;
+        }
+#endif
+         //RWD OCT97: the extra cbSize field SHOULD be there, = 0
+         //fact chunk contains size in samples
+        if(f->fmtchunkEx.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT){
+                if(POS64(f->factchunkoffset) > 0){        /*RWD 01:2004 */
+#if defined _WIN32
+                        if(SetFilePointer(f->fileno,f->factchunkoffset,NULL,FILE_BEGIN)== 0xFFFFFFFF
+#else
+                        if(fsetpos(f->fileno,&f->factchunkoffset)
+#endif
+                                ||write_dw_lsf((DWORD)(f->datachunksize / (f->fmtchunkEx.Format.wBitsPerSample / sizeof(char))),f)){  /*RWD 2007 */
+                                rsferrno = ESFWRERR;
+                                rsferrstr = "Write error: Can't update fact chunk for floatsam data";
+                                return -1;
+                        }
+                }
+        }
+
+#if defined _WIN32
+    if(SetFilePointer(f->fileno, f->datachunkoffset - sizeof(DWORD),NULL, FILE_BEGIN)== 0xFFFFFFFF
+#else
+    seekdist = POS64(f->datachunkoffset) - sizeof(DWORD);
+    POS64(bytepos) = seekdist;
+    if(fsetpos(f->fileno, &bytepos)
+#endif
+         ||write_dw_lsf((DWORD) f->datachunksize, f) ) {   /*RWD 2007 */
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update data size";
+                return -1;
+        }
+        return 0;
+}
+
+ /******* SFSY98 VERSION*******
+  ******* main format data already there - just update durations
+  */
+
+//RWD.5.99 TODO: need to be more clever with extended formats:
+//must distinguish 32bit int and float
+//this is used when header has already been set
+static int
+wavupdate98(time_t thistime, struct sf_file *f)
+{
+    fpos_t bytepos;
+        if(! f->header_set)
+                return -1;
+
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                f->datachunksize /= 2;
+
+        f->mainchunksize = POS64(f->datachunkoffset) + (DWORD) f->datachunksize - 2*sizeof(DWORD) + f->extrachunksizes;
+#if defined _WIN32
+        if(SetFilePointer(f->fileno, 4L,NULL, FILE_BEGIN)== 0xFFFFFFFF
+#else
+        if(fseek(f->fileno, 4L, SEEK_SET) < 0
+#endif
+                                ||write_dw_lsf(f->mainchunksize, f)){
+                rsferrno = ESFWRERR;
+                rsferrstr = "SFSY98: Write error: Can't update datachunk size";
+                return -1;
+        }
+#ifdef NOTDEF
+        if(f->infochanged){
+#ifdef _WIN32
+                if(SetFilePointer(f->fileno, f->fmtchunkoffset,NULL, FILE_BEGIN)== 0xFFFFFFFF
+#else
+                if(fsetpos(f->fileno, &f->fmtchunkoffset)
+#endif
+                                ||write_w_lsf(f->fmtchunkEx.Format.wFormatTag, f)
+                                ||write_w_lsf(f->fmtchunkEx.Format.nChannels, f)
+                                ||write_dw_lsf(f->fmtchunkEx.Format.nSamplesPerSec, f)
+                                ||write_dw_lsf(f->fmtchunkEx.Format.nAvgBytesPerSec, f)
+                                ||write_w_lsf(f->fmtchunkEx.Format.nBlockAlign, f)
+                                ||write_w_lsf(f->fmtchunkEx.Format.wBitsPerSample, f)){
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "SFSYS98: Write error: Can't update chunk sizes or info chunk";
+                        return -1;
+                }
+        }
+#endif
+        if(f->fmtchunkEx.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT){
+         //fact chunk contains size in samples
+                //RWD.5.99 may not be using it...
+                if(POS64(f->factchunkoffset) > 0){
+#if defined _WIN32
+                        if(SetFilePointer(f->fileno,f->factchunkoffset,NULL,FILE_BEGIN)== 0xFFFFFFFF
+#else
+                        if(fsetpos(f->fileno,&f->factchunkoffset)
+#endif
+                                || write_dw_lsf((DWORD)(f->datachunksize / (f->fmtchunkEx.Format.wBitsPerSample / sizeof(char))),f))    {
+                                rsferrno = ESFWRERR;
+                                rsferrstr = "SFSYS98: Write error: Can't update fact chunk for floatsam file";
+                                return -1;
+                        }
+                }
+        }
+
+        //RWD.6.5.99 update peak chunk
+
+        if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,f->peakchunkoffset + sizeof(DWORD),NULL,FILE_BEGIN)== 0xFFFFFFFF
+#else
+        fpos_t target = f->peakchunkoffset;
+           POS64(target) += sizeof(DWORD);
+                //if(lseek(f->fileno,f->peakchunkoffset + sizeof(DWORD),SEEK_SET) < 0
+        if(fsetpos(f->fileno,&target)
+#endif
+                        || write_dw_lsf((DWORD) thistime,f)
+                        || write_peak_lsf(f->fmtchunkEx.Format.nChannels,f)) {
+
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "SFSYS98: Write error: Can't update peak chunk";
+                        return -1;
+                }
+
+        }
+
+
+
+
+#if defined _WIN32
+         if(SetFilePointer(f->fileno, f->datachunkoffset - sizeof(DWORD),NULL, FILE_BEGIN)== 0xFFFFFFFF
+        ||write_dw_lsf((DWORD) f->datachunksize, f) ) {
+#else
+    bytepos = f->datachunkoffset;
+    POS64(bytepos) -= sizeof(DWORD);
+    //if(lseek(f->fileno, f->datachunkoffset - sizeof(DWORD), SEEK_SET) < 0
+        //              ||write_dw_lsf((DWORD) f->datachunksize, f) ) {
+    if(fsetpos(f->fileno, &bytepos)
+                        ||write_dw_lsf((DWORD) f->datachunksize, f) ) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "SFSYS98: Write error: Can't update chunk sizes or info chunk";
+                return -1;
+        }
+        return 0;
+}
+
+
+static int
+aiffupdate(time_t thistime,struct sf_file *f)
+{
+        DWORD numsampleframes;
+    fpos_t bytepos;
+        struct aiffchunk *ap = f->aiffchunks;
+        int commafterdata = 0;
+
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+        case 8:
+                f->datachunksize /= 2;
+                numsampleframes = (DWORD) f->datachunksize;   /* RWD 2007 added DWORD casts */
+                break;
+        case 16:
+                numsampleframes = (DWORD) f->datachunksize / 2;
+                break;
+        case 32:
+                numsampleframes = (DWORD) f->datachunksize / 4;
+                break;
+        default:
+                abort();
+        }
+        numsampleframes /= f->fmtchunkEx.Format.nChannels;
+
+        f->mainchunksize = sizeof(DWORD) + 2*sizeof(DWORD) + 26 + 2*sizeof(DWORD) + (DWORD)(f->datachunksize + f->extrachunksizes);
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,4L,NULL,FILE_BEGIN) == 0xFFFFFFFF
+       ||write_dw_msf((DWORD) POS64(f->mainchunksize), f)
+       ||SetFilePointer(f->fileno,(long)(POS64(f->datachunkoffset) - 3*sizeof(DWORD)),NULL,FILE_BEGIN) == 0xFFFFFFFF
+       ||write_dw_msf((DWORD)(f->datachunksize + 2*sizeof(DWORD)), f) ) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update main or data chunk size";
+                return -1;
+        }
+#else
+    bytepos = f->datachunkoffset;
+    POS64(bytepos) -= 3 * sizeof(DWORD);
+        if(fseek(f->fileno, 4L, SEEK_SET)
+         ||write_dw_msf((DWORD) f->mainchunksize, f)
+         ||fsetpos(f->fileno, &bytepos)
+         ||write_dw_msf((DWORD)(f->datachunksize + 2*sizeof(DWORD)), f) ) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update main or data chunk size";
+                return -1;
+        }
+#endif
+        if(POS64(f->fmtchunkoffset) > POS64(f->datachunkoffset)) {
+                commafterdata++;
+                POS64(f->fmtchunkoffset) = POS64(f->datachunkoffset) + (DWORD)( (f->datachunksize+1)&~1);
+        }
+        //RWD NB no support for AIFC here...
+#ifdef NOTDEF
+        else
+                f->fmtchunkoffset -= 2*sizeof(DWORD);
+#endif
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,f->fmtchunkoffset,NULL,FILE_BEGIN) == 0xFFFFFFFF){
+#else
+        if(fsetpos(f->fileno, &f->fmtchunkoffset) ) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't seek to COMM chunk";
+                return -1;
+        }
+
+        if(
+#ifdef NOTDEF
+                write_dw_msf(TAG('C','O','M','M'), f)
+         ||write_dw_msf(18, f)
+         ||
+#endif
+           write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+         ||write_dw_msf(numsampleframes, f)
+         ||write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+         ||write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) ) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update COMM chunk";
+                return -1;
+        }
+
+
+                //RWD.6.5.99 update peak chunk
+
+        if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,f->peakchunkoffset + sizeof(DWORD),NULL,FILE_BEGIN)== 0xFFFFFFFF
+#else
+           bytepos = f->peakchunkoffset;
+           POS64(bytepos) += sizeof(DWORD);
+                if(fsetpos(f->fileno,&bytepos)
+#endif
+                        || write_dw_msf((DWORD) thistime,f)
+                        || write_peak_msf(f->fmtchunkEx.Format.nChannels,f)) {
+
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "SFSYS98: Write error: Can't update peak chunk";
+                        return -1;
+                }
+
+        }
+
+
+        if(!commafterdata) {
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,(long)(f->datachunkoffset + (f->datachunksize+1)&~1),NULL,FILE_BEGIN)==0xFFFFFFFF) {
+#else
+            bytepos = f->datachunkoffset;
+            POS64(bytepos) += (f->datachunksize+1)&~1;
+                if(fsetpos(f->fileno, &bytepos) ) {
+#endif
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "Can't seek to end, to update extra chunks";
+                        return -1;
+                }
+        }
+
+        for(; ap != 0; ap = ap->next) {
+                if(POS64(ap->offset) < POS64(f->datachunkoffset))
+                        continue;
+                if(write_dw_msf(ap->tag, f)
+                 ||write_dw_msf(ap->size, f)
+                 ||dowrite(f, ap->buf, (ap->size+1)&~1) ) {
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "Can't update extra chunk";
+                        return -1;
+                }
+        }
+        return 0;
+}
+
+
+static int
+aiffupdate98(time_t thistime,struct sf_file *f)
+{
+    DWORD numsampleframes = 0;
+#ifdef linux
+    fpos_t bytepos = {0};
+#else
+  fpos_t bytepos = 0;
+#endif
+    struct aiffchunk *ap = f->aiffchunks;
+    int commafterdata = 0;
+
+    if(! f->header_set)
+        return -1;
+    if(f->infochanged){
+        switch(f->fmtchunkEx.Format.wBitsPerSample) {
+            case 8:
+        f->datachunksize /= 2;
+        numsampleframes = (DWORD) f->datachunksize;
+        break;
+      case 16:
+                        numsampleframes = (DWORD) f->datachunksize/2;
+                        break;
+                case 20:                //NB not allowed by AIFF - no header field for masked sizes
+                case 24:
+                        numsampleframes = (DWORD) f->datachunksize / 3;
+                        break;
+                case 32:
+                        numsampleframes = (DWORD) f->datachunksize/4;
+                        break;
+                default:
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "cannot update with unknown sample format";
+                        return -1;
+                        break;
+                }
+        }
+        // else if bitspersample== 8....  ?
+        numsampleframes /= f->fmtchunkEx.Format.nChannels;
+        /* RWD 8.99 this may be wrong...*/
+        /*f->mainchunksize = sizeof(DWORD) + 2*sizeof(DWORD) + 26 + 2*sizeof(DWORD) + f->datachunksize + f->extrachunksizes;
+         */
+        f->mainchunksize = POS64(f->datachunkoffset) + (DWORD) f->datachunksize - 2 * sizeof(DWORD);
+
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,4L,NULL,FILE_BEGIN) == 0xFFFFFFFF
+#else
+        bytepos = f->datachunkoffset;
+        POS64(bytepos) -= 3*sizeof(DWORD);
+        if(fseek(f->fileno, 4L, SEEK_SET) < 0
+#endif
+         ||write_dw_msf(f->mainchunksize, f)
+#if defined _WIN32
+         ||SetFilePointer(f->fileno,(long)(f->datachunkoffset - 3*sizeof(DWORD)),NULL,FILE_BEGIN) == 0xFFFFFFFF
+#else
+//       ||lseek(f->fileno, f->datachunkoffset - 3*sizeof(DWORD), SEEK_SET) < 0
+     ||fsetpos(f->fileno, &bytepos)
+#endif
+         ||write_dw_msf((DWORD) f->datachunksize + 2*sizeof(DWORD), f) ) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update main or data chunk size";
+                return -1;
+        }
+
+        if(POS64(f->fmtchunkoffset) > POS64(f->datachunkoffset)) {
+                commafterdata++;
+                POS64(f->fmtchunkoffset) = POS64(f->datachunkoffset) + (DWORD)((f->datachunksize+1)&~1);   /*RWD 2007 */
+        }
+        //RWD don't want to do this - might be AIFC - don't need to anyway!
+#ifdef NOTDEF
+        else
+                f->fmtchunkoffset -= 2*sizeof(DWORD);
+#endif
+#if defined _WIN32
+        if(SetFilePointer(f->fileno,f->fmtchunkoffset,NULL,FILE_BEGIN) == 0xFFFFFFFF){
+#else
+        if(fsetpos(f->fileno, &f->fmtchunkoffset) ) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't seek to COMM chunk";
+                return -1;
+        }
+        //strictly, we should check the new format and redo the AIFC stuff,
+        //OR reject if infochanged
+        //at least, disallow format conversion in AIFC!
+
+        if(
+#ifdef NOTDEF
+                write_dw_msf(TAG('C','O','M','M'), f)
+         ||write_dw_msf(18, f)
+         ||
+#endif
+           write_w_msf(f->fmtchunkEx.Format.nChannels, f)
+         ||write_dw_msf(numsampleframes, f)
+         ||write_w_msf(f->fmtchunkEx.Format.wBitsPerSample, f)
+         ||write_dw_toex(f->fmtchunkEx.Format.nSamplesPerSec, f) ) {
+                rsferrno = ESFWRERR;
+                rsferrstr = "Write error: Can't update COMM chunk";
+                return -1;
+        }
+
+
+    // update peak chunk
+
+        if((f->min_header >= SFILE_PEAKONLY) && f->peaks){
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,f->peakchunkoffset + sizeof(DWORD),NULL,FILE_BEGIN)== 0xFFFFFFFF
+#else
+        bytepos = f->peakchunkoffset;
+        POS64(bytepos) += sizeof(DWORD);
+                if(fsetpos(f->fileno,&bytepos)
+#endif
+                        || write_dw_msf((DWORD) thistime,f)
+                        || write_peak_msf(f->fmtchunkEx.Format.nChannels,f)) {
+
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "SFSYS98: Write error: Can't update peak chunk";
+                        return -1;
+                }
+
+        }
+
+
+        if(!commafterdata) {
+#if defined _WIN32
+                if(SetFilePointer(f->fileno,(long)(f->datachunkoffset + (f->datachunksize+1)&~1),NULL,FILE_BEGIN)==0xFFFFFFFF) {
+#else
+        bytepos = f->datachunkoffset;
+        POS64(bytepos) += (f->datachunksize+1)&~1;
+       // if(lseek(f->fileno, f->datachunkoffset + (f->datachunksize+1)&~1, SEEK_SET) < 0) {
+        if(fsetpos(f->fileno, &bytepos) ) {
+#endif
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "Can't seek to end, to update extra chunks";
+                        return -1;
+                }
+        }
+        /* RWD: NB if we have these, they will start correctly after a pad byte,
+         * and we will just trust that these chunks are kosher!*/
+        if(ap){
+                for(; ap != 0; ap = ap->next) {
+                        if(POS64(ap->offset) < POS64(f->datachunkoffset))
+                                continue;
+                        if(write_dw_msf(ap->tag, f)
+                        ||write_dw_msf(ap->size, f)
+                        ||dowrite(f, ap->buf, (ap->size+1)&~1) ) {
+                                rsferrno = ESFWRERR;
+                                rsferrstr = "Can't update extra chunk";
+                                return -1;
+                        }
+                }
+        }
+        /*RWD AIFF requires pad byte at end, for 8bit and 24bit sample types*/
+        else {
+#ifdef FILE64_WIN
+                __int64 size = f->datachunkoffset +  (f->datachunksize+1)&~1;
+#else
+                DWORD size = POS64(f->datachunkoffset) +  ((f->datachunksize+1)&~1);
+#endif
+#if defined _WIN32
+                if(w_ch_size(f->fileno, size) < 0) {
+#else
+                if(ftruncate(fileno(f->fileno), (off_t) size) < 0) {
+#endif
+                        rsferrno = ESFWRERR;
+                        rsferrstr = "write error truncating file";
+                        return -1;
+                }
+        }
+
+        return 0;
+}
+
+int
+sfclose(int sfd)
+{
+        struct sf_file *f;
+        int rc = 0;
+
+        if((f = findfile(sfd)) == 0){     //RWD: may already have been closed... see errrmsg
+                rsferrstr = "SFSYS: close: bad file ID (already closed?)";
+                return -1;
+        }
+
+#ifdef ENABLE_PVX
+    if(f->pvxprops != NULL){
+        //either readonly input file, or new output file. If latter, must fill in analysis fields from sfprops
+        //including fmtx
+        //set pvxdata first, then set wfmt from that
+        if(!f->readonly) {
+            int origsize = 0,origrate=0,winlen=0,decfac=0;
+            float arate = 0.0;
+            int res;
+            //sfgetprop(int sfd, const char *propname, char *dest, int lim)
+# ifdef _DEBUG
+            assert(f->pvxprops->nAnalysisBins > 0);
+# endif
+            res = sfgetprop(sfd,"original sampsize",(char*) &origsize,sizeof(int));
+            if(res < 0){
+                fprintf(stderr,"error from sfgetprop: original sampsize\n");
+                return -1;
+            }
+            else
+                fprintf(stderr, "sfclose: got original sampsize = %d\n",origsize);
+            
+            
+            res = sfgetprop(sfd,"original sample rate",(char*) &origrate,sizeof(int));
+            if(res < 0){
+                fprintf(stderr,"error from sfgetprop: original samplerate\n");
+                return -1;
+            }
+            else
+                fprintf(stderr, "sfclose: got original sample rate = %d\n",origrate);
+            res = sfgetprop(sfd,"arate",(char*) &arate,sizeof(float));
+            if(res < 0){
+                fprintf(stderr,"error from sfgetprop: arate\n");
+                return -1;
+            }
+            else
+                fprintf(stderr, "sfclose: got arate = %f\n",arate);
+            
+            res = sfgetprop(sfd,"analwinlen",(char*) &winlen,sizeof(int));
+            res = sfgetprop(sfd,"decfactor",(char*) &decfac,sizeof(int));
+            
+            //this comes from dz->infile->stype
+            
+            switch(origsize){
+            case SAMP_SHORT:
+                f->fmtchunkEx.Format.wBitsPerSample = 16;
+                break;
+            case SAMP_2024:
+            case SAMP_2424:
+                    f->fmtchunkEx.Format.wBitsPerSample = 24;
+                    break;
+            case SAMP_LONG:
+            case SAMP_FLOAT:
+                    f->fmtchunkEx.Format.wBitsPerSample = 32;
+            }
+            
+            f->pvxprops->wWordFormat = PVOC_IEEE_FLOAT;  /* no plans for a doubles implementation yet */
+            f->pvxprops->wAnalFormat = PVOC_AMP_FREQ;
+            f->pvxprops->wSourceFormat = origsize == SAMP_FLOAT ? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
+            f->pvxprops->wWindowType = /* probably*/ PVOC_HANN; /* whatever is set in CDP pvoc these days */
+            f->pvxprops->fAnalysisRate = arate;
+            /* we already have nAnalysisBins, can't change that anyway */
+            f->pvxprops->dwWinlen = winlen;
+            f->pvxprops->dwOverlap = decfac;
+
+            f->fmtchunkEx.Format.nSamplesPerSec = origrate;
+            f->fmtchunkEx.Format.nChannels = 1;
+            f->fmtchunkEx.Format.wBitsPerSample = 32;
+            f->fmtchunkEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+            f->fmtchunkEx.Format.nBlockAlign = f->fmtchunkEx.Format.nChannels * sizeof(float);
+            f->fmtchunkEx.Format.nAvgBytesPerSec =f->fmtchunkEx.Format.nBlockAlign * f->fmtchunkEx.Format.nSamplesPerSec;
+            f->fmtchunkEx.Format.cbSize = 62;
+
+            f->filetype = riffwav;
+        }
+        
+
+        if((rc = pvoc_update_closefile(f->pvxfileno,f->pvxprops,&f->fmtchunkEx)) < 0){
+            fprintf(stderr,"sfclose: can't close pvx file\n");
+            return -1;
+        }
+        else {
+//            fprintf(stderr,"pvx file closed OK!\n");
+            freesffile(sfd-SFDBASE);
+        }
+        return rc;
+    }
+    /* RWD TODO: handle f->todelete, typically set by call to sfunlink(sfd) */
+#endif
+        if((f->infochanged || f->propschanged) && !f->todelete && !f->readonly) {
+                unsigned int cdptime;
+                time_t now = time(0);
+                cdptime = (unsigned int) now;
+                //RWD.5.99
+                if((f->min_header >= SFILE_CDP) && POS64(f->propoffset) > 0)
+                        sfputprop(sfd, "DATE", /* (char *)&now */ (char *) &cdptime, sizeof(/*long*/int));
+
+                switch(f->filetype) {
+                case riffwav:
+                case wave_ex:
+                        if(f->header_set)
+                                rc = wavupdate98(now,f);          //RWD.6.5.99
+                        else
+                                rc = wavupdate(f);
+                        break;
+                case eaaiff:
+                case aiffc:
+                        if(f->header_set)
+                                rc = aiffupdate98(now,f);
+                        else
+                                rc = aiffupdate(now,f);                  //RWD.6.5.99
+                        break;
+                default:
+                        //abort();
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "SFSYS98: aif-c files not supported";
+                        return -1;
+                        break;
+                }
+                f->propschanged = 1;
+        }
+
+        if(f->min_header >= SFILE_CDP && POS64(f->propoffset) >= 0 && f->propschanged && !f->todelete && !f->readonly)
+                if(writeprops(f) < 0)
+                        rc = -1;
+
+#if defined _WIN32
+        if(!CloseHandle(f->fileno)) {
+#else
+        if(fclose(f->fileno) < 0) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "write error: system had trouble closing file";
+                rc = -1;
+        }
+
+        if(f->todelete
+                &&  f->filename != NULL
+#if defined _WIN32
+         && !DeleteFile(f->filename)){
+
+#else
+     && remove(f->filename) < 0) {
+
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "can't remove soundfile";
+                rc = -1;
+        }
+        freesffile(sfd-SFDBASE);
+
+        return rc;
+}
+
+
+//return true size in bytes: of SHORTS or FLOATS file
+
+__int64
+sfsize(int sfd)
+{
+        struct sf_file *f;
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        return (__int64)(f->sizerequested == ES_EXIST ? f->datachunksize : f->sizerequested);
+}
+
+
+#ifdef FILE64_WIN
+int
+sfadjust(int sfd, __int64 delta)
+{
+        struct sf_file *f;
+        __int64 newsize;
+
+        if(delta > 0) {
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "Can't extend a soundfile";
+                return -1;
+        }
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "can't adjust size of read-only file";
+                return -1;
+        }
+
+        f->infochanged = 1;
+
+        switch(f->sizerequested) {
+        case ES_EXIST:
+                if(f->datachunksize + delta < 0) {
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "can't make soundfile with negative size";
+                        return -1;
+                }
+                f->datachunksize += delta;
+                break;
+        default:
+                if(f->sizerequested + delta < 0) {
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "can't make soundfile with negative size";
+                        return -1;
+                }
+                if(f->sizerequested + delta >= f->datachunksize)
+                        return 0;
+                f->sizerequested += delta;
+                f->datachunksize = f->sizerequested;
+                break;
+        }
+        newsize = f->datachunkoffset;
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                newsize += f->datachunksize/2;
+        else
+                newsize += f->datachunksize;
+
+#if defined _WIN32
+        if(w_ch_size(f->fileno, newsize) < 0) {
+#else
+        if(chsize(f->fileno, newsize) < 0) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "write error truncating file";
+                return -1;
+        }
+        return 0;               /* is this right? */
+}
+
+#else
+int
+sfadjust(int sfd, __int64 delta)
+{
+        struct sf_file *f;
+        __int64 newsize;
+
+        if(delta > 0) {
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "Can't extend a soundfile";
+                return -1;
+        }
+        if((f = findfile(sfd)) == 0)
+                return -1;
+
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "can't adjust size of read-only file";
+                return -1;
+        }
+
+        f->infochanged = 1;
+
+        switch(f->sizerequested) {
+        case ES_EXIST:
+                if( f->datachunksize + delta < 0) {
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "can't make soundfile with negative size";
+                        return -1;
+                }
+                f->datachunksize += delta;
+                break;
+        default:
+                if(f->sizerequested + delta < 0) {
+                        rsferrno = ESFBADPARAM;
+                        rsferrstr = "can't make soundfile with negative size";
+                        return -1;
+                }
+                if(f->sizerequested + delta >= f->datachunksize)
+                        return 0;
+                f->sizerequested += delta;
+                f->datachunksize = f->sizerequested;
+                break;
+        }
+        newsize = (__int64) POS64(f->datachunkoffset);
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8)
+                newsize += f->datachunksize/2;
+        else
+                newsize += f->datachunksize;
+
+#if defined _WIN32
+        if(w_ch_size(f->fileno, newsize) < 0) {
+#else
+        if(chsize(fileno(f->fileno), newsize) < 0) {
+#endif
+                rsferrno = ESFWRERR;
+                rsferrstr = "write error truncating file";
+                return -1;
+        }
+        return 0;               /* is this right? */
+}
+#endif
+
+int
+sfunlink(int sfd)
+{
+        struct sf_file *f;
+
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "can't delete read-only file";
+                return -1;
+        }
+        f->todelete = 1;
+        return 0;
+}
+
+int
+sfrename(int sfd, const char *newname)
+{
+        struct sf_file *f;
+        char *path = NULL;
+        if((f = findfile(sfd)) == 0)
+                return -1;
+        if(f->readonly) {
+                rsferrno = ESFREADONLY;
+                rsferrstr = "can't rename read-only file";
+                return -1;
+        }
+        if(f->filename == 0) {
+                rsferrno = ESFNOMEM;
+                rsferrstr = "Couldn't remember name of file";
+                return -1;
+        }
+        path = mksfpath(newname);
+        if(path==NULL){
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "can't rename file changing its type";
+                return -1;
+        }
+
+        if(f->filetype != gettypefromname98(path)) {
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "can't rename file changing its type";
+                return -1;
+        }
+
+        if(rename(f->filename, path) != 0) {
+                rsferrno = ESFDUPFNAME;
+                rsferrstr = "file already exists, or can't rename across devices";
+                return -1;
+        }
+        free(f->filename);
+        f->filename = _fullpath(NULL, path, 0);
+        return 0;
+}
+
+/*
+ *      Property access routines
+ */
+//RWD.6.99 mega-special case for sample type now!
+int
+sfgetprop(int sfd, const char *propname, char *dest, int lim)
+{
+    int lret =  SAMP_MASKED;                //RWD.6.99
+    int res,containersize;
+    struct sf_file *f;
+    struct property *pp;
+
+    if((f = findfile(sfd)) == 0)
+        return -1;
+    if(strcmp(propname, "channels") == 0)
+        lret = f->fmtchunkEx.Format.nChannels;
+    else if(strcmp(propname, "sample rate") == 0)
+        lret = f->fmtchunkEx.Format.nSamplesPerSec;
+    else if(strcmp(propname, "sample type") == 0) {
+        //RWD.6.99 lets accept all formats!
+        //is this good for AIFF?
+        containersize = 8 * (f->fmtchunkEx.Format.nBlockAlign /         f->fmtchunkEx.Format.nChannels);
+        switch(containersize){
+            case(32):
+                if(f->fmtchunkEx.Format.wFormatTag== WAVE_FORMAT_IEEE_FLOAT
+                        || (f->fmtchunkEx.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE
+                        && (
+                            (compare_guids(&(f->fmtchunkEx.SubFormat),&(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
+                            ||
+                            (compare_guids(&(f->fmtchunkEx.SubFormat),&(SUBTYPE_AMBISONIC_B_FORMAT_IEEE_FLOAT)))
+                            )
+                        )
+                  )  {
+                    lret =  SAMP_FLOAT;
+                     break;
+                }
+                else{
+                    //int format: may be masked
+                    switch(f->fmtchunkEx.Samples.wValidBitsPerSample){
+                        case(32):
+                            lret = SAMP_LONG;
+                            break;
+                        case(24):
+                            lret = SAMP_2432;
+                            break;
+                        default:
+                            lret = SAMP_MASKED;
+                            break;
+
+                    }
+                }
+                break;
+            case(24):
+                    switch(f->fmtchunkEx.Samples.wValidBitsPerSample){
+                    case(24):
+                            lret = SAMP_2424;
+                            break;
+                    case(20):
+                            lret = SAMP_2024;
+                            break;
+                    default:
+                            lret = SAMP_MASKED;
+                            break;
+
+                    }
+                    break;
+            case(16):
+            case(8):
+                    lret = SAMP_SHORT;
+                    break;
+            default:
+                    break;
+            }
+
+    } else {
+            if(f->min_header < SFILE_CDP){
+               rsferrno = ESFNOTFOUND;
+               rsferrstr = "no CDP properties in minimum header";
+               return -1;
+            }
+
+            if(POS64(f->propoffset) >= 0)
+                    for(pp = f->props; pp != 0; pp = pp->next) {
+                            if(strcmp(propname, pp->name) == 0) {
+                                    res = min(pp->size, lim);
+                                    memcpy(dest, pp->data, res);
+#ifdef ENABLE_PVX
+# ifdef _DEBUG
+//                                fprintf(stderr,"sfgetprop: found prop %s\n",propname);
+# endif
+#endif
+                                    return res;
+                            }
+                    }
+            rsferrno = ESFNOTFOUND;
+            rsferrstr = "Property not defined in file";
+#ifdef ENABLE_PVX
+# ifdef _DEBUG
+ //       fprintf(stderr,"failed to find property %s, propoffset = %lld\n",propname,POS64(f->propoffset));
+# endif
+#endif
+            return -1;
+    }
+    /*
+     *      return a standard property as a long
+     */
+    res = min(lim, sizeof(int));
+    memcpy(dest, &lret, res);
+    return res;
+}
+
+static int
+getlong(int *dest, char *src, int size, int error)
+{
+    if(size != sizeof(int)) {
+        rsferrno = error;
+        rsferrstr = "Bad size for standard property";
+        return -1;
+    }
+    memcpy(dest, src, sizeof(int));
+    return 0;
+}
+
+//RWD.6.99 trap attempts to update primary properties of streamable file
+// ATARI trapped, but ATARI ~could~ support 32bit longs, but who cares?
+int
+sfputprop(int sfd, char *propname, char *src, int size)
+{
+    int data;
+    struct sf_file *f;
+    struct property *pp, **ppp;
+    char *np;
+    
+    if((f = findfile(sfd)) == 0)
+        return -1;
+    if(f->readonly) {
+        rsferrno = ESFREADONLY;
+        rsferrstr = "can't set property in a read-only file";
+        return -1;
+    }
+    
+    if(strcmp(propname, "channels") == 0) {
+        if(getlong(&data, src, size, ESFBADNCHANS) < 0)
+            return -1;
+        //RWD.6.99
+        if(f->header_set && (data != (int)f->fmtchunkEx.Format.nChannels)){
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "not allowed to alter existing property";
+            return -1;
+            
+        }
+        f->fmtchunkEx.Format.nChannels = (short)data;
+        f->infochanged = 1;
+        return 0;
+    } else if(strcmp(propname, "sample rate") == 0) {
+        
+        if(getlong(&data, src, size, ESFBADRATE) < 0)
+            return -1;
+        //RWD.6.99
+        if(f->header_set && (data != (int) f->fmtchunkEx.Format.nSamplesPerSec)){
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "not allowed to alter existing property";
+            return -1;
+        }
+        f->fmtchunkEx.Format.nSamplesPerSec = data;
+        f->infochanged = 1;
+        return 0;
+    } else if(strcmp(propname, "sample type") == 0) {
+        if(f->header_set){
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "not allowed to alter existing property";
+            return -1;
+        }
+        if(getlong(&data, src, size, ESFBADPARAM) < 0)
+            return -1;
+        
+        if(data < SAMP_SHORT && data >= SAMP_MASKED) {
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "cannot change to unsupported sample type";
+            return -1;
+        }
+        
+        if(f->fmtchunkEx.Format.wBitsPerSample == 8) {
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "Can't change sample type when accessing 8 bits/sample file";
+            return -1;
+        }
+        
+        switch(data){
+            case(SAMP_LONG):
+            case(SAMP_FLOAT):
+            case(SAMP_2432):
+                f->fmtchunkEx.Format.wBitsPerSample = 32;
+                break;
+            case(SAMP_2424):
+            case(SAMP_2024):
+                f->fmtchunkEx.Format.wBitsPerSample = 24;
+                break;
+            case(SAMP_SHORT):
+                f->fmtchunkEx.Format.wBitsPerSample = 16;
+                break;
+            case(SAMP_BYTE):
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "8-bit files not supportewd for writing";
+                return -1;
+            default:
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "cannot set unsupported sample type";
+                return -1;
+                break;
+                
+        }
+        f->infochanged = 1;
+        return 0;
+    }
+    
+    /*
+     *      now deal with extended attributes
+     */
+    
+    if((f->min_header < SFILE_CDP) || POS64(f->propoffset) < 0) {
+        rsferrno = ESFNOSPACE;
+        rsferrstr = "No property chunk in this .wav file";
+        return -1;
+    }
+    
+    for(pp = f->props; pp != 0; pp = pp->next) {
+        if(strcmp(propname, pp->name) == 0) {
+            if(f->curpropsize + 2*(size - pp->size) > f->proplim) {
+                rsferrno = ESFNOSPACE;
+                rsferrstr = "No space in extended properties for bigger property data";
+                return -1;
+            }
+            if((np = (char *) malloc(size)) == 0) {
+                rsferrno = ESFNOMEM;
+                rsferrstr = "No memory for bigger property data";
+                return -1;
+            }
+            memcpy(np, src, size);
+            free(pp->data);
+            pp->data = np;
+            pp->size = size;
+            f->curpropsize -= 2*(size - pp->size);
+            f->propschanged = 1;
+            return 0;
+        }
+    }
+    
+    /* adding new property */
+    if(f->curpropsize + (signed int)strlen(propname) + 2 + 2*size > f->proplim) {
+        rsferrno = ESFNOSPACE;
+        rsferrstr = "No space in extended properties for new property data";
+        return -1;
+    }
+    for(ppp = &f->props; *ppp != 0; ppp = &(*ppp)->next)
+        ;
+    if((*ppp = ALLOC(struct property)) == 0
+       ||((*ppp)->name = (char *) malloc(strlen(propname)+1)) == 0
+       ||((*ppp)->data = (char *) malloc(size)) == 0) {
+        rsferrno = ESFNOMEM;
+        rsferrstr = "No memory for bigger property data";
+        return -1;
+    }
+    strcpy((*ppp)->name, propname);
+    memcpy((*ppp)->data, src, size);
+    (*ppp)->size = size;
+    (*ppp)->next = 0;
+    f->curpropsize += strlen(propname) + 2 + 2*size;
+    f->propschanged = 1;
+    return 0;
+}
+
+
+//RWD.6.5.99 not used internally by header routines, return 0 for true
+int sfputpeaks(int sfd,int channels,const CHPEAK peakdata[])
+{
+    int i;
+    struct sf_file *f;
+    
+    if((f = findfile(sfd)) == 0)
+        return -1;
+    if(f->readonly) {
+        rsferrno = ESFREADONLY;
+        rsferrstr = "can't set property in a read-only file";
+        return -1;
+    }
+    
+    if(f->min_header < SFILE_PEAKONLY){
+        rsferrno = ESFNOSPACE;
+        rsferrstr = "no space for peak data in minimum header";
+        return -1;
+        
+        
+    }
+    
+    if(f->peaks==NULL){
+        rsferrno = ESFREADONLY;
+        rsferrstr = "peak data not initialized";
+        return -1;
+        
+    }
+    for(i=0;i < channels; i++){
+        f->peaks[i].value = peakdata[i].value;
+        f->peaks[i].position = peakdata[i].position;
+    }
+    return 0;
+}
+//NB this one not used internally by header routines, return 1 for true
+int sfreadpeaks(int sfd,int channels,CHPEAK peakdata[],int *peaktime)
+{
+
+    int i;
+    struct sf_file *f;
+
+    if((f = findfile(sfd)) == 0)
+        return -1;
+
+    if(f->peaks==NULL){                //NOT an error: just don't have the chunk
+        *peaktime = 0;
+        return 0;
+    }
+    *peaktime = (int) f->peaktime;
+    for(i=0;i < channels; i++){
+        peakdata[i].value = f->peaks[i].value;
+        peakdata[i].position = f->peaks[i].position;
+    }
+    return 1;
+}
+
+
+int
+sfrmprop(int sfd, char *propname)
+{
+    struct sf_file *f;
+    struct property **ppp;
+
+    if((f = findfile(sfd)) == 0)
+        return -1;
+
+    if(f->readonly) {
+        rsferrno = ESFREADONLY;
+        rsferrstr = "can't remove property from a read-only file";
+        return -1;
+    }
+
+    if(strcmp(propname, "channels") == 0
+           ||strcmp(propname, "sample rate") == 0
+           ||strcmp(propname, "sample type") == 0) {
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "Cannot remove standard property";
+        return -1;
+    }
+
+    if(f->min_header < SFILE_CDP){
+        rsferrno = ESFNOTFOUND;
+        rsferrstr = "minimum header - no CDP properties present";
+        return -1;
+    }
+
+
+    if(POS64(f->propoffset) >= 0)
+        for(ppp = &f->props; *ppp != 0; ppp = &(*ppp)->next)
+            if(strcmp((*ppp)->name, propname) == 0) {
+                struct property *p = *ppp;
+                f->curpropsize -= strlen(propname) + 2 + p->size;
+                f->propschanged = 1;
+                free(p->name);
+                free(p->data);
+                *ppp = p->next;
+                free(p);
+                return 0;
+            }
+
+    rsferrno = ESFNOTFOUND;
+    rsferrstr = "Property not found";
+    return -1;
+}
+
+int
+sfdirprop(int sfd, int (*func)(char *propname, int propsize))
+{
+    struct sf_file *f;
+    struct property *pp;
+
+    if((f = findfile(sfd)) == 0)
+        return -1;
+
+    if(func("channels", sizeof( int)) != 0
+         ||func("sample type", sizeof( int)) != 0
+         ||func("sample rate", sizeof( int)) != 0)
+        return SFDIR_FOUND;
+
+    for(pp = f->props; pp != 0; pp = pp->next)
+        if(func(pp->name, pp->size) != 0)
+            return SFDIR_FOUND;
+    return SFDIR_NOTFOUND;
+}
+//CDP98
+
+
+#if defined _WIN32 && defined _MSC_VER
+long cdp_round(double fval)
+{
+    int result;
+    _asm{
+        fld     fval
+        fistp   result
+        mov     eax,result
+    }
+    return (long) result;
+}
+
+#else
+# if defined __GNUWIN32__
+// TODO: learn and use gcc asm syntax!
+long cdp_round(double fval)
+{
+    int k;
+        k = (int)(fabs(fval)+0.5);
+        if(fval < 0.0)
+            k = -k;
+        return (long) k;
+}
+
+# endif
+
+#endif
+
+
+int sfformat(int sfd, fileformat *pfmt)
+{
+    struct sf_file *f;
+    if(pfmt==NULL)
+            return 0;
+    if((f = findfile(sfd)) == 0)
+            return -1;
+
+    if(f->filetype ==  riffwav)
+        *pfmt = WAVE;
+    else if(f->filetype == wave_ex)
+        *pfmt = WAVE_EX;
+    else if(f->filetype== eaaiff)
+        *pfmt = AIFF;
+    else if(f->filetype ==aiffc)
+        *pfmt = AIFC;
+#ifdef ENABLE_PVX
+    else if(f->filetype == pvxfile)
+        *pfmt = PVOCEX;
+#endif
+    else
+        return -1;
+
+    return 0;
+}
+
+int sfgetchanmask(int sfd)
+{
+    struct sf_file *f;
+    //int mask = 0;   //default is generic (unassigned)
+    if((f = findfile(sfd)) ==NULL)
+        return -1;
+
+    if(f->filetype==wave_ex)
+        return f->fmtchunkEx.dwChannelMask;
+
+    return 0;
+}
+//private, but used by sndsystem
+int _rsf_getbitmask(int sfd)
+{
+    struct sf_file *f;
+
+    if((f = findfile(sfd)) ==NULL)
+        return 0;
+    return f->bitmask;
+}
+int sf_getchanformat(int sfd, channelformat *chformat)
+{
+    struct sf_file *f;
+    if((f = findfile(sfd)) ==NULL)
+        return -1;
+    if(chformat==NULL)
+        return -1;
+    *chformat = f->chformat;
+    return 0;
+}
+
+const char* sf_getfilename(int sfd)
+{
+    struct sf_file *f;
+
+    if((f = findfile(sfd)) ==NULL)
+        return NULL;
+
+    return (const char *) f->filename;
+
+}
+
+int sf_getcontainersize(int sfd)
+{
+    struct sf_file *f;
+
+    if((f = findfile(sfd)) ==NULL)
+        return -1;
+
+    return (int) f->fmtchunkEx.Format.wBitsPerSample;
+}
+
+
+int sf_getvalidbits(int sfd)
+{
+
+        struct sf_file *f;
+
+        if((f = findfile(sfd)) ==NULL)
+                return -1;
+
+        return (int) f->fmtchunkEx.Samples.wValidBitsPerSample;
+}
+
+//keep this as private func for now, but used by snd routines
+// may have pos >2GB so need unsigned retval
+unsigned int _rsf_getmaxpeak(int sfd,float *peak)
+{
+        struct sf_file *f;
+        int i;
+        double peakval = 0.0;
+        if((f = findfile(sfd)) ==NULL)
+                return 0xFFFFFFFF;
+
+        if(f->peaks == NULL)
+                return 0;
+
+        for(i=0; i < f->fmtchunkEx.Format.nChannels; i++)
+                peakval = max(peakval,(double)(f->peaks[i].value));
+
+
+        *peak = (float) peakval;
+        return 1;
+
+}
+
+int   addprop(struct sf_file *f, char *propname, char *src, int size)
+{
+        struct property /* *pp,*/ **ppp;
+        /*char *np;*/
+
+        if(f->curpropsize + (signed int)strlen(propname) + 2 + 2*size > f->proplim) {
+                rsferrno = ESFNOSPACE;
+                rsferrstr = "No space in extended properties for new property data";
+                return -1;
+        }
+        for(ppp = &f->props; *ppp != 0; ppp = &(*ppp)->next)
+                ;
+        if((*ppp = ALLOC(struct property)) == 0
+         ||((*ppp)->name = (char *) malloc(strlen(propname)+1)) == 0
+         ||((*ppp)->data = (char *) malloc(size)) == 0) {
+                rsferrno = ESFNOMEM;
+                rsferrstr = "No memory for bigger property data";
+                return -1;
+        }
+        strcpy((*ppp)->name, propname);
+        memcpy((*ppp)->data, src, size);
+        (*ppp)->size = size;
+        (*ppp)->next = 0;
+        f->curpropsize += strlen(propname) + 2 + 2*size;
+        f->propschanged = 1;
+        return 0;
+}
+#ifdef ENABLE_PVX
+    //pvxprops to sfsys props
+static int writefirstprop(struct sf_file *f, char *propname, char *src, int size)
+{
+    struct property /* *pp,*/ **ppp;
+        
+    ppp = &f->props;
+    if((*ppp = ALLOC(struct property)) == 0
+       ||((*ppp)->name = (char *) malloc(strlen(propname)+1)) == 0
+       ||((*ppp)->data = (char *) malloc(size)) == 0) {
+        rsferrno = ESFNOMEM;
+        rsferrstr = "No memory for bigger property data";
+        return -1;
+    }
+    strcpy((*ppp)->name, propname);
+    memcpy((*ppp)->data, src, size);
+    (*ppp)->size = size;
+    (*ppp)->next = 0;
+    f->curpropsize += strlen(propname) + 2 + 2*size;
+    f->proplim = 1000;                  // RWD completely arbitrary!
+//    f->propschanged = 1;            // because this is a quasi-creation of ana file
+    return 0;
+}
+#endif
+    
+    
+    /* required mapping:
+     PVX   ---------->               ANA
+     nAnalysisBins                 N...  *2 =  props.chans
+     overlap                        decfac
+     format=PVOC_AMP_FREQ       [no field - always this format, have to reject other type of pvx file]
+     srate                          origrate
+     arate                          arate AND (int) WAV srate
+    */
+#ifdef ENABLE_PVX
+/* may as well set all props here, including basic ones from WAVFORMATEX */
+static int pvx_createprops(struct sf_file *f) {
+    struct property **ppp = &f->props;
+    sampletype samptype;
+    unsigned int origsize, origrate, winlen,decfac,analchans;
+    float arate;
+
+#ifdef _DEBUG
+    assert(f->curpropsize ==0);
+    assert(f->pvxprops != NULL);
+    /* NB oddity of CDP ana format -  sets WAV sample rate as (int) analysis rate,
+       as shown by dirsf */
+ //   assert(f->fmtchunkEx.Format.nSamplesPerSec == 0);
+#endif
+    /* map pvx props to .ana props - not all are identical */
+    if(f->pvxprops->wSourceFormat==WAVE_FORMAT_IEEE_FLOAT)
+        samptype = FLOAT32;
+    else {
+        switch(f->fmtchunkEx.Format.wBitsPerSample){
+        case 24:
+            samptype = INT2424; /* can't represent INT2432 in anaprops) */
+            break;
+        case 32:
+            samptype = INT_32;
+            break;
+        default:
+            samptype = SHORT16;
+            break;
+        }
+    }
+    /* next, we have to convert Format into apparent floatsam file...*/
+    
+    analchans   = f->pvxprops->nAnalysisBins * 2;
+    origsize    = (unsigned int) samptype;
+    origrate    = f->fmtchunkEx.Format.nSamplesPerSec;
+    decfac      = f->pvxprops->dwWinlen;
+    arate       = f->pvxprops->fAnalysisRate;
+    winlen      = f->pvxprops->dwWinlen;
+    decfac      = f->pvxprops->dwOverlap;
+    
+    /* set all WAV fornat fields as an .ana file would do... */
+    // TODO: allocate and populate char* memory block for proerties, as if getting props block from file
+    // call parseprops to load into f->props
+    
+    // have to make first prop entry explicitly, then can call addprop for the rest
+    if(writefirstprop(f,"original sampsize",(char*) & origsize, sizeof(int)) < 0) {
+        rsferrstr = "Failure to write original sample size";
+        return -1;
+    }
+    if(addprop(f,"original sample rate",(char *)&origrate,sizeof(int))<0){
+        rsferrstr = "Failure to write original sample rate";
+        return -1;
+    }
+    if(addprop(f,"arate",(char *) &arate,sizeof(float)) < 0){
+        rsferrstr = "Failure to write analysis sample rate";
+        return -1;
+    }
+    if(addprop(f,"analwinlen",(char *)& winlen,sizeof(int)) < 0){
+        rsferrstr = "Failure to write analysis window length";
+        return -1;
+    }
+    if(addprop(f,"decfactor",(char *)& decfac,sizeof(int)) < 0){
+        rsferrstr = "Failure to write decimation factor";
+        return -1;
+    }
+#ifdef _DEBUG
+//    fprintf(stderr, "added all ana properties OK\n");
+#endif
+    f->propschanged = 0;  // see above: first creation of quasi-ana data
+    return 0;
+}
+#endif
+
+    
+    // not used yet...
+#ifdef ENABLE_PVX
+static int sfprops2pvx(struct sf_file *f)
+{
+    struct property **ppp = &f->props;
+    sampletype samptype;
+    unsigned int origsize, origrate, winlen,decfac,analchans;
+    float arate;
+#ifdef _DEBUG
+    assert(f->curpropsize != 0);
+    assert(f->pvxprops != NULL);
+    assert(f->pvxfileno >=0);
+#endif
+    return 0;
+}
+#endif
+
+
+int sf_makepath(char path[], const char* sfname)
+{
+        char* fullname = mksfpath(sfname);
+        if(fullname==NULL)
+                return -1;
+        strcpy(path,fullname);
+        return 0;
+}

+ 227 - 0
dev/newsfsys/shortcuts.c

@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+ The CDP System is free software; you can redistribute it
+ and/or modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The CDP System is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the CDP System; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA
+ *
+ */
+
+
+
+/***********************************************************
+
+ How to Obtain Filename and Path from a Shell Link or Shortcut
+
+ Last reviewed: July 19, 1996
+ Article ID: Q130698
+ The information in this article applies to:
+
+ * Microsoft Win32 Software Development Kit (SDK), versions 3.51
+      and 4.0
+
+ SUMMARY
+
+ The shortcuts used in Microsoft Windows 95 provide applications and
+ users a way to create shortcuts or links to objects in the shell's
+ namespace. The IShellLink OLE Interface can be used to obtain the
+ path and filename from the shortcut, among other things.
+
+ MORE INFORMATION
+
+ A shortcut allows the user or an application to access an object
+ from anywhere in the namespace. Shortcuts to objects are stored as
+ binary files. These files contain information such as the path to
+ the object, working directory, the path of the icon used to display
+ the object, the description string, and so on.
+
+ Given a shortcut, applications can use the IShellLink interface and
+ its functions to obtain all the pertinent information about that
+ object. The IShellLink interface supports functions such as
+ GetPath(), GetDescription(), Resolve(), GetWorkingDirectory(), and
+ so on.
+
+ Sample Code
+
+ The following code shows how to obtain the filename or path and
+ description of a given link file:
+
+************************************************/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <shlobj.h>
+#include <io.h>
+#include "alias.h"               //RWD: for wrapper funcs
+
+/********* CONTENTS OF ALIAS.H :
+
+           int COMinit(void);
+           void COMclose(void);
+           int getAliasInfo(const char *linkfilename, char *path, char *desc);
+           int fileExists(const char *name);
+
+           *********** END OF CONTENTS OF ALIAS.H ************/
+
+static void ErrorMessage(LPCTSTR str, HRESULT hr);
+
+/* RWD: for encapsulation, this should be declared static
+
+ * GetLinkInfo() fills the filename and path buffer
+ * with relevant information.
+ * hWnd         - calling application's window handle.
+ *
+ * lpszLinkName - name of the link file passed into the function.
+ *
+ * lpszPath     - the buffer that receives the file's path name.
+ *
+ * lpszDescription - the buffer that receives the file's
+ * description.
+ */
+
+HRESULT
+GetLinkInfo( HWND    hWnd,
+             LPCTSTR lpszLinkName,
+             LPSTR   lpszPath,
+             LPSTR   lpszDescription)
+{
+
+    HRESULT hres;
+    IShellLink *pShLink;
+    WIN32_FIND_DATA wfd;
+
+    /* Initialize the return parameters to null strings.*/
+    *lpszPath = '\0';
+    *lpszDescription = '\0';
+
+    /* Call CoCreateInstance to obtain the IShellLink
+     * Interface pointer. This call fails if
+     * CoInitialize is not called, so it is assumed that
+     * CoInitialize has been called.
+     */
+    hres = CoCreateInstance( &CLSID_ShellLink,
+                             NULL,
+                             CLSCTX_INPROC_SERVER,
+                             &IID_IShellLink,
+                             (LPVOID *)&pShLink );
+
+    if (SUCCEEDED(hres))
+        {
+            IPersistFile *ppf;
+
+            /* The IShellLink Interface supports the IPersistFile
+             * interface. Get an interface pointer to it.
+             */
+            hres = pShLink->lpVtbl->QueryInterface(pShLink,
+                                                   &IID_IPersistFile,
+                                                   (LPVOID *)&ppf );
+            if (SUCCEEDED(hres))
+                {
+                    WORD wsz[MAX_PATH];
+
+                    /* Convert the given link name string to a wide character string. */
+                    MultiByteToWideChar( CP_ACP, 0,
+                                         lpszLinkName,
+                                         -1, wsz, MAX_PATH );
+                    /* Load the file. */
+                    hres = ppf->lpVtbl->Load(ppf, wsz, STGM_READ );
+                    if (SUCCEEDED(hres))
+                        {
+                            /* Resolve the link by calling the Resolve() interface function.
+                             * This enables us to find the file the link points to even if
+                             * it has been moved or renamed.
+                             */
+                            hres = pShLink->lpVtbl->Resolve(pShLink,  hWnd,
+                                                            SLR_ANY_MATCH | SLR_NO_UI);
+                            if (SUCCEEDED(hres))
+                                {
+                                    /* Get the path of the file the link points to. */
+                                    hres = pShLink->lpVtbl->GetPath( pShLink, lpszPath,
+                                                                     MAX_PATH,
+                                                                     &wfd,
+                                                                     SLGP_SHORTPATH );
+
+                                    /* Only get the description if we successfully got the path
+                                     * (We can't return immediately because we need to release ppf &
+                                     *  pShLink.)
+                                     */
+                                    if(SUCCEEDED(hres))
+                                        {
+                                            /* Get the description of the link.  ** RWD: probably empty most times! */
+                                            hres = pShLink->lpVtbl->GetDescription(pShLink,
+                                                                                   lpszDescription,
+                                                                                   MAX_PATH );
+                                        }
+                                }
+                        }
+                    ppf->lpVtbl->Release(ppf);
+                }
+            pShLink->lpVtbl->Release(pShLink);
+        }
+    else
+        ErrorMessage("Failure: CoCreateInstance",hres);      /*RWD: eg of error message usage*/
+    return hres;
+}
+
+/********* RWD utility functions *********/
+
+
+int COMinit(void)
+{
+    HRESULT res = CoInitialize(NULL);
+    if(res==S_OK)
+        return 1;
+    else
+        return 0;
+}
+
+void COMclose(void)
+{
+    CoUninitialize();
+}
+
+int fileExists(const char *name)
+{
+    struct _finddata_t file;
+    long hFile;
+
+    hFile = _findfirst((char *)name,&file);
+    return (int)(hFile != -1L);
+}
+/*the wrapper function itself, for a console application (no window handle) */
+/*requires COM to be initialized*/
+int getAliasInfo(const char *linkfilename, char *path, char *desc)
+{
+    HRESULT res;
+    res = GetLinkInfo(NULL, (LPCTSTR) linkfilename, (LPSTR) path, (LPSTR) desc);
+    return (int) SUCCEEDED(res);
+}
+
+/*from Inside COM: DaleRogerson, changed from C++ to plain C*/
+void ErrorMessage(LPCTSTR str, HRESULT hr)
+{
+    void *pMsgBuf;
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                  NULL,hr,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+                  (LPTSTR) &pMsgBuf, 0, NULL);
+
+    printf("\n%s\n%s\n",str,(const char *)pMsgBuf);
+    LocalFree(pMsgBuf);
+}

+ 1741 - 0
dev/newsfsys/snd.c

@@ -0,0 +1,1741 @@
+/* RWD version to support pvx files */
+/*
+ * Copyright (c) 1983-2013 Martin Atkins, Richard Dobson and Composers Desktop Project Ltd
+ * http://people.bath.ac.uk/masrwd
+ * http://www.composersdesktop.com
+ *
+ This file is part of the CDP System.
+
+    The CDP System is free software; you can redistribute it
+    and/or modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    The CDP System is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with the CDP System; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+    02111-1307 USA
+ *
+*/
+
+
+
+/*
+ *      Sound Filing System - Buffered sound system, open/close, etc.
+ *
+ *      Portable version
+ *
+ *      Copyright M. C. Atkins, 1986, 1987, 1993; RWD 2007,2014,2022
+ *      All Rights Reserved.
+ */
+/* RWD: old sfsys functions DEPRECATED and emptied */
+/* RWD Dec 2019  fixed SndCloseEx for int64 comps */
+#ifdef _DEBUG
+#include <stdio.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <sfsys.h>
+#include "sffuncs.h"
+#ifdef unix
+#include <unistd.h>
+#endif
+#ifdef ENABLE_PVX
+# ifdef unix
+#  include "wavdefs.h"
+#  include "pvdefs.h"
+# endif
+# include "pvfileio.h"
+# ifdef _DEBUG
+#  include <assert.h>
+# endif
+#endif
+#ifdef _WIN32
+#ifdef _MSC_VER
+static __inline int cdp_round(double fval)
+{
+    int result;
+    asm{
+        fld     fval
+        fistp   result
+        mov     eax,result /* RWD: need this line? */
+    }
+    return result;
+}
+#else
+# ifndef __GNUWIN32__
+static int cdp_round(double val)
+{
+    int k;
+    k = (int)(fabs(val)+0.5);
+    if(val < 0.0)
+        k = -k;
+    return k;
+}
+# endif
+#endif
+#endif
+
+/*
+ *      This is the main structure describing a procom file
+ */
+struct sndfile {
+    int     flags;              /* various flags */
+    int     fd;                 /* SFfile/gemfile file descripter */
+#ifdef ENABLE_PVX
+    int pvxid;
+#endif
+    char    *buffer;           /* pointer to the buffer */
+    char    *next;             /* next entry in buffer to access */
+    int     bufsiz;             /* the size of the buffer (in sectors) */
+    int     remain;             /* number of samples remaining in buffer */
+    char    *endbuf;           /* end of buffer (for short bufs at eof) */
+    int     samptype;           /* the type of samples in the file */
+    int     lastread;           /* number of bytes obtained from last read */
+    int     true_bufsize;               /*for 24bit alignment, etc: adjust buffer downwards*/
+    int     scale_floats;
+    double  fscalefac;
+};
+
+typedef union {
+    int lsamp;
+    /*float fsamp;                  //any use for this?   */
+    unsigned char bytes[4];
+} SND_SAMP;
+
+
+/*
+ *      values for sndfile.flags
+ */
+#define SNDFLG_WRITTEN  (1)     /* the buffer must be flushed back to disk */
+#define SNDFLG_LASTWR   (4)     /* last i/o was a write (for sndtell) */
+#define SNDFLG_TRUNC    (8)     /* truncate when closed */
+#define SNDFLG_USERBUF  (32)    /* the buffer was supplied by the user */
+#define SNDFLG_ATEND    (64)    /* the last i/o went over the end of the file */
+
+/*
+ *      Various constants/global data
+ */
+#define SNDFDBASE       (4000)          /* sndfd's allocated from here */
+#define SNDBUFSIZE      (64)
+
+
+/*
+ *      global storage
+ */
+/*
+ *      The array of open sndfiles
+ */
+//#if defined ENABLE_PVX
+//static struct sndfile *sndfiles[MAXSNDFILES];
+//#else
+static struct sndfile *sndfiles[MAXSNDFILES];
+//#endif
+/*
+ *      mapping table from sample type, to size
+ */
+
+/* the possible sample types are now:
+
+        SAMP_SHORT      (0)             * 16-bit short integers *
+        SAMP_FLOAT      (1)             * 32-bit (IEEE) floating point *
+        SAMP_BYTE       (2)             //recognize 8bit soundfiles?
+        SAMP_LONG       (3)             // four most likely formats
+        SAMP_2424       (4)
+        SAMP_2432       (5)
+        SAMP_2024       (6)
+        SAMP_MASKED (7)         //some weird WAVE_EX format!
+
+*/
+
+/*RWD.6.99 set this as container-size
+ *private, but sfsys.c needs to have it
+ */
+
+
+
+int sampsize[] = {
+
+/* SAMP_SHORT */        sizeof(short),
+/* SAMP_FLOAT */        sizeof(float),
+/* SAMP_BYTE  */        sizeof(unsigned char),
+/* SAMP_LONG  */        sizeof(int),
+/* SAMP_2424  */        3 * sizeof(unsigned char),
+/* SAMP_2432  */        sizeof(int),
+/* SAMP_2024  */        3 * sizeof(unsigned char),
+/* dummy */             0                 /*for masked formats, have to calc from 1st principles...*/
+};
+
+/*
+ *      Utility routines
+ */
+#define getmem(type)    ((type *)malloc(sizeof(type)))
+extern int _rsf_getmaxpeak(int sfd,float *peak);
+extern int _rsf_getbitmask(int sfd);
+
+/*
+ *      initialisation/termination routines
+ */
+static void
+rsndallclose(void)
+{
+    int fd;
+
+    for(fd = 0; fd < MAXSFNAME; fd++)
+        if(sndfiles[fd] != 0)
+            sndcloseEx(fd + SNDFDBASE);  //RWD was plain sndclose()
+#if defined ENABLE_PVX
+    pvsys_release();
+#endif
+}
+
+static void
+rsndinit()
+{
+    register int fd;
+    static int inited = 0;
+
+    if(inited != 0)
+        return;
+    inited++;
+    for(fd = 0; fd < MAXSFNAME; fd++)
+        sndfiles[fd] = 0;
+    atexit(rsndallclose);       //RWD if we need to be reentrant, we can't use this!
+}
+
+/*
+ *      map a sndfd onto a sndfiles index,
+ *      return 0 on failure
+ */
+
+/*RWD I want a public version of this, for use in ASSERT etc*/
+
+static int
+mapsndfd(int *sfdp)
+{
+        *sfdp -= SNDFDBASE;
+
+        if(*sfdp < 0 || *sfdp >= MAXSNDFILES) {
+                rsferrno = ESFBADADDR;
+                rsferrstr = "sfd is not in valid range";
+                return 0;
+        }
+        if(sndfiles[*sfdp] == 0) {
+                rsferrno = ESFNOTOPEN;
+                rsferrstr = "sndfile is not open (bad sndfd)";
+                return 0;
+        }
+        return 1;
+}
+
+/*
+ *      do the underlying seek
+ * NOTE
+ *      seeks to the end of an sffile are rounded down!!
+ */
+static int
+doseek(struct sndfile *sf, int dist, int whence)
+{
+        return sfseek(sf->fd, dist, whence);
+}
+
+/*RWD Feb 2010 ??? */
+//static long
+//doseekEx(struct sndfile *sf, long dist, int whence)
+//{
+//      return sfseek_buffered(sf->fd, dist, whence);
+//}
+static __int64 doseekEx(struct sndfile *sf, __int64 dist, int whence)
+{
+    return sfseek_buffered(sf->fd, dist, whence);
+}
+
+/*
+ *      sndfilbuf - fill a buffer, for reading
+ *      return true, if an error occurs
+ */
+static int
+sndfilbuf(struct sndfile *sfd)
+{
+    long want /* = SECSIZE * sfd->bufsiz*/ ;          /*RWD: almost a const!*/
+    long got;
+    int rc = 0;
+
+    if(sfd->flags & SNDFLG_ATEND)
+        return 0;
+    want = sfd->true_bufsize;
+    /*only difference - supports other sample sizes*/
+    got = sfread_buffered(sfd->fd, sfd->buffer, want);
+    if(got < 0) {
+        rc = 1;
+        got = 0;
+    }
+    if(got < want)
+        sfd->flags |= SNDFLG_ATEND;
+    sfd->lastread = got;                                    /*bytes of <size>samps*/
+    sfd->flags &= ~(SNDFLG_WRITTEN|SNDFLG_LASTWR);
+    sfd->endbuf = &sfd->buffer[got];
+/*RWD NB all snd funcs think in SAMPLES, of 2 types: SHORTS or FLOATS; all 8bit stuff hidden in sf_calls*/
+    sfd->remain = got / sampsize[sfd->samptype];   /*RWD : adjust for 8bit files...*/
+    sfd->next = sfd->buffer;
+    return rc;
+}
+
+/*
+ *      sndflsbuf - flush a buffer, after writing
+ *      return true, if an error occurs
+ */
+static int
+sndflsbuf(struct sndfile *sfd)
+{
+    int toput = sfd->endbuf - sfd->buffer;
+    int put;
+
+    if(sfd->next == sfd->buffer)
+        return 0;
+    if(sfd->flags & SNDFLG_ATEND)
+        return 0;
+
+    sfd->next = sfd->buffer;
+    sfd->flags &= ~SNDFLG_WRITTEN;
+    sfd->endbuf = sfd->buffer;
+
+    if(!(sfd->flags&SNDFLG_LASTWR) ) {
+        if(doseek(sfd, -sfd->lastread, 1) < 0) {
+            sfd->flags |= SNDFLG_LASTWR;
+            sfd->remain = (sfd->bufsiz<<LOGSECSIZE) / sampsize[sfd->samptype];
+            return 1;
+        }
+    }
+    sfd->flags |= SNDFLG_LASTWR;
+    sfd->remain = (sfd->bufsiz<<LOGSECSIZE) / sampsize[sfd->samptype];
+
+    toput = (toput+SECSIZE-1)&~(SECSIZE-1);
+    if((put = sfwrite(sfd->fd, sfd->buffer, toput)) < 0)
+        return 1;
+    if(put < toput) {
+        sfd->flags |= SNDFLG_ATEND;
+        sfd->remain = 0;
+    }
+    return 0;
+}
+
+
+static int
+sndflsbufEx(struct sndfile *sfd)
+{
+    int toput = sfd->endbuf - sfd->buffer;
+    int put;
+
+    if(sfd->next == sfd->buffer)
+        return 0;
+    if(sfd->flags & SNDFLG_ATEND)
+        return 0;
+
+    sfd->next = sfd->buffer;
+    sfd->flags &= ~SNDFLG_WRITTEN;
+    sfd->endbuf = sfd->buffer;
+
+    if(!(sfd->flags&SNDFLG_LASTWR) ) {
+        if(doseekEx(sfd,(__int64) -sfd->lastread, 1) < 0) {
+            sfd->flags |= SNDFLG_LASTWR;
+            sfd->remain = sfd->true_bufsize / sampsize[sfd->samptype];
+            return 1;
+        }
+    }
+    sfd->flags |= SNDFLG_LASTWR;
+    sfd->remain = sfd->true_bufsize / sampsize[sfd->samptype];
+    if((put = sfwrite_buffered(sfd->fd, sfd->buffer, toput)) < 0)
+        return 1;
+
+    if(put < toput) {
+        sfd->flags |= SNDFLG_ATEND;
+        sfd->remain = 0;
+    }
+    return 0;
+}
+
+
+/*
+ *      free the sample buffer, if it wasn't supplied by a sndsetbuf
+ */
+static void
+freesndbuf(int fd)
+{
+    if(sndfiles[fd]->flags & SNDFLG_USERBUF)
+        return;
+    free(sndfiles[fd]->buffer);
+}
+
+/*
+ *      free the memory for an open SFfile
+ *      used on last close, and failure of creat
+ */
+static void
+freesndfd(int fd)
+{
+    freesndbuf(fd);
+    free((char *)sndfiles[fd]);
+    sndfiles[fd] = 0;
+    return;
+}
+
+/*
+ *      Try to find a snd file descripter
+ *      returns -1, if there aren't any
+ */
+static int
+findsndfd()
+{
+    register int fd = 0;
+
+    while(sndfiles[fd] != 0)
+        if(++fd >= MAXSNDFILES) {
+            rsferrno = ESFNOSFD;
+            rsferrstr = "Too many sndfiles are already open";
+            return -1;
+        }
+
+    rsferrstr = "no memory to open sndfile";
+    rsferrno = ESFNOMEM;
+
+    if((sndfiles[fd] = getmem(struct sndfile)) == 0)
+        return -1;
+    if((sndfiles[fd]->buffer = (char*) malloc(SNDBUFSIZE*SECSIZE)) == 0) {
+        free((char *)sndfiles[fd]);
+        sndfiles[fd] = 0;
+        return -1;
+    }
+#ifdef ENABLE_PVX
+    sndfiles[fd]->pvxid = -1;
+#endif
+    sndfiles[fd]->flags = 0;
+    sndfiles[fd]->bufsiz = SNDBUFSIZE;
+    sndfiles[fd]->lastread = 0;
+    sndfiles[fd]->endbuf = sndfiles[fd]->next = sndfiles[fd]->buffer;
+    sndfiles[fd]->true_bufsize = SNDBUFSIZE * SECSIZE;      /*may get rounded down for 24bit formats*/
+    return fd;
+}
+
+/*
+ *      The user-accessable routines
+ */
+
+/*
+ *      open a sndfile
+ */
+
+int
+sndopenEx(const char *fn,int auto_scale, int access)
+{
+    register struct sndfile *sf;
+    int fd;
+#ifdef ENABLE_PVX
+    PVOCDATA pvxdata;
+    int rc;
+#endif
+    rsndinit();
+
+    if((fd = findsndfd()) < 0)
+        return -1;
+    sf = sndfiles[fd];
+
+    if((sf->fd = sfopenEx(fn,access)) < 0) {
+        freesndfd(fd);
+        return -1;
+    }
+    
+    /* RWD TODO: add pvx support here_ sfopenEx can produce one */
+#ifdef ENABLE_PVX
+    if(sf_ispvx(sf->fd)){
+        if((rc = get_pvxfd(sf->fd ,&pvxdata )) < 0){
+# ifdef _DEBUG
+            fprintf(stderr,"sndopenEx: bad sf id\n");
+# endif
+            return -1;
+        }
+        else if (rc >= 0){
+# ifdef _DEBUG
+            fprintf(stderr,"sndopenEx: file %s is a pvx file!\n", fn);
+# endif
+            return fd + SNDFDBASE;
+        }
+    }
+    // else its a soundfile or a .ana file, carry on  as usual
+#endif
+    /* all the rest just for standard CDP files */
+    sf->remain = 0;
+    if(sfgetprop(sf->fd, "sample type",
+                (char *)&sf->samptype, sizeof(int)) != sizeof(int)) {
+        rsferrno = ESFNOSTYPE;
+        rsferrstr = "no sample type defined";
+        freesndfd(fd);
+        return -1;
+    }
+    /*RWD only used to rescale floatsams when read into shorts (eg for playback)*/
+    sf->scale_floats = 0;
+    sf->fscalefac = 1.0;
+    if(auto_scale && (sf->samptype==SAMP_FLOAT)){
+        float fac = 0.0f;
+        /* RWD 4:2002: I would prefer to rely on PEAK only for this now... */
+        if(sfgetprop(sf->fd,"maxamp",(char *)&fac,sizeof(float)) == sizeof(float)){
+            if(fac > sf->fscalefac){
+                sf->scale_floats = 1;
+                sf->fscalefac = 0.99995 / (double) fac;
+            }
+        }
+        /*get it from PEAK data if available*/
+        else if (_rsf_getmaxpeak(sf->fd,&fac) > 0){
+            if(fac > sf->fscalefac){
+                sf->scale_floats = 1;
+                sf->fscalefac = 0.99995 / (double) fac;
+            }
+        }
+                /*Could also provide a set_autoscale() func, legal only before first read (like sndsetbuf)*/
+    }
+
+    /*adjust internal buffer if reading 24bit formats*/
+    if(sf->samptype==SAMP_2424 || sf->samptype == SAMP_2024)
+        sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype];
+
+    /* need superfluous seek to overcome daft OS problems reopening just-closed file */
+    if(sndseekEx(fd+SNDFDBASE,0,0) != 0)
+        return -1;
+    return fd+SNDFDBASE;
+}
+
+/* RWD PVX note: this is the form called by CDP for ANAL files */
+int
+sndcreat_formatted(const char *fn, int size, int stype,int channels,
+                                   int srate,cdp_create_mode mode)
+{
+    register struct sndfile *sf;
+    int fd;
+
+    rsndinit();
+
+    if((fd = findsndfd()) < 0)
+        return -1;
+    sf = sndfiles[fd];
+
+    sf->flags |= SNDFLG_TRUNC;
+    sf->samptype = stype;
+
+    /*RWD NB sampsize[]  - no slot for 24bit size yet*/
+    /* if pvx file, sets sffile->pvxfileno (which may be 0 ), but returns sf id (which may be 1) */
+    if((sf->fd = sfcreat_formatted(fn, size*sampsize[stype], (__int64 *)0,channels,srate,
+                    stype,mode)) < 0) {
+        freesndfd(fd);
+        return -1;
+    }
+    
+    // now need to set sf->pvxid, if this is a pvx file.
+    sf->remain = sf->true_bufsize / sampsize[stype];
+    return fd+SNDFDBASE;
+}
+
+/* RWD PVX: Trouble is, we need to use this one, to get props */
+/* means just one change (???!!!???) inside cdp2k, mainfunc.c */
+int
+sndcreat_ex(const char *name, int size,SFPROPS *props,int min_header,cdp_create_mode mode)
+{
+        register struct sndfile *sf;
+        int fd;
+        int smpsize;
+        rsndinit();
+
+        if((fd = findsndfd()) < 0)
+                return -1;
+        sf = sndfiles[fd];
+
+        sf->flags |= SNDFLG_TRUNC;
+
+        /* RWD.6.99 write 24bit formats, etc */
+        switch(props->samptype){
+        case(FLOAT32):
+                sf->samptype = SAMP_FLOAT;
+                break;
+        case(SHORT16):
+                sf->samptype = SAMP_SHORT;
+                break;
+        case(INT_32):
+                sf->samptype = SAMP_LONG;
+                break;
+        case(INT2424):
+                sf->samptype = SAMP_2424;
+                /*adjust sndbuffer with 3-byte alignment*/
+                sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype];
+                break;
+        case(INT2024):
+                sf->samptype = SAMP_2024;
+                /*adjust sndbuffer with 3-byte alignment*/
+                sf->true_bufsize = (sf->true_bufsize / sampsize[sf->samptype]) * sampsize[sf->samptype];
+                break;
+        case(SAMP_MASKED):
+                /*don't know how to do this yet...*/
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "cannot write masked sample type";
+                return -1;
+        default:
+                rsferrno = ESFBADPARAM;
+                rsferrstr = "unknown sample type";
+                return -1;
+                break;
+
+        }
+
+        /*sf->samptype = (props->samptype == FLOAT32 ? SAMP_FLOAT : SAMP_SHORT);*/
+        smpsize = sampsize[sf->samptype];
+
+        /*need to return to outsize: size / smpsize - check this...*/
+
+    if((sf->fd = sfcreat_ex(name, (__int64)size *smpsize, (__int64 *)0,
+                        props,min_header,mode)) < 0) {
+                freesndfd(fd);
+                return -1;
+        }
+        sf->remain = sf->true_bufsize / smpsize;
+        return fd+SNDFDBASE;
+}
+
+/*
+ *      close a sndfile
+ */
+
+int
+sndcloseEx(int fd)
+{
+    register struct sndfile *sf;
+    int rc = 0;
+    __int64 length, pos;
+
+    if(!mapsndfd(&fd))
+        return -1;
+    sf = sndfiles[fd];
+#ifdef ENABLE_PVX
+    if(sf_ispvx(sf->fd)){
+      //  if(sf_ispvx(sf->fd,0) >=0) {
+# ifdef _DEBUG
+            fprintf(stderr,"calling sfclose with fd %d\n",sf->fd);
+# endif
+            rc =  sfclose(sf->fd);   // I think..nothing else to do for pvx file!
+            if(rc < 0) {
+# ifdef _DEBUG
+                fprintf(stderr,"sfclose error, rc = %d\n",rc);
+# endif
+                sfperror("sndcloseEx");
+            }
+            freesndfd(fd);
+            return rc;
+      //  }
+    }
+#endif
+
+    if(sf->flags&SNDFLG_TRUNC) {
+        length = sfsize(sf->fd);
+        pos = sndtellEx(fd + SNDFDBASE); //RWD 12-12-19 make sure of 64bit ints in calcs
+        pos *= sampsize[sf->samptype];
+        if(sf->flags  & SNDFLG_WRITTEN )        /* should never exec */
+            sndflsbufEx(sf);                /* rsfsize does it!  */
+        if((rc = sfadjust(sf->fd,pos-length)) < 0) {
+            rsferrno = ESFWRERR;
+            rsferrstr = "can't truncate SFfile";
+        }
+    } else if(sf->flags  & SNDFLG_WRITTEN )
+        sndflsbufEx(sf);
+    rc |= sfclose(sf->fd);
+
+    freesndfd(fd);
+    return rc;
+}
+
+
+/*RWD.6.98 needed for Release98!*/
+int sndunlink(int sndfd)
+{
+    //int size;
+    struct sndfile *sf;
+    /* RWD PVX TODO: hopefully wouldn't need to perform this hack on pvx file....? */
+    (void)sndseekEx(sndfd,0,0);     /*RWD.7.98 hack to fix bug when closing empty file*/
+    if(!mapsndfd(&sndfd))
+        return -1;
+
+    sf = sndfiles[sndfd];
+    return sfunlink(sf->fd);
+}
+
+
+/*
+ *      Return the size (in samples) of a sndfile
+ * NOTE
+ *      the first sndseek will flush buffer if necessary
+ */
+
+int
+sndsizeEx(int sfd)
+{
+    int oldpos;
+    int size;
+
+    if((oldpos = sndtellEx(sfd)) < 0)         /* RWD: pvx handling done */
+        return -1;
+    if((size = sndseekEx(sfd, 0L, 2)) < 0) {
+        size = -1;
+        rsferrno = ESFNOSEEK;
+        rsferrstr = "can't seek to end of file to find size";
+    }
+    if(sndseekEx(sfd, oldpos, 0) < 0) {
+        rsferrno = ESFNOSEEK;
+        rsferrstr = "can't restore position after finding file size";
+        return -1;
+    }
+    return size;
+}
+
+/*
+ *      seek in a sndfile  (of SHORTS or FLOATS)
+ */
+int
+sndseek(int fd, int dist, int whence)
+{
+        //return sndseekEx(fd,dist,whence);
+        return -1;
+}
+
+//RWD NB PVX: returns new file pos (in samples), or -1 for error
+int
+sndseekEx(int fd, int dist, int whence)
+{
+    register struct sndfile *sf;
+    long bufsize;
+    __int64 secpos;
+    __int64 newpos = 0;
+    __int64 gotpos;
+
+    if(!mapsndfd(&fd))
+        return -1;
+    sf = sndfiles[fd];
+#ifdef ENABLE_PVX
+    if(sf_ispvx(sf->fd)){       // RWD TODO: shoukld add test for error return
+        PVOCDATA pvxdata;
+        // we must return currentframepos as raw floatsams
+        int frameoffset;
+        int curframe;
+        int goalframe = 0;
+        int rc = 0;
+        int pvxid;
+        pvxid = get_pvxfd(sf->fd,&pvxdata);
+# ifdef _DEBUG
+        assert(pvxid >= 0);
+        
+        // eventually we will code this permanently...but is this the best place to put this test?
+        if(dist > 0)
+            assert(dist % (pvxdata.nAnalysisBins * 2) == 0);
+# endif
+        frameoffset = dist / (pvxdata.nAnalysisBins * 2);  //NB: always mono stream in CDP
+        
+        switch(whence){
+                //set: go to "dist" samples: must be int multiple of  framesize
+                // seems never to be called for CDP ana routines, except for dist= 0
+        case SEEK_SET:
+            
+            rc = pvoc_seek_mcframe(sf->fd - 1000, frameoffset,SEEK_SET);
+            //or: if dist==0, use pvoc_rewind()?
+            if(rc < 0){
+                return rc;
+            }
+            rc =  pvoc_framepos(sf->fd - 1000) * (pvxdata.nAnalysisBins * 2);
+            break;
+            //tell - Ok to use pvoc_framepos, OR can use sndtellEx below
+        case SEEK_CUR:
+                // not directly handled in pvfileio.c yet
+                rc = pvoc_seek_mcframe(sf->fd - 1000, frameoffset,SEEK_CUR);
+                if(rc < 0){
+                    return rc;
+                }
+                rc =  pvoc_framepos(sf->fd - 1000) * (pvxdata.nAnalysisBins * 2);
+                break;
+            //end: step back dist samps if negative. We do not allow setting beyond EOF.
+        case SEEK_END:
+                rc = pvoc_seek_mcframe(sf->fd - 1000, frameoffset,SEEK_END);
+                if(rc < 0){
+                    return rc;
+                }
+                rc =  pvoc_framepos(sf->fd - 1000);     //frames
+                rc  *= pvxdata.nAnalysisBins * 2;    // samps. caller has to compute nframes
+                break;
+        }
+        return rc;
+    }
+#endif
+    // std CDP files from here
+        bufsize = sf->true_bufsize;
+
+        if((sf->flags & SNDFLG_WRITTEN) && sndflsbufEx(sf))
+                return -1;
+        switch(whence) {
+        case 0:
+                newpos = dist;           /*in SAMPLES: NB must deal with 8bit files!*/
+                break;
+        case 1:
+                newpos = sndtellEx(fd+SNDFDBASE) + dist;
+                break;
+        case 2:
+                newpos = sfsize(sf->fd);           /*size-specific:SHORTSAMS, from datachunksize*/
+                newpos = (newpos / sampsize[sf->samptype]) + dist;              /*make non-size-specific*/
+                break;
+        }
+        if(newpos < 0)
+                newpos = 0;
+        newpos *= sampsize[sf->samptype];               /* byte offset */ /*restore size-specific for doseek*/
+        /*RWD.6.99 still need to do this - not a SECSIZE calc, but just cdp_round to our buffersize*/
+        secpos = (newpos/bufsize)*bufsize;              /* cdp_round down */
+
+/*RWD 2007: NB for FILE64_WIN doseekEx takes and returns __int64 */
+        if((gotpos = doseekEx(sf, secpos, 0)) < 0)/*NB seek might be truncated */        /*gotpos = non-size-specific*/
+                return -1;
+
+        sf->flags &= ~SNDFLG_ATEND;
+
+        if(sndfilbuf(sf)) {                     /* if sndfilbuf fails... */
+                sf->next = sf->buffer;
+                sf->remain = 0;
+                return -1;
+        }
+
+        if(gotpos < secpos) {
+                newpos -= sf->remain;     /*RWD 2007 !!! remain supposed to count ~samples~ */
+                sf->remain = 0;
+        }
+        sf->next = &sf->buffer[(unsigned long)newpos % bufsize];
+        newpos /= sampsize[sf->samptype];
+        sf->remain = (sf->endbuf - sf->next)/sampsize[sf->samptype];
+        return (long) newpos;
+}
+
+/*
+ *      Where are we in the present sndfile
+ * NOTE
+ *      This does not need to flush buffers, etc
+ */
+int
+sndtell(int fd)
+{
+        return -1;
+
+}
+
+/*RWD 2007 FIXME for 64bit reads: */
+/* doseekEx must return unsigned long, or __int64 value */
+/* RWD PVX: this is called only once in all of CDP7/8: pvthreads.cpp, to get current framepos */
+/* direct equivalent is pvoc_framepos() */
+/* but sfsys expects (float) sample pos as for (mono) .ana file */
+/* don't want to clone all the sf calls here (dealing with SECSIZE etc */
+int
+sndtellEx(int fd)
+{
+    struct sndfile *sf;
+        //long off;
+    __int64  off;
+
+    if(!mapsndfd(&fd))      //strips SNDFDBASE: fd = 0
+        return -1;
+    sf = sndfiles[fd];        //sf-fd = 1000
+    
+#ifdef ENABLE_PVX
+    if(sf_ispvx(sf->fd)){
+        int pvxid;
+        PVOCDATA pvxdata;
+        
+        pvxid = get_pvxfd(sf->fd,&pvxdata);
+        if(pvxid >=0){
+        // we must return currentframepos as raw floatsams
+            int32_t framepos;
+            int32_t nsamps;
+            //TODO: sf->fd is sf element, offset by SFDBASE= 1000
+            // need to remove that first - how?
+            framepos = pvoc_framepos(sf->fd - 1000);
+            if(framepos < 0){
+# ifdef _DEBUG
+                fprintf(stderr,"bad return %d from pvoc_framepos\n",framepos);
+# endif
+                return -1;
+            }
+            nsamps = framepos * (pvxdata.nAnalysisBins * 2); //of course, 1 bin  = [amp,freq] duplet
+            return (int) nsamps;
+        }
+    }
+#endif
+    // std CDP files from here
+    
+    if((off = doseekEx(sf, 0L, 1)) < 0) {      /*NB: must return cnt of samples * sizeof(type=SHORTS or FLOATS)*/
+        rsferrno = ESFNOSEEK;
+        rsferrstr = "can't seek to find present position in file";
+        return -1;
+    }
+    if(sf->flags & SNDFLG_LASTWR)                   /*RWD.5.1.99 fails for short sndfiles within a buffer*/
+        off += sf->true_bufsize;
+
+    if(sf->flags & SNDFLG_ATEND)
+        off = sfsize(sf->fd);      /*bytes, of SHORTS or FLOATS samps*/
+    off /= sampsize[sf->samptype];
+
+    return (int)(off - ( (sf->remain < 0) ? 0 : sf->remain));
+}
+
+/*
+ *      let the user supply a larger buffer
+ */
+
+
+/*RWD.6.99 NB bufsize is in SECTORS - may need new version for new formats...*/
+int
+sndsetbuf(int sfd, char *buf, int bufsize)
+{
+        struct sndfile *sf;
+
+        if(!mapsndfd(&sfd))
+                return -1;
+        sf = sndfiles[sfd];
+        if(sf->remain != 0) {
+                rsferrno = ESFLOSEDATA;
+                rsferrstr = "sndsetbuf would lose data";
+                return -1;
+        }
+        freesndbuf(sfd);
+        sf->buffer = buf;
+        sf->bufsiz = bufsize;
+        sf->true_bufsize = sf->bufsiz << LOGSECSIZE;
+        sf->flags |= SNDFLG_USERBUF;
+        return 0;
+}
+
+/*
+ *      snd I/O routines
+ */
+/*
+ *      fgetfloat - read the next float
+ */
+// RWD TODO: looks like several programs still call this!
+
+int
+fgetfloat(float *fp, int sfd)
+{
+        register struct sndfile *sfp;
+
+        if(!mapsndfd(&sfd))
+                return -1;
+        sfp = sndfiles[sfd];
+        if(sfp->remain < 0)
+                return 0;
+        if(sfp->remain-- == 0) {
+                if(sndfilbuf(sfp))
+                        return -1;
+                if(sfp->remain-- <= 0)
+                        return 0;
+        }
+        if(sfp->samptype == SAMP_FLOAT) {
+                *fp = *(float *)sfp->next;
+                sfp->next += sizeof(float);
+        } else {
+                *fp = (float)(*(short *)sfp->next) / (float)MAXSHORT;
+                sfp->next += sizeof(short);
+        }
+        return 1;
+}
+
+int
+fgetfloatEx(float *fp, int sfd,int expect_floats)
+{
+    register struct sndfile *sfp;
+    long lword = 0;
+    long mask;               /*we need signed ints*/
+    SND_SAMP ssamp;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    if(sfp->remain < 0)
+        return 0;
+    if(sfp->remain-- == 0) {
+        if(sndfilbuf(sfp))
+            return -1;
+        if(sfp->remain-- <= 0)
+            return 0;
+        }
+        ssamp.lsamp = 0;
+        switch(sfp->samptype){
+        case(SAMP_FLOAT):
+            *fp = *(float *)sfp->next;
+            sfp->next += sizeof(float);
+            break;
+        case(SAMP_SHORT):
+            *fp = (float)(*(short *)sfp->next) / (float)MAXSHORT;
+            sfp->next += sizeof(short);
+            break;
+        case(SAMP_LONG):
+            if(expect_floats){
+                *fp = *(float *)sfp->next;
+                sfp->next += sizeof(float);
+            }
+            else{
+                *fp = (float)((double) (*(int *)sfp->next) / (float)MAXINT);
+                sfp->next += sizeof(int);
+            }
+            break;
+        case(SAMP_2432):
+            /*mask the word first*/
+            lword =  *(long *)sfp->next;
+            lword &= 0xffffff00;
+            *fp = (float)((double)lword / MAXINT);
+            sfp->next += sizeof(int);
+            break;
+        case(SAMP_2024):        /*need to mask it?*/
+            ssamp.lsamp = 0;
+            mask = _rsf_getbitmask(sfp->fd);
+            if(mask==0)
+                return -1;
+#ifdef LSBFIRST
+            ssamp.bytes[1] = sfp->next[0];
+            ssamp.bytes[2] = sfp->next[1];
+            ssamp.bytes[3] = sfp->next[2];
+#else
+            ssamp.bytes[0] = sfp->next[0];
+            ssamp.bytes[1] = sfp->next[1];
+            ssamp.bytes[2] = sfp->next[2];
+#endif
+            *fp = (float)((double)(ssamp.lsamp & mask) / MAXINT);
+            sfp->next += 3;
+            break;
+        case(SAMP_2424):
+#ifdef LSBFIRST
+            ssamp.bytes[1] = sfp->next[0];
+            ssamp.bytes[2] = sfp->next[1];
+            ssamp.bytes[3] = sfp->next[2];
+#else
+            ssamp.bytes[0] = sfp->next[0];
+            ssamp.bytes[1] = sfp->next[1];
+            ssamp.bytes[2] = sfp->next[2];
+#endif
+            *fp = (float)((double)(ssamp.lsamp) / MAXINT);
+            sfp->next += 3;
+            break;
+        default:
+            rsferrno = ESFBADPARAM;
+            rsferrstr = "attempted to read unknown sample format";
+            return -1;                      /*do others later...*/
+            break;
+        }
+        return 1;
+}
+
+
+
+
+/*
+ *      fputfloat - write the next float
+ */
+int
+fputfloat(float *fp, int sfd)
+{
+        register struct sndfile *sfp;
+
+        if(!mapsndfd(&sfd))
+                return -1;
+        sfp = sndfiles[sfd];
+        if(sfp->flags&SNDFLG_ATEND)
+                return 0;
+        sfp->flags |= SNDFLG_WRITTEN;
+        /* sndtell checks this*/
+        sfp->flags |= SNDFLG_LASTWR;
+
+        if(sfp->samptype == SAMP_FLOAT) {
+                *(float *)sfp->next = *fp;
+                sfp->next += sizeof(float);
+        } else {
+                /* *(short *)sfp->next = (short) floor(0.5 + *fp * MAXSHORT);*/
+                *(short *)sfp->next = (short) cdp_round( *fp * MAXSHORT);
+                sfp->next += sizeof(short);
+        }
+        if(sfp->next > sfp->endbuf)
+                sfp->endbuf = sfp->next;
+        if(--sfp->remain == 0) {
+                if(sndflsbuf(sfp))
+                        return -1;
+        }
+        return 1;
+}
+
+/*RWD.7.99 replace floor calc with cdp_round(): more accurate!*/
+int
+fputfloatEx(float *fp, int sfd)
+{
+    register struct sndfile *sfp;
+    SND_SAMP ssamp;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    if(sfp->flags&SNDFLG_ATEND)
+        return 0;
+    sfp->flags |= SNDFLG_WRITTEN;
+    /* sndtell checks this*/
+    sfp->flags |= SNDFLG_LASTWR;
+    ssamp.lsamp = 0;
+    switch(sfp->samptype){
+    case(SAMP_FLOAT):
+        *(float *)sfp->next = *fp;
+        sfp->next += sizeof(float);
+        break;
+    case(SAMP_SHORT):
+        /* *(short *)sfp->next = (short)floor(0.5 + *fp * MAXSHORT);*/
+        *(short *)sfp->next = (short) cdp_round( *fp * MAXSHORT);
+        sfp->next += sizeof(short);
+        break;
+    case(SAMP_2024):
+        /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/
+        ssamp.lsamp = (int) cdp_round(*fp * MAXINT);
+#ifdef LSBFIRST
+        sfp->next[0] = ssamp.bytes[1] & 0xf0;
+        sfp->next[1] = ssamp.bytes[2];
+        sfp->next[2] = ssamp.bytes[3];
+#else
+        sfp->next[0] = ssamp.bytes[0] & 0xf0;
+        sfp->next[1] = ssamp.bytes[1];
+        sfp->next[2] = ssamp.bytes[2];
+#endif
+        sfp->next += 3;
+        break;
+
+    case(SAMP_2424):
+        /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/
+        ssamp.lsamp = (int) cdp_round(*fp * MAXINT);
+#ifdef LSBFIRST
+        sfp->next[0] = ssamp.bytes[1];
+        sfp->next[1] = ssamp.bytes[2];
+        sfp->next[2] = ssamp.bytes[3];
+#else
+        sfp->next[0] = ssamp.bytes[0];
+        sfp->next[1] = ssamp.bytes[1];
+        sfp->next[2] = ssamp.bytes[2];
+#endif
+        sfp->next += 3;
+        break;
+    case(SAMP_LONG):
+        /* *(long *)sfp->next = (long) floor(0.5 + *fp * MAXINT);*/
+        *(int *)sfp->next = (int) cdp_round(*fp * MAXINT);
+        sfp->next += sizeof(int);
+        break;
+    case(SAMP_2432):
+        /*ssamp.lsamp = (int) floor(0.5 + *fp * MAXINT);*/
+        ssamp.lsamp = (int) cdp_round(*fp * MAXINT);
+        ssamp.bytes[0] = 0;
+        *(int *)sfp->next = ssamp.lsamp;
+        sfp->next += sizeof(int);
+        break;
+    default:
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "attempted to write unknown sample format";
+        return -1;                      /*do others later...*/
+        break;
+    }
+    if(sfp->next > sfp->endbuf)
+        sfp->endbuf = sfp->next;
+    if(--sfp->remain == 0) {
+        if(sndflsbufEx(sfp))
+            return -1;
+    }
+    return 1;
+}
+
+
+
+/*
+ *      fgetfbuf - get a sequence of float-sams
+ */
+
+/*RWD.6.99 probably the tidiest way of dealing with old floatsam files
+* expect_float must be non-zero to trigger
+*/
+int
+fgetfbufEx(float *fp, int n, int sfd,int expect_floats)
+{
+    register struct sndfile *sfp;
+    int chunk;
+    int cnt = 0;
+#ifdef ENABLE_PVX
+    int rc;
+#endif
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    
+#ifdef ENABLE_PVX
+    rc = sf_ispvx(sfp->fd);
+    if(rc < 0){
+# ifdef _DEBUG
+        fprintf(stderr,"fgetfbufEx: bad sf id\n");
+# endif
+        return cnt;
+    }
+
+    else if(rc){
+        PVOCDATA pvxdata;
+        int n_frames = 0;
+        int binsamps;
+        int pvxfd = get_pvxfd(sfp->fd,&pvxdata);
+        // we absolutely have to confirm n is exact multiple of nAnalysisBins * 2)
+        binsamps = pvxdata.nAnalysisBins * 2;
+        if((n % binsamps) != 0){
+# ifdef _DEBUG
+            fprintf(stderr,"fgetfbufEx: buffersize(%d) must be multiple of analysis frame size\n",n);
+# endif
+            cnt = 0;
+        }
+        n_frames = n / binsamps;
+        // test sfp->samptype too?
+        cnt = pvoc_getframes(sfp->fd - 1000, fp, n_frames);
+        if(cnt <= 0)
+            cnt = 0; // EOF or error
+        else
+            cnt *= binsamps;
+        return cnt;
+    }
+#endif
+    // std CDP files from here
+    if(sfp->samptype==SAMP_FLOAT || ((sfp->samptype==INT_32) && expect_floats)){
+        while(cnt < n) {
+            if(sfp->remain == 0) {
+                if(sndfilbuf(sfp) || sfp->remain == 0)
+                    return cnt;
+            }
+            chunk = n - cnt;
+            if(chunk > sfp->remain)
+                chunk = sfp->remain;
+            memcpy((char *)fp, sfp->next, chunk*sizeof(float));
+            sfp->remain -= chunk;
+            sfp->next += chunk*sizeof(float);
+            cnt += chunk;
+            fp += chunk;
+        }
+    }
+    else{
+        sfd += SNDFDBASE;
+        while(cnt < n && (fgetfloatEx(fp++, sfd,expect_floats) > 0))
+            cnt++;
+            /*return cnt;*/
+    }
+    return cnt;
+}
+/*
+ *      fputfbuf - put a sequence of float-sams
+ */
+int
+fputfbuf(float *fp, int n, int sfd)
+{
+    register struct sndfile *sfp;
+    int chunk;
+    int cnt = 0;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    if(sfp->samptype == SAMP_SHORT) {
+        sfd += SNDFDBASE;
+        while(cnt < n && fputfloat(fp++, sfd) > 0)
+            cnt++;
+        return cnt;
+    }
+    while(cnt < n) {
+        sfp->flags |= SNDFLG_WRITTEN;
+        /* sndtell checks this*/
+        sfp->flags |= SNDFLG_LASTWR;
+        chunk = n - cnt;
+        if(chunk > sfp->remain)
+            chunk = sfp->remain;
+        memcpy(sfp->next, (char *)fp, chunk*sizeof(float));
+        sfp->remain -= chunk;
+        sfp->next += chunk*sizeof(float);
+        if(sfp->next > sfp->endbuf)
+            sfp->endbuf = sfp->next;
+        cnt += chunk;
+        fp += chunk;
+        if(sfp->remain == 0) {
+            if(sndflsbuf(sfp))
+                return -1;
+            if(sfp->remain == 0)
+                return cnt;
+        }
+    }
+    return cnt;
+}
+#ifdef ENABLE_PVX
+// not used yet...
+int snd_getpvxfno(int sfd)
+{
+    register struct sndfile *sfp;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+
+    return sfp->pvxid;
+}
+#endif
+
+int
+fputfbufEx(float *fp, int n, int sfd)
+{
+    register struct sndfile *sfp;
+    int chunk;
+    int cnt = 0;
+#ifdef ENABLE_PVX
+    int rc;
+#endif
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+#ifdef ENABLE_PVX
+    rc = sf_ispvx(sfp->fd);
+    if(rc < 0)
+        return -1;
+    else if(rc) {
+        PVOCDATA pvxdata;
+        int n_frames = 0;
+        int binsamps;
+        int rc;
+        int pvxid = -1;
+        
+        pvxid = get_pvxfd(sfp->fd,&pvxdata);
+        if(pvxid < 0){
+# ifdef _DEBUG
+            fprintf(stderr,"fputfbufEx: bad sf id %d\n",pvxid);
+# endif
+            return cnt;
+        }
+        else {
+            // we absolutely have to confirm n is exact multiple of nAnalysisBins * 2)
+        
+# ifdef _DEBUG
+            assert(pvxid >= 0);
+            assert(pvxdata.nAnalysisBins > 0);
+# endif
+            binsamps = pvxdata.nAnalysisBins * 2;
+            if((n % binsamps) != 0){
+# ifdef _DEBUG
+                fprintf(stderr,"fputfbufEx: buffersize(%d) must be multiple of analysis frame size\n",n);
+# endif
+                cnt = 0;
+            }
+            n_frames = n / binsamps;
+            // test sfp->samptype too?
+            cnt = pvoc_putframes(pvxid, fp, n_frames);
+            if(cnt <= 0)
+                cnt = 0; // EOF or error
+            else
+                cnt *= binsamps;
+            //return cnt;
+            return binsamps * n_frames;
+        }
+    }
+    
+#endif
+    // std CFDP files from here
+    
+    if(sfp->samptype == SAMP_SHORT) {
+        sfd += SNDFDBASE;
+        while(cnt < n && fputfloatEx(fp++, sfd) > 0)
+            cnt++;
+        return cnt;
+    }
+
+    if(sfp->samptype == SAMP_FLOAT){
+        while(cnt < n) {
+            sfp->flags |= SNDFLG_WRITTEN;
+            /* sndtell checks this*/
+            sfp->flags |= SNDFLG_LASTWR;
+            chunk = n - cnt;
+            if(chunk > sfp->remain)
+                chunk = sfp->remain;
+            memcpy(sfp->next, (char *)fp, chunk*sizeof(float));
+            sfp->remain -= chunk;
+            sfp->next += chunk*sizeof(float);
+            if(sfp->next > sfp->endbuf)
+                sfp->endbuf = sfp->next;
+            cnt += chunk;
+            fp += chunk;
+            if(sfp->remain == 0) {
+                if(sndflsbufEx(sfp))
+                    return -1;
+                if(sfp->remain == 0)
+                    return cnt;
+            }
+        }
+    }
+
+    else {
+        sfd += SNDFDBASE;
+        while(cnt < n && fputfloatEx(fp++, sfd) > 0)
+            cnt++;
+        return cnt;
+    }
+    return cnt;
+}
+
+/* RWD.7.99 use cdp_round() instead of floor(): later, try shoft/truncate and/or dithering! */
+int
+fgetshortEx(short *sp, int sfd,int expect_floats)
+{
+    struct sndfile *sfp;
+    int lword = 0;
+    int mask;
+    SND_SAMP ssamp;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    if(sfp->remain < 0)
+        return 0;
+    if(sfp->remain-- == 0) {
+        if(sndfilbuf(sfp))
+            return -1;
+        if(sfp->remain-- <= 0)
+            return 0;
+    }
+    ssamp.lsamp = 0;
+    switch(sfp->samptype){
+    case(SAMP_FLOAT):
+        if(sfp->scale_floats==1)
+            /* *sp = (short)(floor(0.5 + (sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))));*/
+            *sp = (short) cdp_round((sfp->fscalefac * (*(float *)sfp->next * MAXSHORT)));
+        else
+            /* *sp = (short)floor(0.5 + *(float *)sfp->next * MAXSHORT);*/
+            *sp = (short) cdp_round( *(float *)sfp->next * MAXSHORT);
+
+        sfp->next += sizeof(float);
+        break;
+    case(SAMP_SHORT):
+        *sp = *(short *)sfp->next;
+        sfp->next += sizeof(short);
+        break;
+    case(SAMP_LONG):
+        if(expect_floats){
+            if(sfp->scale_floats==1)
+                /* *sp = (short)(floor(0.5 + (sfp->fscalefac * (*(float *)sfp->next * MAXSHORT))));*/
+                *sp = (short) cdp_round((sfp->fscalefac * (*(float *)sfp->next * MAXSHORT)));
+            else
+                /* *sp = (short)floor(0.5 + *(float *)sfp->next * MAXSHORT);*/
+                *sp = (short) cdp_round( *(float *)sfp->next * MAXSHORT);
+
+            sfp->next += sizeof(float);
+        }
+        else{
+            *sp = (short)( (*(int *)sfp->next) >> 16);
+            sfp->next += sizeof(int);
+        }
+        break;
+    case(SAMP_2432):
+        /*mask the word first*/
+        lword =  *(int *)sfp->next;
+        lword &= 0xffffff00;
+        *sp = (short)(lword >> 16);
+        sfp->next += sizeof(int);
+        break;
+    case(SAMP_2024):        /*need to mask it?*/
+        mask = _rsf_getbitmask(sfp->fd);
+        if(mask==0)
+            return -1;
+        ssamp.bytes[1] = sfp->next[0];
+        ssamp.bytes[2] = sfp->next[1];
+        ssamp.bytes[3] = sfp->next[2];
+        *sp = (short)((ssamp.lsamp & mask) >> 16);
+        sfp->next += 3;
+        break;
+    case(SAMP_2424):
+        ssamp.bytes[1] = sfp->next[0];
+        ssamp.bytes[2] = sfp->next[1];
+        ssamp.bytes[3] = sfp->next[2];
+        *sp = (short)(ssamp.lsamp >> 16);
+        sfp->next += 3;
+        break;
+    default:
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "attempted to read unknown sample format";
+        return -1;                      /*do others later...*/
+        break;
+    }
+    return 1;
+}
+
+int
+fputshortEx(short *sp, int sfd)
+{
+    register struct sndfile *sfp;
+    SND_SAMP ssamp;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+    if(sfp->flags&SNDFLG_ATEND)
+        return 0;
+    sfp->flags |= SNDFLG_WRITTEN;
+    /* sndtell checks this*/
+    sfp->flags |= SNDFLG_LASTWR;
+    ssamp.lsamp = 0;
+    switch(sfp->samptype){
+    case(SAMP_FLOAT):
+        *(float *)sfp->next = (float)*sp / (float)MAXSHORT;
+        sfp->next += sizeof(float);
+        break;
+    case(SAMP_SHORT):
+        *(short *)sfp->next = *sp;
+        sfp->next += sizeof(short);
+        break;
+    case(SAMP_2024):
+        /*no need to mask; 16 bits anyway!*/
+    case(SAMP_2424):
+        ssamp.lsamp = (int) ((*sp) << 16);
+        sfp->next[0] = ssamp.bytes[1];
+        sfp->next[1] = ssamp.bytes[2];
+        sfp->next[2] = ssamp.bytes[3];
+        sfp->next += 3;
+        break;
+    case(SAMP_LONG):
+    case(SAMP_2432):
+        ssamp.lsamp = (int) ((*sp) << 16);
+        *(int *)sfp->next = ssamp.lsamp;
+        sfp->next += sizeof(int);
+        break;
+    default:
+        rsferrno = ESFBADPARAM;
+        rsferrstr = "attempted to write unknown sample format";
+        return -1;                      /*do others later...*/
+        break;
+    }
+    if(sfp->next > sfp->endbuf)
+        sfp->endbuf = sfp->next;
+    if(--sfp->remain == 0) {
+        if(sndflsbufEx(sfp))
+            return -1;
+    }
+    return 1;
+}
+
+int
+fgetsbufEx(short *sp, int n, int sfd,int expect_floats)
+{
+    register struct sndfile *sfp;
+    int chunk;
+    int cnt = 0;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+
+    switch(sfp->samptype){
+    case(SAMP_SHORT):
+        while(cnt < n) {
+            if(sfp->remain <= 0) {                 /*RWD.6.99 was == 0*/
+                if(sndfilbuf(sfp) || (sfp->remain == 0))
+                    return cnt;
+            }
+            chunk = n - cnt;
+            if(chunk > sfp->remain)
+                chunk = sfp->remain;
+            memcpy((char *)sp, sfp->next, chunk*sizeof(short));
+            sfp->remain -= chunk;
+            sfp->next += chunk*sizeof(short);
+            cnt += chunk;
+            sp += chunk;
+        }
+        break;
+    default:
+        sfd += SNDFDBASE;
+        while(cnt < n && fgetshortEx(sp++, sfd,expect_floats) > 0)
+            cnt++;
+        break;
+    }
+    return cnt;
+}
+
+int
+fputsbufEx(short *sp, int n, int sfd)
+{
+    register struct sndfile *sfp;
+    int chunk;
+    int cnt = 0;
+
+    if(!mapsndfd(&sfd))
+        return -1;
+    sfp = sndfiles[sfd];
+
+    switch(sfp->samptype){
+    case(SAMP_SHORT):
+        while(cnt < n) {
+            sfp->flags |= SNDFLG_WRITTEN;
+
+            /* sndtell checks this*/
+            sfp->flags |= SNDFLG_LASTWR;
+            chunk = n - cnt;
+            if(chunk > sfp->remain)
+                chunk = sfp->remain;
+            memcpy(sfp->next, (char *)sp, chunk*sizeof(short));
+            sfp->remain -= chunk;
+            sfp->next += chunk*sizeof(short);
+            if(sfp->next > sfp->endbuf)
+                sfp->endbuf = sfp->next;
+            cnt += chunk;
+            sp += chunk;
+            if(sfp->remain == 0) {
+                if(sndflsbufEx(sfp))
+                    return -1;
+                if(sfp->remain == 0)
+                    return cnt;
+            }
+        }
+        break;
+    default:
+        sfd += SNDFDBASE;
+        while(cnt < n && fputshortEx(sp++, sfd) > 0)
+            cnt++;
+        break;
+    }
+    return cnt;
+}
+
+
+
+/*
+ *      The property stuff, for sndfiles
+ */
+int
+sndgetprop(int sfd, char *prop, char *dest, int lim)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    return sfgetprop(sndfiles[sfd]->fd, prop, dest, lim);
+}
+
+int
+sndputprop(int sfd, char *prop, char *src, int size)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    if(strcmp(prop, "sample type") == 0) {
+        rsferrno = ESFNOSTYPE;
+        rsferrstr = "can't change sample type on sndfile";
+        return -1;
+    }
+    return sfputprop(sndfiles[sfd]->fd, prop, src, size);
+}
+
+int
+sndrmprop(int sfd, char *prop)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    if(strcmp(prop, "sample type") == 0) {
+        rsferrno = ESFNOSTYPE;
+        rsferrstr = "can't remove sample type on sndfile";
+        return -1;
+    }
+    return sfrmprop(sndfiles[sfd]->fd, prop);
+}
+
+int
+snddirprop(int sfd, int (*func)(char *propname, int propsize))
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    return sfdirprop(sndfiles[sfd]->fd, func);
+}
+
+
+/*RWD OCT97*/
+int sndgetwordsize(int sfd)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    return sfgetwordsize(sndfiles[sfd]->fd);
+}
+
+/*recognise shortcuts in WIN32*/
+#if defined _WIN32
+/*TODO: add arg for full targetname*/
+int snd_is_shortcut(int sfd)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    return sf_is_shortcut(sndfiles[sfd]->fd,NULL);
+}
+#endif
+
+/*RWD.5.99*/
+int snd_fileformat(int sfd, fileformat *pfmt)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+    /* RWD 2022: this should now recognise pvx file as PVOCEX */
+    return sfformat(sndfiles[sfd]->fd,pfmt);
+}
+
+int sndgetchanmask(int sfd)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+
+    return sfgetchanmask(sndfiles[sfd]->fd);
+}
+
+
+
+
+int snd_getchanformat(int sfd, channelformat *chformat)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+
+    return sf_getchanformat(sndfiles[sfd]->fd,chformat);
+
+}
+
+
+int sndreadpeaks(int sfd,int channels,CHPEAK peakdata[],int *peaktime)
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+
+    return sfreadpeaks(sndfiles[sfd]->fd,(int) channels,peakdata,peaktime);
+}
+
+
+
+int sndputpeaks(int sfd,int channels,const CHPEAK peakdata[])
+{
+    if(!mapsndfd(&sfd))
+        return -1;
+
+    return sfputpeaks(sndfiles[sfd]->fd,channels,peakdata);
+}
+
+const char * snd_getfilename(int sfd)
+{
+    if(!mapsndfd(&sfd))
+        return NULL;
+
+    return sf_getfilename(sndfiles[sfd]->fd);
+}
+
+extern int sf_makepath(char path[], const char* sfname);
+
+int snd_makepath(char path[], const char* sfname)
+{
+    return sf_makepath(path,sfname);
+}
+