richarddobson 3 anni fa
parent
commit
773f7ad32d

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

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

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

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