--- /dev/null
+# Make PortAudio for Linux
+# Updated 2001/08/25 Bill Eldridge bill@rfa.org
+# Updated 2001/10/16, philburk@softsynth.com, s/unix_oss/unix_oss/
+
+# A pretty bare makefile, that figures out all the test files
+# and compiles them against the library in the pa_unix_oss directory.
+
+# Do "make all" and then when happy, "make libinstall"
+# (if not happy, "make clean")
+
+# The ldconfig stuff in libinstall is the wrong way to do it -
+# someone tell me the right way, please
+
+
+LIBS = -lm -lpthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+LIBINST = /usr/local/lib
+
+TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
+
+LIBFILES:= ./pa_common/pa_lib.c ./pa_unix_oss/pa_unix_oss.c
+
+.c.o:
+ -gcc -c -I./pa_common $< -o $*.o
+ -gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio
+
+all: sharedlib tests
+
+sharedlib: $(LIBFILES:.c=.o)
+ gcc -shared -o ./pa_unix_oss/libportaudio.so ./pa_common/pa_lib.o ./pa_unix_oss/pa_unix_oss.o
+
+libinstall: ./pa_unix_oss/libportaudio.so
+ @cp -f ./pa_unix_oss/libportaudio.so $(LIBINST)
+ @/sbin/ldconfig -v
+
+tests: $(TESTS:.c=.o)
+
+clean:
+ -@rm -f $(TESTS:.c=.o)
+ -@rm -f $(TESTS:.c=)
+ -@rm -f $(LIBFILES:.c=.o)
+ -@rm -f ./pa_unix_oss/libportaudio.so
+
+
--- /dev/null
+rem Use Astyle to fix style in 'C' files
+cd %1%
+
+fixlines -p *.c
+fixlines -p *.cpp
+fixlines -p *.cc
+
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.c
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cpp
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cc
+del *.orig
+@rem convert line terminators to Unix style LFs
+fixlines -u *.c
+fixlines -u *.cpp
+fixlines -u *.cc
+fixlines -u *.h
+del *.bak
+
+cd ..\
--- /dev/null
+/*
+ * $Id$
+ * Portable Audio I/O Library for ASIO Drivers
+ *
+ * Author: Stephane Letz
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2000-2001 Stephane Letz, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/* Modification History
+
+ 08-03-01 First version : Stephane Letz
+ 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
+ 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
+ 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
+ 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
+ 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
+ the stream is stopped : Stephane Letz
+ 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
+ the stream is stopped : Stephane Letz
+ 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
+ respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
+ 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
+ 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
+ 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
+ 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
+ 11-06-01 Rename functions : Stephane Letz
+ 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
+ 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
+ 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
+
+ TO DO :
+
+ - Check Pa_StopSteam and Pa_AbortStream
+ - Optimization for Input only or Ouput only (really necessary ??)
+ - Opening of several streams
+*/
+
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include "asiosys.h"
+#include "asio.h"
+#include "asiodrivers.h"
+
+#if MAC
+#include <Math64.h>
+#else
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
+enum {
+ // number of input and outputs supported by the host application
+ // you can change these to higher or lower values
+ kMaxInputChannels = 32,
+ kMaxOutputChannels = 32
+};
+
+/* ASIO specific device information. */
+typedef struct internalPortAudioDevice
+{
+ PaDeviceInfo pad_Info;
+} internalPortAudioDevice;
+
+
+/* ASIO driver internal data storage */
+typedef struct PaHostSoundControl
+{
+ // ASIOInit()
+ ASIODriverInfo pahsc_driverInfo;
+
+ // ASIOGetChannels()
+ int32 pahsc_NumInputChannels;
+ int32 pahsc_NumOutputChannels;
+
+ // ASIOGetBufferSize() - sizes in frames per buffer
+ int32 pahsc_minSize;
+ int32 pahsc_maxSize;
+ int32 pahsc_preferredSize;
+ int32 pahsc_granularity;
+
+ // ASIOGetSampleRate()
+ ASIOSampleRate pahsc_sampleRate;
+
+ // ASIOOutputReady()
+ bool pahsc_postOutput;
+
+ // ASIOGetLatencies ()
+ int32 pahsc_inputLatency;
+ int32 pahsc_outputLatency;
+
+ // ASIOCreateBuffers ()
+ ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
+
+ // ASIOGetChannelInfo()
+ ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
+ // The above two arrays share the same indexing, as the data in them are linked together
+
+ // Information from ASIOGetSamplePosition()
+ // data is converted to double floats for easier use, however 64 bit integer can be used, too
+ double nanoSeconds;
+ double samples;
+ double tcSamples; // time code samples
+
+ // bufferSwitchTimeInfo()
+ ASIOTime tInfo; // time info state
+ unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
+
+ // Signal the end of processing in this example
+ bool stopped;
+
+ ASIOCallbacks pahsc_asioCallbacks;
+
+
+ int32 pahsc_userInputBufferFrameOffset; // Position in Input user buffer
+ int32 pahsc_userOutputBufferFrameOffset; // Position in Output user buffer
+ int32 pahsc_hostOutputBufferFrameOffset; // Position in Output ASIO buffer
+
+ int32 past_FramesPerHostBuffer; // Number of frames in ASIO buffer
+
+ int32 pahsc_InputBufferOffset; // Number of null frames for input buffer alignement
+ int32 pahsc_OutputBufferOffset; // Number of null frames for ouput buffer alignement
+
+#if MAC
+ UInt64 pahsc_EntryCount;
+ UInt64 pahsc_LastExitCount;
+#elif WINDOWS
+ LARGE_INTEGER pahsc_EntryCount;
+ LARGE_INTEGER pahsc_LastExitCount;
+#endif
+
+ PaTimestamp pahsc_NumFramesDone;
+
+ internalPortAudioStream *past;
+
+} PaHostSoundControl;
+
+
+//----------------------------------------------------------
+// name of the ASIO device to be used
+#if WINDOWS
+ //#define ASIO_DRIVER_NAME "ASIO Multimedia Driver"
+ #define ASIO_DRIVER_NAME "ASIO Sample"
+#elif MAC
+ //#define ASIO_DRIVER_NAME "Apple Sound Manager"
+ #define ASIO_DRIVER_NAME "ASIO Delta1010"
+ //#define ASIO_DRIVER_NAME "DigiDesign DirectIO"
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) /**/
+
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+#define CARBON_COMPATIBLE (0)
+#define PA_MAX_DEVICE_INFO (32)
+
+#define MIN_INT8 (-0x80)
+#define MAX_INT8 (0x7F)
+
+#define MIN_INT8_FP ((float)-0x80)
+#define MAX_INT8_FP ((float)0x7F)
+
+#define MIN_INT16_FP ((float)-0x8000)
+#define MAX_INT16_FP ((float)0x7FFF)
+
+#define MIN_INT16 (-0x8000)
+#define MAX_INT16 (0x7FFF)
+
+#define MAX_INT32_FP (2147483520.0f) /* 0x0x7FFFFF80 - seems safe */
+
+/************************************************************************************/
+/****************** Data ************************************************************/
+/************************************************************************************/
+static int sNumDevices = 0;
+static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
+static int32 sPaHostError = 0;
+static int sDefaultOutputDeviceID = 0;
+static int sDefaultInputDeviceID = 0;
+
+PaHostSoundControl asioDriverInfo = {0};
+
+#ifdef MAC
+static bool swap = true;
+#elif WINDOWS
+static bool swap = false;
+#endif
+
+// Prototypes
+void bufferSwitch(long index, ASIOBool processNow);
+ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
+void sampleRateChanged(ASIOSampleRate sRate);
+long asioMessages(long selector, long value, void* message, double* opt);
+static void Pa_StartUsageCalculation( internalPortAudioStream *past );
+static void Pa_EndUsageCalculation( internalPortAudioStream *past );
+
+void Pa_ASIO_Convert_Inter_Input(
+ ASIOBufferInfo* nativeBuffer,
+ void* inputBuffer,
+ short NumInputChannels,
+ short NumOuputChannels,
+ short framePerBuffer,
+ short hostFrameOffset,
+ short userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ short index);
+
+void Pa_ASIO_Convert_Inter_Output(
+ ASIOBufferInfo* nativeBuffer,
+ void* outputBuffer,
+ short NumInputChannels,
+ short NumOuputChannels,
+ short framePerBuffer,
+ short hostFrameOffset,
+ short userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ short index);
+
+ void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer,
+ ASIOSampleType nativeFormat,
+ short NumInputChannels,
+ short NumOuputChannels,
+ int index,
+ int hostFrameOffset,
+ int frames);
+
+void Pa_ASIO_Callback_Input(long index);
+void Pa_ASIO_Callback_Output(long index, long framePerBuffer);
+void Pa_ASIO_Callback_End();
+void Pa_ASIO_Clear_User_Buffers();
+
+// Some external references
+extern AsioDrivers* asioDrivers ;
+bool loadAsioDriver(char *name);
+unsigned long get_sys_reference_time();
+
+
+// For callback debugging on Mac : to be removed in the final version
+
+/*
+#define MidiSharePPC_68k
+#include <MidiShare.h>
+
+void SendVal (long val)
+{
+ MidiEvPtr ev = MidiNewEv(typeTempo);
+ if (ev) {
+ Tempo(ev) = val;
+ MidiSendIm(0,ev);
+ }
+}
+
+void MidiPrintText ( char * s)
+{
+ MidiEvPtr e;
+ long c = 0;
+
+ if (e = MidiNewEv(typeTextual)){
+ for (c = 0 ; *s ; s++,c++) MidiAddField (e ,*s);
+ MidiSendIm (0, e);
+ }
+}
+*/
+
+
+/************************************************************************************/
+/****************** Macro ************************************************************/
+/************************************************************************************/
+
+#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ;
+#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ;
+
+#define ClipShort(v) (((v)<MIN_INT16)?MIN_INT16:(((v)>MAX_INT16)?MAX_INT16:(v)))
+#define ClipChar(v) (((v)<MIN_INT8)?MIN_INT8:(((v)>MAX_INT8)?MAX_INT8:(v)))
+#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v)))
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+#ifndef max
+#define max(a,b) ((a)>=(b)?(a):(b))
+#endif
+
+
+// Utilities for alignement buffer size computation
+int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);}
+int PPCM (int a, int b) {return (a*b) / PGCD (a,b);}
+
+// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer alignement
+int Pa_ASIO_CalcFrameShift (int M, int N)
+{
+ int res = 0;
+ for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); }
+ return res;
+}
+
+// We have the following relation :
+// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N
+
+/* ASIO sample type to PortAudio sample type conversion */
+static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type)
+{
+ switch (type) {
+
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ return paInt16;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ return paFloat32;
+
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ return paInt32;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ return paInt24;
+
+ default:
+ return paCustomFormat;
+ }
+}
+
+/* Allocate ASIO buffers, initialise channels */
+static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels,
+ long OutputChannels, long framesPerBuffer)
+{
+ ASIOError err;
+ int i;
+
+ ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
+
+ // Check parameters
+ if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxInputChannels)) return ASE_InvalidParameter;
+
+ for(i = 0; i < InputChannels; i++, info++){
+ info->isInput = ASIOTrue;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+ for(i = 0; i < OutputChannels; i++, info++){
+ info->isInput = ASIOFalse;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+ // Set up the asioCallback structure and create the ASIO data buffer
+ asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch;
+ asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged;
+ asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages;
+ asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
+
+ DBUG(("PortAudio : ASIOCreateBuffers with size = %ld \n", framesPerBuffer));
+
+ err = ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels,
+ framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks);
+ if (err != ASE_OK) return err;
+
+ // Initialise buffers
+ for (i = 0; i < InputChannels + OutputChannels; i++)
+ {
+ asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
+ asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
+ err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]);
+ if (err != ASE_OK) break;
+ }
+
+ err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency);
+
+ DBUG(("PortAudio : InputLatency = %ld latency = %ld msec \n",
+ asioDriverInfo->pahsc_inputLatency,
+ (long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+ DBUG(("PortAudio : OuputLatency = %ld latency = %ld msec \n",
+ asioDriverInfo->pahsc_outputLatency,
+ (long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate)));
+
+ return err;
+}
+
+
+/*
+ Query ASIO driver info :
+
+ First we get all available ASIO drivers located in the ASIO folder,
+ then try to load each one. For each loaded driver, get all needed informations.
+*/
+static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad )
+{
+
+#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+
+ ASIOSampleRate possibleSampleRates[]
+ = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0};
+
+ ASIOChannelInfo channelInfos;
+ long InputChannels,OutputChannels;
+ double *sampleRates;
+ char* names[PA_MAX_DEVICE_INFO] ;
+ PaDeviceInfo *dev;
+ int i;
+ int numDrivers;
+ ASIOError asioError;
+
+ /* Allocate names */
+ for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) names[i] = (char*)PaHost_AllocateFastMemory(32);
+
+ /* MUST BE CHECKED : to force fragments loading on Mac */
+ loadAsioDriver("dummy");
+
+ /* Get names of all available ASIO drivers */
+ asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO);
+
+ /* Check all available ASIO drivers */
+#if MAC
+ numDrivers = asioDrivers->getNumFragments();
+#elif WINDOWS
+ numDrivers = asioDrivers->asioGetNumDev();
+#endif
+ DBUG(("PaASIO_QueryDeviceInfo: numDrivers = %d\n", numDrivers ));
+
+ for (int driver = 0 ; driver < numDrivers ; driver++)
+ {
+
+ #if WINDOWS
+ asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB
+ asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB
+ #endif
+
+ /* If the driver can be loaded : */
+ if ( !loadAsioDriver(names[driver]) )
+ {
+ DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver]));
+ }
+ else if( (asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK )
+ {
+ DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver]));
+ }
+ else if( (ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK))
+ {
+ DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver]));
+ }
+ else
+ {
+
+ /* Gets the name */
+ dev = &(ipad[driver].pad_Info);
+ dev->name = names[driver];
+ names[driver] = 0;
+
+ /* Gets Input and Output channels number */
+ dev->maxInputChannels = InputChannels;
+ dev->maxOutputChannels = OutputChannels;
+
+ DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels ));
+ DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels ));
+
+ /* Make room in case device supports all rates. */
+ sampleRates = (double*)PaHost_AllocateFastMemory( MAX_NUMSAMPLINGRATES * sizeof(double));
+ dev->sampleRates = sampleRates;
+ dev->numSampleRates = 0;
+
+ /* Loop through the possible sampling rates and check each to see if the device supports it. */
+ for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) {
+ if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) {
+ DBUG(("PortAudio : possible sample rate = %d\n", (long)possibleSampleRates[index]));
+ dev->numSampleRates+=1;
+ *sampleRates = possibleSampleRates[index];
+ sampleRates++;
+ }
+ }
+
+ /* We assume that all channels have the same SampleType, so check the first */
+ channelInfos.channel = 0;
+ channelInfos.isInput = 1;
+ ASIOGetChannelInfo(&channelInfos);
+
+ dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type);
+
+ /* unload the driver */
+ ASIODisposeBuffers();
+ ASIOExit();
+
+ sNumDevices++;
+ }
+ }
+
+ /* free only unused names */
+ for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32);
+
+ return paNoError;
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK:
+void sampleRateChanged(ASIOSampleRate sRate)
+{
+ // do whatever you need to do if the sample rate changed
+ // usually this only happens during external sync.
+ // Audio processing is not stopped by the driver, actual sample rate
+ // might not have even changed, maybe only the sample rate status of an
+ // AES/EBU or S/PDIF digital input at the audio device.
+ // You might have to update time/sample related conversion routines, etc.
+}
+
+//----------------------------------------------------------------------------------
+// TAKEN FROM THE ASIO SDK:
+long asioMessages(long selector, long value, void* message, double* opt)
+{
+ // currently the parameters "value", "message" and "opt" are not used.
+ long ret = 0;
+ switch(selector)
+ {
+ case kAsioSelectorSupported:
+ if(value == kAsioResetRequest
+ || value == kAsioEngineVersion
+ || value == kAsioResyncRequest
+ || value == kAsioLatenciesChanged
+ // the following three were added for ASIO 2.0, you don't necessarily have to support them
+ || value == kAsioSupportsTimeInfo
+ || value == kAsioSupportsTimeCode
+ || value == kAsioSupportsInputMonitor)
+ ret = 1L;
+ break;
+
+ case kAsioBufferSizeChange:
+ //printf("kAsioBufferSizeChange \n");
+ break;
+
+ case kAsioResetRequest:
+ // defer the task and perform the reset of the driver during the next "safe" situation
+ // You cannot reset the driver right now, as this code is called from the driver.
+ // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
+ // Afterwards you initialize the driver again.
+ asioDriverInfo.stopped; // In this sample the processing will just stop
+ ret = 1L;
+ break;
+ case kAsioResyncRequest:
+ // This informs the application, that the driver encountered some non fatal data loss.
+ // It is used for synchronization purposes of different media.
+ // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
+ // Windows Multimedia system, which could loose data because the Mutex was hold too long
+ // by another thread.
+ // However a driver can issue it in other situations, too.
+ ret = 1L;
+ break;
+ case kAsioLatenciesChanged:
+ // This will inform the host application that the drivers were latencies changed.
+ // Beware, it this does not mean that the buffer sizes have changed!
+ // You might need to update internal delay data.
+ ret = 1L;
+ //printf("kAsioLatenciesChanged \n");
+ break;
+ case kAsioEngineVersion:
+ // return the supported ASIO version of the host application
+ // If a host applications does not implement this selector, ASIO 1.0 is assumed
+ // by the driver
+ ret = 2L;
+ break;
+ case kAsioSupportsTimeInfo:
+ // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
+ // is supported.
+ // For compatibility with ASIO 1.0 drivers the host application should always support
+ // the "old" bufferSwitch method, too.
+ ret = 1;
+ break;
+ case kAsioSupportsTimeCode:
+ // informs the driver wether application is interested in time code info.
+ // If an application does not need to know about time code, the driver has less work
+ // to do.
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+
+//----------------------------------------------------------------------------------
+// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
+#if NATIVE_INT64
+ #define ASIO64toDouble(a) (a)
+#else
+ const double twoRaisedTo32 = 4294967296.;
+ #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
+#endif
+
+
+ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
+{
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that you take care
+ // about thread synchronization. This is omitted here for simplicity.
+
+ static processedSamples = 0;
+ int result = 0;
+
+ // store the timeInfo for later use
+ asioDriverInfo.tInfo = *timeInfo;
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+
+ if (timeInfo->timeInfo.flags & kSystemTimeValid)
+ asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
+ else
+ asioDriverInfo.nanoSeconds = 0;
+
+ if (timeInfo->timeInfo.flags & kSamplePositionValid)
+ asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+ else
+ asioDriverInfo.samples = 0;
+
+ if (timeInfo->timeCode.flags & kTcValid)
+ asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+ else
+ asioDriverInfo.tcSamples = 0;
+
+ // get the system reference time
+ asioDriverInfo.sysRefTime = get_sys_reference_time();
+
+#if 0
+ // a few debug messages for the Windows device driver developer
+ // tells you the time when driver got its interrupt and the delay until the app receives
+ // the event notification.
+ static double last_samples = 0;
+ char tmp[128];
+ sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
+ OutputDebugString (tmp);
+ last_samples = asioDriverInfo.samples;
+#endif
+
+ // To avoid the callback accessing a desallocated stream
+ if( asioDriverInfo.past == NULL) return 0L;
+
+ // Keep sample position
+ asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
+
+ /* Has a user callback returned '1' to indicate finished at the last ASIO callback? */
+ if( asioDriverInfo.past->past_StopSoon ) {
+
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ index,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ asioDriverInfo.past->past_IsActive = 0;
+
+ // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+ if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+
+ }else {
+
+ /* CPU usage */
+ Pa_StartUsageCalculation(asioDriverInfo.past);
+
+ Pa_ASIO_Callback_Input(index);
+
+ // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
+ if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady();
+
+ Pa_ASIO_Callback_End();
+
+ /* CPU usage */
+ Pa_EndUsageCalculation(asioDriverInfo.past);
+ }
+
+ return 0L;
+}
+
+
+//----------------------------------------------------------------------------------
+void bufferSwitch(long index, ASIOBool processNow)
+{
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that you take care
+ // about thread synchronization. This is omitted here for simplicity.
+
+ // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
+ // though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
+
+ ASIOTime timeInfo;
+ memset (&timeInfo, 0, sizeof (timeInfo));
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+ if(ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
+ timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
+
+ // Call the real callback
+ bufferSwitchTimeInfo (&timeInfo, index, processNow);
+}
+
+//----------------------------------------------------------------------------------
+unsigned long get_sys_reference_time()
+{
+ // get the system reference time
+ #if WINDOWS
+ return timeGetTime();
+ #elif MAC
+ static const double twoRaisedTo32 = 4294967296.;
+ UnsignedWide ys;
+ Microseconds(&ys);
+ double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
+ return (unsigned long)(r / 1000.);
+ #endif
+}
+
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+inline static long Pa_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+/* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+/* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+// TO BE COMPLETED WITH ALL SUPPORTED PA SAMPLE TYPES
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (1.0f / MAX_INT16_FP) * temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (1.0f / MAX_INT32_FP) * temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Flot32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (long)asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (float)temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = temp<<16;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (long) asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (long)((float)temp * MAX_INT32_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+}
+
+ //-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = temp>>16;
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (temp >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ temp = (short) ClipShort(temp);
+ *userBufPtr = temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+ void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (long)asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (short)((float)temp * MAX_INT16_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = (long)asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (short)(((float)temp * MAX_INT16_FP) + dither);
+ temp = ClipShort(temp);
+ *userBufPtr = temp;
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (char)(temp>>8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (char)(temp>>8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (char)(temp>>24);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = temp>>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (char)(temp >> 8);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (char)((float)temp*MAX_INT8_FP); // Is temp a value between -1.0 and 1.0 ??
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+ temp = ClipChar(temp);
+ *userBufPtr = temp ;
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int16_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapShort(temp);
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Input_Int32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (unsigned char)((temp>>24) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = temp>>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED)
+ temp += Pa_TriangularDither() >> 8;
+ temp = ClipShort(temp);
+ *userBufPtr = (unsigned char)((temp>>8) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (float) asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ *userBufPtr = (unsigned char)(((float)temp*MAX_INT8_FP) + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+ else
+ {
+ for( j=0; j<NumInputChannels; j++ ) {
+ float *asioBufPtr = &((float*)nativeBuffer[j].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &inBufPtr[j+(userFrameOffset*NumInputChannels)];
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = (float) asioBufPtr[i];
+ if (swap) temp = SwapLong(temp);
+ temp = (char)(((float)temp * MAX_INT8_FP) + dither);
+ temp = ClipChar(temp);
+ *userBufPtr = (unsigned char)(temp + 0x80);
+ userBufPtr += NumInputChannels;
+ }
+ }
+ }
+}
+
+
+// OUPUT
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Float32_Int16 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags, bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ if( flags & paClipOff ) /* NOTHING */
+ {
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (short) (*userBufPtr * MAX_INT16_FP);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else /* CLIP */
+ {
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ temp = (long) (*userBufPtr * MAX_INT16_FP);
+ temp = ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+
+ for (i= 0; i < framePerBuffer; i++)
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ temp = (long) ((*userBufPtr * MAX_INT16_FP) + dither);
+ temp = ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Float32_Int32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paClipOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (long) (*userBufPtr * MAX_INT32_FP);
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else // CLIP *
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ float temp1 = *userBufPtr;
+ temp1 = ClipFloat(temp1);
+ temp = (long) (temp1*MAX_INT32_FP);
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE TESTED
+
+ void Output_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paClipOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (long) *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = (float)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+
+ }
+ else /* CLIP */
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ float temp1 = *userBufPtr;
+ temp1 = ClipFloat(temp1); // Is is necessary??
+ temp = (long) temp1;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = (float)temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int32_Int16(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ if( flags & paDitherOff )
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short) ((*userBufPtr) >> 16);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+ else
+ {
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (*userBufPtr >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ temp = (short) ClipShort(temp);
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int32_Int32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+ void Output_Int32_Float32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int16_Int16(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int16_Int32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (*userBufPtr)<<16;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+ void Output_Int16_Float32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT16_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int8_Int16(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short)(*userBufPtr)<<8;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_Int8_Int32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = (short)(*userBufPtr)<<24;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+ void Output_Int8_Float32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = *userBufPtr;
+ asioBufPtr[i] = (long)(((float)temp) * (1.0f / MAX_INT8_FP));
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_IntU8_Int16(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 8;
+ if (swap) temp = SwapShort(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Output_IntU8_Int32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 24;
+ if (swap) temp = SwapLong(temp);
+ asioBufPtr[i] = temp;
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// MUST BE CHECKED
+
+ void Output_IntU8_Float32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap)
+{
+ long temp;
+ int i,j;
+
+ for (j= 0; j < NumOuputChannels; j++)
+ {
+ float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)];
+ for( i=0; i<framePerBuffer; i++ )
+ {
+ temp = ((short)((*userBufPtr) - 0x80)) << 24;
+ asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP);
+ userBufPtr += NumOuputChannels;
+ }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output_16 (ASIOBufferInfo* nativeBuffer, int frames, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset)
+{
+ int i,j;
+
+ for( j=0; j<NumOuputChannels; j++ ) {
+ short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output_32 (ASIOBufferInfo* nativeBuffer, int frames, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset)
+{
+ int i,j;
+
+ for( j=0; j<NumOuputChannels; j++ ) {
+ long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset];
+ for (i= 0; i < frames; i++) {asioBufPtr[i] = 0; }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Adaptor_Init()
+{
+ if (asioDriverInfo.past->past_FramesPerUserBuffer <= asioDriverInfo.past_FramesPerHostBuffer) {
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = asioDriverInfo.pahsc_OutputBufferOffset;
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; // empty
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty
+ }else {
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; // empty
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = asioDriverInfo.pahsc_InputBufferOffset;
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+// FIXME : optimization for Input only or output only modes (really necessary ??)
+void Pa_ASIO_Callback_Input( long index)
+{
+ internalPortAudioStream *past = asioDriverInfo.past;
+ long framesInputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; // number of frames available into the host input buffer
+ long framesInputUserBuffer; // number of frames needed to complete the user input buffer
+ long framesOutputHostBuffer; // number of frames needed to complete the host output buffer
+ long framesOuputUserBuffer; // number of frames available into the user output buffer
+ long userResult;
+ long tmp;
+
+ /* Fill host ASIO output with remaining frames in user output */
+ framesOutputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer;
+ framesOuputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userOutputBufferFrameOffset;
+ tmp = min(framesOutputHostBuffer, framesOuputUserBuffer);
+ framesOutputHostBuffer -= tmp;
+ Pa_ASIO_Callback_Output(index,tmp);
+
+ /* Available frames in hostInputBuffer */
+ while (framesInputHostBuffer > 0) {
+
+ /* Number of frames needed to complete an user input buffer */
+ framesInputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userInputBufferFrameOffset;
+
+ if (framesInputHostBuffer >= framesInputUserBuffer) {
+
+ /* Convert ASIO input to user input */
+ Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos,
+ past->past_InputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framesInputUserBuffer,
+ asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+ asioDriverInfo.pahsc_userInputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Call PortAudio callback */
+ userResult = asioDriverInfo.past->past_Callback(past->past_InputBuffer, past->past_OutputBuffer,
+ past->past_FramesPerUserBuffer,past->past_FrameCount,past->past_UserData );
+
+ /* User callback has asked us to stop in the middle of the host buffer */
+ if( userResult != 0) {
+
+ /* Put 0 in the end of the output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ index,
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset,
+ asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset);
+
+ past->past_StopSoon = 1;
+ return;
+ }
+
+
+ /* Full user ouput buffer : write offset */
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset = 0;
+
+ /* Empty user input buffer : read offset */
+ asioDriverInfo.pahsc_userInputBufferFrameOffset = 0;
+
+ /* Fill host ASIO output */
+ tmp = min (past->past_FramesPerUserBuffer,framesOutputHostBuffer);
+ Pa_ASIO_Callback_Output(index,tmp);
+
+ framesOutputHostBuffer -= tmp;
+ framesInputHostBuffer -= framesInputUserBuffer;
+
+ }else {
+
+ /* Convert ASIO input to user input */
+ Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos,
+ past->past_InputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels ,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framesInputHostBuffer,
+ asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer,
+ asioDriverInfo.pahsc_userInputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Update pahsc_userInputBufferFrameOffset */
+ asioDriverInfo.pahsc_userInputBufferFrameOffset += framesInputHostBuffer;
+
+ /* Update framesInputHostBuffer */
+ framesInputHostBuffer = 0;
+ }
+ }
+
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Callback_Output(long index, long framePerBuffer)
+{
+ internalPortAudioStream *past = asioDriverInfo.past;
+
+ if (framePerBuffer > 0) {
+
+ /* Convert user output to ASIO ouput */
+ Pa_ASIO_Convert_Inter_Output (asioDriverInfo.bufferInfos,
+ past->past_OutputBuffer,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ framePerBuffer,
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset,
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ past->past_InputSampleFormat,
+ past->past_Flags,
+ index);
+
+ /* Update hostOuputFrameOffset */
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset += framePerBuffer;
+
+ /* Update userOutputFrameOffset */
+ asioDriverInfo.pahsc_userOutputBufferFrameOffset += framePerBuffer;
+ }
+}
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Callback_End()
+ {
+ /* Empty ASIO ouput : write offset */
+ asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0;
+ }
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+void Pa_ASIO_Clear_User_Buffers()
+{
+ if( asioDriverInfo.past->past_InputBuffer != NULL )
+ {
+ memset( asioDriverInfo.past->past_InputBuffer, 0, asioDriverInfo.past->past_InputBufferSize );
+ }
+ if( asioDriverInfo.past->past_OutputBuffer != NULL )
+ {
+ memset( asioDriverInfo.past->past_OutputBuffer, 0, asioDriverInfo.past->past_OutputBufferSize );
+ }
+}
+
+//-------------------------------------------------------------------------------------------------------------------------------------------------------
+ void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer,
+ ASIOSampleType nativeFormat,
+ short NumInputChannels,
+ short NumOuputChannels,
+ int index,
+ int hostFrameOffset,
+ int frames)
+{
+
+ switch (nativeFormat) {
+
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ Pa_ASIO_Clear_Output_16(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+ break;
+
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ break;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ Pa_ASIO_Clear_Output_32(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset);
+ break;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+//---------------------------------------------------------------------------------------
+void Pa_ASIO_Convert_Inter_Input(
+ ASIOBufferInfo* nativeBuffer,
+ void* inputBuffer,
+ short NumInputChannels,
+ short NumOuputChannels,
+ short framePerBuffer,
+ short hostFrameOffset,
+ short userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ short index)
+{
+
+ if((NumInputChannels > 0) && (nativeBuffer != NULL))
+ {
+ /* Convert from native format to PA format. */
+ switch(paFormat)
+ {
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Flot32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+
+ break;
+ }
+
+ case paInt32:
+ {
+ long *inBufPtr = (long *)inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ short *inBufPtr = (short *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+
+ char *inBufPtr = (char *) inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+
+ unsigned char *inBufPtr = (unsigned char *)inputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
+ Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------------------------
+void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer,
+ void* outputBuffer,
+ short NumInputChannels,
+ short NumOuputChannels,
+ short framePerBuffer,
+ short hostFrameOffset,
+ short userFrameOffset,
+ ASIOSampleType nativeFormat,
+ PaSampleFormat paFormat,
+ PaStreamFlags flags,
+ short index)
+{
+
+ if((NumOuputChannels > 0) && (nativeBuffer != NULL))
+ {
+ /* Convert from PA format to native format */
+
+ switch(paFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset,flags,swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ long *outBufPtr = (long *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ short *outBufPtr = (short *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+
+ }
+ break;
+ }
+
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) outputBuffer;
+
+ switch (nativeFormat) {
+ case ASIOSTInt16LSB:
+ Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt16MSB:
+ Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTInt32LSB:
+ Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTInt32MSB:
+ Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+ case ASIOSTFloat32LSB:
+ Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap);
+ break;
+ case ASIOSTFloat32MSB:
+ Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap);
+ break;
+
+ case ASIOSTInt24LSB: // used for 20 bits as well
+ case ASIOSTInt24MSB: // used for 20 bits as well
+
+ case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+ case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
+
+ // these are used for 32 bit data buffer, with different alignment of the data inside
+ // 32 bit PCI bus systems can more easily used with these
+
+ case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
+
+
+ case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
+ case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
+ case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
+ case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
+ DBUG(("Not yet implemented : please report the problem\n"));
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+}
+
+
+
+/* Load a ASIO driver corresponding to the required device */
+static PaError Pa_ASIO_loadDevice (long device)
+{
+ PaDeviceInfo * dev = &(sDevices[device].pad_Info);
+
+ if (!loadAsioDriver((char *) dev->name)) return paHostError;
+ if (ASIOInit(&asioDriverInfo.pahsc_driverInfo) != ASE_OK) return paHostError;
+ if (ASIOGetChannels(&asioDriverInfo.pahsc_NumInputChannels, &asioDriverInfo.pahsc_NumOutputChannels) != ASE_OK) return paHostError;
+ if (ASIOGetBufferSize(&asioDriverInfo.pahsc_minSize, &asioDriverInfo.pahsc_maxSize, &asioDriverInfo.pahsc_preferredSize, &asioDriverInfo.pahsc_granularity) != ASE_OK) return paHostError;
+
+ if(ASIOOutputReady() == ASE_OK)
+ asioDriverInfo.pahsc_postOutput = true;
+ else
+ asioDriverInfo.pahsc_postOutput = false;
+
+ return paNoError;
+}
+
+//---------------------------------------------------
+static int GetHighestBitPosition( unsigned long n )
+{
+ int pos = -1;
+ while( n != 0 )
+ {
+ pos++;
+ n = n >> 1;
+ }
+ return pos;
+}
+
+//-----------------------------------------------------------------------------
+static int GetFirstMultiple(long min, long val ){ return ((min + val - 1) / val) * val; }
+
+//-----------------------------------------------------------------------------
+static int GetFirstPossibleDivisor(long max, long val )
+{
+ for (int i = 2; i < 20; i++) {if (((val%i) == 0) && ((val/i) <= max)) return (val/i); }
+ return val;
+}
+
+//------------------------------------------------------------------------
+static int IsPowerOfTwo( unsigned long n ) { return ((n & (n-1)) == 0); }
+
+
+/*******************************************************************
+* Determine size of native ASIO audio buffer size
+* Input parameters : FramesPerUserBuffer, NumUserBuffers
+* Output values : FramesPerHostBuffer, OutputBufferOffset or InputtBufferOffset
+*/
+
+static PaError PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ long requestedBufferSize;
+ long firstMultiple, firstDivisor;
+
+ // Compute requestedBufferSize
+ if( past->past_NumUserBuffers < 1 ){
+ requestedBufferSize = past->past_FramesPerUserBuffer;
+ }else{
+ requestedBufferSize = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+ }
+
+ // Adjust FramesPerHostBuffer using requestedBufferSize, ASIO minSize and maxSize,
+ if (requestedBufferSize < asioDriverInfo.pahsc_minSize){
+
+ firstMultiple = GetFirstMultiple(asioDriverInfo.pahsc_minSize, requestedBufferSize);
+
+ if (firstMultiple <= asioDriverInfo.pahsc_maxSize)
+ asioDriverInfo.past_FramesPerHostBuffer = firstMultiple;
+ else
+ asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_minSize;
+
+ }else if (requestedBufferSize > asioDriverInfo.pahsc_maxSize){
+
+ firstDivisor = GetFirstPossibleDivisor(asioDriverInfo.pahsc_maxSize, requestedBufferSize);
+
+ if ((firstDivisor >= asioDriverInfo.pahsc_minSize) && (firstDivisor <= asioDriverInfo.pahsc_maxSize))
+ asioDriverInfo.past_FramesPerHostBuffer = firstDivisor;
+ else
+ asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_maxSize;
+ }else{
+ asioDriverInfo.past_FramesPerHostBuffer = requestedBufferSize;
+ }
+
+ // If ASIO buffer size needs to be a power of two
+ if( asioDriverInfo.pahsc_granularity < 0 ){
+ // Needs to be a power of two.
+
+ if( !IsPowerOfTwo( asioDriverInfo.past_FramesPerHostBuffer ) )
+ {
+ int highestBit = GetHighestBitPosition(asioDriverInfo.past_FramesPerHostBuffer);
+ asioDriverInfo.past_FramesPerHostBuffer = 1 << (highestBit + 1);
+ }
+ }
+
+ DBUG(("----------------------------------\n"));
+ DBUG(("PortAudio : minSize = %ld \n",asioDriverInfo.pahsc_minSize));
+ DBUG(("PortAudio : preferredSize = %ld \n",asioDriverInfo.pahsc_preferredSize));
+ DBUG(("PortAudio : maxSize = %ld \n",asioDriverInfo.pahsc_maxSize));
+ DBUG(("PortAudio : granularity = %ld \n",asioDriverInfo.pahsc_granularity));
+ DBUG(("PortAudio : User buffer size = %d\n", asioDriverInfo.past->past_FramesPerUserBuffer ));
+ DBUG(("PortAudio : ASIO buffer size = %d\n", asioDriverInfo.past_FramesPerHostBuffer ));
+
+ if (asioDriverInfo.past_FramesPerHostBuffer > past->past_FramesPerUserBuffer){
+
+ // Computes the MINIMUM value of null frames shift for the output buffer alignement
+ asioDriverInfo.pahsc_OutputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+ asioDriverInfo.pahsc_InputBufferOffset = 0;
+ DBUG(("PortAudio : Minimum BufferOffset for Output = %d\n", asioDriverInfo.pahsc_OutputBufferOffset));
+ }else{
+
+ //Computes the MINIMUM value of null frames shift for the input buffer alignement
+ asioDriverInfo.pahsc_InputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer);
+ asioDriverInfo.pahsc_OutputBufferOffset = 0;
+ DBUG(("PortAudio : Minimum BufferOffset for Input = %d\n", asioDriverInfo.pahsc_InputBufferOffset));
+ }
+
+ return paNoError;
+}
+
+
+/***********************************************************************/
+int Pa_CountDevices()
+{
+ PaError err ;
+
+ if( sNumDevices <= 0 )
+ {
+ /* Force loading of ASIO drivers */
+ err = Pa_ASIO_QueryDeviceInfo(sDevices);
+ if( err != paNoError ) goto error;
+ }
+
+ return sNumDevices;
+
+error:
+ PaHost_Term();
+ DBUG(("Pa_CountDevices: returns %d\n", err ));
+ return err;
+}
+
+/***********************************************************************/
+PaError PaHost_Init( void )
+{
+ /* Have we already initialized the device info? */
+ return (Pa_CountDevices() < 0 ) ? paHostError : paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_Term( void )
+{
+ int i;
+ PaDeviceInfo *dev;
+ double *rates;
+ PaError result = paNoError;
+
+ /* Free allocated sample rate arrays and names*/
+ for( i=0; i<sNumDevices; i++ ){
+ dev = &sDevices[i].pad_Info;
+ rates = (double *) dev->sampleRates;
+ if((rates != NULL)) PaHost_FreeFastMemory(rates, MAX_NUMSAMPLINGRATES * sizeof(double));
+ dev->sampleRates = NULL;
+ if(dev->name != NULL) PaHost_FreeFastMemory((void *) dev->name, 32);
+ dev->name = NULL;
+ }
+
+ sNumDevices = 0;
+
+ /* Dispose : if not done by Pa_CloseStream */
+ if(ASIODisposeBuffers() != ASE_OK) result = paHostError;
+ if(ASIOExit() != ASE_OK) result = paHostError;
+
+ /* remove the loaded ASIO driver */
+ asioDrivers->removeCurrentDriver();
+
+ return result;
+}
+
+/***********************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ ASIOError err;
+ int32 device;
+
+ /* TO DO : Check if a stream already runs */
+ //if (asioDriverInfo.past != NULL) return paHostError; (does not work)
+
+ /* Check the device number */
+ if ((past->past_InputDeviceID != paNoDevice)
+ &&(past->past_OutputDeviceID != paNoDevice)
+ &&(past->past_InputDeviceID != past->past_OutputDeviceID))
+ {
+ return paInvalidDeviceId;
+ }
+
+ /* Allocation */
+ memset(&asioDriverInfo, 0, sizeof(PaHostSoundControl));
+ past->past_DeviceData = (void*) &asioDriverInfo;
+
+
+ /* FIXME */
+ asioDriverInfo.past = past;
+
+ /* load the ASIO device */
+ device = (past->past_InputDeviceID < 0) ? past->past_OutputDeviceID : past->past_InputDeviceID;
+ result = Pa_ASIO_loadDevice(device);
+ if (result != paNoError) goto error;
+
+ /* Check ASIO parameters and input parameters */
+ if ((past->past_NumInputChannels > asioDriverInfo.pahsc_NumInputChannels)
+ || (past->past_NumOutputChannels > asioDriverInfo.pahsc_NumOutputChannels)) {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+
+ /* Set sample rate */
+ if (ASIOSetSampleRate(past->past_SampleRate) != ASE_OK) {
+ result = paInvalidSampleRate;
+ goto error;
+ }
+
+ /* if OK calc buffer size */
+ result = PaHost_CalcNumHostBuffers( past );
+ if (result != paNoError) goto error;
+
+
+ /*
+ Allocating input and output buffers number for the real past_NumInputChannels and past_NumOutputChannels
+ optimize the data transfer.
+ */
+
+ asioDriverInfo.pahsc_NumInputChannels = past->past_NumInputChannels;
+ asioDriverInfo.pahsc_NumOutputChannels = past->past_NumOutputChannels;
+
+ /* Allocate ASIO buffers and callback*/
+ err = Pa_ASIO_CreateBuffers(&asioDriverInfo,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ if (err == ASE_OK)
+ return paNoError;
+ else if (err == ASE_NoMemory)
+ result = paInsufficientMemory;
+ else if (err == ASE_InvalidParameter)
+ result = paInvalidChannelCount;
+ else if (err == ASE_InvalidMode)
+ result = paBufferTooBig;
+ else
+ result = paHostError;
+
+error:
+ ASIOExit();
+ return result;
+
+}
+
+/***********************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ #if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+ #endif
+
+ /* Dispose */
+ if(ASIODisposeBuffers() != ASE_OK) result = paHostError;
+ if(ASIOExit() != ASE_OK) result = paHostError;
+
+ /* Free data and device for output. */
+ past->past_DeviceData = NULL;
+ asioDriverInfo.past = NULL;
+
+ return result;
+}
+
+/***********************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ /* Clear the index 0 host output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ 0,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ /* Clear the index 1 host output buffer */
+ Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos,
+ asioDriverInfo.pahsc_channelInfos[0].type,
+ asioDriverInfo.pahsc_NumInputChannels,
+ asioDriverInfo.pahsc_NumOutputChannels,
+ 1,
+ 0,
+ asioDriverInfo.past_FramesPerHostBuffer);
+
+ Pa_ASIO_Clear_User_Buffers();
+
+ Pa_ASIO_Adaptor_Init();
+
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ /* Nothing to do ?? */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ /* Nothing to do ?? */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ /* Nothing to do */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ // TO DO : count of samples
+ past->past_IsActive = 1;
+ return (ASIOStart() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ // TO DO : count of samples
+ past->past_IsActive = 0;
+ return (ASIOStop() == ASE_OK) ? paNoError : paHostError;
+}
+
+/***********************************************************************/
+// TO BE CHECKED
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_NumFramesDone;
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ #if MAC
+ void *addr = NewPtrClear( numBytes );
+ if( (addr == NULL) || (MemError () != 0) ) return NULL;
+
+ #if (CARBON_COMPATIBLE == 0)
+ if( HoldMemory( addr, numBytes ) != noErr )
+ {
+ DisposePtr( (Ptr) addr );
+ return NULL;
+ }
+ #endif
+ return addr;
+ #elif WINDOWS
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+ #endif
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ #if MAC
+ if( addr == NULL ) return;
+ #if CARBON_COMPATIBLE
+ (void) numBytes;
+ #else
+ UnholdMemory( addr, numBytes );
+ #endif
+ DisposePtr( (Ptr) addr );
+ #elif WINDOWS
+ if( addr != NULL ) free( addr );
+ #endif
+}
+
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+ #if MAC
+ int32 sleepTime, endTime;
+ /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
+ sleepTime = ((msec * 60) + 999) / 1000;
+ if( sleepTime < 1 ) sleepTime = 1;
+ endTime = TickCount() + sleepTime;
+ do{
+ DBUGX(("Sleep for %d ticks.\n", sleepTime ));
+ WaitNextEvent( 0, NULL, sleepTime, NULL ); /* Use this just to sleep without getting events. */
+ sleepTime = endTime - TickCount();
+ } while( sleepTime > 0 );
+ #elif WINDOWS
+ Sleep( msec );
+ #endif
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ return &sDevices[id].pad_Info;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ return sDefaultInputDeviceID;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ return sDefaultOutputDeviceID;
+}
+
+/*************************************************************************/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+ // TO BE IMPLEMENTED : using the ASIOGetLatency call??
+ return 2;
+}
+
+/*************************************************************************/
+int32 Pa_GetHostError( void )
+{
+ int32 err = sPaHostError;
+ sPaHostError = 0;
+ return err;
+}
+
+
+#ifdef MAC
+
+/**************************************************************************/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ UnsignedWide widePad;
+ if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+ Microseconds( &widePad );
+ pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
+}
+/**************************************************************************/
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ UnsignedWide widePad;
+ UInt64 CurrentCount;
+ long InsideCount;
+ long TotalCount;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+/* Measure CPU utilization during this callback. Note that this calculation
+** assumes that we had the processor the whole time.
+*/
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+ Microseconds( &widePad );
+ CurrentCount = UnsignedWideToUInt64( widePad );
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount);
+ TotalCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount);
+/* Low pass filter the result because sometimes we get called several times in a row.
+* That can cause the TotalCount to be very low which can cause the usage to appear
+* unnaturally high. So we must filter numerator and denominator separately!!!
+*/
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+}
+
+#elif WINDOWS
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+/* Query system timer for usage analysis and to prevent overuse of CPU. */
+ QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ LARGE_INTEGER CurrentCount = { 0, 0 };
+ LONGLONG InsideCount;
+ LONGLONG TotalCount;
+/*
+** Measure CPU utilization during this callback. Note that this calculation
+** assumes that we had the processor the whole time.
+*/
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( QueryPerformanceCounter( &CurrentCount ) )
+ {
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+ TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart;
+/* Low pass filter the result because sometimes we get called several times in a row.
+ * That can cause the TotalCount to be very low which can cause the usage to appear
+ * unnaturally high. So we must filter numerator and denominator separately!!!
+ */
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+ }
+}
+
+#endif
+
+
+
+
--- /dev/null
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ---
+ *
+ * Significant portions of this file are based on sample code from Be. The
+ * Be Sample Code Licence follows:
+ *
+ * Copyright 1991-1999, Be Incorporated.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions, and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include <be/media/BufferGroup.h>
+#include <be/media/Buffer.h>
+#include <be/media/TimeSource.h>
+
+#include "PlaybackNode.h"
+
+#define PRINT(x) { printf x; fflush(stdout); }
+
+#ifdef DEBUG
+#define DBUG(x) PRINT(x)
+#else
+#define DBUG(x)
+#endif
+
+
+PaPlaybackNode::PaPlaybackNode(uint32 channels, float frame_rate, uint32 frames_per_buffer,
+ PortAudioCallback* callback, void *user_data) :
+ BMediaNode("PortAudio input node"),
+ BBufferProducer(B_MEDIA_RAW_AUDIO),
+ BMediaEventLooper(),
+ mAborted(false),
+ mRunning(false),
+ mBufferGroup(NULL),
+ mDownstreamLatency(0),
+ mStartTime(0),
+ mCallback(callback),
+ mUserData(user_data),
+ mFramesPerBuffer(frames_per_buffer)
+{
+ DBUG(("Constructor called.\n"));
+
+ mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
+ mPreferredFormat.u.raw_audio.channel_count = channels;
+ mPreferredFormat.u.raw_audio.frame_rate = frame_rate;
+ mPreferredFormat.u.raw_audio.byte_order =
+ (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
+ mPreferredFormat.u.raw_audio.buffer_size =
+ media_raw_audio_format::wildcard.buffer_size;
+
+ mOutput.destination = media_destination::null;
+ mOutput.format = mPreferredFormat;
+
+ /* The amount of time it takes for this node to produce a buffer when
+ * asked. Essentially, it is how long the user's callback takes to run.
+ * We set this to be the length of the sound data each buffer of the
+ * requested size can hold. */
+ //mInternalLatency = (bigtime_t)(1000000 * frames_per_buffer / frame_rate);
+
+ /* ACK! it seems that the mixer (at least on my machine) demands that IT
+ * specify the buffer size, so for now I'll just make a generic guess here */
+ mInternalLatency = 1000000 / 20;
+}
+
+
+
+PaPlaybackNode::~PaPlaybackNode()
+{
+ DBUG(("Destructor called.\n"));
+ Quit(); /* Stop the BMediaEventLooper thread */
+}
+
+
+/*************************
+ *
+ * Local methods
+ *
+ */
+
+bool PaPlaybackNode::IsRunning()
+{
+ return mRunning;
+}
+
+
+PaTimestamp PaPlaybackNode::GetStreamTime()
+{
+ BTimeSource *timeSource = TimeSource();
+ PaTimestamp time = (timeSource->Now() - mStartTime) *
+ mPreferredFormat.u.raw_audio.frame_rate / 1000000;
+ return time;
+}
+
+
+void PaPlaybackNode::SetSampleFormat(PaSampleFormat inFormat,
+ PaSampleFormat outFormat)
+{
+ uint32 beOutFormat;
+
+ switch(outFormat)
+ {
+ case paFloat32:
+ beOutFormat = media_raw_audio_format::B_AUDIO_FLOAT;
+ mOutputSampleWidth = 4;
+ break;
+
+ case paInt16:
+ beOutFormat = media_raw_audio_format::B_AUDIO_SHORT;
+ mOutputSampleWidth = 2;
+ break;
+
+ case paInt32:
+ beOutFormat = media_raw_audio_format::B_AUDIO_INT;
+ mOutputSampleWidth = 4;
+ break;
+
+ case paInt8:
+ beOutFormat = media_raw_audio_format::B_AUDIO_CHAR;
+ mOutputSampleWidth = 1;
+ break;
+
+ case paUInt8:
+ beOutFormat = media_raw_audio_format::B_AUDIO_UCHAR;
+ mOutputSampleWidth = 1;
+ break;
+
+ case paInt24:
+ case paPackedInt24:
+ case paCustomFormat:
+ DBUG(("Unsupported output format: %x\n", outFormat));
+ break;
+
+ default:
+ DBUG(("Unknown output format: %x\n", outFormat));
+ }
+
+ mPreferredFormat.u.raw_audio.format = beOutFormat;
+ mFramesPerBuffer * mPreferredFormat.u.raw_audio.channel_count * mOutputSampleWidth;
+}
+
+BBuffer *PaPlaybackNode::FillNextBuffer(bigtime_t time)
+{
+ /* Get a buffer from the buffer group */
+ BBuffer *buf = mBufferGroup->RequestBuffer(
+ mOutput.format.u.raw_audio.buffer_size, BufferDuration());
+ unsigned long frames = mOutput.format.u.raw_audio.buffer_size /
+ mOutputSampleWidth / mOutput.format.u.raw_audio.channel_count;
+ bigtime_t start_time;
+ int ret;
+
+ if( !buf )
+ {
+ DBUG(("Unable to allocate a buffer\n"));
+ return NULL;
+ }
+
+ start_time = mStartTime +
+ (bigtime_t)((double)mSamplesSent /
+ (double)mOutput.format.u.raw_audio.frame_rate /
+ (double)mOutput.format.u.raw_audio.channel_count *
+ 1000000.0);
+
+ /* Now call the user callback to get the data */
+ ret = mCallback(NULL, /* Input buffer */
+ buf->Data(), /* Output buffer */
+ frames, /* Frames per buffer */
+ mSamplesSent / mOutput.format.u.raw_audio.channel_count, /* timestamp */
+ mUserData);
+
+ if( ret )
+ mAborted = true;
+
+ media_header *hdr = buf->Header();
+
+ hdr->type = B_MEDIA_RAW_AUDIO;
+ hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
+ hdr->time_source = TimeSource()->ID();
+ hdr->start_time = start_time;
+
+ return buf;
+}
+
+
+
+
+/*************************
+ *
+ * BMediaNode methods
+ *
+ */
+
+BMediaAddOn *PaPlaybackNode::AddOn( int32 * ) const
+{
+ DBUG(("AddOn() called.\n"));
+ return NULL; /* we don't provide service to outside applications */
+}
+
+
+status_t PaPlaybackNode::HandleMessage( int32 message, const void *data,
+ size_t size )
+{
+ DBUG(("HandleMessage() called.\n"));
+ return B_ERROR; /* we don't define any custom messages */
+}
+
+
+
+
+/*************************
+ *
+ * BMediaEventLooper methods
+ *
+ */
+
+void PaPlaybackNode::NodeRegistered()
+{
+ DBUG(("NodeRegistered() called.\n"));
+
+ /* Start the BMediaEventLooper thread */
+ SetPriority(B_REAL_TIME_PRIORITY);
+ Run();
+
+ /* set up as much information about our output as we can */
+ mOutput.source.port = ControlPort();
+ mOutput.source.id = 0;
+ mOutput.node = Node();
+ ::strcpy(mOutput.name, "PortAudio Playback");
+}
+
+
+void PaPlaybackNode::HandleEvent( const media_timed_event *event,
+ bigtime_t lateness, bool realTimeEvent )
+{
+ // DBUG(("HandleEvent() called.\n"));
+ status_t err;
+
+ switch(event->type)
+ {
+ case BTimedEventQueue::B_START:
+ DBUG((" Handling a B_START event\n"));
+ if( RunState() != B_STARTED )
+ {
+ mStartTime = event->event_time + EventLatency();
+ mSamplesSent = 0;
+ mAborted = false;
+ mRunning = true;
+ media_timed_event firstEvent( mStartTime,
+ BTimedEventQueue::B_HANDLE_BUFFER );
+ EventQueue()->AddEvent( firstEvent );
+ }
+ break;
+
+ case BTimedEventQueue::B_STOP:
+ DBUG((" Handling a B_STOP event\n"));
+ mRunning = false;
+ EventQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true,
+ BTimedEventQueue::B_HANDLE_BUFFER );
+ break;
+
+ case BTimedEventQueue::B_HANDLE_BUFFER:
+ //DBUG((" Handling a B_HANDLE_BUFFER event\n"));
+
+ /* make sure we're started and connected */
+ if( RunState() != BMediaEventLooper::B_STARTED ||
+ mOutput.destination == media_destination::null )
+ break;
+
+ BBuffer *buffer = FillNextBuffer(event->event_time);
+
+ /* make sure we weren't aborted while this routine was running.
+ * this can happen in one of two ways: either the callback returned
+ * nonzero (in which case mAborted is set in FillNextBuffer() ) or
+ * the client called AbortStream */
+ if( mAborted )
+ {
+ if( buffer )
+ buffer->Recycle();
+ Stop(0, true);
+ break;
+ }
+
+ if( buffer )
+ {
+ err = SendBuffer(buffer, mOutput.destination);
+ if( err != B_OK )
+ buffer->Recycle();
+ }
+
+ mSamplesSent += mOutput.format.u.raw_audio.buffer_size / mOutputSampleWidth;
+
+ /* Now schedule the next buffer event, so we can send another
+ * buffer when this one runs out. We calculate when it should
+ * happen by calculating when the data we just sent will finish
+ * playing.
+ *
+ * NOTE, however, that the event will actually get generated
+ * earlier than we specify, to account for the latency it will
+ * take to produce the buffer. It uses the latency value we
+ * specified in SetEventLatency() to determine just how early
+ * to generate it. */
+
+ /* totalPerformanceTime includes the time represented by the buffer
+ * we just sent */
+ bigtime_t totalPerformanceTime = (bigtime_t)((double)mSamplesSent /
+ (double)mOutput.format.u.raw_audio.channel_count /
+ (double)mOutput.format.u.raw_audio.frame_rate * 1000000.0);
+
+ bigtime_t nextEventTime = mStartTime + totalPerformanceTime;
+
+ media_timed_event nextBufferEvent(nextEventTime,
+ BTimedEventQueue::B_HANDLE_BUFFER);
+ EventQueue()->AddEvent(nextBufferEvent);
+
+ break;
+
+ }
+}
+
+
+
+
+/*************************
+ *
+ * BBufferProducer methods
+ *
+ */
+
+status_t PaPlaybackNode::FormatSuggestionRequested( media_type type,
+ int32 /*quality*/, media_format* format )
+{
+ /* the caller wants to know this node's preferred format and provides
+ * a suggestion, asking if we support it */
+ DBUG(("FormatSuggestionRequested() called.\n"));
+
+ if(!format)
+ return B_BAD_VALUE;
+
+ *format = mPreferredFormat;
+
+ /* we only support raw audio (a wildcard is okay too) */
+ if ( type == B_MEDIA_UNKNOWN_TYPE || type == B_MEDIA_RAW_AUDIO )
+ return B_OK;
+ else
+ return B_MEDIA_BAD_FORMAT;
+}
+
+
+status_t PaPlaybackNode::FormatProposal( const media_source& output,
+ media_format* format )
+{
+ /* This is similar to FormatSuggestionRequested(), but it is actually part
+ * of the negotiation process. We're given the opportunity to specify any
+ * properties that are wildcards (ie. properties that the other node doesn't
+ * care one way or another about) */
+ DBUG(("FormatProposal() called.\n"));
+
+ /* Make sure this proposal really applies to our output */
+ if( output != mOutput.source )
+ return B_MEDIA_BAD_SOURCE;
+
+ /* We return two things: whether we support the proposed format, and our own
+ * preferred format */
+ *format = mPreferredFormat;
+
+ if( format->type == B_MEDIA_UNKNOWN_TYPE || format->type == B_MEDIA_RAW_AUDIO )
+ return B_OK;
+ else
+ return B_MEDIA_BAD_FORMAT;
+}
+
+
+status_t PaPlaybackNode::FormatChangeRequested( const media_source& source,
+ const media_destination& destination, media_format* io_format, int32* )
+{
+ /* we refuse to change formats, supporting only 1 */
+ DBUG(("FormatChangeRequested() called.\n"));
+
+ return B_ERROR;
+}
+
+
+status_t PaPlaybackNode::GetNextOutput( int32* cookie, media_output* out_output )
+{
+ /* this is where we allow other to enumerate our outputs -- the cookie is
+ * an integer we can use to keep track of where we are in enumeration. */
+ DBUG(("GetNextOutput() called.\n"));
+
+ if( *cookie == 0 )
+ {
+ *out_output = mOutput;
+ *cookie = 1;
+ return B_OK;
+ }
+
+ return B_BAD_INDEX;
+}
+
+
+status_t PaPlaybackNode::DisposeOutputCookie( int32 cookie )
+{
+ DBUG(("DisposeOutputCookie() called.\n"));
+ return B_OK;
+}
+
+
+void PaPlaybackNode::LateNoticeReceived( const media_source& what,
+ bigtime_t how_much, bigtime_t performance_time )
+{
+ /* This function is called as notification that a buffer we sent wasn't
+ * received by the time we stamped it with -- it got there late. Basically,
+ * it means we underestimated our own latency, so we should increase it */
+ DBUG(("LateNoticeReceived() called.\n"));
+
+ if( what != mOutput.source )
+ return;
+
+ if( RunMode() == B_INCREASE_LATENCY )
+ {
+ mInternalLatency += how_much;
+ SetEventLatency( mDownstreamLatency + mInternalLatency );
+ DBUG(("Increasing latency to %Ld\n", mDownstreamLatency + mInternalLatency));
+ }
+ else
+ DBUG(("I don't know what to do with this notice!"));
+}
+
+
+void PaPlaybackNode::EnableOutput( const media_source& what, bool enabled,
+ int32* )
+{
+ DBUG(("EnableOutput() called.\n"));
+ /* stub -- we don't support this yet */
+}
+
+
+status_t PaPlaybackNode::PrepareToConnect( const media_source& what,
+ const media_destination& where, media_format* format,
+ media_source* out_source, char* out_name )
+{
+ /* the final stage of format negotiations. here we _must_ make specific any
+ * remaining wildcards */
+ DBUG(("PrepareToConnect() called.\n"));
+
+ /* make sure this really refers to our source */
+ if( what != mOutput.source )
+ return B_MEDIA_BAD_SOURCE;
+
+ /* make sure we're not already connected */
+ if( mOutput.destination != media_destination::null )
+ return B_MEDIA_ALREADY_CONNECTED;
+
+ if( format->type != B_MEDIA_RAW_AUDIO )
+ return B_MEDIA_BAD_FORMAT;
+
+ if( format->u.raw_audio.format != mPreferredFormat.u.raw_audio.format )
+ return B_MEDIA_BAD_FORMAT;
+
+ if( format->u.raw_audio.buffer_size ==
+ media_raw_audio_format::wildcard.buffer_size )
+ {
+ DBUG(("We were left to decide buffer size: choosing 2048"));
+ format->u.raw_audio.buffer_size = 2048;
+ }
+ else
+ DBUG(("Using consumer specified buffer size of %lu.\n",
+ format->u.raw_audio.buffer_size));
+
+ /* Reserve the connection, return the information */
+ mOutput.destination = where;
+ mOutput.format = *format;
+ *out_source = mOutput.source;
+ strncpy( out_name, mOutput.name, B_MEDIA_NAME_LENGTH );
+
+ return B_OK;
+}
+
+
+void PaPlaybackNode::Connect(status_t error, const media_source& source,
+ const media_destination& destination, const media_format& format, char* io_name)
+{
+ DBUG(("Connect() called.\n"));
+
--- /dev/null
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <be/media/MediaRoster.h>
+#include <be/media/MediaEventLooper.h>
+#include <be/media/BufferProducer.h>
+
+#include "portaudio.h"
+
+class PaPlaybackNode :
+ public BBufferProducer,
+ public BMediaEventLooper
+{
+
+public:
+ PaPlaybackNode( uint32 channels, float frame_rate, uint32 frames_per_buffer,
+ PortAudioCallback *callback, void *user_data );
+ ~PaPlaybackNode();
+
+
+ /* Local methods ******************************************/
+
+ BBuffer *FillNextBuffer(bigtime_t time);
+ void SetSampleFormat(PaSampleFormat inFormat, PaSampleFormat outFormat);
+ bool IsRunning();
+ PaTimestamp GetStreamTime();
+
+ /* BMediaNode methods *************************************/
+
+ BMediaAddOn* AddOn( int32 * ) const;
+ status_t HandleMessage( int32 message, const void *data, size_t size );
+
+ /* BMediaEventLooper methods ******************************/
+
+ void HandleEvent( const media_timed_event *event, bigtime_t lateness,
+ bool realTimeEvent );
+ void NodeRegistered();
+
+ /* BBufferProducer methods ********************************/
+
+ status_t FormatSuggestionRequested( media_type type, int32 quality,
+ media_format* format );
+ status_t FormatProposal( const media_source& output, media_format* format );
+ status_t FormatChangeRequested( const media_source& source,
+ const media_destination& destination, media_format* io_format, int32* );
+
+ status_t GetNextOutput( int32* cookie, media_output* out_output );
+ status_t DisposeOutputCookie( int32 cookie );
+
+ void LateNoticeReceived( const media_source& what, bigtime_t how_much,
+ bigtime_t performance_time );
+ void EnableOutput( const media_source& what, bool enabled, int32* _deprecated_ );
+
+ status_t PrepareToConnect( const media_source& what,
+ const media_destination& where, media_format* format,
+ media_source* out_source, char* out_name );
+ void Connect(status_t error, const media_source& source,
+ const media_destination& destination, const media_format& format,
+ char* io_name);
+ void Disconnect(const media_source& what, const media_destination& where);
+
+ status_t SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup);
+
+ bool mAborted;
+
+private:
+ media_output mOutput;
+ media_format mPreferredFormat;
+ uint32 mOutputSampleWidth, mFramesPerBuffer;
+ BBufferGroup *mBufferGroup;
+ bigtime_t mDownstreamLatency, mInternalLatency, mStartTime;
+ uint64 mSamplesSent;
+ PortAudioCallback *mCallback;
+ void *mUserData;
+ bool mRunning;
+
+};
+
--- /dev/null
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * BeOS Media Kit Implementation by Joshua Haberman
+ *
+ * Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <be/app/Application.h>
+#include <be/kernel/OS.h>
+#include <be/media/RealtimeAlloc.h>
+#include <be/media/MediaRoster.h>
+#include <be/media/TimeSource.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "portaudio.h"
+#include "pa_host.h"
+
+#include "PlaybackNode.h"
+
+#define PRINT(x) { printf x; fflush(stdout); }
+
+#ifdef DEBUG
+#define DBUG(x) PRINT(x)
+#else
+#define DBUG(x)
+#endif
+
+typedef struct PaHostSoundControl
+{
+ /* These members are common to all modes of operation */
+ media_node pahsc_TimeSource; /* the sound card's DAC. */
+ media_format pahsc_Format;
+
+ /* These methods are specific to playing mode */
+ media_node pahsc_OutputNode; /* output to the mixer */
+ media_node pahsc_InputNode; /* reads data from user callback -- PA specific */
+
+ media_input pahsc_MixerInput; /* input jack on the soundcard's mixer. */
+ media_output pahsc_PaOutput; /* output jack from the PA node */
+
+ PaPlaybackNode *pahsc_InputNodeInstance;
+
+}
+PaHostSoundControl;
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ /* stub */
+ return 0;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ /* stub */
+ return 0;
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ /* stub */
+ return NULL;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ /* stub */
+ return 1;
+}
+
+/*************************************************************************/
+PaError PaHost_Init( void )
+{
+ /* we have to create this in order to use BMediaRoster. I hope it doesn't
+ * cause problems */
+ be_app = new BApplication("application/x-vnd.portaudio-app");
+
+ return paNoError;
+}
+
+PaError PaHost_Term( void )
+{
+ delete be_app;
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+ DBUG(("IsRunning returning: %s\n",
+ pahsc->pahsc_InputNodeInstance->IsRunning() ? "true" : "false"));
+
+ return (PaError)pahsc->pahsc_InputNodeInstance->IsRunning();
+}
+
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ bigtime_t very_soon, start_latency;
+ status_t err;
+ BMediaRoster *roster = BMediaRoster::Roster(&err);
+ PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+
+ /* for some reason, err indicates an error (though nothing it wrong)
+ * when the DBUG macro in pa_lib.c is enabled. It's reproducably
+ * linked. Weird. */
+ if( !roster /* || err != B_OK */ )
+ {
+ DBUG(("No media server! err=%d, roster=%x\n", err, roster));
+ return paHostError;
+ }
+
+ /* tell the node when to start -- since there aren't any other nodes
+ * starting that we have to wait for, just tell it to start now
+ */
+
+ BTimeSource *timeSource = roster->MakeTimeSourceFor(pahsc->pahsc_TimeSource);
+ very_soon = timeSource->PerformanceTimeFor( BTimeSource::RealTime() );
+ timeSource->Release();
+
+ /* Add the latency of starting the network of nodes */
+ err = roster->GetStartLatencyFor( pahsc->pahsc_TimeSource, &start_latency );
+ very_soon += start_latency;
+
+ err = roster->StartNode( pahsc->pahsc_InputNode, very_soon );
+ /* No need to start the mixer -- it's always running */
+
+ return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+ BMediaRoster *roster = BMediaRoster::Roster();
+
+ if( !roster )
+ {
+ DBUG(("No media roster!\n"));
+ return paHostError;
+ }
+
+ if( !pahsc )
+ return paHostError;
+
+ /* this crashes, and I don't know why yet */
+ // if( abort )
+ // pahsc->pahsc_InputNodeInstance->mAborted = true;
+
+ roster->StopNode(pahsc->pahsc_InputNode, 0, /* immediate = */ true);
+
+ return paNoError;
+}
+
+
+/*************************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ status_t err;
+ BMediaRoster *roster = BMediaRoster::Roster(&err);
+ PaHostSoundControl *pahsc;
+
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+
+ if( !roster /* || err != B_OK */ )
+ {
+ /* no media server! */
+ DBUG(("No media server.\n"));
+ goto error;
+ }
+
+ if ( past->past_NumInputChannels > 0 && past->past_NumOutputChannels > 0 )
+ {
+ /* filter -- not implemented yet */
+ goto error;
+ }
+ else if ( past->past_NumInputChannels > 0 )
+ {
+ /* recorder -- not implemented yet */
+ goto error;
+ }
+ else
+ {
+ /* player ****************************************************************/
+
+ status_t err;
+ int32 num;
+
+ /* First we need to create the three components (like components in a stereo
+ * system). The mixer component is our interface to the sound card, data
+ * we write there will get played. The BePA_InputNode component is the node
+ * which represents communication with the PA client (it is what calls the
+ * client's callbacks). The time source component is the sound card's DAC,
+ * which allows us to slave the other components to it instead of the system
+ * clock. */
+ err = roster->GetAudioMixer( &pahsc->pahsc_OutputNode );
+ if( err != B_OK )
+ {
+ DBUG(("Couldn't get default mixer.\n"));
+ goto error;
+ }
+
+ err = roster->GetTimeSource( &pahsc->pahsc_TimeSource );
+ if( err != B_OK )
+ {
+ DBUG(("Couldn't get time source.\n"));
+ goto error;
+ }
+
+ pahsc->pahsc_InputNodeInstance = new PaPlaybackNode(2, 44100,
+ past->past_FramesPerUserBuffer, past->past_Callback, past->past_UserData );
+ pahsc->pahsc_InputNodeInstance->SetSampleFormat(0,
+ past->past_OutputSampleFormat);
+ err = roster->RegisterNode( pahsc->pahsc_InputNodeInstance );
+ if( err != B_OK )
+ {
+ DBUG(("Unable to register node.\n"));
+ goto error;
+ }
+
+ roster->GetNodeFor( pahsc->pahsc_InputNodeInstance->Node().node,
+ &pahsc->pahsc_InputNode );
+ if( err != B_OK )
+ {
+ DBUG(("Unable to get input node.\n"));
+ goto error;
+ }
+
+ /* Now we have three components (nodes) sitting next to each other. The
+ * next step is to look at them and find their inputs and outputs so we can
+ * wire them together. */
+ err = roster->GetFreeInputsFor( pahsc->pahsc_OutputNode,
+ &pahsc->pahsc_MixerInput, 1, &num, B_MEDIA_RAW_AUDIO );
+ if( err != B_OK || num < 1 )
+ {
+ DBUG(("Couldn't get the mixer input.\n"));
+ goto error;
+ }
+
+ err = roster->GetFreeOutputsFor( pahsc->pahsc_InputNode,
+ &pahsc->pahsc_PaOutput, 1, &num, B_MEDIA_RAW_AUDIO );
+ if( err != B_OK || num < 1 )
+ {
+ DBUG(("Couldn't get PortAudio output.\n"));
+ goto error;
+ }
+
+
+ /* We've found the input and output -- the final step is to run a wire
+ * between them so they are connected. */
+
+ /* try to make the mixer input adapt to what PA sends it */
+ pahsc->pahsc_Format = pahsc->pahsc_PaOutput.format;
+ roster->Connect( pahsc->pahsc_PaOutput.source,
+ pahsc->pahsc_MixerInput.destination, &pahsc->pahsc_Format,
+ &pahsc->pahsc_PaOutput, &pahsc->pahsc_MixerInput );
+
+
+ /* Actually, there's one final step -- tell them all to sync to the
+ * sound card's DAC */
+ roster->SetTimeSourceFor( pahsc->pahsc_InputNode.node,
+ pahsc->pahsc_TimeSource.node );
+ roster->SetTimeSourceFor( pahsc->pahsc_OutputNode.node,
+ pahsc->pahsc_TimeSource.node );
+
+ }
+
+ return paNoError;
+
+error:
+ PaHost_CloseStream( past );
+ return paHostError;
+}
+
+/*************************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+ status_t err;
+ BMediaRoster *roster = BMediaRoster::Roster(&err);
+
+ if( !roster )
+ {
+ DBUG(("Couldn't get media roster\n"));
+ return paHostError;
+ }
+
+ if( !pahsc )
+ return paHostError;
+
+ /* Disconnect all the connections we made when opening the stream */
+
+ roster->Disconnect(pahsc->pahsc_InputNode.node, pahsc->pahsc_PaOutput.source,
+ pahsc->pahsc_OutputNode.node, pahsc->pahsc_MixerInput.destination);
+
+ DBUG(("Calling ReleaseNode()"));
+ roster->ReleaseNode(pahsc->pahsc_InputNode);
+
+ /* deleting the node shouldn't be necessary -- it is reference counted, and will
+ * delete itself when its references drop to zero. the call to ReleaseNode()
+ * above should decrease its reference count */
+ pahsc->pahsc_InputNodeInstance = NULL;
+
+ return paNoError;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
+
+ return pahsc->pahsc_InputNodeInstance->GetStreamTime();
+}
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+ /* snooze() takes microseconds */
+ snooze( msec * 1000 );
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ /* BeOS supports non-pagable memory through pools -- a pool is an area
+ * of physical memory that is locked. It would be best to pre-allocate
+ * that pool and then hand out memory from it, but we don't know in
+ * advance how much we'll need. So for now, we'll allocate a pool
+ * for every request we get, storing a pointer to the pool at the
+ * beginning of the allocated memory */
+ rtm_pool *pool;
+ void *addr;
+ long size = numBytes + sizeof(rtm_pool *);
+ static int counter = 0;
+ char pool_name[100];
+
+ /* Every pool needs a unique name. */
+ sprintf(pool_name, "PaPoolNumber%d", counter++);
+
+ if( rtm_create_pool( &pool, size, pool_name ) != B_OK )
+ return 0;
+
+ addr = rtm_alloc( pool, size );
+ if( addr == NULL )
+ return 0;
+
+ memset( addr, 0, numBytes );
+ *((rtm_pool **)addr) = pool; // store the pointer to the pool
+ addr = (rtm_pool **)addr + 1; // and return the next location in memory
+
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ rtm_pool *pool;
+
+ if( addr == NULL )
+ return;
+
+ addr = (rtm_pool **)addr - 1;
+ pool = *((rtm_pool **)addr);
+
+ rtm_free( addr );
+ rtm_delete_pool( pool );
+}
--- /dev/null
+#ifndef PA_HOST_H
+#define PA_HOST_H
+
+/*
+ * $Id$
+ * Host dependant internal API for PortAudio
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.softsynth.com/portaudio/
+ * DirectSound and Macintosh Implementation
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#ifndef SUPPORT_AUDIO_CAPTURE
+#define SUPPORT_AUDIO_CAPTURE (1)
+#endif
+
+#ifndef int32
+ typedef long int32;
+#endif
+#ifndef uint32
+ typedef unsigned long uint32;
+#endif
+#ifndef int16
+ typedef short int16;
+#endif
+#ifndef uint16
+ typedef unsigned short uint16;
+#endif
+
+#define PA_MAGIC (0x18273645)
+
+/************************************************************************************/
+/****************** Structures ******************************************************/
+/************************************************************************************/
+
+typedef struct internalPortAudioStream
+{
+ uint32 past_Magic; /* ID for struct to catch bugs. */
+ /* User specified information. */
+ uint32 past_FramesPerUserBuffer;
+ uint32 past_NumUserBuffers;
+ double past_SampleRate; /* Closest supported sample rate. */
+ int past_NumInputChannels;
+ int past_NumOutputChannels;
+ PaDeviceID past_InputDeviceID;
+ PaDeviceID past_OutputDeviceID;
+ PaSampleFormat past_InputSampleFormat;
+ PaSampleFormat past_OutputSampleFormat;
+ void *past_DeviceData;
+ PortAudioCallback *past_Callback;
+ void *past_UserData;
+ uint32 past_Flags;
+ /* Flags for communicating between foreground and background. */
+ volatile int past_IsActive; /* Background is still playing. */
+ volatile int past_StopSoon; /* Background should keep playing when buffers empty. */
+ volatile int past_StopNow; /* Background should stop playing now. */
+ /* These buffers are used when the native format does not match the user format. */
+ void *past_InputBuffer;
+ uint32 past_InputBufferSize;
+ void *past_OutputBuffer;
+ uint32 past_OutputBufferSize;
+ /* Measurements */
+ uint32 past_NumCallbacks;
+ PaTimestamp past_FrameCount; /* Frames output to buffer. */
+ /* For measuring CPU utilization. */
+ double past_AverageInsideCount;
+ double past_AverageTotalCount;
+ double past_Usage;
+ int past_IfLastExitValid;
+}
+internalPortAudioStream;
+
+/************************************************************************************/
+/****************** Prototypes ******************************************************/
+/************************************************************************************/
+
+PaError PaHost_Init( void );
+PaError PaHost_Term( void );
+
+PaError PaHost_OpenStream( internalPortAudioStream *past );
+PaError PaHost_CloseStream( internalPortAudioStream *past );
+
+PaError PaHost_StartOutput( internalPortAudioStream *past );
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort );
+PaError PaHost_StartInput( internalPortAudioStream *past );
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort );
+PaError PaHost_StartEngine( internalPortAudioStream *past );
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
+PaError PaHost_StreamActive( internalPortAudioStream *past );
+
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer );
+
+long Pa_CallConvertFloat32( internalPortAudioStream *past,
+ float *nativeInputBuffer,
+ float *nativeOutputBuffer );
+
+void *PaHost_AllocateFastMemory( long numBytes );
+void PaHost_FreeFastMemory( void *addr, long numBytes );
+
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr );
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable,
+ int numRates, double frameRate );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOST_H */
--- /dev/null
+/*
+ * $Id$
+ * Portable Audio I/O Library
+ * Host Independant Layer
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification History:
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+ PLB20010820 - fix dither and shift for recording PaUInt8 format
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifdef _WIN32
+#ifndef __MWERKS__
+#include <memory.h>
+#endif /* __MWERKS__ */
+#else /* !_WIN32 */
+#include <memory.h>
+#endif /* _WIN32 */
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/* The reason we might NOT want to validate the rate before opening the stream
+ * is because many DirectSound drivers lie about the rates they actually support.
+ */
+#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
+
+/*
+O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
+*/
+
+#ifndef FALSE
+ #define FALSE (0)
+ #define TRUE (!FALSE)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
+
+static PaError Pa_KillStream( PortAudioStream *stream, int abort );
+
+/***********************************************************************/
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
+{
+ double err, minErr = allowableError;
+ int i, bestFit = -1;
+
+ for( i=0; i<numRates; i++ )
+ {
+ err = fabs( frameRate - rateTable[i] );
+ if( err < minErr )
+ {
+ minErr = err;
+ bestFit = i;
+ }
+ }
+ return bestFit;
+}
+
+/**************************************************************************
+** Make sure sample rate is legal and also convert to enumeration for driver.
+*/
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr )
+{
+ long bestRateIndex;
+ const PaDeviceInfo *pdi;
+ pdi = Pa_GetDeviceInfo( id );
+ if( pdi == NULL )
+ {
+ return paInvalidDeviceId;
+ }
+
+ if( pdi->numSampleRates == -1 )
+ {
+ /* Is it out of range? */
+ if( (requestedFrameRate < pdi->sampleRates[0]) ||
+ (requestedFrameRate > pdi->sampleRates[1]) )
+ {
+ return paInvalidSampleRate;
+ }
+
+ *closestFrameRatePtr = requestedFrameRate;
+ }
+ else
+ {
+ bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
+ if( bestRateIndex < 0 ) return paInvalidSampleRate;
+ *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
+ }
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError Pa_OpenStream(
+ PortAudioStream** streamPtrPtr,
+ PaDeviceID inputDeviceID,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDeviceID,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ unsigned long streamFlags,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ internalPortAudioStream *past = NULL;
+ PaError result = paNoError;
+ int bitsPerInputSample;
+ int bitsPerOutputSample;
+ /* Print passed parameters. */
+ DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
+ streamPtrPtr, inputDeviceID, numInputChannels,
+ inputSampleFormat, inputDriverInfo ));
+ DBUG((" %d, %d, %d, %p, /* output */\n",
+ outputDeviceID, numOutputChannels,
+ outputSampleFormat, outputDriverInfo ));
+ DBUG((" %g, %d, %d, 0x%x, , %p )\n",
+ sampleRate, framesPerBuffer, numberOfBuffers,
+ streamFlags, userData ));
+
+ /* Check for parameter errors. */
+ if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
+ if( streamPtrPtr == NULL ) return paBadStreamPtr;
+ if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
+ if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
+ {
+ return paInvalidDeviceId;
+ }
+ if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
+
+#if SUPPORT_AUDIO_CAPTURE
+ if( inputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( inputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerInputSample = 8 * size;
+ if( (numInputChannels <= 0) ) return paInvalidChannelCount;
+ }
+#else
+ if( inputDeviceID >= 0 )
+ {
+ return paInvalidChannelCount;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ else
+ {
+ if( numInputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerInputSample = 0;
+ }
+
+ if( outputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( outputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerOutputSample = 8 * size;
+ if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
+ }
+ else
+ {
+ if( numOutputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerOutputSample = 0;
+ }
+
+ if( callback == NULL ) return paNullCallback;
+
+ /* Allocate and clear stream structure. */
+ past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
+ if( past == NULL ) return paInsufficientMemory;
+ memset( past, 0, sizeof(internalPortAudioStream) );
+ AddTraceMessage("Pa_OpenStream: past", (long) past );
+
+ past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
+ past->past_FramesPerUserBuffer = framesPerBuffer;
+ past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
+ past->past_Callback = callback;
+ past->past_UserData = userData;
+ past->past_OutputSampleFormat = outputSampleFormat;
+ past->past_InputSampleFormat = inputSampleFormat;
+ past->past_OutputDeviceID = outputDeviceID;
+ past->past_InputDeviceID = inputDeviceID;
+ past->past_NumInputChannels = numInputChannels;
+ past->past_NumOutputChannels = numOutputChannels;
+ past->past_Flags = streamFlags;
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ {
+ result = paInvalidSampleRate;
+ goto cleanup;
+ }
+
+ /* Allocate buffers that may be used for format conversion from user to native buffers. */
+ if( numInputChannels > 0 )
+ {
+
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ /* Allocate single Input buffer. */
+ past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
+ past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
+ if( past->past_InputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_InputBuffer = NULL;
+ }
+
+ /* Allocate single Output buffer. */
+ if( numOutputChannels > 0 )
+ {
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
+ past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
+ if( past->past_OutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_OutputBuffer = NULL;
+ }
+
+ result = PaHost_OpenStream( past );
+ if( result < 0 ) goto cleanup;
+
+ *streamPtrPtr = (void *) past;
+
+ return result;
+
+cleanup:
+ if( past != NULL ) Pa_CloseStream( past );
+ *streamPtrPtr = NULL;
+ return result;
+}
+
+
+/*************************************************************************/
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ return Pa_OpenStream(
+ stream,
+ ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ numInputChannels, sampleFormat, NULL,
+ ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ numOutputChannels, sampleFormat, NULL,
+ sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
+}
+
+/*************************************************************************/
+PaError Pa_CloseStream( PortAudioStream* stream)
+{
+ PaError result;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_CloseStream()\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ Pa_AbortStream( past );
+ result = PaHost_CloseStream( past );
+
+ if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
+ if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
+ PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
+
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StartStream( PortAudioStream *stream )
+{
+ PaError result = paHostError;
+ internalPortAudioStream *past;
+
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ past->past_FrameCount = 0.0;
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StartInput( past );
+ DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StartOutput( past );
+ DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ result = PaHost_StartEngine( past );
+ DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+
+ return paNoError;
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StopStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 0 );
+}
+
+/*************************************************************************/
+PaError Pa_AbortStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 1 );
+}
+
+/*************************************************************************/
+static PaError Pa_KillStream( PortAudioStream *stream, int abort )
+{
+ PaError result = paNoError;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_StopStream().\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_StopEngine( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StopInput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StopOutput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+error:
+ past->past_Usage = 0;
+ past->past_IfLastExitValid = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_StreamActive( PortAudioStream *stream )
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return PaHost_StreamActive( past );
+}
+
+/*************************************************************************/
+const char *Pa_GetErrorText( PaError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case paNoError: msg = "Success"; break;
+ case paHostError: msg = "Host error."; break;
+ case paInvalidChannelCount: msg = "Invalid number of channels."; break;
+ case paInvalidSampleRate: msg = "Invalid sample rate."; break;
+ case paInvalidDeviceId: msg = "Invalid device ID."; break;
+ case paInvalidFlag: msg = "Invalid flag."; break;
+ case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
+ case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
+ case paInsufficientMemory: msg = "Insufficient memory."; break;
+ case paBufferTooBig: msg = "Buffer too big."; break;
+ case paBufferTooSmall: msg = "Buffer too small."; break;
+ case paNullCallback: msg = "No callback routine specified."; break;
+ case paBadStreamPtr: msg = "Invalid stream pointer."; break;
+ case paTimedOut : msg = "Wait Timed Out."; break;
+ case paInternalError: msg = "Internal PortAudio Error."; break;
+ case paDeviceUnavailable: msg = "Device Unavailable."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+/*
+ Get CPU Load as a fraction of total CPU time.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ The amount may vary depending on CPU load.
+ This function may be called from the callback function.
+*/
+double Pa_GetCPULoad( PortAudioStream* stream)
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return (double) paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return past->past_Usage;
+}
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+static long Pa_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+ /* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+ /* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Int16, call user code, then convert output
+** to Int16 format for native use.
+** Assumes host native format is paInt16.
+** Returns result from user callback.
+*/
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer )
+{
+ long temp;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ unsigned int i;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+#if SUPPORT_AUDIO_CAPTURE
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ /* Convert from native format to PA format. */
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ switch(past->past_InputSampleFormat)
+ {
+
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ /* Convert 16 bit data to 32 bit integers */
+ int *inBufPtr = (int *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] << 16;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ /* Already in correct format so don't copy. */
+ inputBuffer = nativeInputBuffer;
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+ char *inBufPtr = (char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (char)(temp >> 8);
+ }
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+ unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 8; /* PLB20010820 */
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */
+ if( outputBuffer != NULL )
+ {
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ switch(past->past_OutputSampleFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ if( past->past_Flags & paClipOff ) /* NOTHING */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
+ }
+ }
+ else /* CLIP */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = (long)(outBufPtr[i] * 32767.0f);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ float dithered = (outBufPtr[i] * (32767.0f)) + dither;
+ temp = (long) (dithered);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ int *outBufPtr = (int *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ /* Shift one bit down before dithering so that we have room for overflow from add. */
+ temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ }
+
+ return userResult;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Float32, call user code, then convert output
+** to Float32 format for native use.
+** Assumes host native format is Float32.
+** Returns result from user callback.
+** FIXME - Unimplemented for formats other than paFloat32!!!!
+*/
+long Pa_CallConvertFloat32( internalPortAudioStream *past,
+ float *nativeInputBuffer,
+ float *nativeOutputBuffer )
+{
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ inputBuffer = nativeInputBuffer; /* FIXME */
+ }
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */ /* FIXME */
+ return userResult;
+}
+
+/*************************************************************************/
+PaError Pa_Initialize( void )
+{
+ if( gInitCount++ > 0 ) return paNoError;
+ ResetTraceMessages();
+ return PaHost_Init();
+}
+
+PaError Pa_Terminate( void )
+{
+ PaError result = paNoError;
+
+ if( gInitCount == 0 ) return paNoError;
+ else if( --gInitCount == 0 )
+ {
+ result = PaHost_Term();
+ DumpTraceMessages();
+ }
+ return result;
+}
+
+/*************************************************************************/
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int size;
+ switch(format )
+ {
+
+ case paUInt8:
+ case paInt8:
+ size = 1;
+ break;
+
+ case paInt16:
+ size = 2;
+ break;
+
+ case paPackedInt24:
+ size = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ case paInt24:
+ size = 4;
+ break;
+
+ default:
+ size = paSampleFormatNotSupported;
+ break;
+ }
+ return (PaError) size;
+}
+
+
--- /dev/null
+/*
+ * $Id$
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void ResetTraceMessages()
+{
+ traceIndex = 0;
+}
+
+/*********************************************************************/
+void DumpTraceMessages()
+{
+ int i;
+ int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
+
+ printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+ for( i=0; i<numDump; i++ )
+ {
+ printf("%3d: %s = 0x%08X\n",
+ i, traceTextArray[i], traceIntArray[i] );
+ }
+ ResetTraceMessages();
+ fflush(stdout);
+}
+
+/*********************************************************************/
+void AddTraceMessage( char *msg, int data )
+{
+ if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
+ {
+ traceBlock = 1;
+ /* DumpTraceMessages(); */
+ }
+ else if( traceIndex < MAX_TRACE_RECORDS )
+ {
+ traceTextArray[traceIndex] = msg;
+ traceIntArray[traceIndex] = data;
+ traceIndex++;
+ }
+}
+
+#endif
--- /dev/null
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id$
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+#define TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
+#define MAX_TRACE_RECORDS (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+ /************************************************************************************/
+ /****************** Prototypes ******************************************************/
+ /************************************************************************************/
+
+#if TRACE_REALTIME_EVENTS
+
+ void DumpTraceMessages();
+ void ResetTraceMessages();
+ void AddTraceMessage( char *msg, int data );
+
+#else
+
+#define AddTraceMessage(msg,data) /* noop */
+#define ResetTraceMessages() /* noop */
+#define DumpTraceMessages() /* noop */
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
--- /dev/null
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+PaError Pa_Terminate( void );
+
+/*
+ Return host specific error.
+ This can be called after receiving a paHostError.
+*/
+long Pa_GetHostError( void );
+
+/*
+ Translate the error number into a human readable message.
+*/
+const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+
+ Formats marked "always available" are supported (emulated) by all devices.
+
+ The floating point representation uses +1.0 and -1.0 as the respective
+ maximum and minimum.
+
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pa_CountDevices()-1.
+
+ Devices may support input, output or both. Device 0 is always the "default"
+ device and should support at least stereo in and out if that is available
+ on the taget platform _even_ if this involves kludging an input/output
+ device on platforms that usually separate input from output. Other platform
+ specific devices are specified by positive device ids.
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+int Pa_CountDevices();
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
+
+ Return the default device ID or paNoDevice if there is no devices.
+ The result can be passed to Pa_OpenStream().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ID by using
+ the supplied application "pa_devs".
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void );
+PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time useful for syncronisation. The type is used in the outTime
+ argument to the callback function and the result of Pa_StreamTime()
+*/
+
+typedef double PaTimestamp;
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must
+ not be manipulated or freed. The pointer is only guaranteed to be valid
+ between calls to Pa_Initialize() and Pa_Terminate().
+*/
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID );
+
+/*
+ PortAudioCallback is implemented by clients of the portable audio api.
+
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+
+ return value:
+ The callback can return a nonzero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ nonzero value must fill the entire outputBuffer.
+
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+
+*/
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+
+ [ suggestions? ]
+*/
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ device input record for the device specified in the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+
+ inputSampleFormat is the format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the sound devices native formats (nativeSampleFormats in the device info
+ record) and additionally 16 and 32 bit integer and 32 bit floating point
+ formats. Support for other formats is implementation defined.
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ sampleRate is the desired sampleRate for input and output
+
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+
+ numberOfBuffers is the number of buffers used for
+ multibuffered communication with the platform specific audio
+ routines. If you pass zero, then an optimum value will be
+ chosen for you internally. This parameter is provided only
+ as a guide - and does not imply that an implementation must
+ use multibuffered i/o when reliable double buffering is
+ available (such as SndPlayDoubleBuffer() on the Macintosh.)
+
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behavior of the
+ streaming process. Some flags may only be relevant to certain buffer formats.
+
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+
+ return value:
+ Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a nonzero error code is returned (see
+ PAError above) and the value of stream is invalid.
+
+*/
+
+PaError Pa_OpenStream( PortAudioStream** stream,
+ PaDeviceID inputDevice,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDevice,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PaStreamFlags streamFlags,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
+ opens the default input and/or ouput devices. Most parameters have
+ identical meaning to their Pa_OpenStream() counterparts, with the following
+ exceptions:
+
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
+
+ sampleFormat applies to both the input and output buffers.
+*/
+
+PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+*/
+
+PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+ Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ Pa_StopStream() waits until all pending audio buffers have been played.
+ Pa_AbortStream() stops playing immediately without waiting for pending
+ buffers to complete.
+*/
+
+PaError Pa_StartStream( PortAudioStream *stream );
+
+PaError Pa_StopStream( PortAudioStream *stream );
+
+PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one when the stream is playing audio,
+ zero when not playing, or a negative error number if the
+ stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+*/
+
+PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time for the stream in samples.
+ This time may be used as a time reference (for example syncronising audio to
+ MIDI).
+*/
+
+PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ The "CPU Load" is a fraction of total CPU time consumed by the
+ stream's audio processing.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+*/
+double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
+ the current host based on minimum latency.
+ On the PC, for the DirectSound implementation, latency can be optionally set
+ by user by setting an environment variable.
+ For example, to set latency to 200 msec, put:
+
+ set PA_MIN_LATENCY_MSEC=200
+
+ in the AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+*/
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely
+ on this for accurate musical timing.
+*/
+void Pa_Sleep( long msec );
+
+/*
+ Return size in bytes of a single sample in a given PaSampleFormat
+ or paSampleFormatNotSupported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
--- /dev/null
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio DLL Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+// changed by zplane.developement in order to generate a DLL
+
+#ifndef __PADLLENTRY_HEADER_INCLUDED__
+
+#define __PADLLENTRY_HEADER_INCLUDED__
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError
+} PaErrorNum;
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+typedef double PaTimestamp;
+
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable default clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+extern PaError (__cdecl* Pa_Initialize)( void );
+
+
+
+extern PaError (__cdecl* Pa_Terminate)( void );
+
+
+extern long (__cdecl* Pa_GetHostError)( void );
+
+
+extern const char* (__cdecl* Pa_GetErrorText)( PaError );
+
+
+
+extern int (__cdecl* Pa_CountDevices)(void);
+
+extern PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
+
+extern PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
+
+
+extern const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
+
+
+
+extern PaError (__cdecl* Pa_OpenStream)(
+ PortAudioStream ** ,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ double ,
+ unsigned long ,
+ unsigned long ,
+ unsigned long ,
+ PortAudioCallback *,
+ void * );
+
+
+
+extern PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+extern PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
+
+
+extern PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
+
+extern PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
+
+extern PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
+
+extern PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
+
+extern PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
+
+extern double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
+
+extern int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
+
+extern void (__cdecl* Pa_Sleep)( long msec );
+
+extern PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
+
+#endif // __PADLLENTRY_HEADER_INCLUDED__
+
--- /dev/null
+//////////////////////////////////////////////////////////////////////////
+
+
+HINSTANCE pPaDll;
+
+/*
+ the function pointers to the PortAudio DLLs
+*/
+
+PaError (__cdecl* Pa_Initialize)( void );
+
+
+
+PaError (__cdecl* Pa_Terminate)( void );
+
+
+long (__cdecl* Pa_GetHostError)( void );
+
+
+const char* (__cdecl* Pa_GetErrorText)( PaError );
+
+
+int (__cdecl* Pa_CountDevices)(void);
+
+PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
+
+PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
+
+
+const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
+
+
+
+PaError (__cdecl* Pa_OpenStream)(
+ PortAudioStream ** ,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ double ,
+ unsigned long ,
+ unsigned long ,
+ unsigned long ,
+ PortAudioCallback *,
+ void * );
+
+
+
+PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
+
+
+PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
+
+PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
+
+PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
+
+double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
+
+int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
+
+void (__cdecl* Pa_Sleep)( long msec );
+
+PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
+
+
+//////////////////////////////////////////////////////////////////////////
+
+...
+
+ZERROR AudioEngine::DirectXSupport(ZBOOL bSupDX)
+{
+ if (bSupDX)
+ if (CheckForDirectXSupport())
+ bSupportDirectX = _TRUE;
+ else
+ return _NO_SOUND;
+ else
+ bSupportDirectX = _FALSE;
+ return _NO_ERROR;
+}
+
+
+
+ZBOOL AudioEngine::CheckForDirectXSupport()
+{
+ HMODULE pTestDXLib;
+ FARPROC pFunctionality;
+
+ pTestDXLib=LoadLibrary("DSOUND");
+ if (pTestDXLib!=NULL) // check if there is a DirectSound
+ {
+ pFunctionality = GetProcAddress(pTestDXLib, (char*) 7);
+ if (pFunctionality!=NULL)
+ {
+ FreeLibrary(pTestDXLib);
+ return _TRUE;
+ }
+ else
+ {
+ FreeLibrary(pTestDXLib);
+ return _FALSE;
+ }
+ }
+ else
+ return _FALSE;
+}
+
+
+ZERROR AudioEngine::LoadPALib()
+{
+#ifdef _DEBUG
+ if (bSupportDirectX)
+ pPaDll = LoadLibrary("PA_DXD");
+ else
+ pPaDll = LoadLibrary("PA_MMED");
+#else
+ if (bSupportDirectX)
+ pPaDll = LoadLibrary("PA_DX");
+ else
+ pPaDll = LoadLibrary("PA_MME");
+#endif
+ if (pPaDll!=NULL)
+ {
+
+ Pa_Initialize = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Initialize");
+ Pa_Terminate = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Terminate");
+ Pa_GetHostError = (long (__cdecl* )( void )) GetProcAddress(pPaDll,"Pa_GetHostError");
+ Pa_GetErrorText = (const char* (__cdecl* )( PaError )) GetProcAddress(pPaDll,"Pa_GetErrorText");
+ Pa_CountDevices = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_CountDevices");
+ Pa_GetDefaultInputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultInputDeviceID");
+ Pa_GetDefaultOutputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultOutputDeviceID");
+ Pa_GetDeviceInfo = (const PaDeviceInfo* (__cdecl* )( PaDeviceID)) GetProcAddress(pPaDll,"Pa_GetDeviceInfo");
+ Pa_OpenStream = ( PaError (__cdecl* )(
+ PortAudioStream ** ,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ PaDeviceID ,
+ int ,
+ PaSampleFormat ,
+ void *,
+ double ,
+ unsigned long ,
+ unsigned long ,
+ unsigned long ,
+ PortAudioCallback *,
+ void * )) GetProcAddress(pPaDll,"Pa_OpenStream");
+
+ Pa_OpenDefaultStream = (PaError (__cdecl* )( PortAudioStream** ,
+ int ,
+ int ,
+ PaSampleFormat ,
+ double ,
+ unsigned long ,
+ unsigned long ,
+ PortAudioCallback *,
+ void * )) GetProcAddress(pPaDll,"Pa_OpenDefaultStream");
+ Pa_CloseStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_CloseStream");
+ Pa_StartStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StartStream");
+ Pa_StopStream = (PaError (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_StopStream");
+ Pa_AbortStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_AbortStream");
+ Pa_StreamActive = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StreamActive");
+ Pa_StreamTime = (PaTimestamp (__cdecl* )( PortAudioStream *))GetProcAddress(pPaDll,"Pa_StreamTime");
+ Pa_GetCPULoad = (double (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_GetCPULoad");
+ Pa_GetMinNumBuffers = (int (__cdecl* )( int , double )) GetProcAddress(pPaDll,"Pa_GetMinNumBuffers");
+ Pa_Sleep = (void (__cdecl* )( long )) GetProcAddress(pPaDll,"Pa_Sleep");
+ Pa_GetSampleSize = (PaError (__cdecl* )( PaSampleFormat )) GetProcAddress(pPaDll,"Pa_GetSampleSize");
+
+ return _NO_ERROR;
+ }
+ else
+ return _DLL_NOT_FOUND;
+}
+
+ZERROR AudioEngine::UnLoadPALib()
+{
+ if (pPaDll!=NULL)
+ FreeLibrary(pPaDll);
+ return _NO_ERROR;
+}
+
+...
--- /dev/null
+/*
+ * Portable Audio I/O Library
+ * Host Independant Layer
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* Modification History:
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifdef _WIN32
+#ifndef __MWERKS__
+#include <memory.h>
+#endif /* __MWERKS__ */
+#else /* !_WIN32 */
+#include <memory.h>
+#endif /* _WIN32 */
+
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/* The reason we might NOT want to validate the rate before opening the stream
+ * is because many DirectSound drivers lie about the rates they actually support.
+ */
+#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
+
+/*
+O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
+*/
+
+#ifndef FALSE
+ #define FALSE (0)
+ #define TRUE (!FALSE)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
+
+static PaError Pa_KillStream( PortAudioStream *stream, int abort );
+
+/***********************************************************************/
+int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
+{
+ double err, minErr = allowableError;
+ int i, bestFit = -1;
+
+ for( i=0; i<numRates; i++ )
+ {
+ err = fabs( frameRate - rateTable[i] );
+ if( err < minErr )
+ {
+ minErr = err;
+ bestFit = i;
+ }
+ }
+ return bestFit;
+}
+
+/**************************************************************************
+** Make sure sample rate is legal and also convert to enumeration for driver.
+*/
+PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
+ double *closestFrameRatePtr )
+{
+ long bestRateIndex;
+ const PaDeviceInfo *pdi;
+ pdi = Pa_GetDeviceInfo( id );
+ if( pdi == NULL ) return paInvalidDeviceId;
+
+ if( pdi->numSampleRates == -1 )
+ {
+ /* Is it out of range? */
+ if( (requestedFrameRate < pdi->sampleRates[0]) ||
+ (requestedFrameRate > pdi->sampleRates[1]) )
+ {
+ return paInvalidSampleRate;
+ }
+
+ *closestFrameRatePtr = requestedFrameRate;
+ }
+ else
+ {
+ bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
+ if( bestRateIndex < 0 ) return paInvalidSampleRate;
+ *closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
+ }
+ return paNoError;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_OpenStream(
+ PortAudioStream** streamPtrPtr,
+ PaDeviceID inputDeviceID,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDeviceID,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ unsigned long streamFlags,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ internalPortAudioStream *past = NULL;
+ PaError result = paNoError;
+ int bitsPerInputSample;
+ int bitsPerOutputSample;
+ /* Print passed parameters. */
+ DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
+ streamPtrPtr, inputDeviceID, numInputChannels,
+ inputSampleFormat, inputDriverInfo ));
+ DBUG((" %d, %d, %d, %p, /* output */\n",
+ outputDeviceID, numOutputChannels,
+ outputSampleFormat, outputDriverInfo ));
+ DBUG((" %g, %d, %d, 0x%x, , %p )\n",
+ sampleRate, framesPerBuffer, numberOfBuffers,
+ streamFlags, userData ));
+
+ /* Check for parameter errors. */
+ if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
+ if( streamPtrPtr == NULL ) return paBadStreamPtr;
+ if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
+ if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
+ if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId;
+ if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
+
+#if SUPPORT_AUDIO_CAPTURE
+ if( inputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( inputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerInputSample = 8 * size;
+ if( (numInputChannels <= 0) ) return paInvalidChannelCount;
+ }
+#else
+ if( inputDeviceID >= 0 )
+ {
+ return paInvalidChannelCount;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ else
+ {
+ if( numInputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerInputSample = 0;
+ }
+
+ if( outputDeviceID >= 0 )
+ {
+ PaError size = Pa_GetSampleSize( outputSampleFormat );
+ if( size < 0 ) return size;
+ bitsPerOutputSample = 8 * size;
+ if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
+ }
+ else
+ {
+ if( numOutputChannels > 0 ) return paInvalidChannelCount;
+ bitsPerOutputSample = 0;
+ }
+
+ if( callback == NULL ) return paNullCallback;
+
+ /* Allocate and clear stream structure. */
+ past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
+ if( past == NULL ) return paInsufficientMemory;
+ memset( past, 0, sizeof(internalPortAudioStream) );
+ AddTraceMessage("Pa_OpenStream: past", (long) past );
+
+ past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
+ past->past_FramesPerUserBuffer = framesPerBuffer;
+ past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
+ past->past_Callback = callback;
+ past->past_UserData = userData;
+ past->past_OutputSampleFormat = outputSampleFormat;
+ past->past_InputSampleFormat = inputSampleFormat;
+ past->past_OutputDeviceID = outputDeviceID;
+ past->past_InputDeviceID = inputDeviceID;
+ past->past_NumInputChannels = numInputChannels;
+ past->past_NumOutputChannels = numOutputChannels;
+ past->past_Flags = streamFlags;
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ {
+ result = paInvalidSampleRate;
+ goto cleanup;
+ }
+
+ /* Allocate buffers that may be used for format conversion from user to native buffers. */
+ if( numInputChannels > 0 )
+ {
+
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ /* Allocate single Input buffer. */
+ past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
+ past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
+ if( past->past_InputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_InputBuffer = NULL;
+ }
+
+ /* Allocate single Output buffer. */
+ if( numOutputChannels > 0 )
+ {
+#if PA_VALIDATE_RATE
+ result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
+ if( result < 0 )
+ {
+ goto cleanup;
+ }
+#else
+ past->past_SampleRate = sampleRate;
+#endif
+ past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
+ past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
+ if( past->past_OutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ }
+ else
+ {
+ past->past_OutputBuffer = NULL;
+ }
+
+ result = PaHost_OpenStream( past );
+ if( result < 0 ) goto cleanup;
+
+ *streamPtrPtr = (void *) past;
+
+ return result;
+
+cleanup:
+ if( past != NULL ) Pa_CloseStream( past );
+ *streamPtrPtr = NULL;
+ return result;
+}
+
+
+/*************************************************************************/
+DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData )
+{
+ return Pa_OpenStream(
+ stream,
+ ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ numInputChannels, sampleFormat, NULL,
+ ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ numOutputChannels, sampleFormat, NULL,
+ sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_CloseStream( PortAudioStream* stream)
+{
+ PaError result;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_CloseStream()\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ Pa_AbortStream( past );
+ result = PaHost_CloseStream( past );
+
+ if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
+ if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
+ PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
+
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StartStream( PortAudioStream *stream )
+{
+ PaError result = paHostError;
+ internalPortAudioStream *past;
+
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ past->past_FrameCount = 0.0;
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StartInput( past );
+ DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StartOutput( past );
+ DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ result = PaHost_StartEngine( past );
+ DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+
+ return paNoError;
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StopStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 0 );
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_AbortStream( PortAudioStream *stream )
+{
+ return Pa_KillStream( stream, 1 );
+}
+
+/*************************************************************************/
+static PaError Pa_KillStream( PortAudioStream *stream, int abort )
+{
+ PaError result = paNoError;
+ internalPortAudioStream *past;
+
+ DBUG(("Pa_StopStream().\n"));
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+
+ if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_StopEngine( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
+ if( result < 0 ) goto error;
+ }
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ result = PaHost_StopInput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ result = PaHost_StopOutput( past, abort );
+ DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
+ if( result != paNoError ) goto error;
+ }
+
+error:
+ past->past_Usage = 0;
+ past->past_IfLastExitValid = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_StreamActive( PortAudioStream *stream )
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return PaHost_StreamActive( past );
+}
+
+/*************************************************************************/
+DLL_API const char *Pa_GetErrorText( PaError errnum )
+{
+ const char *msg;
+
+ switch(errnum)
+ {
+ case paNoError: msg = "Success"; break;
+ case paHostError: msg = "Host error."; break;
+ case paInvalidChannelCount: msg = "Invalid number of channels."; break;
+ case paInvalidSampleRate: msg = "Invalid sample rate."; break;
+ case paInvalidDeviceId: msg = "Invalid device ID."; break;
+ case paInvalidFlag: msg = "Invalid flag."; break;
+ case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
+ case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
+ case paInsufficientMemory: msg = "Insufficient memory."; break;
+ case paBufferTooBig: msg = "Buffer too big."; break;
+ case paBufferTooSmall: msg = "Buffer too small."; break;
+ case paNullCallback: msg = "No callback routine specified."; break;
+ case paBadStreamPtr: msg = "Invalid stream pointer."; break;
+ case paTimedOut : msg = "Wait Timed Out."; break;
+ case paInternalError: msg = "Internal PortAudio Error."; break;
+ default: msg = "Illegal error number."; break;
+ }
+ return msg;
+}
+
+/*
+ Get CPU Load as a fraction of total CPU time.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ The amount may vary depending on CPU load.
+ This function may be called from the callback function.
+*/
+DLL_API double Pa_GetCPULoad( PortAudioStream* stream)
+{
+ internalPortAudioStream *past;
+ if( stream == NULL ) return (double) paBadStreamPtr;
+ past = (internalPortAudioStream *) stream;
+ return past->past_Usage;
+}
+
+/*************************************************************
+** Calculate 2 LSB dither signal with a triangular distribution.
+** Ranged properly for adding to a 32 bit integer prior to >>15.
+*/
+#define DITHER_BITS (15)
+#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
+static long Pa_TriangularDither( void )
+{
+ static unsigned long previous = 0;
+ static unsigned long randSeed1 = 22222;
+ static unsigned long randSeed2 = 5555555;
+ long current, highPass;
+ /* Generate two random numbers. */
+ randSeed1 = (randSeed1 * 196314165) + 907633515;
+ randSeed2 = (randSeed2 * 196314165) + 907633515;
+ /* Generate triangular distribution about 0. */
+ current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
+ /* High pass filter to reduce audibility. */
+ highPass = current - previous;
+ previous = current;
+ return highPass;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Int16, call user code, then convert output
+** to Int16 format for native use.
+** Assumes host native format is paInt16.
+** Returns result from user callback.
+*/
+long Pa_CallConvertInt16( internalPortAudioStream *past,
+ short *nativeInputBuffer,
+ short *nativeOutputBuffer )
+{
+ long temp;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ unsigned int i;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+#if SUPPORT_AUDIO_CAPTURE
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ /* Convert from native format to PA format. */
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ switch(past->past_InputSampleFormat)
+ {
+
+ case paFloat32:
+ {
+ float *inBufPtr = (float *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ /* Convert 16 bit data to 32 bit integers */
+ int *inBufPtr = (int *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = nativeInputBuffer[i] << 16;
+ }
+ break;
+ }
+
+ case paInt16:
+ {
+ /* Already in correct format so don't copy. */
+ inputBuffer = nativeInputBuffer;
+ break;
+ }
+
+ case paInt8:
+ {
+ /* Convert 16 bit data to 8 bit chars */
+ char *inBufPtr = (char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 7;
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (char)(temp >> 8);
+ }
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ /* Convert 16 bit data to 8 bit unsigned chars */
+ unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
+ inputBuffer = past->past_InputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = nativeInputBuffer[i];
+ temp += Pa_TriangularDither() >> 7;
+ temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ inBufPtr[i] = (unsigned char)(temp + 0x80);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */
+ if( outputBuffer != NULL )
+ {
+ unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ switch(past->past_OutputSampleFormat)
+ {
+ case paFloat32:
+ {
+ float *outBufPtr = (float *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ if( past->past_Flags & paClipOff ) /* NOTHING */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
+ }
+ }
+ else /* CLIP */
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ temp = (long)(outBufPtr[i] * 32767.0f);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ }
+ else
+ {
+ /* If you dither then you have to clip because dithering could push the signal out of range! */
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ float dither = Pa_TriangularDither()*DITHER_SCALE;
+ float dithered = (outBufPtr[i] * (32767.0f)) + dither;
+ temp = (long) (dithered);
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt32:
+ {
+ int *outBufPtr = (int *) past->past_OutputBuffer;
+ if( past->past_Flags & paDitherOff )
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
+ }
+ }
+ else
+ {
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ /* Shift one bit down before dithering so that we have room for overflow from add. */
+ temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
+ temp = temp >> 15;
+ *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
+ }
+ }
+ break;
+ }
+
+ case paInt8:
+ {
+ char *outBufPtr = (char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
+ }
+ break;
+ }
+
+ case paUInt8:
+ {
+ unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
+ for( i=0; i<samplesPerBuffer; i++ )
+ {
+ *nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ }
+
+ return userResult;
+}
+
+/*************************************************************************
+** Called by host code.
+** Convert input from Float32, call user code, then convert output
+** to Float32 format for native use.
+** Assumes host native format is Float32.
+** Returns result from user callback.
+** FIXME - Unimplemented for formats other than paFloat32!!!!
+*/
+long Pa_CallConvertFloat32( internalPortAudioStream *past,
+ float *nativeInputBuffer,
+ float *nativeOutputBuffer )
+{
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ int userResult;
+ void *inputBuffer = NULL;
+ void *outputBuffer = NULL;
+
+ /* Get native data from DirectSound. */
+ if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
+ {
+ inputBuffer = nativeInputBuffer; // FIXME
+ }
+
+ /* Are we doing output time? */
+ if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
+ {
+ /* May already be in native format so just write directly to native buffer. */
+ outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
+ nativeOutputBuffer : past->past_OutputBuffer;
+ }
+ /*
+ AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
+ AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
+ */
+ /* Call user callback routine. */
+ userResult = past->past_Callback(
+ inputBuffer,
+ outputBuffer,
+ past->past_FramesPerUserBuffer,
+ past->past_FrameCount,
+ past->past_UserData );
+
+ past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
+
+ /* Convert to native format if necessary. */ // FIXME
+ return userResult;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_Initialize( void )
+{
+ if( gInitCount++ > 0 ) return paNoError;
+ ResetTraceMessages();
+ return PaHost_Init();
+}
+
+DLL_API PaError Pa_Terminate( void )
+{
+ PaError result = paNoError;
+
+ if( gInitCount == 0 ) return paNoError;
+ else if( --gInitCount == 0 )
+ {
+ result = PaHost_Term();
+ DumpTraceMessages();
+ }
+ return result;
+}
+
+/*************************************************************************/
+DLL_API PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int size;
+ switch(format )
+ {
+
+ case paUInt8:
+ case paInt8:
+ size = 1;
+ break;
+
+ case paInt16:
+ size = 2;
+ break;
+
+ case paPackedInt24:
+ size = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ case paInt24:
+ size = 4;
+ break;
+
+ default:
+ size = paSampleFormatNotSupported;
+ break;
+ }
+ return (PaError) size;
+}
+
+
--- /dev/null
+#ifndef PORT_AUDIO_H
+#define PORT_AUDIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+// added by zplane.developement in order to generate a DLL
+
+#if defined(PA_MME_EXPORTS) || defined(PA_DX_EXPORTS)
+#define DLL_API __declspec( dllexport )
+#elif defined(_LIB) || defined(_STATIC_LINK) || defined(_STATIC_APP)
+#define DLL_API
+#else
+#define DLL_API __declspec(dllexport)
+#endif
+
+
+typedef int PaError;
+typedef enum {
+ paNoError = 0,
+
+ paHostError = -10000,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDeviceId,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError
+} PaErrorNum;
+
+/*
+ Pa_Initialize() is the library initialisation function - call this before
+ using the library.
+*/
+
+DLL_API PaError Pa_Initialize( void );
+
+/*
+ Pa_Terminate() is the library termination function - call this after
+ using the library.
+*/
+
+DLL_API PaError Pa_Terminate( void );
+
+/*
+ Return host specific error.
+ This can be called after receiving a paHostError.
+*/
+DLL_API long Pa_GetHostError( void );
+
+/*
+ Translate the error number into a human readable message.
+*/
+DLL_API const char *Pa_GetErrorText( PaError errnum );
+
+/*
+ Sample formats
+
+ These are formats used to pass sound data between the callback and the
+ stream. Each device has a "native" format which may be used when optimum
+ efficiency or control over conversion is required.
+
+ Formats marked "always available" are supported (emulated) by all devices.
+
+ The floating point representation uses +1.0 and -1.0 as the respective
+ maximum and minimum.
+
+*/
+
+typedef unsigned long PaSampleFormat;
+#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
+#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
+#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
+#define paInt24 ((PaSampleFormat) (1<<3))
+#define paPackedInt24 ((PaSampleFormat) (1<<4))
+#define paInt8 ((PaSampleFormat) (1<<5))
+#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
+#define paCustomFormat ((PaSampleFormat) (1<<16))
+
+/*
+ Device enumeration mechanism.
+
+ Device ids range from 0 to Pa_CountDevices()-1.
+
+ Devices may support input, output or both. Device 0 is always the "default"
+ device and should support at least stereo in and out if that is available
+ on the taget platform _even_ if this involves kludging an input/output
+ device on platforms that usually separate input from output. Other platform
+ specific devices are specified by positive device ids.
+*/
+
+typedef int PaDeviceID;
+#define paNoDevice -1
+
+typedef struct
+{
+ int structVersion;
+ const char *name;
+ int maxInputChannels;
+ int maxOutputChannels;
+ /* Number of discrete rates, or -1 if range supported. */
+ int numSampleRates;
+ /* Array of supported sample rates, or {min,max} if range supported. */
+ const double *sampleRates;
+ PaSampleFormat nativeSampleFormats;
+}
+PaDeviceInfo;
+
+
+DLL_API int Pa_CountDevices();
+/*
+ Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
+
+ Return the default device ID or paNoDevice if there is no devices.
+ The result can be passed to Pa_OpenStream().
+
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+
+ The user should first determine the available device ID by using
+ the supplied application "pa_devs".
+*/
+DLL_API PaDeviceID Pa_GetDefaultInputDeviceID( void );
+DLL_API PaDeviceID Pa_GetDefaultOutputDeviceID( void );
+
+/*
+ PaTimestamp is used to represent a continuous sample clock with arbitrary
+ start time useful for syncronisation. The type is used in the outTime
+ argument to the callback function and the result of Pa_StreamTime()
+*/
+
+typedef double PaTimestamp;
+
+/*
+ Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
+ referring to the device specified by id.
+ If id is out of range the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must
+ not be manipulated or freed. The pointer is guaranteed to be valid until
+ between calls to Pa_Initialize() and Pa_Terminate().
+*/
+
+DLL_API const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id );
+
+/*
+ PortAudioCallback is implemented by clients of the portable audio api.
+
+ inputBuffer and outputBuffer are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream() (see below).
+
+ framesPerBuffer is the number of sample frames to be processed by the callback.
+
+ outTime is the time in samples when the buffer(s) processed by
+ this callback will begin being played at the audio output.
+ See also Pa_StreamTime()
+
+ userData is the value of a user supplied pointer passed to Pa_OpenStream()
+ intended for storing synthesis data etc.
+
+ return value:
+ The callback can return a nonzero value to stop the stream. This may be
+ useful in applications such as soundfile players where a specific duration
+ of output is required. However, it is not necessary to utilise this mechanism
+ as StopStream() will also terminate the stream. A callback returning a
+ nonzero value must fill the entire outputBuffer.
+
+ NOTE: None of the other stream functions may be called from within the
+ callback function except for Pa_GetCPULoad().
+
+*/
+
+typedef int (PortAudioCallback)(
+ void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+
+/*
+ Stream flags
+
+ These flags may be supplied (ored together) in the streamFlags argument to
+ the Pa_OpenStream() function.
+
+ [ suggestions? ]
+*/
+
+#define paNoFlag (0)
+#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
+#define paDitherOff (1<<1) /* disable default dithering */
+#define paPlatformSpecificFlags (0x00010000)
+typedef unsigned long PaStreamFlags;
+
+/*
+ A single PortAudioStream provides multiple channels of real-time
+ input and output audio streaming to a client application.
+ Pointers to PortAudioStream objects are passed between PortAudio functions.
+*/
+
+typedef void PortAudioStream;
+#define PaStream PortAudioStream
+
+/*
+ Pa_OpenStream() opens a stream for either input, output or both.
+
+ stream is the address of a PortAudioStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ inputDevice is the id of the device used for input (see PaDeviceID above.)
+ inputDevice may be paNoDevice to indicate that an input device is not required.
+
+ numInputChannels is the number of channels of sound to be delivered to the
+ callback. It can range from 1 to the value of maxInputChannels in the
+ device input record for the device specified in the inputDevice parameter.
+ If inputDevice is paNoDevice numInputChannels is ignored.
+
+ inputSampleFormat is the format of inputBuffer provided to the callback
+ function. inputSampleFormat may be any of the formats described by the
+ PaSampleFormat enumeration (see above). PortAudio guarantees support for
+ the sound devices native formats (nativeSampleFormats in the device info
+ record) and additionally 16 and 32 bit integer and 32 bit floating point
+ formats. Support for other formats is implementation defined.
+
+ inputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ inputDriverInfo is never required for correct operation. If not used
+ inputDriverInfo should be NULL.
+
+ outputDevice is the id of the device used for output (see PaDeviceID above.)
+ outputDevice may be paNoDevice to indicate that an output device is not required.
+
+ numOutputChannels is the number of channels of sound to be supplied by the
+ callback. See the definition of numInputChannels above for more details.
+
+ outputSampleFormat is the sample format of the outputBuffer filled by the
+ callback function. See the definition of inputSampleFormat above for more
+ details.
+
+ outputDriverInfo is a pointer to an optional driver specific data structure
+ containing additional information for device setup or stream processing.
+ outputDriverInfo is never required for correct operation. If not used
+ outputDriverInfo should be NULL.
+
+ sampleRate is the desired sampleRate for input and output
+
+ framesPerBuffer is the length in sample frames of all internal sample buffers
+ used for communication with platform specific audio routines. Wherever
+ possible this corresponds to the framesPerBuffer parameter passed to the
+ callback function.
+
+ numberOfBuffers is the number of buffers used for multibuffered
+ communication with the platform specific audio routines. This parameter is
+ provided only as a guide - and does not imply that an implementation must
+ use multibuffered i/o when reliable double buffering is available (such as
+ SndPlayDoubleBuffer() on the Macintosh.)
+
+ streamFlags may contain a combination of flags ORed together.
+ These flags modify the behavior of the
+ streaming process. Some flags may only be relevant to certain buffer formats.
+
+ callback is a pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers (see above for details.)
+
+ userData is a client supplied pointer which is passed to the callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers.
+
+ return value:
+ Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
+ valid PortAudioStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails a nonzero error code is returned (see
+ PAError above) and the value of stream is invalid.
+
+*/
+
+DLL_API PaError Pa_OpenStream( PortAudioStream** stream,
+ PaDeviceID inputDevice,
+ int numInputChannels,
+ PaSampleFormat inputSampleFormat,
+ void *inputDriverInfo,
+ PaDeviceID outputDevice,
+ int numOutputChannels,
+ PaSampleFormat outputSampleFormat,
+ void *outputDriverInfo,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PaStreamFlags streamFlags,
+ PortAudioCallback *callback,
+ void *userData );
+
+
+/*
+ Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
+ opens the default input and/or ouput devices. Most parameters have
+ identical meaning to their Pa_OpenStream() counterparts, with the following
+ exceptions:
+
+ If either numInputChannels or numOutputChannels is 0 the respective device
+ is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
+
+ sampleFormat applies to both the input and output buffers.
+*/
+
+DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ unsigned long numberOfBuffers,
+ PortAudioCallback *callback,
+ void *userData );
+
+/*
+ Pa_CloseStream() closes an audio stream, flushing any pending buffers.
+*/
+
+DLL_API PaError Pa_CloseStream( PortAudioStream* );
+
+/*
+ Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
+ When Pa_StopStream() returns, all pending audio buffers have been played.
+ Pa_AbortStream() stops playing immediately without waiting for pending
+ buffers to complete.
+*/
+
+DLL_API PaError Pa_StartStream( PortAudioStream *stream );
+
+DLL_API PaError Pa_StopStream( PortAudioStream *stream );
+
+DLL_API PaError Pa_AbortStream( PortAudioStream *stream );
+
+/*
+ Pa_StreamActive() returns one when the stream is playing audio,
+ zero when not playing, or a negative error number if the
+ stream is invalid.
+ The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
+ but may also become inactive if the callback returns a non-zero value.
+ In the latter case, the stream is considered inactive after the last
+ buffer has finished playing.
+*/
+
+DLL_API PaError Pa_StreamActive( PortAudioStream *stream );
+
+/*
+ Pa_StreamTime() returns the current output time for the stream in samples.
+ This time may be used as a time reference (for example syncronising audio to
+ MIDI).
+*/
+
+DLL_API PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+/*
+ The "CPU Load" is a fraction of total CPU time consumed by the
+ stream's audio processing.
+ A value of 0.5 would imply that PortAudio and the sound generating
+ callback was consuming roughly 50% of the available CPU time.
+ This function may be called from the callback function or the application.
+*/
+DLL_API double Pa_GetCPULoad( PortAudioStream* stream );
+
+/*
+ Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
+ the current host based on minimum latency.
+ On the PC, for the DirectSound implementation, latency can be optionally set
+ by user by setting an environment variable.
+ For example, to set latency to 200 msec, put:
+
+ set PA_MIN_LATENCY_MSEC=200
+
+ in the AUTOEXEC.BAT file and reboot.
+ If the environment variable is not set, then the latency will be determined
+ based on the OS. Windows NT has higher latency than Win95.
+*/
+
+DLL_API int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
+
+/*
+ Sleep for at least 'msec' milliseconds.
+ You may sleep longer than the requested time so don't rely
+ on this for accurate musical timing.
+*/
+DLL_API void Pa_Sleep( long msec );
+
+/*
+ Return size in bytes of a single sample in a given PaSampleFormat
+ or paSampleFormatNotSupported.
+*/
+DLL_API PaError Pa_GetSampleSize( PaSampleFormat format );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORT_AUDIO_H */
--- /dev/null
+/*
+ * $Id$
+ * Portable Audio I/O Library for Macintosh
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Special thanks to Chris Rolfe for his many helpful suggestions, bug fixes,
+ * and code contributions.
+ * Thanks also to Tue Haste Andersen, Alberto Ricci, Nico Wald,
+ * Roelf Toxopeus and Tom Erbe for testing the code and making
+ * numerous suggestions.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* Modification History
+ PLB20010415 - ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID
+ PLB20010415 - Device Scan was crashing for anything other than siBadSoundInDevice, but some Macs may return other errors!
+ PLB20010420 - Fix TIMEOUT in record mode.
+ PLB20010420 - Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON
+ PLB20010907 - Pass unused event to WaitNextEvent to prevent Mac OSX crash. Thanks Dominic Mazzoni.
+ PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni.
+ PLB20011009 - Use NewSndCallBackUPP() for CARBON
+*/
+/*
+COMPATIBILITY
+This Macintosh implementation is designed for use with Mac OS 7, 8 and
+9 on PowerMacs, and OS X if compiled with CARBON
+
+OUTPUT
+A circular array of CmpSoundHeaders is used as a queue. For low latency situations
+there will only be two small buffers used. For higher latency, more and larger buffers
+may be used.
+To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followed
+by a callbackCmd which informs us when the buffer has been processsed.
+
+INPUT
+The SndInput Manager SPBRecord call is used for sound input. If only
+input is used, then the PA user callback is called from the Input completion proc.
+For full-duplex, or output only operation, the PA callback is called from the
+HostBuffer output completion proc. In that case, input sound is passed to the
+callback by a simple FIFO.
+
+TODO:
+O- Add support for native sample data formats other than int16.
+O- Review buffer sizing. Should it be based on result of siDeviceBufferInfo query?
+O- Determine default devices somehow.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <math.h>
+/* Mac specific includes */
+#include "OSUtils.h"
+#include <MacTypes.h>
+#include <Math64.h>
+#include <Errors.h>
+#include <Sound.h>
+#include <SoundInput.h>
+#include <SoundComponents.h>
+#include <Devices.h>
+#include <DateTimeUtils.h>
+#include <Timer.h>
+#include <Gestalt.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#ifndef FALSE
+ #define FALSE (0)
+ #define TRUE (!FALSE)
+#endif
+
+/* Debugging output macros. */
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) /**/
+#define DBUGX(x) /* PRINT(x) /**/
+
+#define MAC_PHYSICAL_FRAMES_PER_BUFFER (512) /* Minimum number of stereo frames per SoundManager double buffer. */
+#define MAC_VIRTUAL_FRAMES_PER_BUFFER (4096) /* Need this many when using Virtual Memory for recording. */
+#define PA_MIN_NUM_HOST_BUFFERS (2)
+#define PA_MAX_NUM_HOST_BUFFERS (16) /* Do not exceed!! */
+#define PA_MAX_DEVICE_INFO (32)
+
+/* Conversions for 16.16 fixed point code. */
+#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0))
+#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16)))
+
+/************************************************************************************/
+/****************** Structures ******************************************************/
+/************************************************************************************/
+/* Use for passing buffers from input callback to output callback for processing. */
+typedef struct MultiBuffer
+{
+ char *buffers[PA_MAX_NUM_HOST_BUFFERS];
+ int numBuffers;
+ int nextWrite;
+ int nextRead;
+}
+MultiBuffer;
+
+/* Define structure to contain all Macintosh specific data. */
+typedef struct PaHostSoundControl
+{
+ UInt64 pahsc_EntryCount;
+ UInt64 pahsc_LastExitCount;
+ /* Use char instead of Boolean for atomic operation. */
+ volatile char pahsc_IsRecording; /* Recording in progress. Set by foreground. Cleared by background. */
+ volatile char pahsc_StopRecording; /* Signal sent to background. */
+ volatile char pahsc_IfInsideCallback;
+ /* Input */
+ SPB pahsc_InputParams;
+ SICompletionUPP pahsc_InputCompletionProc;
+ MultiBuffer pahsc_InputMultiBuffer;
+ int32 pahsc_BytesPerInputHostBuffer;
+ int32 pahsc_InputRefNum;
+ /* Output */
+ CmpSoundHeader pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS];
+ int32 pahsc_BytesPerOutputHostBuffer;
+ SndChannelPtr pahsc_Channel;
+ SndCallBackUPP pahsc_OutputCompletionProc;
+ int32 pahsc_NumOutsQueued;
+ int32 pahsc_NumOutsPlayed;
+ PaTimestamp pahsc_NumFramesDone;
+ /* Init Time -------------- */
+ int32 pahsc_NumHostBuffers;
+ int32 pahsc_FramesPerHostBuffer;
+ int32 pahsc_UserBuffersPerHostBuffer;
+ int32 pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */
+}
+PaHostSoundControl;
+
+/* Mac specific device information. */
+typedef struct internalPortAudioDevice
+{
+ long pad_DeviceRefNum;
+ long pad_DeviceBufferSize;
+ Component pad_Identifier;
+ PaDeviceInfo pad_Info;
+}
+internalPortAudioDevice;
+
+/************************************************************************************/
+/****************** Data ************************************************************/
+/************************************************************************************/
+static int sNumDevices = 0;
+static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
+static int32 sPaHostError = 0;
+static int sDefaultOutputDeviceID;
+static int sDefaultInputDeviceID;
+
+/************************************************************************************/
+/****************** Prototypes ******************************************************/
+/************************************************************************************/
+static PaError PaMac_TimeSlice( internalPortAudioStream *past, int16 *macOutputBufPtr );
+static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr );
+static PaError PaMac_RecordNext( internalPortAudioStream *past );static void StartLoadCalculation( internalPortAudioStream *past );
+static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate );
+static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h );
+static PaError PaMac_ScanInputDevices( void );
+static PaError PaMac_ScanOutputDevices( void );
+static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad );
+static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad );
+static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader );
+static void PaMac_EndLoadCalculation( internalPortAudioStream *past );
+static void PaMac_PlayNext ( internalPortAudioStream *past, int index );
+static long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index );
+static pascal void PaMac_InputCompletionProc(SPBPtr recParams);
+static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd);
+static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index );
+long PaHost_GetTotalBufferFrames( internalPortAudioStream *past );
+static int Mac_IsVirtualMemoryOn( void );
+static void PToCString(unsigned char* inString, char* outString);
+char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf );
+char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf );
+int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf );
+int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf );
+int MultiBuffer_IsWriteable( MultiBuffer *mbuf );
+int MultiBuffer_IsReadable( MultiBuffer *mbuf );
+void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf );
+void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf );
+
+/*************************************************************************
+** Simple FIFO index control for multiple buffers.
+** Read and Write indices range from 0 to 2N-1.
+** This allows us to distinguish between full and empty.
+*/
+char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf )
+{
+ return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers];
+}
+char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf )
+{
+ return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers];
+}
+int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf )
+{
+ return mbuf->nextRead % mbuf->numBuffers;
+}
+int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf )
+{
+ return mbuf->nextWrite % mbuf->numBuffers;
+}
+
+int MultiBuffer_IsWriteable( MultiBuffer *mbuf )
+{
+ int bufsFull = mbuf->nextWrite - mbuf->nextRead;
+ if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
+ return (bufsFull < mbuf->numBuffers);
+}
+int MultiBuffer_IsReadable( MultiBuffer *mbuf )
+{
+ int bufsFull = mbuf->nextWrite - mbuf->nextRead;
+ if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
+ return (bufsFull > 0);
+}
+void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf )
+{
+ int temp = mbuf->nextRead + 1;
+ mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
+}
+void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf )
+{
+ int temp = mbuf->nextWrite + 1;
+ mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
+}
+
+/*************************************************************************
+** String Utility by Chris Rolfe
+*/
+static void PToCString(unsigned char* inString, char* outString)
+{
+ long i;
+ for(i=0; i<inString[0]; i++) /* convert Pascal to C string */
+ outString[i] = inString[i+1];
+ outString[i]=0;
+}
+
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+ int i;
+ PaDeviceInfo *dev;
+ double *rates;
+ /* Free any allocated sample rate arrays. */
+ for( i=0; i<sNumDevices; i++ )
+ {
+ dev = &sDevices[i].pad_Info;
+ rates = (double *) dev->sampleRates;
+ if( (rates != NULL) ) free( rates ); /* MEM_011 */
+ dev->sampleRates = NULL;
+ if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */
+ dev->name = NULL;
+ }
+ sNumDevices = 0;
+ return paNoError;
+}
+
+/*************************************************************************
+ PaHost_Init() is the library initialization function - call this before
+ using the library.
+*/
+PaError PaHost_Init( void )
+{
+ PaError err;
+ NumVersionVariant version;
+
+ version.parts = SndSoundManagerVersion();
+ DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole));
+
+ /* Have we already initialized the device info? */
+ err = (PaError) Pa_CountDevices();
+ if( err < 0 ) return err;
+ else return paNoError;
+}
+
+/*************************************************************************
+ PaMac_ScanOutputDevices() queries the properties of all output devices.
+*/
+static PaError PaMac_ScanOutputDevices( void )
+{
+ PaError err;
+ Component identifier=0;
+ ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 };
+ long numComponents, i;
+
+ /* Search the system linked list for output components */
+ numComponents = CountComponents (&criteria);
+ identifier = 0;
+ sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */
+ for (i = 0; i < numComponents; i++)
+ {
+ /* passing nil returns first matching component. */
+ identifier = FindNextComponent( identifier, &criteria);
+ sDevices[sNumDevices].pad_Identifier = identifier;
+
+ /* Set up for default OUTPUT devices. */
+ err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] );
+ if( err < 0 ) return err;
+ else sNumDevices++;
+
+ }
+
+ return paNoError;
+}
+
+/*************************************************************************
+ PaMac_ScanInputDevices() queries the properties of all input devices.
+*/
+static PaError PaMac_ScanInputDevices( void )
+{
+ Str255 deviceName;
+ int count;
+ Handle iconHandle;
+ PaError err;
+ OSErr oserr;
+ count = 1;
+ sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */
+ while(true)
+ {
+ /* Thanks Chris Rolfe and Alberto Ricci for this trick. */
+ oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle);
+ DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr ));
+#if 1
+ /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */
+ if(oserr != noErr) break; /* Some type of error is expected when count > devices */
+#else
+ if(oserr == siBadSoundInDevice)
+ { /* it's expected when count > devices */
+ oserr = noErr;
+ break;
+ }
+ if(oserr != noErr)
+ {
+ ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr ));
+ sPaHostError = oserr;
+ return paHostError;
+ }
+#endif
+ DisposeHandle(iconHandle); /* Don't need the icon */
+
+ err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] );
+ DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err ));
+ if( err < 0 ) return err;
+ else if( err == 1 ) sNumDevices++;
+ }
+
+ return paNoError;
+}
+
+/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */
+/* Thanks to Chris Rolfe for help with this query. */
+#pragma options align=mac68k
+typedef struct
+{
+ int16 numRates;
+ UnsignedFixed (**rates)[]; /* Handle created by SPBGetDeviceInfo */
+}
+SRateInfo;
+#pragma options align=reset
+
+/*************************************************************************
+** PaMac_QueryOutputDeviceInfo()
+** Query information about a named output device.
+** Clears contents of ipad and writes info based on queries.
+** Return one if OK,
+** zero if device cannot be used,
+** or negative error.
+*/
+static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad )
+{
+ int len;
+ OSErr err;
+ PaDeviceInfo *dev = &ipad->pad_Info;
+ SRateInfo srinfo = {0};
+ int numRates;
+ ComponentDescription tempD;
+ Handle nameH=nil, infoH=nil, iconH=nil;
+
+ memset( ipad, 0, sizeof(internalPortAudioDevice) );
+
+ dev->structVersion = 1;
+ dev->maxInputChannels = 0;
+ dev->maxOutputChannels = 2;
+ dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */
+
+ /* Get sample rates supported. */
+ err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo);
+ if(err != noErr)
+ {
+ ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err ));
+ goto error;
+ }
+ numRates = srinfo.numRates;
+ DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = 0x%x\n", srinfo.numRates ));
+ if( numRates == 0 )
+ {
+ dev->numSampleRates = -1;
+ numRates = 2;
+ }
+ else
+ {
+ dev->numSampleRates = numRates;
+ }
+ dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
+ /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
+ DisposeHandle((Handle) srinfo.rates);
+
+ /* Device name */
+ /* we pass an existing handle for the component name;
+ we don't care about the info (type, subtype, etc.) or icon, so set them to nil */
+ infoH = nil;
+ iconH = nil;
+ nameH = NewHandle(0);
+ if(nameH == nil) return paInsufficientMemory;
+ err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err ));
+ goto error;
+ }
+ len = (*nameH)[0] + 1;
+ dev->name = (char *) malloc(len); /* MEM_010 */
+ if( dev->name == NULL )
+ {
+ DisposeHandle(nameH);
+ return paInsufficientMemory;
+ }
+ else
+ {
+ PToCString((unsigned char *)(*nameH), (char *) dev->name);
+ DisposeHandle(nameH);
+ }
+
+ DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name ));
+ return paNoError;
+
+error:
+ sPaHostError = err;
+ return paHostError;
+
+}
+
+/*************************************************************************
+** PaMac_QueryInputDeviceInfo()
+** Query information about a named input device.
+** Clears contents of ipad and writes info based on queries.
+** Return one if OK,
+** zero if device cannot be used,
+** or negative error.
+*/
+static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad )
+{
+ PaError result = paNoError;
+ int len;
+ OSErr err;
+ long mRefNum = 0;
+ long tempL;
+ int16 tempS;
+ Fixed tempF;
+ PaDeviceInfo *dev = &ipad->pad_Info;
+ SRateInfo srinfo = {0};
+ int numRates;
+
+ memset( ipad, 0, sizeof(internalPortAudioDevice) );
+ dev->maxOutputChannels = 0;
+
+ /* Open device based on name. If device is in use, it may not be able to open in write mode. */
+ err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum);
+ if (err)
+ {
+ /* If device is in use, it may not be able to open in write mode so try read mode. */
+ err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err ));
+ sPaHostError = err;
+ return paHostError;
+ }
+ }
+
+ /* Define macros for printing out device info. */
+#define PrintDeviceInfo(selector,var) \
+ err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \
+ if (err) { \
+ DBUG(("query %s failed\n", #selector )); \
+ }\
+ else { \
+ DBUG(("query %s = 0x%x\n", #selector, var )); \
+ }
+
+ PrintDeviceInfo( siContinuous, tempS );
+ PrintDeviceInfo( siAsync, tempS );
+ PrintDeviceInfo( siNumberChannels, tempS );
+ PrintDeviceInfo( siSampleSize, tempS );
+ PrintDeviceInfo( siSampleRate, tempF );
+ PrintDeviceInfo( siChannelAvailable, tempS );
+ PrintDeviceInfo( siActiveChannels, tempL );
+ PrintDeviceInfo( siDeviceBufferInfo, tempL );
+
+ err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
+ if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL ));
+ /* Can we use this device? */
+ err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err ));
+ goto error;
+ }
+ if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */
+
+ err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err ));
+ goto error;
+ }
+ dev->maxInputChannels = tempS;
+
+ /* Get sample rates supported. */
+ err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err ));
+ goto error;
+ }
+
+ numRates = srinfo.numRates;
+ DBUG(("numRates = 0x%x\n", numRates ));
+ if( numRates == 0 )
+ {
+ dev->numSampleRates = -1;
+ numRates = 2;
+ }
+ else
+ {
+ dev->numSampleRates = numRates;
+ }
+ dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
+ /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
+ DisposeHandle((Handle) srinfo.rates);
+
+ /* Get size of device buffer. */
+ err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err ));
+ goto error;
+ }
+ ipad->pad_DeviceBufferSize = tempL;
+ DBUG(("siDeviceBufferInfo = %d\n", tempL ));
+
+ /* Set format based on sample size. */
+ err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS);
+ if (err)
+ {
+ ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err ));
+ goto error;
+ }
+ switch( tempS )
+ {
+ case 0x0020:
+ dev->nativeSampleFormats = paInt32; /* FIXME - warning, code probably won't support this! */
+ break;
+ case 0x0010:
+ default: /* FIXME - What about other formats? */
+ dev->nativeSampleFormats = paInt16;
+ break;
+ }
+ DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats ));
+
+ /* Device name */
+ len = deviceName[0] + 1; /* Get length of Pascal string */
+ dev->name = (char *) malloc(len); /* MEM_010 */
+ if( dev->name == NULL )
+ {
+ result = paInsufficientMemory;
+ goto cleanup;
+ }
+ PToCString(deviceName, (char *) dev->name);
+ DBUG(("deviceName = %s\n", dev->name ));
+ result = (PaError) 1;
+ /* All done so close up device. */
+cleanup:
+ if( mRefNum ) SPBCloseDevice(mRefNum);
+ return result;
+
+error:
+ if( mRefNum ) SPBCloseDevice(mRefNum);
+ sPaHostError = err;
+ return paHostError;
+
+useless:
+ if( mRefNum ) SPBCloseDevice(mRefNum);
+ return (PaError) 0;
+}
+
+/*************************************************************************
+** Allocate a double array and fill it with listed sample rates.
+*/
+static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h )
+{
+ OSErr err = noErr;
+ SInt8 hState;
+ int i;
+ UnsignedFixed *fixedRates;
+ double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */
+ if( rates == NULL ) return NULL;
+ /* Save and restore handle state as suggested by TechNote at:
+ http://developer.apple.com/technotes/tn/tn1122.html
+ */
+ hState = HGetState (h);
+ if (!(err = MemError ()))
+ {
+ HLock (h);
+ if (!(err = MemError ( )))
+ {
+ fixedRates = (UInt32 *) *h;
+ for( i=0; i<numRates; i++ )
+ {
+ rates[i] = UnsignedFixedToDouble(fixedRates[i]);
+ }
+
+ HSetState (h,hState);
+ err = MemError ( );
+ }
+ }
+ if( err )
+ {
+ free( rates );
+ ERR_RPT(("Error in PaMac_GetSampleRatesFromHandle = %d\n", err ));
+ }
+ return rates;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ PaError err;
+ DBUG(("Pa_CountDevices()\n"));
+ /* If no devices, go find some. */
+ if( sNumDevices <= 0 )
+ {
+ err = PaMac_ScanOutputDevices();
+ if( err != paNoError ) goto error;
+ err = PaMac_ScanInputDevices();
+ if( err != paNoError ) goto error;
+ }
+ return sNumDevices;
+
+error:
+ PaHost_Term();
+ DBUG(("Pa_CountDevices: returns %d\n", err ));
+ return err;
+
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ return &sDevices[id].pad_Info;
+}
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ return sDefaultInputDeviceID;
+}
+
+/*************************************************************************/
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ return sDefaultOutputDeviceID;
+}
+
+/**************************************************************************/
+static void StartLoadCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ UnsignedWide widePad;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ Microseconds( &widePad );
+ pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
+}
+
+/**************************************************************************/
+static void PaMac_EndLoadCalculation( internalPortAudioStream *past )
+{
+ UnsignedWide widePad;
+ UInt64 CurrentCount;
+ long InsideCount;
+ long TotalCount;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Measure CPU utilization during this callback. Note that this calculation
+ ** assumes that we had the processor the whole time.
+ */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+ Microseconds( &widePad );
+ CurrentCount = UnsignedWideToUInt64( widePad );
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount);
+ TotalCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount);
+ /* Low pass filter the result because sometimes we get called several times in a row.
+ * That can cause the TotalCount to be very low which can cause the usage to appear
+ * unnaturally high. So we must filter numerator and denominator separately!!!
+ */
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+}
+
+/***********************************************************************
+** Called by Pa_StartStream()
+*/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ pahsc->pahsc_IsRecording = 0;
+ pahsc->pahsc_StopRecording = 0;
+ pahsc->pahsc_InputMultiBuffer.nextWrite = 0;
+ pahsc->pahsc_InputMultiBuffer.nextRead = 0;
+ return PaMac_RecordNext( past );
+}
+
+/***********************************************************************
+** Called by Pa_StopStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ int32 timeOutMsec;
+ PaError result = paNoError;
+ OSErr err = 0;
+ long mRefNum;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ (void) abort;
+
+ mRefNum = pahsc->pahsc_InputRefNum;
+
+ DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum ));
+ if( mRefNum )
+ {
+ DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
+ if( pahsc->pahsc_IsRecording )
+ {
+ /* PLB20010420 - Fix TIMEOUT in record mode. */
+ pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */
+ err = SPBStopRecording(mRefNum);
+ DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
+
+ /* Calculate timeOut longer than longest time it could take to play one buffer. */
+ timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate);
+ /* Keep querying sound channel until it is no longer busy playing. */
+ while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0))
+ {
+ Pa_Sleep(20);
+ timeOutMsec -= 20;
+ }
+ if( timeOutMsec <= 0 )
+ {
+ ERR_RPT(("PaHost_StopInput: timed out!\n"));
+ return paTimedOut;
+ }
+ }
+ }
+ if( err )
+ {
+ sPaHostError = err;
+ result = paHostError;
+ }
+
+ DBUG(("PaHost_StopInput: finished.\n", mRefNum ));
+ return result;
+}
+
+/***********************************************************************/
+static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader )
+{
+ sndHeader->numChannels = past->past_NumOutputChannels;
+ sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate);
+ sndHeader->loopStart = 0;
+ sndHeader->loopEnd = 0;
+ sndHeader->encode = cmpSH;
+ sndHeader->baseFrequency = kMiddleC;
+ sndHeader->markerChunk = nil;
+ sndHeader->futureUse2 = nil;
+ sndHeader->stateVars = nil;
+ sndHeader->leftOverSamples = nil;
+ sndHeader->compressionID = 0;
+ sndHeader->packetSize = 0;
+ sndHeader->snthID = 0;
+ sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day;
+ sndHeader->sampleArea[0] = 0;
+ sndHeader->format = kSoundNotCompressed;
+}
+
+/***********************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ SndCommand pauseCommand;
+ SndCommand resumeCommand;
+ int i;
+ OSErr error;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ if( pahsc->pahsc_Channel == NULL ) return paInternalError;
+
+ past->past_StopSoon = 0;
+ past->past_IsActive = 1;
+ pahsc->pahsc_NumOutsQueued = 0;
+ pahsc->pahsc_NumOutsPlayed = 0;
+ pahsc->pahsc_NumFramesDone = 0.0;
+
+ /* Pause channel so it does not do back ground processing while we are still filling the queue. */
+ pauseCommand.cmd = pauseCmd;
+ pauseCommand.param1 = pauseCommand.param2 = 0;
+ error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true);
+ if (noErr != error) goto exit;
+
+ /* Queue all of the buffers so we start off full. */
+ for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+ {
+ PaMac_PlayNext( past, i );
+ }
+
+ /* Resume channel now that the queue is full. */
+ resumeCommand.cmd = resumeCmd;
+ resumeCommand.param1 = resumeCommand.param2 = 0;
+ error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand );
+ if (noErr != error) goto exit;
+
+ return paNoError;
+exit:
+ past->past_IsActive = 0;
+ sPaHostError = error;
+ ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error ));
+ return paHostError;
+}
+
+/*******************************************************************/
+long PaHost_GetTotalBufferFrames( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer);
+}
+
+/***********************************************************************
+** Called by Pa_StopStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ int32 timeOutMsec;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ if( pahsc->pahsc_Channel == NULL ) return paNoError;
+
+ DBUG(("PaHost_StopOutput()\n"));
+ if( past->past_IsActive == 0 ) return paNoError;
+
+ /* Set flags for callback function to see. */
+ if( abort ) past->past_StopNow = 1;
+ past->past_StopSoon = 1;
+ /* Calculate timeOut longer than longest time it could take to play all buffers. */
+ timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate);
+ /* Keep querying sound channel until it is no longer busy playing. */
+ while( past->past_IsActive && (timeOutMsec > 0))
+ {
+ Pa_Sleep(20);
+ timeOutMsec -= 20;
+ }
+ if( timeOutMsec <= 0 )
+ {
+ ERR_RPT(("PaHost_StopOutput: timed out!\n"));
+ return paTimedOut;
+ }
+ else return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ (void) past; /* Prevent unused variable warnings. */
+ return paNoError;
+}
+
+/***********************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ (void) past; /* Prevent unused variable warnings. */
+ (void) abort; /* Prevent unused variable warnings. */
+ return paNoError;
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording );
+}
+int Mac_IsVirtualMemoryOn( void )
+{
+ long attr;
+ OSErr result = Gestalt( gestaltVMAttr, &attr );
+ DBUG(("gestaltVMAttr : 0x%x\n", attr ));
+ return ((attr >> gestaltVMHasPagingControl ) & 1);
+}
+
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ int32 minNumBuffers;
+ int32 minFramesPerHostBuffer;
+ int32 minTotalFrames;
+ int32 userBuffersPerHostBuffer;
+ int32 framesPerHostBuffer;
+ int32 numHostBuffers;
+ /* Calculate minimum and maximum sizes based on timing and sample rate. */
+ minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer;
+ minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+ DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+ /* Determine number of user buffers based on minimum latency. */
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+ minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+ /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+ if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+ {
+ userBuffersPerHostBuffer =
+ (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+ past->past_FramesPerUserBuffer;
+ }
+ else
+ {
+ userBuffersPerHostBuffer = 1;
+ }
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ /* Make sure we have anough WAVE buffers. */
+ if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+ {
+ numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+ }
+ else
+ {
+ /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+ while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+ {
+ userBuffersPerHostBuffer += 1;
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ }
+ }
+
+ pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+ pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+ pahsc->pahsc_NumHostBuffers = numHostBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ OSErr err;
+ PaError result = paHostError;
+ PaHostSoundControl *pahsc;
+ int i;
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ return paInsufficientMemory;
+ }
+ past->past_DeviceData = (void *) pahsc;
+
+ /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */
+ if( (past->past_NumInputChannels > 0) && Mac_IsVirtualMemoryOn() )
+ {
+ pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER;
+ }
+ else
+ {
+ pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER;
+ }
+
+ PaHost_CalcNumHostBuffers( past );
+
+ /* ------------------ OUTPUT */
+ if( past->past_NumOutputChannels > 0 )
+ {
+ /* Create sound channel to which we can send commands. */
+ pahsc->pahsc_Channel = 0L;
+ err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */
+ if(err != 0)
+ {
+ ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err ));
+ goto error;
+ }
+
+ /* Install our callback function pointer straight into the sound channel structure */
+ /* Use new CARBON name for callback procedure. */
+#if TARGET_API_MAC_CARBON
+ pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc);
+#else
+ pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc);
+#endif
+
+ pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc;
+
+ pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16);
+ for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+ {
+ char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer);
+ if (buf == NULL)
+ {
+ ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer ));
+ goto memerror;
+ }
+
+ PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] );
+ pahsc->pahsc_SoundHeaders[i].samplePtr = buf;
+ pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer;
+
+ }
+ }
+#ifdef SUPPORT_AUDIO_CAPTURE
+ /* ------------------ INPUT */
+ /* Use double buffer scheme that matches output. */
+ if( past->past_NumInputChannels > 0 )
+ {
+ int16 tempS;
+ long tempL;
+ Fixed tempF;
+ long mRefNum;
+ unsigned char noname = 0; /* FIXME - use real device names. */
+#if TARGET_API_MAC_CARBON
+ pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc);
+#else
+ pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc);
+#endif
+ pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16);
+ for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+ {
+ char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer);
+ if ( buf == NULL )
+ {
+ ERR_RPT(("PaHost_OpenStream: could not allocate input buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer ));
+ goto memerror;
+ }
+ pahsc->pahsc_InputMultiBuffer.buffers[i] = buf;
+ }
+ pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers;
+
+ err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); /* FIXME - use name so we get selected device */
+ // FIXME err = SPBOpenDevice( (const unsigned char *) sDevices[past->past_InputDeviceID].pad_Info.name, siWritePermission, &mRefNum);
+ if (err) goto error;
+ pahsc->pahsc_InputRefNum = mRefNum;
+ DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum ));
+
+ /* Set input device characteristics. */
+ tempS = 1;
+ err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS);
+ if (err)
+ {
+ ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err ));
+ goto error;
+ }
+
+ tempL = 0x03;
+ err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
+ if (err)
+ {
+ DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err ));
+ }
+
+ /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */
+ tempS = past->past_NumInputChannels;
+ err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS);
+ if (err)
+ {
+ ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err ));
+ goto error;
+ }
+
+ tempF = ((unsigned long)past->past_SampleRate) << 16;
+ err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF);
+ if (err)
+ {
+ ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err ));
+ goto error;
+ }
+
+ /* Setup record-parameter block */
+ pahsc->pahsc_InputParams.inRefNum = mRefNum;
+ pahsc->pahsc_InputParams.milliseconds = 0; // not used
+ pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc;
+ pahsc->pahsc_InputParams.interruptRoutine = 0;
+ pahsc->pahsc_InputParams.userLong = (long) past;
+ pahsc->pahsc_InputParams.unused1 = 0;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ DBUG(("PaHost_OpenStream: complete.\n"));
+ return paNoError;
+
+error:
+ PaHost_CloseStream( past );
+ ERR_RPT(("PaHost_OpenStream: sPaHostError = 0x%x.\n", err ));
+ sPaHostError = err;
+ return paHostError;
+
+memerror:
+ PaHost_CloseStream( past );
+ return paInsufficientMemory;
+}
+
+/***********************************************************************
+** Called by Pa_CloseStream().
+** May be called during error recovery or cleanup code
+** so protect against NULL pointers.
+*/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ OSErr err = 0;
+ int i;
+ PaHostSoundControl *pahsc;
+
+ DBUG(("PaHost_CloseStream( 0x%x )\n", past ));
+
+ if( past == NULL ) return paBadStreamPtr;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ if( past->past_NumOutputChannels > 0 )
+ {
+ /* TRUE means flush now instead of waiting for quietCmd to be processed. */
+ if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE);
+ {
+ for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
+ {
+ Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr;
+ if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer );
+ }
+ }
+ }
+
+ if( past->past_NumInputChannels > 0 )
+ {
+ if( pahsc->pahsc_InputRefNum )
+ {
+ err = SPBCloseDevice(pahsc->pahsc_InputRefNum);
+ pahsc->pahsc_InputRefNum = 0;
+ if( err )
+ {
+ sPaHostError = err;
+ result = paHostError;
+ }
+ }
+ {
+ for (i = 0; i<pahsc->pahsc_InputMultiBuffer.numBuffers; i++)
+ {
+ Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i];
+ if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer );
+ }
+ }
+ }
+
+ past->past_DeviceData = NULL;
+ PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) );
+
+ DBUG(("PaHost_CloseStream: complete.\n", past ));
+ return result;
+}
+/*************************************************************************/
+int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
+{
+ return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate );
+}
+/*************************************************************************/
+static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate )
+{
+ int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer;
+ int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost;
+ if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS;
+ (void) sampleRate;
+ return numBufs;
+}
+
+/*************************************************************************/
+void Pa_Sleep( int32 msec )
+{
+ EventRecord event;
+ int32 sleepTime, endTime;
+ /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
+ sleepTime = ((msec * 60) + 999) / 1000;
+ if( sleepTime < 1 ) sleepTime = 1;
+ endTime = TickCount() + sleepTime;
+ do
+ {
+ DBUGX(("Sleep for %d ticks.\n", sleepTime ));
+ /* Use WaitNextEvent() to sleep without getting events. */
+ /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent
+ * Mac OSX crash. Thanks Dominic Mazzoni. */
+ WaitNextEvent( 0, &event, sleepTime, NULL );
+ sleepTime = endTime - TickCount();
+ }
+ while( sleepTime > 0 );
+}
+/*************************************************************************/
+int32 Pa_GetHostError( void )
+{
+ int32 err = sPaHostError;
+ sPaHostError = 0;
+ return err;
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = NewPtrClear( numBytes );
+ if( (addr == NULL) || (MemError () != 0) ) return NULL;
+
+#if (TARGET_API_MAC_CARBON == 0)
+ if( HoldMemory( addr, numBytes ) != noErr )
+ {
+ DisposePtr( (Ptr) addr );
+ return NULL;
+ }
+#endif
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr == NULL ) return;
+#if TARGET_API_MAC_CARBON
+ (void) numBytes;
+#else
+ UnholdMemory( addr, numBytes );
+#endif
+ DisposePtr( (Ptr) addr );
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_NumFramesDone;
+}
+
+/**************************************************************************
+** Callback for Input, SPBRecord()
+*/
+int gRecordCounter = 0;
+int gPlayCounter = 0;
+pascal void PaMac_InputCompletionProc(SPBPtr recParams)
+{
+ PaError result = paNoError;
+ int finished = 1;
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+
+ gRecordCounter += 1; /* debug hack to see if engine running */
+
+ /* Get our PA data from Mac structure. */
+ past = (internalPortAudioStream *) recParams->userLong;
+ if( past == NULL ) return;
+
+ if( past->past_Magic != PA_MAGIC )
+ {
+ AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past );
+ AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic );
+ goto error;
+ }
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ past->past_NumCallbacks += 1;
+
+ /* Have we been asked to stop recording? */
+ if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error;
+
+ /* If there are no output channels, then we need to call the user callback function from here.
+ * Otherwise we will call the user code during the output completion routine.
+ */
+ if(past->past_NumOutputChannels == 0)
+ {
+ pahsc->pahsc_NumFramesDone += pahsc->pahsc_FramesPerHostBuffer; // Advance for recording
+ result = PaMac_CallUserLoop( past, NULL );
+ }
+
+ /* Did user code ask us to stop? If not, issue another recording request. */
+ if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) )
+ {
+ result = PaMac_RecordNext( past );
+ if( result != paNoError ) pahsc->pahsc_IsRecording = 0;
+ }
+ else goto error;
+
+ return;
+
+error:
+ pahsc->pahsc_IsRecording = 0;
+ pahsc->pahsc_StopRecording = 0;
+ return;
+}
+
+/***********************************************************************
+** Called by either input or output completion proc.
+** Grabs input data if any present, and calls PA conversion code,
+** that in turn calls user code.
+*/
+static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ int16 *inPtr = NULL;
+ int i;
+
+ /* Advance read index for sound input FIFO here, independantly of record/write process. */
+ if(past->past_NumInputChannels > 0)
+ {
+ if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) )
+ {
+ inPtr = (int16 *) MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer );
+ MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer );
+ }
+ }
+
+ /* Call user code enough times to fill buffer. */
+ if( (inPtr != NULL) || (outPtr != NULL) )
+ {
+ StartLoadCalculation( past ); /* CPU usage */
+
+ for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+ {
+ result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr );
+ if( result != 0)
+ {
+ /* Recording might be in another process, so tell it to stop with a flag. */
+ pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording;
+ break;
+ }
+ /* Advance sample pointers. */
+ if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels;
+ if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
+ }
+
+ PaMac_EndLoadCalculation( past );
+ }
+ return result;
+}
+
+/***********************************************************************
+** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager.
+*/
+static PaError PaMac_RecordNext( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ OSErr err;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ /* Get pointer to next buffer to record into. */
+ pahsc->pahsc_InputParams.bufferPtr = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer );
+
+ /* Advance write index if there is room. Otherwise keep writing same buffer. */
+ if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) )
+ {
+ MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer );
+ }
+
+ AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr );
+ AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite );
+
+ /* Setup parameters and issue an asynchronous recording request. */
+ pahsc->pahsc_InputParams.bufferLength = pahsc->pahsc_BytesPerInputHostBuffer;
+ pahsc->pahsc_InputParams.count = pahsc->pahsc_BytesPerInputHostBuffer;
+ err = SPBRecord(&pahsc->pahsc_InputParams, true);
+ if( err )
+ {
+ AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err );
+ sPaHostError = err;
+ result = paHostError;
+ }
+ else
+ {
+ pahsc->pahsc_IsRecording = 1;
+ }
+ return result;
+}
+
+/**************************************************************************
+** Callback for Output Playback()
+** Return negative error, 0 to continue, 1 to stop.
+*/
+long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index )
+{
+ PaHostSoundControl *pahsc;
+ long result = 0;
+ int finished = 1;
+ char *outPtr;
+
+ gPlayCounter += 1; /* debug hack */
+
+ past->past_NumCallbacks += 1;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return -1;
+ /* Are we nested?! */
+ if( pahsc->pahsc_IfInsideCallback ) return 0;
+ pahsc->pahsc_IfInsideCallback = 1;
+ /* Get pointer to buffer to fill. */
+ outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr;
+ /* Combine with any sound input, and call user callback. */
+ result = PaMac_CallUserLoop( past, (int16 *) outPtr );
+
+ pahsc->pahsc_IfInsideCallback = 0;
+ return result;
+}
+
+/*************************************************************************************
+** Called by SoundManager when ready for another buffer.
+*/
+static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd)
+{
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+ (void) theChannel;
+ (void) theCallBackCmd;
+
+ /* Get our data from Mac structure. */
+ past = (internalPortAudioStream *) theCallBackCmd->param2;
+ if( past == NULL ) return;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ pahsc->pahsc_NumOutsPlayed += 1;
+ pahsc->pahsc_NumFramesDone += pahsc->pahsc_FramesPerHostBuffer;
+
+ PaMac_BackgroundManager( past, theCallBackCmd->param1 );
+}
+
+/*******************************************************************/
+static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ /* Has someone asked us to abort by calling Pa_AbortStream()? */
+ if( past->past_StopNow )
+ {
+ SndCommand command;
+ /* Clear the queue of any pending commands. */
+ command.cmd = flushCmd;
+ command.param1 = command.param2 = 0;
+ SndDoImmediate( pahsc->pahsc_Channel, &command );
+ /* Then stop currently playing buffer, if any. */
+ command.cmd = quietCmd;
+ SndDoImmediate( pahsc->pahsc_Channel, &command );
+ past->past_IsActive = 0;
+ }
+ /* Has someone asked us to stop by calling Pa_StopStream()
+ * OR has a user callback returned '1' to indicate finished.
+ */
+ else if( past->past_StopSoon )
+ {
+ if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 )
+ {
+ past->past_IsActive = 0; /* We're finally done. */
+ }
+ }
+ else
+ {
+ PaMac_PlayNext( past, index );
+ }
+ return result;
+}
+
+/*************************************************************************************
+** Fill next buffer with sound and queue it for playback.
+*/
+static void PaMac_PlayNext ( internalPortAudioStream *past, int index )
+{
+ OSErr error;
+ long result;
+ SndCommand playCmd;
+ SndCommand callbackCmd;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ /* If this was the last buffer, or abort requested, then just be done. */
+ if ( past->past_StopSoon ) goto done;
+ /* Load buffer with sound. */
+ result = PaMac_FillNextOutputBuffer ( past, index );
+ if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */
+ else if( result < 0 ) goto done;
+ /* Play the next buffer. */
+ playCmd.cmd = bufferCmd;
+ playCmd.param1 = 0;
+ playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ];
+ error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true );
+ if( error != noErr ) goto gotError;
+ /* Ask for a callback when it is done. */
+ callbackCmd.cmd = callBackCmd;
+ callbackCmd.param1 = index;
+ callbackCmd.param2 = (long)past;
+ error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true );
+ if( error != noErr ) goto gotError;
+ pahsc->pahsc_NumOutsQueued += 1;
+
+ return;
+
+gotError:
+ sPaHostError = error;
+done:
+ return;
+}
--- /dev/null
+/*
+ * $Id$
+ * pa_mac_core.c
+ * Implementation of PortAudio for Mac OS X Core Audio
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * CHANGE HISTORY:
+
+ 3.29.2001 - Phil Burk - First pass... converted from Window MME code with help from Darren.
+ 3.30.2001 - Darren Gibbs - Added more support for dynamically querying device info.
+ 12.7.2001 - Gord Peters - Tweaks to compile on PA V17 and OS X 10.1
+ */
+#include <CoreServices/CoreServices.h>
+#include <CoreAudio/CoreAudio.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/************************************************* Constants ********/
+#define PA_USE_TIMER_CALLBACK (0) /* Select between two options for background task. 0=thread, 1=timer */
+#define PA_USE_HIGH_LATENCY (0) /* For debugging glitches. */
+
+/* Switches for debugging. */
+#define PA_SIMULATE_UNDERFLOW (0) /* Set to one to force an underflow of the output buffer. */
+
+/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
+#define PA_TRACE_RUN (0)
+#define PA_TRACE_START_STOP (1)
+
+#define PA_MIN_MSEC_PER_HOST_BUFFER (10)
+#define PA_MAX_MSEC_PER_HOST_BUFFER (100) /* Do not exceed unless user buffer exceeds */
+#define PA_MIN_NUM_HOST_BUFFERS (3)
+#define PA_MAX_NUM_HOST_BUFFERS (16) /* OK to exceed if necessary */
+#define PA_MIN_LATENCY_MSEC (200)
+
+#define MIN_TIMEOUT_MSEC (1000)
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+/************************************************* Definitions ********/
+
+/**************************************************************
+ * Structure for internal host specific stream data.
+ * This is allocated on a per stream basis.
+ */
+typedef struct PaHostSoundControl
+{
+ /* Input -------------- */
+ int pahsc_BytesPerHostInputBuffer;
+ int pahsc_BytesPerUserInputBuffer; /* native buffer size in bytes */
+ /* Output -------------- */
+ AudioDeviceID pahsc_OutputAudioDeviceID;
+ int pahsc_BytesPerHostOutputBuffer;
+ int pahsc_BytesPerUserOutputBuffer; /* native buffer size in bytes */
+ /* Run Time -------------- */
+ PaTimestamp pahsc_FramesPlayed;
+ long pahsc_LastPosition; /* used to track frames played. */
+ /* For measuring CPU utilization. */
+ // LARGE_INTEGER pahsc_EntryCount;
+ // LARGE_INTEGER pahsc_LastExitCount;
+ /* Init Time -------------- */
+ int pahsc_NumHostBuffers;
+ int pahsc_FramesPerHostBuffer;
+ int pahsc_UserBuffersPerHostBuffer;
+
+ // CRITICAL_SECTION pahsc_StreamLock; /* Mutext to prevent threads from colliding. */
+ int pahsc_StreamLockInited;
+
+}
+PaHostSoundControl;
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumDevices = 0;
+static PaDeviceInfo **sDevicePtrs = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+static AudioDeviceID *sDeviceIDList; // Array of AudioDeviceIDs
+
+static const char sMapperSuffixInput[] = " - Input";
+static const char sMapperSuffixOutput[] = " - Output";
+
+/************************************************* Macros ********/
+
+/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
+#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
+
+/************************************************* Prototypes **********/
+
+static PaError Pa_QueryDevices( void );
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past );
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ // QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+#if 0
+ // LARGE_INTEGER CurrentCount = { 0, 0 };
+ // LONGLONG InsideCount;
+ // LONGLONG TotalCount;
+ /*
+ ** Measure CPU utilization during this callback. Note that this calculation
+ ** assumes that we had the processor the whole time.
+ */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( QueryPerformanceCounter( &CurrentCount ) )
+ {
+ if( past->past_IfLastExitValid )
+ {
+ InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+ TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart;
+ /* Low pass filter the result because sometimes we get called several times in a row.
+ * That can cause the TotalCount to be very low which can cause the usage to appear
+ * unnaturally high. So we must filter numerator and denominator separately!!!
+ */
+ past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * InsideCount));
+ past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * TotalCount));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ }
+ pahsc->pahsc_LastExitCount = CurrentCount;
+ past->past_IfLastExitValid = 1;
+ }
+#endif
+}
+
+static PaDeviceID Pa_QueryDefaultDevice( int deviceEnum )
+{
+ OSStatus err = noErr;
+ UInt32 count;
+ int i;
+ AudioDeviceID tempDevice = kAudioDeviceUnknown;
+ PaDeviceID defaultDeviceID;
+
+ // get the default output device for the HAL
+ // it is required to pass the size of the data to be returned
+ count = sizeof(AudioDeviceID);
+ err = AudioHardwareGetProperty( deviceEnum, &count, (void *) &tempDevice);
+ if (err != noErr) goto Bail;
+ // find index of default device
+ defaultDeviceID = paNoDevice;
+ for( i=0; i<sNumDevices; i++ )
+ {
+ if( sDeviceIDList[i] == tempDevice )
+ {
+ defaultDeviceID = i;
+ break;
+ }
+ }
+Bail:
+ return defaultDeviceID;
+}
+
+/****************************************** END CPU UTILIZATION *******/
+
+static PaError Pa_QueryDevices( void )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+ Boolean outWritable;
+ int numBytes;
+
+ // find out how many audio devices there are, if any
+ err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable);
+ if (err != noErr)
+ ERR_RPT(("Couldn't get info about list of audio devices\n"));
+
+ // calculate the number of device available
+ sNumDevices = outSize / sizeof(AudioDeviceID);
+
+ // Bail if there aren't any devices
+ if (sNumDevices < 1)
+ ERR_RPT(("No Devices Available\n"));
+
+ // make space for the devices we are about to get
+ sDeviceIDList = malloc(outSize);
+
+ // get an array of AudioDeviceIDs
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sDeviceIDList);
+ if (err != noErr)
+ ERR_RPT(("Couldn't get list of audio device IDs\n"));
+
+ /* Allocate structures to hold device info pointers. */
+ numBytes = sNumDevices * sizeof(PaDeviceInfo *);
+ sDevicePtrs = PaHost_AllocateFastMemory( numBytes );
+ if( sDevicePtrs == NULL ) return paInsufficientMemory;
+
+ sDefaultInputDeviceID = Pa_QueryDefaultDevice( kAudioHardwarePropertyDefaultInputDevice );
+ sDefaultOutputDeviceID = Pa_QueryDefaultDevice( kAudioHardwarePropertyDefaultOutputDevice );
+
+ return paNoError;
+}
+
+/************************************************************************************/
+long Pa_GetHostError()
+{
+ return sPaHostError;
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ if( sNumDevices <= 0 ) Pa_Initialize();
+ return sNumDevices;
+}
+
+/*************************************************************************/
+
+/* Allocate a string containing the device name. */
+char *deviceNameFromID(AudioDeviceID deviceID )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+ Boolean outWritable;
+ char *deviceName = nil;
+ // query size of name
+ err = AudioDeviceGetPropertyInfo(deviceID, 0, false, kAudioDevicePropertyDeviceName, &outSize, &outWritable);
+ if (err == noErr)
+ {
+ deviceName = malloc( outSize + 1);
+ if( deviceName )
+ {
+ err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyDeviceName, &outSize, deviceName);
+ // FIXME if (err == noErr)
+ }
+ }
+
+ return deviceName;
+}
+
+/*************************************************************************/
+
+void deviceDataFormatFromID(AudioDeviceID deviceID , AudioStreamBasicDescription *desc )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+
+ outSize = sizeof(*desc);
+ err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyStreamFormat, &outSize, desc);
+}
+
+/*************************************************************************/
+// An AudioStreamBasicDescription is passed in to query whether or not
+// the format is supported. A kAudioDeviceUnsupportedFormatError will
+// be returned if the format is not supported and kAudioHardwareNoError
+// will be returned if it is supported. AudioStreamBasicDescription
+// fields set to 0 will be ignored in the query, but otherwise values
+// must match exactly.
+
+Boolean deviceDoesSupportFormat(AudioDeviceID deviceID , AudioStreamBasicDescription *desc )
+{
+ OSStatus err = noErr;
+ UInt32 outSize;
+
+ outSize = sizeof(*desc);
+ err = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyStreamFormatSupported, &outSize, desc);
+
+ if (err == kAudioHardwareNoError)
+ return true;
+ else
+ return false;
+}
+
+/*************************************************************************/
+// return an error string
+char* coreAudioErrorString (int errCode )
+{
+ char *str;
+
+ switch (errCode)
+ {
+ case kAudioHardwareUnspecifiedError:
+ str = "kAudioHardwareUnspecifiedError";
+ break;
+ case kAudioHardwareNotRunningError:
+ str = "kAudioHardwareNotRunningError";
+ break;
+ case kAudioHardwareUnknownPropertyError:
+ str = "kAudioHardwareUnknownPropertyError";
+ break;
+ case kAudioDeviceUnsupportedFormatError:
+ str = "kAudioDeviceUnsupportedFormatError";
+ break;
+ case kAudioHardwareBadPropertySizeError:
+ str = "kAudioHardwareBadPropertySizeError";
+ break;
+ case kAudioHardwareIllegalOperationError:
+ str = "kAudioHardwareIllegalOperationError";
+ break;
+ default:
+ str = "UNKNWON ERROR!";
+ break;
+ }
+
+ return str;
+}
+
+/*************************************************************************
+** If a PaDeviceInfo structure has not already been created,
+** then allocate one and fill it in for the selected device.
+**
+** We create one extra input and one extra output device for the WAVE_MAPPER.
+** [Does anyone know how to query the default device and get its name?]
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES 4 /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+
+ PaDeviceInfo *deviceInfo;
+ AudioDeviceID devID;
+ double *sampleRates; /* non-const ptr */
+ double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0};
+ int index;
+ AudioStreamBasicDescription formatDesc;
+ Boolean result;
+
+ if( id < 0 || id >= sNumDevices )
+ return NULL;
+
+ if( sDevicePtrs[ id ] != NULL )
+ {
+ return sDevicePtrs[ id ];
+ }
+
+ deviceInfo = PaHost_AllocateFastMemory( sizeof(PaDeviceInfo) );
+ if( deviceInfo == NULL ) return NULL;
+
+ deviceInfo->structVersion = 1;
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->numSampleRates = -1;
+
+
+ // fill in format descriptor
+ if( id < sNumDevices )
+ {
+ devID = sDeviceIDList[ id ];
+
+ // Get the device name
+ deviceInfo->name = deviceNameFromID( devID );
+
+ // Figure out supported sample rates
+ // Make room in case device supports all rates.
+ sampleRates = (double*)PaHost_AllocateFastMemory( MAX_NUMSAMPLINGRATES * sizeof(double) );
+ deviceInfo->sampleRates = sampleRates;
+ deviceInfo->numSampleRates = 0;
+
+ // Loop through the possible sampling rates and check each to see if the device supports it.
+ for (index = 0; index < MAX_NUMSAMPLINGRATES; index ++)
+ {
+ memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) );
+ formatDesc.mSampleRate = possibleSampleRates[index];
+ result = deviceDoesSupportFormat( devID, &formatDesc );
+
+ if (result == true)
+ {
+ deviceInfo->numSampleRates += 1;
+ *sampleRates = possibleSampleRates[index];
+ sampleRates++;
+ }
+ }
+
+ // Get data format info from the device.
+ deviceDataFormatFromID(devID, &formatDesc);
+
+ deviceInfo->maxInputChannels = 0; // FIXME - Input and Output are separate devices!
+ deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame;
+
+ // FIXME - where to put current sample rate?: formatDesc.mSampleRate
+
+ // Right now the Core Audio headers only define one formatID: LinearPCM
+ // Apparently LinearPCM must be Float32 for now.
+ // FIXME -
+ switch (formatDesc.mFormatID)
+ {
+ case kAudioFormatLinearPCM:
+ deviceInfo->nativeSampleFormats = paFloat32;
+
+ // FIXME - details about the format are in these flags.
+ // formatDesc.mFormatFlags
+
+ // here are the possibilities
+ // kLinearPCMFormatFlagIsFloat // set for floating point, clear for integer
+ // kLinearPCMFormatFlagIsBigEndian // set for big endian, clear for little
+ // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer,
+ // only valid if kLinearPCMFormatFlagIsFloat is clear
+ // kLinearPCMFormatFlagIsPacked // set if the sample bits are packed as closely together as possible,
+ // clear if they are high or low aligned within the channel
+ // kLinearPCMFormatFlagIsAlignedHigh // set if the sample bits are placed
+
+ break;
+ default:
+ deviceInfo->nativeSampleFormats = paFloat32; // FIXME
+ break;
+ }
+
+ }
+
+ sDevicePtrs[ id ] = deviceInfo;
+ return deviceInfo;
+
+ /* FIXME
+ error:
+ free( sampleRates );
+ free( deviceInfo );
+ */
+
+ return NULL;
+}
+
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+** set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE (32)
+#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
+
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+#if 0
+ UInt32 hresult;
+ char envbuf[PA_ENV_BUF_SIZE];
+ PaDeviceID recommendedID = paNoDevice;
+
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ recommendedID = atoi( envbuf );
+ }
+
+ return recommendedID;
+#endif
+ return paNoDevice;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sNumDevices == 0 )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+
+/**********************************************************************
+** Check for environment variable, else query devices and use result.
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultInputDeviceID;
+ }
+ return result;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultOutputDeviceID;
+ }
+ return result;
+}
+
+/**********************************************************************
+** Initialize Host dependant part of API.
+*/
+
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+ PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+ return Pa_MaybeQueryDevices();
+}
+
+/**********************************************************************
+** Fill any available output buffers and use any available
+** input buffers by calling user callback.
+*/
+static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferList* inInputData,
+ AudioBufferList* outOutputData )
+{
+ PaError result = 0;
+ char *inBufPtr;
+ char *outBufPtr;
+ int gotInput = 0;
+ int gotOutput = 0;
+ int i;
+ int buffersProcessed = 0;
+ int done = 0;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+ past->past_NumCallbacks += 1;
+
+#if PA_SIMULATE_UNDERFLOW
+ if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
+ {
+ Sleep(UNDER_SLEEP_FOR);
+ }
+#endif
+
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
+#endif
+
+ Pa_StartUsageCalculation( past );
+
+ /* If we are using output, then we need an empty output buffer. */
+ gotOutput = 0;
+ if( past->past_NumOutputChannels > 0 )
+ {
+ outBufPtr = outOutputData->mBuffers[0].mData,
+ gotOutput = 1;
+ }
+
+ /* If we are using input, then we need a full input buffer. */
+ gotInput = 0;
+ inBufPtr = NULL;
+ if( past->past_NumInputChannels > 0 )
+ {
+ inBufPtr = inInputData->mBuffers[0].mData,
+ gotInput = 1;
+ }
+
+ buffersProcessed += 1;
+
+ /* Each host buffer contains multiple user buffers so do them all now. */
+ for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+ {
+ if( done )
+ {
+ if( gotOutput )
+ {
+ /* Clear remainder of wave buffer if we are waiting for stop. */
+ AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
+ memset( outBufPtr, 0, pahsc->pahsc_BytesPerUserOutputBuffer );
+ }
+ }
+ else
+ {
+ /* Convert 16 bit native data to user data and call user routine. */
+ result = Pa_CallConvertFloat32( past, (float *) inBufPtr, (float *) outBufPtr );
+ if( result != 0) done = 1;
+ }
+ if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
+ if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
+ }
+
+ Pa_EndUsageCalculation( past );
+
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
+#endif
+
+ return (result != 0) ? result : done;
+}
+
+OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr)
+{
+
+ PaError result = 0;
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+ past = (internalPortAudioStream *) contextPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ // Just Curious: printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers);
+
+ /* Has someone asked us to abort by calling Pa_AbortStream()? */
+ if( past->past_StopNow )
+ {
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ /* Has someone asked us to stop by calling Pa_StopStream()
+ * OR has a user callback returned '1' to indicate finished.
+ */
+ else if( past->past_StopSoon )
+ {
+ // FIXME - pretend all done
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ else
+ {
+ /* Process full input buffer and fill up empty output buffers. */
+ if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0)
+ {
+ /* User callback has asked us to stop. */
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result );
+#endif
+ past->past_StopSoon = 1; /* Request that audio play out then stop. */
+ result = paNoError;
+ }
+ }
+
+ // FIXME PaHost_UpdateStreamTime( pahsc );
+
+ return result;
+}
+
+static int PaHost_CalcTimeOut( internalPortAudioStream *past )
+{
+ /* Calculate timeOut longer than longest time it could take to play all buffers. */
+ int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+ if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
+ return timeOut;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenInputStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ int bytesPerInputFrame;
+ const PaDeviceInfo *pad;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
+ pad = Pa_GetDeviceInfo( past->past_InputDeviceID );
+ if( pad == NULL ) return paInternalError;
+
+ bytesPerInputFrame = Pa_GetSampleSize(pad->nativeSampleFormats) * past->past_NumInputChannels;
+ pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame;
+ pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserInputBuffer;
+ // FIXME
+ return result;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ const PaDeviceInfo *pad;
+ UInt32 bytesPerHostBuffer;
+ UInt32 dataSize;
+ UInt32 hardwareLatency = -1;
+ Boolean writeable = false;
+ OSStatus err = noErr;
+ int bytesPerOutputFrame;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+ pad = Pa_GetDeviceInfo( past->past_OutputDeviceID );
+ if( pad == NULL ) return paInternalError;
+
+ pahsc->pahsc_OutputAudioDeviceID = sDeviceIDList[past->past_OutputDeviceID];
+
+ bytesPerOutputFrame = Pa_GetSampleSize(pad->nativeSampleFormats) * past->past_NumOutputChannels;
+ pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame;
+ pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserOutputBuffer;
+
+ // FIXME - force host buffer to user size
+
+ // change the bufferSize of the device
+ bytesPerHostBuffer = past->past_OutputBufferSize; // FIXME
+ dataSize = sizeof(UInt32);
+ err = AudioDeviceSetProperty( pahsc->pahsc_OutputAudioDeviceID, 0, 0, false,
+ kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer);
+ if( err != noErr )
+ {
+ ERR_RPT(("Could not force buffer size!"));
+ result = paHostError;
+ goto error;
+ }
+
+ // FIXME -- this isn't working for some reason. I can't figure out what the error code is.
+ dataSize = sizeof(UInt32);
+ err = AudioDeviceGetProperty(pahsc->pahsc_OutputAudioDeviceID, 0, false, kAudioDevicePropertyLatency, &dataSize, &writeable);
+ if( err != noErr )
+ {
+ PRINT(("A CoreAudio error occurred: %s.\n", coreAudioErrorString( err ) ));
+ }
+
+ err = AudioDeviceGetProperty(pahsc->pahsc_OutputAudioDeviceID, 0, false, kAudioDevicePropertyLatency, &dataSize, (void *)&hardwareLatency);
+ if ( err != noErr )
+ {
+ PRINT(("A CoreAudio error occurred: %s.\n", coreAudioErrorString( err ) ));
+ }
+ else
+ {
+ PRINT(("Output Stream Hardware Latency = %d frames.\n", hardwareLatency ));
+ }
+
+ return result;
+
+error:
+ return result;
+}
+
+/*******************************************************************/
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer;
+}
+
+
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = past->past_DeviceData;
+ unsigned int minNumBuffers;
+ int minFramesPerHostBuffer;
+ int maxFramesPerHostBuffer;
+ int minTotalFrames;
+ int userBuffersPerHostBuffer;
+ int framesPerHostBuffer;
+ int numHostBuffers;
+
+ /* Calculate minimum and maximum sizes based on timing and sample rate. */
+ minFramesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+ minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+ DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+
+ maxFramesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+ maxFramesPerHostBuffer = (maxFramesPerHostBuffer + 7) & ~7;
+ DBUG(("PaHost_CalcNumHostBuffers: maxFramesPerHostBuffer = %d\n", maxFramesPerHostBuffer ));
+
+ /* Determine number of user buffers based on minimum latency. */
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+
+ minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+
+ /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+ if( (int) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+ {
+ userBuffersPerHostBuffer =
+ (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+ past->past_FramesPerUserBuffer;
+ }
+ else
+ {
+ userBuffersPerHostBuffer = 1;
+ }
+
+
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+
+ /* Make sure we have anough WAVE buffers. */
+ if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+ {
+ numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+ }
+ else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
+ ((int) past->past_FramesPerUserBuffer < (maxFramesPerHostBuffer/2) ) )
+ {
+ /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+ while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+ {
+ userBuffersPerHostBuffer += 1;
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ /* If we have gone too far, back up one. */
+ if( (framesPerHostBuffer > maxFramesPerHostBuffer) ||
+ (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
+ {
+ userBuffersPerHostBuffer -= 1;
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ break;
+ }
+ }
+ }
+
+ pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+ pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+ pahsc->pahsc_NumHostBuffers = numHostBuffers;
+
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+
+ /* Figure out how user buffers fit into WAVE buffers. */
+ // FIXME - just force for now
+
+ pahsc->pahsc_UserBuffersPerHostBuffer = 1;
+ pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer;
+ pahsc->pahsc_NumHostBuffers = 2; // FIXME - dunno?!
+
+ {
+ int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
+ PRINT(("PortAudio on OSX - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
+ }
+
+ /* ------------------ OUTPUT */
+ if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_OpenOutputStream( past );
+ if( result < 0 ) goto error;
+ }
+
+ /* ------------------ INPUT */
+ if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) )
+ {
+ result = PaHost_OpenInputStream( past );
+ if( result < 0 ) goto error;
+ }
+
+ return result;
+
+error:
+ PaHost_CloseStream( past );
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ OSStatus err = noErr;
+
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ if( past->past_OutputDeviceID != paNoDevice )
+ {
+ // Associate an IO proc with the device and pass a pointer to the audio data context
+ err = AudioDeviceAddIOProc(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc, past);
+ if (err != noErr) goto error;
+
+ // start playing sound through the device
+ err = AudioDeviceStart(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto error;
+ }
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ if( past->past_InputDeviceID != paNoDevice )
+ {
+ // FIXME
+ }
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+#if PA_USE_TIMER_CALLBACK
+ int resolution;
+ int bufsPerTimerCallback;
+ int msecPerBuffer;
+#endif /* PA_USE_TIMER_CALLBACK */
+
+ past->past_StopSoon = 0;
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+ pahsc->pahsc_FramesPlayed = 0.0;
+ pahsc->pahsc_LastPosition = 0;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
+#endif
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ int timeOut;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ past->past_StopSoon = 1;
+ /* If aborting, tell background thread to stop NOW! */
+ if( abort ) past->past_StopNow = 1;
+
+ timeOut = PaHost_CalcTimeOut( past );
+ past->past_IsActive = 0;
+
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+
+ // FIXME
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+
+ // FIXME if( pahsc->pahsc_OutputAudioDeviceID != ??? )
+ {
+ OSStatus err = noErr;
+
+ err = AudioDeviceStop(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto Bail;
+
+ err = AudioDeviceRemoveIOProc(pahsc->pahsc_OutputAudioDeviceID, (AudioDeviceIOProc)appIOProc);
+ if (err != noErr) goto Bail;
+ }
+Bail:
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+ /* Free data and device for output. */
+
+ /* Free data and device for input. */
+
+ free( pahsc );
+ past->past_DeviceData = NULL;
+
+ return paNoError;
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+ int minLatencyMsec = 0;
+ double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+ int minBuffers;
+
+ /* Let user determine minimal latency by setting environment variable. */
+ {
+ minLatencyMsec = PA_MIN_LATENCY_MSEC;
+ }
+
+ DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+ minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+ if( minBuffers < 2 ) minBuffers = 2;
+
+ return minBuffers;
+}
+
+/*************************************************************************
+** Cleanup device info.
+*/
+PaError PaHost_Term( void )
+{
+ int i;
+
+ if( sNumDevices > 0 )
+ {
+
+ if( sDevicePtrs != NULL )
+ {
+
+ for( i=0; i<sNumDevices; i++ )
+ {
+ if( sDevicePtrs[i] != NULL )
+ {
+
+ free( (char*)sDevicePtrs[i]->name );
+ free( (void*)sDevicePtrs[i]->sampleRates );
+ free( sDevicePtrs[i] );
+ }
+ }
+
+ free( sDevicePtrs );
+ sDevicePtrs = NULL;
+ }
+
+ sNumDevices = 0;
+ }
+ return paNoError;
+}
+
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+ /* struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select( 0, NULL, NULL, NULL, &timeout );
+ */
+ usleep( msec * 1000 );
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) past->past_IsActive;
+}
+
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ // FIXME PaHost_UpdateStreamTime( pahsc );
+ return pahsc->pahsc_FramesPlayed;
+}
--- /dev/null
+# Make PortAudio for Silicon Graphics IRIX (6.2)
+# Pieter suurmond, august 17, 2001. (v15 pa_sgi sub-version #0.04)
+
+# Instead of "-lpthread", as with linux,
+# just the audio- and math-library for SGI:
+LIBS = -laudio -lm
+
+CDEFINES = -I../pa_common
+
+# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
+# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
+# And use for example -O2 or -O3 for better optimization:
+CFLAGS = -O2
+PASRC = ../pa_common/pa_lib.c pa_sgi.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2).
+#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK
+TESTC = $(PASRC) ../pa_tests/patest_record.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK but needs more than 4 buffers to do without glitches.
+#TESTC = $(PASRC) ../pa_tests/patest_saw.c # Seems OK (does it gracefully exit?).
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore.
+
+# Tests that do not yet work.
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default
+#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default
+#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # THIS CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-(
+ # PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH.
+ # OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON???
+ # HMMM I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS!
+TESTH = $(PAINC)
+
+all: patest
+
+#(cc may be changed to gcc)
+patest: $(TESTC) $(TESTH) Makefile
+ cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
--- /dev/null
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * SGI IRIX implementation by Pieter Suurmond, aug 17, 2001.
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+Modfication History:
+ 8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
+ 8/17/2001 - (v15 pa_sgi sub-version #0.04), wow, much is working now! Send this to Phil & Ross.
+ ID-stuff is nicer here now than in the linux_oss-alpha-version, v15. Was able to get
+ rid of a number of globals (like "sNumDevices" for instance).
+TODO:
+ - Test under IRIX 6.5.
+ - Maybe use pthread instead of (platform-specific) poll/select/pollfd-calls.
+ (As Gary Scavone more or less suggested(?)... But can I get enough process-control then?)
+ - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
+ and maybe also the other natively supported formats? (might increase performance)
+ - Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet,
+ seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?)
+ - Make sure that the fuzz-test doesn't let all IRIX crash (especially with higher buff-sizes).
+ - The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
+REFERENCES:
+ - IRIX 6.2 man pages regarding SGI AL library.
+ - IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and
+ may not be publically available on the internet).
+*/
+
+#include <stdio.h> /* Standard libraries. */
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <math.h>
+
+#include "portaudio.h" /* Portaudio headers. */
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include <sys/time.h> /* System specific (IRIX 6.2). */
+#include <sys/types.h>
+#include <sys/prctl.h>
+#include <sys/schedctl.h>
+#include <fcntl.h> /* fcntl.h needed. */
+#include <unistd.h> /* For streams, ioctl(), etc. */
+#include <signal.h>
+#include <ulocks.h>
+#include <poll.h>
+#include <limits.h>
+#include <dmedia/audio.h>
+
+/*--------------------------------------------*/
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) PRINT(x)
+#define DBUGX(x) /* PRINT(x) */
+
+#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
+#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */
+
+typedef struct internalPortAudioDevice /* IRIX specific device info: */
+{
+ PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */
+ long pad_ALdevice; /* SGI-number! */
+ double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
+ char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
+ PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
+ /* int structVersion; */
+ /* const char* name; */
+ /* int maxInputChannels, maxOutputChannels; */
+ /* int numSampleRates; Num rates, or -1 if range supprtd. */
+ /* const double* sampleRates; Array of supported sample rates, */
+ /* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */
+ struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */
+}
+internalPortAudioDevice;
+
+typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */
+{
+ ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
+ pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
+ int pahsc_threadPID; /* Sproc()-result, written by PaHost_StartEngine(). */
+ short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */
+ *pahsc_NativeOutputBuffer;
+ unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */
+ pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */
+ unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */
+ pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */
+ struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
+ pahsc_LastExitTime;
+ long pahsc_InsideCountSum,
+ pahsc_TotalCountSum;
+}
+PaHostSoundControl;
+
+/*----------------------------- Shared Data ------------------------------------------------------*/
+static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
+static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
+
+/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
+/* (copied from source pa_linux_oss/pa_linux_oss.c) */
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval itimer;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
+}
+
+static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
+{
+ long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
+ return usecs;
+}
+
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval currentTime;
+ long insideCount;
+ long totalCount; /* Measure CPU utilization during this callback. */
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return;
+ if (getitimer( ITIMER_REAL, ¤tTime ) == 0 )
+ {
+ if (past->past_IfLastExitValid)
+ {
+ insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime );
+ pahsc->pahsc_InsideCountSum += insideCount;
+ totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime );
+ pahsc->pahsc_TotalCountSum += totalCount;
+ /* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
+ /* Low pass filter the result because sometimes we get called several times in a row. */
+ /* That can cause the TotalCount to be very low which can cause the usage to appear */
+ /* unnaturally high. So we must filter numerator and denominator separately!!! */
+ if (pahsc->pahsc_InsideCountSum > 0)
+ {
+ past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
+ past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
+ (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
+ past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
+ pahsc->pahsc_InsideCountSum = 0;
+ pahsc->pahsc_TotalCountSum = 0;
+ }
+ }
+ past->past_IfLastExitValid = 1;
+ }
+ pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
+ pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
+ setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
+ past->past_IfLastExitValid = 1;
+} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
+
+
+/*--------------------------------------------------------------------------------------*/
+PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
+{ /* call to report via ERR_RPT(), yields a PaError-num. */
+ const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
+ switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
+ {
+ case AL_BAD_OUT_OF_MEM:
+ ERR_RPT(("%sout of memory.\n", a));
+ return paInsufficientMemory; /* Known PaError. */
+ case AL_BAD_CONFIG:
+ ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_CHANNELS:
+ ERR_RPT(("%schannels not 1,2 or 4.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_NO_PORTS:
+ ERR_RPT(("%sout of audio ports.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE:
+ ERR_RPT(("%swrong device number.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DEVICE_ACCESS:
+ ERR_RPT(("%swrong device access.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_DIRECTION:
+ ERR_RPT(("%sinvalid direction.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_SAMPFMT:
+ ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_FLOATMAX:
+ ERR_RPT(("%smax float value is zero.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_WIDTH:
+ ERR_RPT(("%sunsupported samplewidth.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_QSIZE:
+ ERR_RPT(("%sinvalid queue size.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PVBUFFER:
+ ERR_RPT(("%sPVbuffer null.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_NEG:
+ ERR_RPT(("%snegative bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_BUFFERLENGTH_ODD:
+ ERR_RPT(("%sodd bufferlength.\n", a));
+ return paHostError; /* Generic PaError. */
+ case AL_BAD_PARAM:
+ ERR_RPT(("%sparameter not valid for device.\n", a));
+ return paHostError; /* Generic PaError. */
+ default:
+ ERR_RPT(("%sunknown error.\n", a));
+ return paHostError; /* Generic PaError. */
+ }
+}
+
+/*------------------------------------------------------------------------------------------*/
+/* Tries to set various rates and formats and fill in the device info structure. */
+static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
+ PaDeviceID id, /* (DefaultI|ODeviceID()) */
+ char* name, /* (for example "SGI AL") */
+ internalPortAudioDevice* pad) /* Result written to pad. */
+{
+ int format;
+ long min, max; /* To catch hardware characteristics. */
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ /*--------------------------------------------------------------------------------------*/
+ pad->pad_ALdevice = ALdev; /* Set the AL device number. */
+ pad->pad_DeviceID = id; /* Set the PA device number. */
+ if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
+ {
+ ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
+ return paHostError;
+ }
+ strcpy(pad->pad_DeviceName, name); /* Write name-string. */
+ pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
+ /*--------------------------------- natively supported sample formats: -----------------*/
+ pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
+ /* Then also choose other CallConvertXX()! */
+ /*--------------------------------- number of available i/o channels: ------------------*/
+ if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxInputChannels = max;
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
+ if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
+ return translateSGIerror();
+ pad->pad_Info.maxOutputChannels = max;
+ DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
+ /*--------------------------------- supported samplerates: ----------------------*/
+ pad->pad_Info.numSampleRates = 7;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates;
+ pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
+ pad->pad_SampleRates[1] = (double)AL_RATE_11025;
+ pad->pad_SampleRates[2] = (double)AL_RATE_16000;
+ pad->pad_SampleRates[3] = (double)AL_RATE_22050;
+ pad->pad_SampleRates[4] = (double)AL_RATE_32000;
+ pad->pad_SampleRates[5] = (double)AL_RATE_44100;
+ pad->pad_SampleRates[6] = (double)AL_RATE_48000;
+ if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
+ return translateSGIerror(); /* double -> long. */
+ if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
+ goto weird;
+ if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
+ return translateSGIerror();
+ if (max != (long)(0.5 + pad->pad_SampleRates[6]))
+ {
+weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
+ return paHostError; /* Or make it a warning and just carry on... */
+ }
+ /*-------------------------------------------------------------------------------*/
+ return paNoError;
+}
+
+
+/*--------------------------------------------------------------------------------*/
+int Pa_CountDevices() /* Name of this function suggests it only counts and */
+{ /* is NOT destructive, it however resets whole PA ! */
+ int numDevices = 0; /* Let 's not do that here. */
+ internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
+#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
+ if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
+ Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
+#endif /* friendly to clients that forgot to initialize PA. */
+ while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
+ {
+ numDevices++;
+ currentDevice = currentDevice->pad_Next;
+ }
+ return numDevices;
+}
+
+
+
+/*-------------------------------------------------------------------------------*/
+static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
+{
+ int numDevices = 0;
+ internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
+ internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
+ while (pad) /* pad may be NULL, that's ok, return 0. */
+ { /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
+ if (pad->pad_DeviceID == id) /* This the device we were looking for? */
+ res = pad; /* But keep on(!) counting so we don't */
+ numDevices++; /* have to call Pa_CountDevices() later. */
+ pad = pad->pad_Next; /* Advance to the next device or NULL. */
+ } /* No assumptions about order of ID's in */
+ if (!res) /* the list. */
+ ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
+ if ((id < 0) || (id >= numDevices))
+ {
+ ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
+#if 1 /* Be strict, even when found, */
+ res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
+#endif
+
+ }
+ return res;
+}
+
+/*----------------------------------------------------------------------*/
+const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
+{
+ PaDeviceInfo* res = (PaDeviceInfo*)NULL;
+ internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
+ if (pad)
+ res = &pad->pad_Info; /* Not finding the specified ID is not */
+ if (!res) /* the same as &pad->pad_Info == NULL. */
+ ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
+ return res; /* So (maybe) a second/third ERR_RPT(). */
+}
+
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultInputDeviceID(void)
+{
+ return 0; /* 0 is the default device ID. */
+}
+/*------------------------------------------------*/
+PaDeviceID Pa_GetDefaultOutputDeviceID(void)
+{
+ return 0;
+}
+
+/*--------------------------------------------------------------------------------------*/
+/* Build linked list with all available audio devices on this SGI machine. */
+PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
+{
+ internalPortAudioDevice* pad;
+ PaError r = paNoError;
+ int audioLibFileID;
+
+ if (sDeviceList) /* Allow re-init, only warn, no err. */
+ {
+ ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
+ return r;
+ }
+ /*------------- ADD THE SGI DEFAULT DEVICES TO THE LIST: --------------------------------------*/
+ audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
+ if (audioLibFileID < 0) /* IO port. On failure, machine */
+ { /* has no audio ability. */
+ ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
+ return paHostError;
+ }
+ close(audioLibFileID); /* Allocate fast mem to hold device info. */
+ pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
+ if (pad == NULL)
+ return paInsufficientMemory;
+ memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
+ r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
+ Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
+ "AL default", /* A suitable name. */
+ pad); /* Write args and queried info into pad. */
+ if (r != paNoError)
+ {
+ ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
+ }
+ else
+ sDeviceList = pad; /* First element in linked list (pad->pad_Next is already NULL). */
+ /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LIST: --------------*/
+ /*---------------------------------------------------------------------------------*/
+ return r;
+}
+
+/*------------------------------------------------------------------*/
+usema_t *SendSema, /* These variables are shared between the audio */
+*RcvSema; /* handling process and the main process. */
+
+/*--------------------------------------------------------------------------------------------*/
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* MIN()-function is used below. */
+#define kPollSEMA 0 /* To index the pollfd-array, reads nicer than just */
+#define kPollOUT 1 /* numbers. */
+#define kPollIN 2
+void Pa_SgiAudioProcess(void *v) /* This function is sproc-ed by PaHost_StartEngine() */
+{ /* as a separate thread. (Argument must be void*). */
+ short evtLoop=1; /* Reset by parent indirectly, or at local errors. */
+ PaError result;
+ struct pollfd PollFD[3]; /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
+ internalPortAudioStream *past = (internalPortAudioStream*)v; /* Copy void-ptr-argument.*/
+ PaHostSoundControl *pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (pahsc == NULL)
+ {
+ sPaHostError = paInternalError; /* The only way is to signal error to shared area?! */
+ goto skipALL; /* Sproc-ed threads MAY NOT RETURN paInternalError. */
+ }
+ past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
+ DBUG(("entering thread.\n"));
+ PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN); /* ALgetfd returns -1 on failures */
+ PollFD[kPollIN].events = POLLOUT; /* such as ALport not there. */
+ /* .events = POLLOUT ??? ok? */
+ PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
+ PollFD[kPollOUT].events = POLLOUT; /* .events = POLLOUT is OK. */
+ schedctl(NDPRI, NDPHIMIN); /* Sets non-degrading priority for this process. */
+ PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777); /* To communicate with parent. */
+ PollFD[kPollSEMA].events = POLLIN; /* .events = POLLIN is OK. */
+ uspsema(SendSema); /* Blocks until the next (first)signal is received???? */
+ do
+ {
+ /*---------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ----------*/
+ if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */
+ /* For input port, fill point is number of locations in the sample queue that must be */
+ /* filled in order to trigger a return from select(). (or poll()) */
+ /* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
+ if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputBuffer))
+ { /* Same amount as transferred per time. */
+ ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
+ sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
+ goto skipIO;
+ }
+ if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */
+ /* For output port, fill point is number of locations that must be free in order to */
+ /* wake up from select(). (or poll()) */
+ if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputBuffer))
+ {
+ ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
+ sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
+ goto skipIO;
+ } /* poll() with timeout=-1 makes it block until a requested */
+ poll(PollFD, 3, -1); /* event occurs or until call is interrupted. If fd-value in */
+ /* array <0, events is ignored and revents is set to 0. */
+ /*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
+ if (PollFD[kPollSEMA].revents & POLLIN)
+ {
+ if (past->past_StopNow || past->past_StopSoon)
+ evtLoop = 0;
+ uspsema(SendSema); /* Decrements count of previously allocated semaphore SendSema. If count */
+ /* is then negative, semaphore will logically block calling process until */
+ /* count is incremented due to an usvsema call made by another process. */
+ usvsema(RcvSema); /* Increments the count associated with RcvSema. If there are any */
+ /* processes queued waiting for the semaphore the first one is awakened. */
+ /* Here usvsema sends an acknowledgement to the "SendAudioCommand()". */
+ if (past->past_StopNow)
+ goto skipIO; /* Else go on for a little while... */
+ }
+ /*------------------------------------- FREE-EVENT FROM INPUT BUFFER: ----------------------------*/
+ if (PollFD[kPollIN].revents & POLLOUT) /* Don't need to check (pahsc->pahsc_NativeInputBuffer): */
+ { /* if buffer was not there, ALport not there, no events! */
+ if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_SamplesPerInputBuffer))
+ { /* Here again: number of samples instead of number of frames. */
+ ERR_RPT(("ALreadsamps() failed.\n"));
+ sPaHostError = paInternalError;
+ goto skipIO; /* (Using exit(-1) would be a bit rude.) */
+ }
+ }
+ /*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
+ if ((PollFD[kPollIN].revents & POLLOUT) || (PollFD[kPollOUT].revents & POLLOUT))
+ { /* To be sure we that really DID input-transfer or gonna DO output-transfer, and that it is */
+ /* not just "sema"- (i.e. user)-event, or some other system-event that awakened the poll(). */
+ Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
+ result = Pa_CallConvertInt16(past, /* user data and call user routine. */
+ pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_NativeOutputBuffer);
+ Pa_EndUsageCalculation(past);
+ if (result)
+ {
+ DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
+ goto skipIO; /* But it is apparently NOT an error! */
+ } /* Just letting the userCallBack stop us. */
+ }
+ /*------------------------------------- FILLED-EVENT FROM OUTPUT BUFFER: -------------------------*/
+ if (PollFD[kPollOUT].revents & POLLOUT) /* Don't need to check (pahsc->pahsc_NativeOutputBuffer) */
+ { /* because if filedescriptor not there, no event for it. */
+ if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
+ pahsc->pahsc_SamplesPerOutputBuffer))
+ {
+ ERR_RPT(("ALwritesamps() failed.\n"));
+ sPaHostError = paInternalError; /* Better use SEMAS also for messaging back to parent! */
+ goto skipIO;
+ }
+ }
+ /*------------------------------------------------------------------------------------------------*/
+ }
+ while (evtLoop); /* Set at init-time, only reset indirectly via semaphore, */
+skipIO: /* or in case of error, only within this loop. */
+ DBUG(("leaving thread.\n")); /* Don't we need to cleanup semaphore-stuff / thread itself? */
+skipALL: /* (make threadPID in the struct -1 again, for instance?) */
+ past->past_IsActive = 0;
+ pahsc->pahsc_threadPID == -1; /* Signal back, so StopEngine() for example won't call KILL().... hmmm doesn't work yet! */
+ /* SHIT: when this process ends, it also KILLS of the parent!!! because sigint = kill?? */
+ exit(0); /* Unix process. SIGCLD is sent to parent (if prctl wa called with 0 by parent). */
+} /* if prctl was called with 9 by parent, then when this process ends, it also */
+/* KILLS off the parent!!! */
+
+/*--------------------------------------------------------------------------------------*/
+PaError PaHost_OpenStream(internalPortAudioStream *past)
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
+ ALconfig sgiALconfig = NULL; /* IRIX-datatype. */
+ long pvbuf[8]; /* To get/set hardware configs. */
+ long sr, ALqsize;
+ DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
+ if (!past)
+ {
+ ERR_RPT(("Streampointer NULL!\n"));
+ result = paBadStreamPtr; goto done;
+ }
+ pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
+ if (pahsc == NULL)
+ {
+ ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
+ result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
+ }
+ memset(pahsc, 0, sizeof(PaHostSoundControl)); /* Hmmm, not too elegant, besides, the */
+ pahsc->pahsc_threadPID = -1; /* pahsc_threadPID must be inited to */
+ past->past_DeviceData = (void*)pahsc; /* -1 instead of 0! ?? */
+ /*--------------------------------------------------- Allocate native buffers: --------*/
+ pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumInputChannels; /* audio-thread. */
+ pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
+ if (past->past_NumInputChannels > 0) /* Assumes short = 16 bits! */
+ {
+ pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
+ if( pahsc->pahsc_NativeInputBuffer == NULL )
+ {
+ ERR_RPT(("(normal yet) Memory allocation failed.\n"));
+ result = paInsufficientMemory;
+ goto done;
+ }
+ }
+ pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
+ past->past_NumOutputChannels; /* audio-thread. */
+ pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
+ if (past->past_NumOutputChannels > 0) /* Assumes short = 16 bits! */
+ {
+ pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
+ if (pahsc->pahsc_NativeOutputBuffer == NULL)
+ {
+ ERR_RPT(("(normal yet) Memory allocation failed.\n"));
+ result = paInsufficientMemory; goto done;
+ }
+ }
+ /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
+ ALseterrorhandler(0); /* 0 = turn off the default error handler. */
+ pvbuf[0] = AL_INPUT_RATE;
+ pvbuf[2] = AL_INPUT_COUNT;
+ pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
+ pvbuf[6] = AL_OUTPUT_COUNT;
+ sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */
+ if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
+ { /* sponding native AL-number(s). */
+ padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
+ if (!padIN)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
+ result = paHostError; goto done;
+ }
+ if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
+ goto sgiError; /* the same AL-device, the AL-library might */
+ if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
+ { /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
+ if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
+ another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
+ result = paHostError; goto done;
+ }
+ pvbuf[1] = sr; /* Then set input-rate. */
+ if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
+ goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
+ }
+ }
+ if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels<=0! */
+ { /* We use padOUT/IN later on, or at least 1 of both. */
+ padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
+ if (!padOUT)
+ {
+ ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
+ result = paHostError; goto done;
+ }
+ if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
+ goto sgiError;
+ if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr))
+ { /* Output needed and rate different from current harware-rate. */
+ if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
+ {
+ ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
+ another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
+ result = paHostError; goto done; /* Will free again the inputbuffer */
+ } /* that was just created above. */
+ pvbuf[5] = sr; /* Then set output-rate. */
+ if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
+ goto sgiError;
+ }
+ }
+ /*------------------------------------------ Construct an audio-port-configuration ----------*/
+ sgiALconfig = ALnewconfig(); /* Change the SGI-AL-default-settings. */
+ if (sgiALconfig == (ALconfig)0) /* sgiALconfig is released here after use! */
+ goto sgiError; /* See that sgiALconfig is NOT released! */
+ if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
+ goto sgiError;
+ if (ALsetwidth (sgiALconfig, AL_SAMPLE_16)) /* Only meaningful when sample format for */
+ goto sgiError; /* config is set to two's complement format. */
+ /************************ Future versions might (dynamically) switch to 32-bit floats? *******
+ if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
+ goto sgiError;
+ if (ALsetfloatmax (sgiALconfig, 1.0)) Only meaningful when sample format for config
+ goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
+ /*---------- ?? --------------------*/
+ /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+ minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
+ past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
+ minNumBuffers : past->past_NumUserBuffers;
+ /*------------------------------------------------ Set internal AL queuesize (in samples) ----*/
+ if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer)
+ ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer;
+ else /* Take the largest of the two amounts. */
+ ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer;
+ ALqsize *= 4; /* 4 times as large as amount per transfer! */
+ if (ALsetqueuesize(sgiALconfig, ALqsize)) /* Or should we use past_NumUserBuffers here? */
+ goto sgiError; /* Using 2 times may give glitches... */
+ /* Have to work on ALsetqueuesize() above, fillpoints in the thread are ok now. */
+
+ /* Do ALsetchannels() later, apart per input and/or output. */
+ /*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/
+ if (past->past_OutputDeviceID == past->past_InputDeviceID) /* Who SETS these devive-numbers? */
+ {
+ if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
+ {
+ DBUG(("PaHost_OpenStream: opening both input and output channels.\n"));
+ /*------------------------- open output port: ----------------------------------*/
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+ if (pahsc->pahsc_ALportOUT == (ALport)0)
+ goto sgiError;
+ /*------------------------- open input port: -----------------------------------*/
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+ if (pahsc->pahsc_ALportIN == (ALport)0)
+ goto sgiError; /* For some reason the "patest_wire.c"-test crashes! */
+ } /* Probably due to too small buffersizes?.... */
+ else
+ {
+ ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
+ result = paHostError;
+ goto done;
+ }
+ }
+ else /* (OutputDeviceID != InputDeviceID) */
+ {
+ if (past->past_NumOutputChannels > 0) /* WRITE-ONLY: */
+ {
+ /*------------------------- open output port: ----------------------------------*/
+ DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n",
+ past->past_NumOutputChannels, padOUT->pad_DeviceName));
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
+ if (pahsc->pahsc_ALportOUT == (ALport)0)
+ goto sgiError;
+ }
+ if (past->past_NumInputChannels > 0) /* READ-ONLY: */
+ {
+ /*------------------------- open input port: -----------------------------------*/
+ DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n",
+ past->past_NumInputChannels, padIN->pad_DeviceName));
+ if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
+ goto sgiError; /* Returns 0 on success, -1 on failure. */
+ pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
+ if (pahsc->pahsc_ALportIN == (ALport)0)
+ goto sgiError;
+ }
+ }
+ DBUG(("PaHost_OpenStream() succeeded.\n"));
+ goto done; /* (no errors occured) */
+sgiError:
+ result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+done:
+ if (sgiALconfig)
+ ALfreeconfig(sgiALconfig); /* We don't need that struct anymore. */
+ if (result != paNoError)
+ PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
+ return result;
+}
+
+/*-----------------------------------------------------*/
+PaError PaHost_StartOutput(internalPortAudioStream *past)
+{
+ return paNoError; /* Hmm, not implemented yet? */
+}
+PaError PaHost_StartInput(internalPortAudioStream *past)
+{
+ return paNoError;
+}
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StartEngine(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ usptr_t *arena;
+ if (!past) /* Test argument. */
+ {
+ ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
+ return paBadStreamPtr;
+ }
+ pahsc = (PaHostSoundControl*)past->past_DeviceData;
+ if (!pahsc)
+ {
+ ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
+ return paHostError;
+ }
+ past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
+ past->past_StopNow = 0; /* Why don't we check pahsc for NULL? */
+ past->past_IsActive = 1;
+
+ /* Although the pthread_create() function, as well as <pthread.h>, may be */
+ /* available in IRIX, use sproc() on SGI to create audio-background-thread. */
+ /* (Linux/oss uses pthread_create() instead of __clone() because: */
+ /* - pthread_create also works for other UNIX systems like Solaris, */
+ /* - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().) */
+
+ usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* (From SGI-AL-examples, file */
+ arena = usinit(tmpnam(0)); /* motifexample.c, function */
+ SendSema = usnewpollsema(arena, 0); /* InitializeAudioProcess().) */
+ RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process
+ will be permitted through a semaphore at a time. Values > 1
+ imply that up to val resources may be simultaneously used, but requests
+ for more than val resources cause the calling process to block until a
+ resource comes free (by a process holding a resource performing a
+ usvsema(). IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
+ prctl(PR_SETEXITSIG, 0);/* No not (void*)9, but 0, which doesn't kill the parent! */
+ /* PR_SETEXITSIG controls whether all members of a share group will be
+ signaled if any one of them leaves the share group
+ (either via exit() or exec()). If second
+ argument, interpreted as an int is 0, then normal IRIX
+ process termination rules apply, namely that the parent
+ is sent a SIGCLD upon death of child, but no indication
+ of death of parent is given. If the second argument is
+ a valid signal number then if any member
+ of a share group leaves the share group, a signal is
+ sent to ALL surviving members of the share group. */
+ /* SPAWN AUDIO-CHILD: */
+ pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
+ PR_SALL, /* new process, or -1. */
+ (void*)past); /* Pass past as optional */
+ if (pahsc->pahsc_threadPID == -1) /* third void-ptr-arg. */
+ {
+ ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
+ sPaHostError = oserror(); /* Pass native error-number to shared area. */
+ return paHostError; /* But return the generic error-number. */
+ }
+ return paNoError; /* Hmmm, errno may come from other threads in same group! */
+} /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
+
+/*------------------------------------------------------------------------------*/
+PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
+{
+ int hres;
+ long timeOut;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl*)past->past_DeviceData;
+
+ if (pahsc == NULL)
+ return result; /* paNoError */
+ past->past_StopSoon = 1; /* Tell background thread to stop generating */
+ if (abort) /* more and to let current data play out. If */
+ past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
+
+ /*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
+ usvsema(SendSema); /* Increments count associated with SendSema. */
+ /* Wait for the response. */
+ uspsema(RcvSema); /* Decrements count of previously allocated */
+ /* semaphore specified by RcvSema. */
+ /* Must be sure sub-process is really there,... otherwise above semas block?! */
+ if (pahsc->pahsc_threadPID != -1) /* Did we really init it to -1 somewhere? */
+ {
+ /* How to JOIN a "sproc"-thread to recover memory resources????? */
+ /* hres = pthread_join( pahsc->pahsc_ThreadPID, NULL ); */
+ DBUG(("PaHost_StopEngine() is about to KILL(SIGKILL) audio-thread.\n"));
+ if (kill(pahsc->pahsc_threadPID, SIGKILL)) /* Or SIGTERM or SIGQUIT(core) */
+ { /* Returns -1 in case of error. */
+ result = paHostError;
+ sPaHostError = oserror(); /* Hmmm, other threads may also write here! */
+ ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
+ }
+ else
+ pahsc->pahsc_threadPID = -1; /* Notify that we've killed this thread. */
+ }
+ past->past_IsActive = 0; /* Even when kill() failed and pahsc_threadPID still there??? */
+ return result;
+}
+
+/*---------------------------------------------------------------*/
+PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
+{
+ return paNoError; /* Not implemented yet? */
+}
+PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream(internalPortAudioStream *past)
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+
+ if (past == NULL)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
+ return result; /* This test prevents from freeing NULL-pointers. */
+
+ if (pahsc->pahsc_ALportIN)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportIN))
+ result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
+ else /* But go on anyway... to release other stuff... */
+ pahsc->pahsc_ALportIN = (ALport)0;
+ }
+ if (pahsc->pahsc_ALportOUT)
+ {
+ if (ALcloseport(pahsc->pahsc_ALportOUT))
+ result = translateSGIerror();
+ else
+ pahsc->pahsc_ALportOUT = (ALport)0;
+ }
+ if (pahsc->pahsc_NativeInputBuffer)
+ {
+ free(pahsc->pahsc_NativeInputBuffer);
+ pahsc->pahsc_NativeInputBuffer = NULL;
+ }
+ if (pahsc->pahsc_NativeOutputBuffer)
+ {
+ free(pahsc->pahsc_NativeOutputBuffer);
+ pahsc->pahsc_NativeOutputBuffer = NULL;
+ }
+ PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl)); /* Not just free(pahsc) because */
+ past->past_DeviceData = NULL; /* PaHost_OpenStream(); allocated FAST (thus mpinned?) MEM. */
+ return result;
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+** set PA_MIN_LATENCY_MSEC=200
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be
+** determined based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+ return 2;
+}
+/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
+/* Do we HAVE to do it this way under IRIX???.... */
+/*--------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------*/
+PaError PaHost_Term(void) /* Frees all of the linked devices. */
+{ /* Called by Pa_Terminate() from pa_lib.c. */
+ internalPortAudioDevice *pad = sDeviceList,
+ *nxt;
+ while (pad)
+ {
+ DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
+ nxt = pad->pad_Next;
+ PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
+ pad = nxt; /* PaHost_Init allocated this FAST MEM.*/
+ }
+ sDeviceList = (internalPortAudioDevice*)NULL;
+ return 0; /* Got rid of sNumDevices=0; */
+}
+
+/***********************************************************************/
+void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select(0, NULL, NULL, NULL, &timeout);
+#else
+ long usecs = msec * 1000;
+ usleep( usecs );
+#endif
+}
+
+/*---------------------------------------------------------------------------------------*/
+/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
+/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
+/* a call to PaHost_FreeFastMemory(). */
+void *PaHost_AllocateFastMemory(long numBytes)
+{
+ void *addr = malloc(numBytes); /* mpin() reads into memory all pages over the given */
+ if (addr) /* range and locks the pages into memory. A counter */
+ { /* is incremented each time the page is locked. The */
+ if (mpin(addr, numBytes)) /* superuser can lock as many pages as it wishes, */
+ { /* others are limited to the configurable PLOCK_MA. */
+ ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
+#if 1
+ free(addr); /* You MAY cut out these 2 lines to be less strict, */
+ addr = NULL; /* you then only get the warning but PA goes on... */
+#endif /* Only problem then may be corresponding munpin() */
+ } /* call at PaHost_FreeFastMemory(), below. */
+ memset(addr, 0, numBytes); /* Locks established with mlock are not inherited by */
+ } /* a child process after a fork. Furthermore, IRIX- */
+ return addr; /* man-pages warn against mixing both mpin and mlock */
+} /* in 1 piece of code, so stick to mpin()/munpin() ! */
+
+
+/*---------------------------------------------------------------------------------------*/
+/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
+/* call to PaHost_AllocateFastMemory(). */
+void PaHost_FreeFastMemory(void *addr, long numBytes)
+{
+ if (addr)
+ {
+ if (munpin(addr, numBytes)) /* Will munpin() fail when it was never mpinned? */
+ ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
+ free(addr); /* But go on, try to release it, just warn... */
+ }
+}
+
+/*----------------------------------------------------------*/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if (past == NULL)
+ return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if (pahsc == NULL)
+ return paInternalError;
+ return (PaError)(past->past_IsActive != 0);
+}
+
+/*-------------------------------------------------------------------*/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ /* FIXME - return actual frames played, not frames generated.
+ ** Need to query the output device somehow.
+ */
+ return past->past_FrameCount;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_dual.c
+ * Try to open TWO streams on separate cards.
+ * Play a sine sweep using the Portable Audio api for several seconds.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define DEV_ID_1 (13)
+#define DEV_ID_2 (15)
+#define NUM_SECONDS (8)
+#define SLEEP_DUR (800)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#if 0
+#define MIN_LATENCY_MSEC (200)
+#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS (0)
+#endif
+#define MIN_FREQ (100.0f)
+#define MAX_FREQ (4000.0f)
+#define FREQ_SCALAR (1.00002f)
+#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (400)
+typedef struct
+{
+ float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+ float phase_increment;
+ float left_phase;
+ float right_phase;
+}
+paTestData;
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase );
+float LookupSine( paTestData *data, float phase )
+{
+ float fIndex = phase*TABLE_SIZE;
+ int index = (int) fIndex;
+ float fract = fIndex - index;
+ float lo = data->sine[index];
+ float hi = data->sine[index+1];
+ float val = lo + fract*(hi-lo);
+ return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = LookupSine(data, data->left_phase); /* left */
+ *out++ = LookupSine(data, data->right_phase); /* right */
+ data->left_phase += data->phase_increment;
+ if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+ data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+ if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+ /* sweep frequency then start over. */
+ data->phase_increment *= FREQ_SCALAR;
+ if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+ }
+ return 0;
+}
+
+PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID,
+ paTestData *data );
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream1, *stream2;
+ PaError err;
+ paTestData DATA1, DATA2;
+ printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = TestStart( &stream1, DEV_ID_1, &DATA1 );
+ if( err != paNoError ) goto error;
+ err = TestStart( &stream2, DEV_ID_2, &DATA2 );
+ if( err != paNoError ) goto error;
+ printf("Hit ENTER\n");
+ getchar();
+ err = Pa_StopStream( stream1 );
+ if( err != paNoError ) goto error;
+ err = Pa_StopStream( stream2 );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
+PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data )
+{
+ PortAudioStream *stream;
+ PaError err;
+ int i;
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data->sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data->sine[TABLE_SIZE] = data->sine[0]; // set guard point
+ data->left_phase = data->right_phase = 0.0;
+ data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+ printf("PortAudio Test: output device = %d\n", devID );
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ devID,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ *streamPtr = stream;
+ return 0;
+error:
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_multi_in.c
+ * Pass output from each of multiple channels
+ * to a stereo output using the Portable Audio api.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "portaudio.h"
+//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec")
+#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
+//#define OUTPUT_DEVICE (18)
+#define SAMPLE_RATE (22050)
+#define FRAMES_PER_BUFFER (256)
+#define MIN_LATENCY_MSEC (400)
+#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+typedef struct
+{
+ int liveChannel;
+ int numChannels;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ float *in = (float*)inputBuffer;
+ int i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( in == NULL ) return 0;
+ for( i=0; i<(int)framesPerBuffer; i++ )
+ {
+ /* Copy one channel of input to output. */
+ *out++ = in[data->liveChannel];
+ *out++ = in[data->liveChannel];
+ in += data->numChannels;
+ }
+ return 0;
+}
+/*******************************************************************/
+int PaFindDeviceByName( const char *name )
+{
+ int i;
+ int numDevices;
+ const PaDeviceInfo *pdi;
+ int len = strlen( name );
+ PaDeviceID result = paNoDevice;
+ numDevices = Pa_CountDevices();
+ for( i=0; i<numDevices; i++ )
+ {
+ pdi = Pa_GetDeviceInfo( i );
+ if( strncmp( name, pdi->name, len ) == 0 )
+ {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ PaDeviceID inputDevice;
+ const PaDeviceInfo *pdi;
+ printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS );
+ data.liveChannel = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+#ifdef INPUT_DEVICE_NAME
+ printf("Try to use device: %s\n", INPUT_DEVICE_NAME );
+ inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME);
+ if( inputDevice == paNoDevice )
+ {
+ printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME );
+ inputDevice = Pa_GetDefaultInputDeviceID();
+ }
+#else
+ printf("Using default input device.\n");
+ inputDevice = Pa_GetDefaultInputDeviceID();
+#endif
+ pdi = Pa_GetDeviceInfo( inputDevice );
+ if( pdi == NULL )
+ {
+ printf("Could not get device info!\n");
+ goto error;
+ }
+ data.numChannels = pdi->maxInputChannels;
+ printf("Input Device name is %s\n", pdi->name );
+ printf("Input Device has %d channels.\n", pdi->maxInputChannels);
+ err = Pa_OpenStream(
+ &stream,
+ inputDevice,
+ pdi->maxInputChannels,
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ OUTPUT_DEVICE,
+ 2,
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ data.liveChannel = 0;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ for( i=0; i<data.numChannels; i++ )
+ {
+ data.liveChannel = i;
+ printf("Channel %d being sent to output. Hit ENTER for next channel.", i );
+ fflush(stdout);
+ getchar();
+ }
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+
+ err = Pa_CloseStream( stream );
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_multi.c
+ * Play a sine wave on each of multiple channels,
+ * using the Portable Audio api.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_CHANNELS (8)
+// #define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
+#define OUTPUT_DEVICE (18)
+#define NUM_SECONDS (NUM_CHANNELS*4)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_CHANNEL (SAMPLE_RATE/2)
+#define FRAMES_PER_BUFFER (256)
+#define MIN_LATENCY_MSEC (400)
+#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (800)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int phase;
+ int liveChannel;
+ int count;
+ unsigned int sampsToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ int i, j;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( data->sampsToGo < framesPerBuffer )
+ {
+ finished = 1;
+ }
+ else
+ {
+ for( i=0; i<(int)framesPerBuffer; i++ )
+ {
+ for( j=0; j<NUM_CHANNELS; j++ )
+ {
+ /* Output sine wave only on live channel. */
+ *out++ = (j==data->liveChannel) ? data->sine[data->phase] : 0.0f;
+ /* Play each channel at a higher frequency. */
+ data->phase += 1 + data->liveChannel;
+ if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
+ }
+ /* Switch channels every so often. */
+ if( --data->count <= 0 )
+ {
+ data->count = FRAMES_PER_CHANNEL;
+ data->liveChannel += 1;
+ if( data->liveChannel >= NUM_CHANNELS ) data->liveChannel = 0;
+ }
+ }
+ data->sampsToGo -= framesPerBuffer;
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+ printf("PortAudio Test: output sine wave. %d buffers\n", NUM_BUFFERS );
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.phase = 0;
+ data.count = FRAMES_PER_CHANNEL;
+ data.liveChannel = 0;
+ data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice, /* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ OUTPUT_DEVICE,
+ NUM_CHANNELS,
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Is callback being called?\n");
+ for( i=0; i<NUM_SECONDS; i++ )
+ {
+ printf("data.sampsToGo = %d\n", data.sampsToGo );
+ Pa_Sleep( 1000 );
+ }
+ /* Stop sound until ENTER hit. */
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_CloseStream( stream );
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_record.c
+ * Record input into an array.
+ * Save array to a file.
+ * Based on patest_record.c but with various ugly debug hacks thrown in.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include "portaudio.h"
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (10)
+#define SLEEP_DUR_MSEC (200)
+#define REC_BUF_FRAMES (1<<10)
+#define NUM_REC_BUFS (0)
+#if 0
+#define PA_SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define PA_SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+typedef struct
+{
+ long frameIndex; /* Index into sample array. */
+ long maxFrameIndex;
+ long samplesPerFrame;
+ SAMPLE *recordedSamples;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int recordCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ SAMPLE *rptr = (SAMPLE*)inputBuffer;
+ SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+ long framesToCalc;
+ unsigned long i;
+ int finished;
+ unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
+
+ (void) outputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ framesToCalc = framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ finished = 0;
+ }
+ if( inputBuffer == NULL )
+ {
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *wptr++ = 0; /* left */
+ *wptr++ = 0; /* right */
+ }
+ }
+ else
+ {
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ }
+ data->frameIndex += framesToCalc;
+ return finished;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int playCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+ SAMPLE *wptr = (SAMPLE*)outputBuffer;
+ unsigned long i;
+ int finished;
+ unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+ if( outputBuffer == NULL ) return 0;
+ (void) inputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ /* final buffer... */
+ for( i=0; i<framesLeft; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = 0; /* left */
+ *wptr++ = 0; /* right */
+ }
+ data->frameIndex += framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ data->frameIndex += framesPerBuffer;
+ finished = 0;
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ long totalFrames;
+ long numSamples;
+ long numBytes;
+ long i;
+ printf("patest_record.c\n"); fflush(stdout);
+
+ data.frameIndex = 0;
+ data.samplesPerFrame = 2;
+ data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
+
+ printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
+ numSamples = totalFrames * data.samplesPerFrame;
+ numBytes = numSamples * sizeof(SAMPLE);
+ data.recordedSamples = (SAMPLE *) malloc( numBytes );
+ if( data.recordedSamples == NULL )
+ {
+ printf("Could not allocate record array.\n");
+ exit(1);
+ }
+ for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ /* Record some audio. */
+ err = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(),
+ data.samplesPerFrame, /* stereo input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ paNoDevice,
+ 0,
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ REC_BUF_FRAMES, /* frames per buffer */
+ NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ recordCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Now recording!\n"); fflush(stdout);
+ for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+ {
+ if( Pa_StreamActive( stream ) <= 0)
+ {
+ printf("Stream inactive!\n");
+ break;
+ }
+ if( data.maxFrameIndex <= data.frameIndex )
+ {
+ printf("Buffer recording complete.\n");
+ break;
+ }
+ Pa_Sleep(100);
+ printf("index = %d\n", data.frameIndex );
+ }
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ /* Playback recorded data. */
+ data.frameIndex = 0;
+ printf("Begin playback.\n"); fflush(stdout);
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* NO input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(),
+ data.samplesPerFrame, /* stereo output */
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ 1024, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ playCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for playback to finish.\n"); fflush(stdout);
+ for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
+ {
+ Pa_Sleep(100);
+ printf("index = %d\n", data.frameIndex );
+ }
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Done.\n"); fflush(stdout);
+ {
+ SAMPLE max = 0;
+ SAMPLE posVal;
+ int i;
+ for( i=0; i<numSamples; i++ )
+ {
+ posVal = data.recordedSamples[i];
+ if( posVal < 0 ) posVal = -posVal;
+ if( posVal > max ) max = posVal;
+ }
+ printf("Largest recorded sample = %d\n", max );
+ }
+ /* Write recorded data to a file. */
+#if 0
+ {
+ FILE *fid;
+ fid = fopen("recorded.raw", "wb");
+ if( fid == NULL )
+ {
+ printf("Could not open file.");
+ }
+ else
+ {
+ fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
+ fclose( fid );
+ printf("Wrote data to 'recorded.raw'\n");
+ }
+ }
+#endif
+ free( data.recordedSamples );
+ Pa_Terminate();
+ return 0;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ if( err == paHostError )
+ {
+ fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
+ }
+ return -1;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_sine.c
+ * Play a sine sweep using the Portable Audio api for several seconds.
+ * Hacked test for debugging PA.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
+//#define OUTPUT_DEVICE (11)
+#define NUM_SECONDS (8)
+#define SLEEP_DUR (800)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#if 1
+#define MIN_LATENCY_MSEC (200)
+#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS (0)
+#endif
+#define MIN_FREQ (100.0f)
+#define MAX_FREQ (4000.0f)
+#define FREQ_SCALAR (1.00002f)
+#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (400)
+typedef struct
+{
+ float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+ float phase_increment;
+ float left_phase;
+ float right_phase;
+ unsigned int framesToGo;
+}
+paTestData;
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase );
+float LookupSine( paTestData *data, float phase )
+{
+ float fIndex = phase*TABLE_SIZE;
+ int index = (int) fIndex;
+ float fract = fIndex - index;
+ float lo = data->sine[index];
+ float hi = data->sine[index+1];
+ float val = lo + fract*(hi-lo);
+ return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ int framesToCalc;
+ int i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( data->framesToGo < framesPerBuffer )
+ {
+ framesToCalc = data->framesToGo;
+ data->framesToGo = 0;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ data->framesToGo -= framesPerBuffer;
+ }
+
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *out++ = LookupSine(data, data->left_phase); /* left */
+ *out++ = LookupSine(data, data->right_phase); /* right */
+ data->left_phase += data->phase_increment;
+ if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+ data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+ if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+ /* sweep frequency then start over. */
+ data->phase_increment *= FREQ_SCALAR;
+ if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
+ }
+ /* zero remainder of final buffer */
+ for( ; i<(int)framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+ printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
+ data.left_phase = data.right_phase = 0.0;
+ data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
+ data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ printf("totalSamps = %d\n", totalSamps );
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ OUTPUT_DEVICE,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Is callback being called?\n");
+ for( i=0; i<((NUM_SECONDS+1)*1000); i+=SLEEP_DUR )
+ {
+ printf("data.framesToGo = %d\n", data.framesToGo ); fflush(stdout);
+ Pa_Sleep( SLEEP_DUR );
+ }
+ /* Stop sound until ENTER hit. */
+ printf("Call Pa_StopStream()\n");
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * debug_sine_wait.c
+ * Play a sine wave using the Portable Audio api until we hit ENTER.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS (20)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (64)
+#define NUM_BUFFERS (16)
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Hit ENTER to stop.\n");
+ gets(stdin);
+
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ patest1.c
+ Ring modulate the audio input with a 441hz sine wave for 20 seconds
+ using the Portable Audio api
+ Author: Ross Bencina <rossb@audiomulch.com>
+ Modifications:
+ April 5th, 2001 - PLB - Check for NULL inputBuffer.
+*/
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+typedef struct
+{
+ float sine[100];
+ int phase;
+ int sampsToGo;
+}
+patest1data;
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long bufferFrames,
+ PaTimestamp outTime, void *userData )
+{
+ patest1data *data = (patest1data*)userData;
+ float *in = (float*)inputBuffer;
+ float *out = (float*)outputBuffer;
+ int framesToCalc = bufferFrames;
+ unsigned long i;
+ int finished = 0;
+ if(inputBuffer == NULL) return 0;
+ if( data->sampsToGo < bufferFrames )
+ {
+ finished = 1;
+ }
+ for( i=0; i<bufferFrames; i++ )
+ {
+ *out++ = *in++;
+ *out++ = *in++;
+ if( data->phase >= 100 )
+ data->phase = 0;
+ }
+ data->sampsToGo -= bufferFrames;
+ /* zero remainder of final buffer if not already done */
+ for( ; i<bufferFrames; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ return finished;
+}
+int main(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+ PaStream *stream;
+ PaError err;
+ patest1data data;
+ int i;
+ int inputDevice = Pa_GetDefaultInputDeviceID();
+ int outputDevice = Pa_GetDefaultOutputDeviceID();
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<100; i++ )
+ data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+ data.phase = 0;
+ data.sampsToGo = 44100 * 4; // 20 seconds
+ /* initialise portaudio subsytem */
+ Pa_Initialize();
+ err = Pa_OpenStream(
+ &stream,
+ inputDevice,
+ 2, /* stereo input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ outputDevice,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ 44100.,
+ // 22050, /* half second buffers */
+ // 4, /* four buffers */
+ 512, /* half second buffers */
+ 0, /* four buffers */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patest1Callback,
+ &data );
+ if( err == paNoError )
+ {
+ err = Pa_StartStream( stream );
+ // printf( "Press any key to end.\n" );
+ // getc( stdin ); //wait for input before exiting
+ // Pa_AbortStream( stream );
+
+ printf( "Waiting for stream to complete...\n" );
+
+ while( Pa_StreamActive( stream ) )
+ Pa_Sleep(1000); /* sleep until playback has finished */
+
+ err = Pa_CloseStream( stream );
+ }
+ else
+ {
+ fprintf( stderr, "An error occured while opening the portaudio stream\n" );
+ if( err == paHostError )
+ fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
+ else
+ fprintf( stderr, "Error number: %d\n", err );
+ }
+ Pa_Terminate();
+ printf( "bye\n" );
+
+ return 0;
+}
--- /dev/null
+/*
+ * $Id$
+ * pa_devs.c
+ * List available devices.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ int numDevices;
+ const PaDeviceInfo *pdi;
+ PaError err;
+ Pa_Initialize();
+ numDevices = Pa_CountDevices();
+ if( numDevices < 0 )
+ {
+ printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
+ err = numDevices;
+ goto error;
+ }
+ printf("Number of devices = %d\n", numDevices );
+ for( i=0; i<numDevices; i++ )
+ {
+ pdi = Pa_GetDeviceInfo( i );
+ printf("---------------------------------------------- #%d", i );
+ if( i == Pa_GetDefaultInputDeviceID() ) printf(" DefaultInput");
+ if( i == Pa_GetDefaultOutputDeviceID() ) printf(" DefaultOutput");
+ printf("\nName = %s\n", pdi->name );
+ printf("Max Inputs = %d", pdi->maxInputChannels );
+ printf(", Max Outputs = %d\n", pdi->maxOutputChannels );
+ if( pdi->numSampleRates == -1 )
+ {
+ printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
+ }
+ else
+ {
+ printf("Sample Rates =");
+ for( j=0; j<pdi->numSampleRates; j++ )
+ {
+ printf(" %8.2f,", pdi->sampleRates[j] );
+ }
+ printf("\n");
+ }
+ printf("Native Sample Formats = ");
+ if( pdi->nativeSampleFormats & paInt8 ) printf("paInt8, ");
+ if( pdi->nativeSampleFormats & paUInt8 ) printf("paUInt8, ");
+ if( pdi->nativeSampleFormats & paInt16 ) printf("paInt16, ");
+ if( pdi->nativeSampleFormats & paInt32 ) printf("paInt32, ");
+ if( pdi->nativeSampleFormats & paFloat32 ) printf("paFloat32, ");
+ if( pdi->nativeSampleFormats & paInt24 ) printf("paInt24, ");
+ if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, ");
+ printf("\n");
+ }
+ Pa_Terminate();
+
+ printf("----------------------------------------------\n");
+ return 0;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * pa_fuzz.c
+ * Distort input like a fuzz boz.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (44100)
+#define PA_SAMPLE_TYPE paFloat32
+#define FRAMES_PER_BUFFER (64)
+
+typedef float SAMPLE;
+
+float CubicAmplifier( float input );
+static int fuzzCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+/* Non-linear amplifier with soft distortion curve. */
+float CubicAmplifier( float input )
+{
+ float output, temp;
+ if( input < 0.0 )
+ {
+ temp = input + 1.0f;
+ output = (temp * temp * temp) - 1.0f;
+ }
+ else
+ {
+ temp = input - 1.0f;
+ output = (temp * temp * temp) + 1.0f;
+ }
+
+ return output;
+}
+#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x))))
+
+static int gNumNoInputs = 0;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int fuzzCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ SAMPLE *out = (SAMPLE*)outputBuffer;
+ SAMPLE *in = (SAMPLE*)inputBuffer;
+ unsigned int i;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) userData;
+
+ if( inputBuffer == NULL )
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left - silent */
+ *out++ = 0; /* right - silent */
+ }
+ gNumNoInputs += 1;
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = FUZZ(*in++); /* left - distorted */
+ *out++ = *in++; /* right - clean */
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ err = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(), /* default output device */
+ 2, /* stereo input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ 0, // paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ fuzzCallback,
+ NULL );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+
+ printf("Hit ENTER to stop program.\n");
+ getchar();
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+
+ printf("Finished. gNumNoInputs = %d\n", gNumNoInputs );
+ Pa_Terminate();
+ return 0;
+
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+/*
+ * $Id$
+ * paminlat.c
+ * Experiment with different numbers of buffers to determine the
+ * minimum latency for a computer.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define DEFAULT_BUFFER_SIZE (128)
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+/* Very simple synthesis routine to generate two sine waves. */
+static int paminlatCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned int i;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return 0;
+}
+void main( int argc, char **argv );
+void main( int argc, char **argv )
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int go;
+ int numBuffers = 0;
+ int minBuffers = 0;
+ int framesPerBuffer;
+ double sampleRate = 44100.0;
+ char str[256];
+ printf("paminlat - Determine minimum latency for your computer.\n");
+ printf(" usage: paminlat {framesPerBuffer}\n");
+ printf(" for example: paminlat 256\n");
+ printf("Adjust your stereo until you hear a smooth tone in each speaker.\n");
+ printf("Then try to find the smallest number of buffers that still sounds smooth.\n");
+ printf("Note that the sound will stop momentarily when you change the number of buffers.\n");
+ /* Get bufferSize from command line. */
+ framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE;
+ printf("Frames per buffer = %d\n", framesPerBuffer );
+ /* Initialise sinusoidal wavetable. */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ /* Ask PortAudio for the recommended minimum number of buffers. */
+ numBuffers = minBuffers = Pa_GetMinNumBuffers( framesPerBuffer, sampleRate );
+ printf("NumBuffers set to %d based on a call to Pa_GetMinNumBuffers()\n", numBuffers );
+ /* Try different numBuffers in a loop. */
+ go = 1;
+ while( go )
+ {
+
+ printf("Latency = framesPerBuffer * numBuffers = %d * %d = %d frames = %d msecs.\n",
+ framesPerBuffer, numBuffers, framesPerBuffer*numBuffers,
+ (int)((1000 * framesPerBuffer * numBuffers) / sampleRate) );
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ sampleRate,
+ framesPerBuffer,/* 46 msec buffers */
+ numBuffers, /* number of buffers */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ paminlatCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ if( stream == NULL ) goto error;
+ /* Start audio. */
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ /* Ask user for a new number of buffers. */
+ printf("\nMove windows around to see if the sound glitches.\n");
+ printf("NumBuffers currently %d, enter new number, or 'q' to quit: ", numBuffers );
+ gets( str );
+ if( str[0] == 'q' ) go = 0;
+ else
+ {
+ numBuffers = atol( str );
+ if( numBuffers < minBuffers )
+ {
+ printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers );
+ numBuffers = minBuffers;
+ }
+ }
+ /* Stop sound until ENTER hit. */
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ }
+ printf("A good setting for latency would be somewhat higher than\n");
+ printf("the minimum latency that worked.\n");
+ printf("PortAudio: Test finished.\n");
+ Pa_Terminate();
+ return;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+}
--- /dev/null
+/*
+ * $Id$
+ * paqa_devs.c
+ * Self Testing Quality Assurance app for PortAudio
+ * Try to open each device and run through all the
+ * possible configurations.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#include "pa_trace.h"
+/****************************************** Definitions ***********/
+#define MODE_INPUT (0)
+#define MODE_OUTPUT (1)
+typedef struct PaQaData
+{
+ unsigned long framesLeft;
+ int numChannels;
+ int bytesPerSample;
+ int mode;
+ short sawPhase;
+ PaSampleFormat format;
+}
+PaQaData;
+/****************************************** Prototypes ***********/
+static void TestDevices( int mode );
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+ int numChannels );
+static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
+ int numChannels, PaSampleFormat format );
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+/****************************************** Globals ***********/
+static int gNumPassed = 0;
+static int gNumFailed = 0;
+/****************************************** Macros ***********/
+/* Print ERROR if it fails. Tally success or failure. */
+/* Odd do-while wrapper seems to be needed for some compilers. */
+#define EXPECT(_exp) \
+ do \
+ { \
+ if ((_exp)) {\
+ /* printf("SUCCESS for %s\n", #_exp ); */ \
+ gNumPassed++; \
+ } \
+ else { \
+ printf("ERROR - 0x%x - %s for %s\n", result, \
+ ((result == 0) ? "-" : Pa_GetErrorText(result)), \
+ #_exp ); \
+ gNumFailed++; \
+ goto error; \
+ } \
+ } while(0)
+/*******************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ unsigned long i;
+ short phase;
+ PaQaData *data = (PaQaData *) userData;
+ (void) inputBuffer;
+ (void) outTime;
+
+ /* Play simle sawtooth wave. */
+ if( data->mode == MODE_OUTPUT )
+ {
+ phase = data->sawPhase;
+ switch( data->format )
+ {
+ case paFloat32:
+ {
+ float *out = (float *) outputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ phase += 0x123;
+ *out++ = (float) (phase * (1.0 / 32768.0));
+ if( data->numChannels == 2 )
+ {
+ *out++ = (float) (phase * (1.0 / 32768.0));
+ }
+ }
+ }
+ break;
+
+ case paInt32:
+ {
+ int *out = (int *) outputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ phase += 0x123;
+ *out++ = ((int) phase ) << 16;
+ if( data->numChannels == 2 )
+ {
+ *out++ = ((int) phase ) << 16;
+ }
+ }
+ }
+ break;
+ case paInt16:
+ {
+ short *out = (short *) outputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ phase += 0x123;
+ *out++ = phase;
+ if( data->numChannels == 2 )
+ {
+ *out++ = phase;
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ unsigned char *out = (unsigned char *) outputBuffer;
+ unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
+ for( i=0; i<numBytes; i++ )
+ {
+ *out++ = 0;
+ }
+ }
+ break;
+ }
+ data->sawPhase = phase;
+ }
+ /* Are we through yet? */
+ if( data->framesLeft > framesPerBuffer )
+ {
+ AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
+ data->framesLeft -= framesPerBuffer;
+ return 0;
+ }
+ else
+ {
+ AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
+ data->framesLeft = 0;
+ return 1;
+ }
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PaError result;
+ EXPECT( ((result=Pa_Initialize()) == 0) );
+ printf("Test OUTPUT ---------------\n");
+ TestDevices( MODE_OUTPUT );
+ printf("Test INPUT ---------------\n");
+ TestDevices( MODE_INPUT );
+error:
+ Pa_Terminate();
+ printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
+}
+/*******************************************************************
+* Try each output device, through its full range of capabilities. */
+static void TestDevices( int mode )
+{
+ int id,jc,kr;
+ int maxChannels;
+ const PaDeviceInfo *pdi;
+ int numDevices = Pa_CountDevices();
+ /* Iterate through all devices. */
+ for( id=0; id<numDevices; id++ )
+ {
+ pdi = Pa_GetDeviceInfo( id );
+ /* Try 1 to maxChannels on each device. */
+ maxChannels = ( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels;
+ for( jc=1; jc<=maxChannels; jc++ )
+ {
+ printf("Name = %s\n", pdi->name );
+ /* Try each legal sample rate. */
+ if( pdi->numSampleRates == -1 )
+ {
+ double low, high;
+ low = pdi->sampleRates[0];
+ high = pdi->sampleRates[1];
+ if( low < 8000.0 ) low = 8000.0;
+ TestFormats( mode, id, low, jc );
+#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); }
+
+ TESTSR(11025.0);
+ TESTSR(22050.0);
+ TESTSR(44100.0);
+ TestFormats( mode, id, high, jc );
+ }
+ else
+ {
+ for( kr=0; kr<pdi->numSampleRates; kr++ )
+ {
+ TestFormats( mode, id, pdi->sampleRates[kr], jc );
+ }
+ }
+ }
+ }
+}
+/*******************************************************************/
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+ int numChannels )
+{
+ TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */
+ TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */
+ TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */
+ /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */
+}
+/*******************************************************************/
+static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
+ int numChannels, PaSampleFormat format )
+{
+ PortAudioStream *stream = NULL;
+ PaError result;
+ PaQaData myData;
+#define FRAMES_PER_BUFFER (64)
+ printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n",
+ ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
+ deviceID, sampleRate, numChannels, format);
+ /* Setup data for synthesis thread. */
+ myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */
+ myData.numChannels = numChannels;
+ myData.mode = mode;
+ myData.format = format;
+ switch( format )
+ {
+ case paFloat32:
+ case paInt32:
+ case paInt24:
+ myData.bytesPerSample = 4;
+ break;
+ case paPackedInt24:
+ myData.bytesPerSample = 3;
+ break;
+ default:
+ myData.bytesPerSample = 2;
+ break;
+ }
+ EXPECT( ((result = Pa_OpenStream(
+ &stream,
+ ( mode == MODE_INPUT ) ? deviceID : paNoDevice,
+ ( mode == MODE_INPUT ) ? numChannels : 0,
+ format,
+ NULL,
+ ( mode == MODE_OUTPUT ) ? deviceID : paNoDevice,
+ ( mode == MODE_OUTPUT ) ? numChannels : 0,
+ format,
+ NULL,
+ sampleRate,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ QaCallback,
+ &myData )
+ ) == 0) );
+ if( stream )
+ {
+ PaTimestamp oldStamp, newStamp;
+ unsigned long oldFrames;
+ int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
+ int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+ int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
+ if( msec < minDelay ) msec = minDelay;
+ printf("msec = %d\n", msec); /**/
+ EXPECT( ((result=Pa_StartStream( stream )) == 0) );
+ /* Check to make sure PortAudio is advancing timeStamp. */
+ result = paNoError;
+ oldStamp = Pa_StreamTime(stream);
+ Pa_Sleep(msec);
+ newStamp = Pa_StreamTime(stream);
+ printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/
+ EXPECT( (oldStamp < newStamp) );
+ /* Check to make sure callback is decrementing framesLeft. */
+ oldFrames = myData.framesLeft;
+ Pa_Sleep(msec);
+ printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames, myData.framesLeft ); /**/
+ EXPECT( (oldFrames > myData.framesLeft) );
+ EXPECT( ((result=Pa_CloseStream( stream )) == 0) );
+ stream = NULL;
+ }
+error:
+ if( stream != NULL ) Pa_CloseStream( stream );
+ return result;
+}
--- /dev/null
+/*
+ * $Id$
+ * paqa_devs.c
+ * Self Testing Quality Assurance app for PortAudio
+ * Do lots of bad things to test error reporting.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/****************************************** Definitions ***********/
+#define MODE_INPUT (0)
+#define MODE_OUTPUT (1)
+#define FRAMES_PER_BUFFER (64)
+#define SAMPLE_RATE (44100.0)
+#define NUM_BUFFERS (0)
+typedef struct PaQaData
+{
+ unsigned long framesLeft;
+ int numChannels;
+ int bytesPerSample;
+ int mode;
+}
+PaQaData;
+/****************************************** Prototypes ***********/
+static void TestDevices( int mode );
+static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
+ int numChannels );
+static int TestBadOpens( void );
+static int TestBadActions( void );
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+/****************************************** Globals ***********/
+static int gNumPassed = 0;
+static int gNumFailed = 0;
+/****************************************** Macros ***********/
+/* Print ERROR if it fails. Tally success or failure. */
+/* Odd do-while wrapper seems to be needed for some compilers. */
+#define EXPECT(_exp) \
+ do \
+ { \
+ if ((_exp)) {\
+ gNumPassed++; \
+ } \
+ else { \
+ printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
+ gNumFailed++; \
+ goto error; \
+ } \
+ } while(0)
+#define HOPEFOR(_exp) \
+ do \
+ { \
+ if ((_exp)) {\
+ gNumPassed++; \
+ } \
+ else { \
+ printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
+ gNumFailed++; \
+ } \
+ } while(0)
+/*******************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int QaCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ unsigned long i;
+ unsigned char *out = (unsigned char *) outputBuffer;
+ PaQaData *data = (PaQaData *) userData;
+ (void) inputBuffer; /* Prevent "unused variable" warnings. */
+ (void) outTime;
+
+ /* Zero out buffer so we don't hear terrible noise. */
+ if( data->mode == MODE_OUTPUT )
+ {
+ unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
+ for( i=0; i<numBytes; i++ )
+ {
+ *out++ = 0;
+ }
+ }
+ /* Are we through yet? */
+ if( data->framesLeft > framesPerBuffer )
+ {
+ data->framesLeft -= framesPerBuffer;
+ return 0;
+ }
+ else
+ {
+ data->framesLeft = 0;
+ return 1;
+ }
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PaError result;
+ EXPECT( ((result=Pa_Initialize()) == 0) );
+ TestBadOpens();
+ TestBadActions();
+error:
+ Pa_Terminate();
+ printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
+ return 0;
+}
+/*******************************************************************/
+static int TestBadOpens( void )
+{
+ PortAudioStream *stream = NULL;
+ PaError result;
+ PaQaData myData;
+ /* Setup data for synthesis thread. */
+ myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
+ myData.numChannels = 1;
+ myData.mode = MODE_OUTPUT;
+ HOPEFOR( (/* No devices specified. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ paNoDevice, 0, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidDeviceId) );
+ HOPEFOR( ( /* Out of range input device specified. */
+ (result = Pa_OpenStream(
+ &stream,
+ Pa_CountDevices(), 0, paFloat32, NULL,
+ paNoDevice, 0, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidDeviceId) );
+
+ HOPEFOR( ( /* Out of range output device specified. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_CountDevices(), 0, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidDeviceId) );
+ HOPEFOR( ( /* Zero input channels. */
+ (result = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL,
+ paNoDevice, 0, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidChannelCount) );
+ HOPEFOR( ( /* Zero output channels. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidChannelCount) );
+ HOPEFOR( ( /* Nonzero input channels but no device. */
+ (result = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL,
+ paNoDevice, 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidChannelCount) );
+
+ HOPEFOR( ( /* Nonzero output channels but no device. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 2, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidChannelCount) );
+ HOPEFOR( ( /* NULL stream pointer. */
+ (result = Pa_OpenStream(
+ NULL,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paBadStreamPtr) );
+ HOPEFOR( ( /* Low sample rate. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ 1.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidSampleRate) );
+ HOPEFOR( ( /* High sample rate. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ 10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == paInvalidSampleRate) );
+ HOPEFOR( ( /* NULL callback. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ NULL,
+ &myData )
+ ) == paNullCallback) );
+ HOPEFOR( ( /* Bad flag. */
+ (result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ (1<<3),
+ QaCallback,
+ &myData )
+ ) == paInvalidFlag) );
+ if( stream != NULL ) Pa_CloseStream( stream );
+ return result;
+}
+/*******************************************************************/
+static int TestBadActions( void )
+{
+ PortAudioStream *stream = NULL;
+ PaError result;
+ PaQaData myData;
+ /* Setup data for synthesis thread. */
+ myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
+ myData.numChannels = 1;
+ myData.mode = MODE_OUTPUT;
+ /* Default output. */
+ EXPECT( ((result = Pa_OpenStream(
+ &stream,
+ paNoDevice, 0, paFloat32, NULL,
+ Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
+ SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
+ paClipOff,
+ QaCallback,
+ &myData )
+ ) == 0) );
+ HOPEFOR( ((result = Pa_StartStream( NULL )) == paBadStreamPtr) );
+ HOPEFOR( ((result = Pa_StopStream( NULL )) == paBadStreamPtr) );
+ HOPEFOR( ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) );
+ HOPEFOR( ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) );
+ HOPEFOR( ((result = (PaError)Pa_StreamTime( NULL )) != 0) );
+ HOPEFOR( ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) );
+error:
+ if( stream != NULL ) Pa_CloseStream( stream );
+ return result;
+}
--- /dev/null
+/*
+ $Id$
+ patest1.c
+ Ring modulate the audio input with a sine wave for 20 seconds
+ using the Portable Audio api
+ Author: Ross Bencina <rossb@audiomulch.com>
+ Modifications:
+ April 5th, 2001 - PLB - Check for NULL inputBuffer.
+*/
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+typedef struct
+{
+ float sine[100];
+ int phase;
+ int sampsToGo;
+}
+patest1data;
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long bufferFrames,
+ PaTimestamp outTime, void *userData )
+{
+ patest1data *data = (patest1data*)userData;
+ float *in = (float*)inputBuffer;
+ float *out = (float*)outputBuffer;
+ int framesToCalc = bufferFrames;
+ unsigned long i;
+ int finished = 0;
+ /* Check to see if any input data is available. */
+ if(inputBuffer == NULL) return 0;
+ if( data->sampsToGo < bufferFrames )
+ {
+ framesToCalc = data->sampsToGo;
+ finished = 1;
+ }
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *out++ = *in++ * data->sine[data->phase]; /* left */
+ *out++ = *in++ * data->sine[data->phase++]; /* right */
+ if( data->phase >= 100 )
+ data->phase = 0;
+ }
+ data->sampsToGo -= framesToCalc;
+ /* zero remainder of final buffer if not already done */
+ for( ; i<bufferFrames; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ return finished;
+}
+int main(int argc, char* argv[]);
+int main(int argc, char* argv[])
+{
+ PaStream *stream;
+ PaError err;
+ patest1data data;
+ int i;
+ int inputDevice = Pa_GetDefaultInputDeviceID();
+ int outputDevice = Pa_GetDefaultOutputDeviceID();
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<100; i++ )
+ data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
+ data.phase = 0;
+ data.sampsToGo = 44100 * 20; // 20 seconds
+ /* initialise portaudio subsytem */
+ Pa_Initialize();
+ err = Pa_OpenStream(
+ &stream,
+ inputDevice,
+ 2, /* stereo input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ outputDevice,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ 44100.,
+ 512, /* small buffers */
+ 0, /* let PA determine number of buffers */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patest1Callback,
+ &data );
+ if( err == paNoError )
+ {
+ err = Pa_StartStream( stream );
+ printf( "Press any key to end.\n" );
+ getc( stdin ); //wait for input before exiting
+ Pa_AbortStream( stream );
+
+ printf( "Waiting for stream to complete...\n" );
+
+ while( Pa_StreamActive( stream ) )
+ Pa_Sleep(1000); /* sleep until playback has finished */
+
+ err = Pa_CloseStream( stream );
+ }
+ else
+ {
+ fprintf( stderr, "An error occured while opening the portaudio stream\n" );
+ if( err == paHostError )
+ fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
+ else
+ fprintf( stderr, "Error number: %d\n", err );
+ }
+ Pa_Terminate();
+ printf( "bye\n" );
+
+ return 0;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_buffer.c
+ * Test opening streams with different buffer sizes.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (1)
+#define SAMPLE_RATE (44100)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+
+#define BUFFER_TABLE 9
+long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345};
+
+typedef struct
+{
+ short sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+ unsigned int sampsToGo;
+}
+paTestData;
+PaError TestOnce( int buffersize );
+
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ short *out = (short*)outputBuffer;
+ unsigned int i;
+ int finished = 0;
+ (void) inputBuffer; /* Prevent "unused variable" warnings. */
+ (void) outTime;
+
+ if( data->sampsToGo < framesPerBuffer )
+ {
+ /* final buffer... */
+
+ for( i=0; i<data->sampsToGo; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ /* zero remainder of final buffer */
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+
+ finished = 1;
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ data->sampsToGo -= framesPerBuffer;
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i;
+ PaError err;
+ printf("Test opening streams with different buffer sizes\n\n");
+
+ for (i = 0 ; i < BUFFER_TABLE; i++)
+ {
+ printf("Buffer size %d\n", buffer_table[i]);
+ err = TestOnce(buffer_table[i]);
+ if( err < 0 ) return 0;
+
+ }
+}
+
+
+PaError TestOnce( int buffersize )
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+ }
+ data.left_phase = data.right_phase = 0;
+ data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paInt16, /* sample format */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paInt16, /* sample format */
+ NULL,
+ SAMPLE_RATE,
+ buffersize, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patest1Callback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for sound to finish.\n");
+ Pa_Sleep(1000);
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ return paNoError;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_clip.c
+ * Play a sine wave using the Portable Audio api for several seconds
+ * at an amplitude that would require clipping.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (4)
+#define SAMPLE_RATE (44100)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct paTestData
+{
+ float sine[TABLE_SIZE];
+ float amplitude;
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+PaError PlaySine( paTestData *data, unsigned long flags, float amplitude );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ float amplitude = data->amplitude;
+ unsigned int i;
+ (void) inputBuffer; /* Prevent "unused variable" warnings. */
+ (void) outTime;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = amplitude * data->sine[data->left_phase]; /* left */
+ *out++ = amplitude * data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PaError err;
+ paTestData DATA;
+ int i;
+ printf("PortAudio Test: output sine wave with and without clipping.\n");
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ printf("\nHalf amplitude. Should sound like sine wave.\n"); fflush(stdout);
+ err = PlaySine( &DATA, paClipOff | paDitherOff, 0.5f );
+ if( err < 0 ) goto error;
+ printf("\nFull amplitude. Should sound like sine wave.\n"); fflush(stdout);
+ err = PlaySine( &DATA, paClipOff | paDitherOff, 0.999f );
+ if( err < 0 ) goto error;
+ printf("\nOver range with clipping and dithering turned OFF. Should sound very nasty.\n");
+ fflush(stdout);
+ err = PlaySine( &DATA, paClipOff | paDitherOff, 1.1f );
+ if( err < 0 ) goto error;
+ printf("\nOver range with clipping and dithering turned ON. Should sound smoother than previous.\n");
+ fflush(stdout);
+ err = PlaySine( &DATA, paNoFlag, 1.1f );
+ if( err < 0 ) goto error;
+ printf("\nOver range with paClipOff but dithering ON.\n"
+ "That forces clipping ON so it should sound the same as previous.\n");
+ fflush(stdout);
+ err = PlaySine( &DATA, paClipOff, 1.1f );
+ if( err < 0 ) goto error;
+ return 0;
+error:
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return 1;
+}
+/*****************************************************************************/
+PaError PlaySine( paTestData *data, unsigned long flags, float amplitude )
+{
+ PortAudioStream *stream;
+ PaError err;
+ data->left_phase = data->right_phase = 0;
+ data->amplitude = amplitude;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ 1024,
+ 0, /* number of buffers, if zero then use default minimum */
+ flags, /* we won't output out of range samples so don't bother clipping them */
+ sineCallback,
+ data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Sleep( NUM_SECONDS * 1000 );
+ printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ return paNoError;
+error:
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_dither.c
+ * Attempt to hear difference between dithered and non-dithered signal.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (4)
+#define SAMPLE_RATE (44100)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct paTestData
+{
+ float sine[TABLE_SIZE];
+ float amplitude;
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int sineCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ float amplitude = data->amplitude;
+ unsigned int i;
+ (void) outTime;
+ (void) inputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = amplitude * data->sine[data->left_phase]; /* left */
+ *out++ = amplitude * data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PaError err;
+ paTestData DATA;
+ int i;
+ float amplitude = 32.0 / (1<<15);
+ printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n");
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ printf("\nNo treatment..\n"); fflush(stdout);
+ err = PlaySine( &DATA, paClipOff | paDitherOff, amplitude );
+ if( err < 0 ) goto error;
+ printf("\nClip.\n");
+ fflush(stdout);
+ err = PlaySine( &DATA, paDitherOff, amplitude );
+ if( err < 0 ) goto error;
+ printf("\nClip and Dither.\n");
+ fflush(stdout);
+ err = PlaySine( &DATA, paNoFlag, amplitude );
+ if( err < 0 ) goto error;
+ return 0;
+error:
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
+/*****************************************************************************/
+PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
+{
+ PortAudioStream *stream;
+ PaError err;
+ data->left_phase = data->right_phase = 0;
+ data->amplitude = amplitude;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ 1024,
+ 0, /* number of buffers, if zero then use default minimum */
+ flags, /* we won't output out of range samples so don't bother clipping them */
+ sineCallback,
+ (void *)data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Sleep( NUM_SECONDS * 1000 );
+ printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ return paNoError;
+error:
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * Hear the latency caused by bug buffers.
+ * Play a sine wave and change frequency based on letter input.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#if 1
+#define MIN_LATENCY_MSEC (2000)
+#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#else
+#define NUM_BUFFERS (0)
+#endif
+#define MIN_FREQ (100.0f)
+#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (400)
+typedef struct
+{
+ float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
+ float phase_increment;
+ float left_phase;
+ float right_phase;
+}
+paTestData;
+float LookupSine( paTestData *data, float phase );
+/* Convert phase between and 1.0 to sine value
+ * using linear interpolation.
+ */
+float LookupSine( paTestData *data, float phase )
+{
+ float fIndex = phase*TABLE_SIZE;
+ int index = (int) fIndex;
+ float fract = fIndex - index;
+ float lo = data->sine[index];
+ float hi = data->sine[index+1];
+ float val = lo + fract*(hi-lo);
+ return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ int i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = LookupSine(data, data->left_phase); /* left */
+ *out++ = LookupSine(data, data->right_phase); /* right */
+ data->left_phase += data->phase_increment;
+ if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
+ data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
+ if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
+ }
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int done = 0;
+ printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
+ data.left_phase = data.right_phase = 0.0;
+ data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ OUTPUT_DEVICE,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Play ASCII keyboard. Hit 'q' to stop. (Use RETURN key on Mac)\n");
+ while ( !done )
+ {
+ float freq;
+ int index;
+ char c;
+ do
+ {
+ c = getchar();
+ }
+ while( c < ' '); /* Strip white space and control chars. */
+
+ if( c == 'q' ) done = 1;
+ index = c % 26;
+ freq = MIN_FREQ + (index * 40.0);
+ data.phase_increment = CalcPhaseIncrement(freq);
+ }
+ printf("Call Pa_StopStream()\n");
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_leftright.c
+ * Play different tone sine waves that alternate between left and right channel.
+ * The low tone should be on the left channel.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (8)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (512)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+ int toggle;
+ int countDown;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ if( data->toggle )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = 0; /* right */
+ }
+ else
+ {
+ *out++ = 0; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ }
+
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+
+ if( data->countDown < 0 )
+ {
+ data->countDown = SAMPLE_RATE;
+ data->toggle = !data->toggle;
+ }
+ data->countDown -= framesPerBuffer;
+
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int timeout;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ data.countDown = SAMPLE_RATE;
+
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Play for several seconds.\n");
+ timeout = NUM_SECONDS * 4;
+ while( timeout > 0 )
+ {
+ printf("Countdown = %d, Toggle = %d\n", data.countDown, data.toggle );
+ fflush( stdout );
+ Pa_Sleep( 1000/4 );
+ timeout -= 1;
+ }
+
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_longsine.c
+ * Play a sine wave using the Portable Audio api forever.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define SAMPLE_RATE (44100)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned int i;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ printf("PortAudio Test: output sine wave.\n");
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ 256, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Hit ENTER to stop program.\n");
+ getchar();
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_many.c
+ * Start and stop the PortAudio Driver multiple times.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (1)
+#define SAMPLE_RATE (44100)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct
+{
+ short sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+ unsigned int sampsToGo;
+}
+paTestData;
+PaError TestOnce( void );
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patest1Callback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ short *out = (short*)outputBuffer;
+ unsigned int i;
+ int finished = 0;
+ (void) inputBuffer; /* Prevent "unused variable" warnings. */
+ (void) outTime;
+
+ if( data->sampsToGo < framesPerBuffer )
+ {
+ /* final buffer... */
+
+ for( i=0; i<data->sampsToGo; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ /* zero remainder of final buffer */
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+
+ finished = 1;
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ data->sampsToGo -= framesPerBuffer;
+ }
+ return finished;
+}
+/*******************************************************************/
+#ifdef MACINTOSH
+int main(void);
+int main(void)
+{
+ int i;
+ PaError err;
+ int numLoops = 10;
+ printf("Loop %d times.\n", numLoops );
+ for( i=0; i<numLoops; i++ )
+ {
+ printf("Loop %d out of %d.\n", i+1, numLoops );
+ err = TestOnce();
+ if( err < 0 ) return 0;
+ }
+}
+#else
+int main(int argc, char **argv);
+int main(int argc, char **argv)
+{
+ PaError err;
+ int i, numLoops = 10;
+ if( argc > 1 )
+ {
+ numLoops = atoi(argv[1]);
+ }
+ for( i=0; i<numLoops; i++ )
+ {
+ printf("Loop %d out of %d.\n", i+1, numLoops );
+ err = TestOnce();
+ if( err < 0 ) return 1;
+ }
+ printf("Test complete.\n");
+ return 0;
+}
+#endif
+PaError TestOnce( void )
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+ }
+ data.left_phase = data.right_phase = 0;
+ data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paInt16, /* sample format */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paInt16, /* sample format */
+ NULL,
+ SAMPLE_RATE,
+ 1024, /* frames per buffer */
+ 8, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patest1Callback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for sound to finish.\n");
+ Pa_Sleep(1000);
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ return paNoError;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_maxsines.c
+ * How many sine waves can we calculate and play in less than 80% CPU Load.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define MAX_SINES (500)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (512)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TWOPI (M_PI * 2.0)
+
+typedef struct paTestData
+{
+ int numSines;
+ double phases[MAX_SINES];
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int j;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ float output = 0.0;
+ double phaseInc = 0.02;
+ double phase;
+ for( j=0; j<data->numSines; j++ )
+ {
+ /* Advance phase of next oscillator. */
+ phase = data->phases[j];
+ phase += phaseInc;
+ if( phase > TWOPI ) phase -= TWOPI;
+
+ phaseInc *= 1.02;
+ if( phaseInc > 0.5 ) phaseInc *= 0.5;
+
+ /* This is not a very efficient way to calc sines. */
+ output += (float) sin( phase );
+ data->phases[j] = phase;
+ }
+
+
+ *out++ = (float) (output / data->numSines);
+ }
+ return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data = {0};
+ double load;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 1, /* mono output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+
+ do
+ {
+ data.numSines++;
+ Pa_Sleep( 200 );
+
+ load = Pa_GetCPULoad( stream );
+ printf("numSines = %d, CPU load = %f\n", data.numSines, load );
+ }
+ while( load < 0.8 );
+
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ patest_pink.c
+ Generate Pink Noise using Gardner method.
+ Optimization suggested by James McCartney uses a tree
+ to select which random value to replace.
+ x x x x x x x x x x x x x x x x
+ x x x x x x x x
+ x x x x
+ x x
+ x
+ Tree is generated by counting trailing zeros in an increasing index.
+ When the index is zero, no random number is selected.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define PINK_MAX_RANDOM_ROWS (30)
+#define PINK_RANDOM_BITS (24)
+#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
+typedef struct
+{
+ long pink_Rows[PINK_MAX_RANDOM_ROWS];
+ long pink_RunningSum; /* Used to optimize summing of generators. */
+ int pink_Index; /* Incremented each sample. */
+ int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
+ float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
+}
+PinkNoise;
+/* Prototypes */
+static unsigned long GenerateRandomNumber( void );
+void InitializePinkNoise( PinkNoise *pink, int numRows );
+float GeneratePinkNoise( PinkNoise *pink );
+/************************************************************/
+/* Calculate pseudo-random 32 bit number based on linear congruential method. */
+static unsigned long GenerateRandomNumber( void )
+{
+ /* Change this seed for different random sequences. */
+ static unsigned long randSeed = 22222;
+ randSeed = (randSeed * 196314165) + 907633515;
+ return randSeed;
+}
+/************************************************************/
+/* Setup PinkNoise structure for N rows of generators. */
+void InitializePinkNoise( PinkNoise *pink, int numRows )
+{
+ int i;
+ long pmax;
+ pink->pink_Index = 0;
+ pink->pink_IndexMask = (1<<numRows) - 1;
+ /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
+ pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
+ pink->pink_Scalar = 1.0f / pmax;
+ /* Initialize rows. */
+ for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
+ pink->pink_RunningSum = 0;
+}
+#define PINK_MEASURE
+#ifdef PINK_MEASURE
+float pinkMax = -999.0;
+float pinkMin = 999.0;
+#endif
+/* Generate Pink noise values between -1.0 and +1.0 */
+float GeneratePinkNoise( PinkNoise *pink )
+{
+ long newRandom;
+ long sum;
+ float output;
+ /* Increment and mask index. */
+ pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
+ /* If index is zero, don't update any random values. */
+ if( pink->pink_Index != 0 )
+ {
+ /* Determine how many trailing zeros in PinkIndex. */
+ /* This algorithm will hang if n==0 so test first. */
+ int numZeros = 0;
+ int n = pink->pink_Index;
+ while( (n & 1) == 0 )
+ {
+ n = n >> 1;
+ numZeros++;
+ }
+ /* Replace the indexed ROWS random value.
+ * Subtract and add back to RunningSum instead of adding all the random
+ * values together. Only one changes each time.
+ */
+ pink->pink_RunningSum -= pink->pink_Rows[numZeros];
+ newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+ pink->pink_RunningSum += newRandom;
+ pink->pink_Rows[numZeros] = newRandom;
+ }
+
+ /* Add extra white noise value. */
+ newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+ sum = pink->pink_RunningSum + newRandom;
+ /* Scale to range of -1.0 to 0.9999. */
+ output = pink->pink_Scalar * sum;
+#ifdef PINK_MEASURE
+ /* Check Min/Max */
+ if( output > pinkMax ) pinkMax = output;
+ else if( output < pinkMin ) pinkMin = output;
+#endif
+ return output;
+}
+/*******************************************************************/
+#define PINK_TEST
+#ifdef PINK_TEST
+/* Context for callback routine. */
+typedef struct
+{
+ PinkNoise leftPink;
+ PinkNoise rightPink;
+ unsigned int sampsToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ int finished;
+ int i;
+ int numFrames;
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ (void) inputBuffer; /* Prevent "unused variable" warnings. */
+ (void) outTime;
+
+ /* Are we almost at end. */
+ if( data->sampsToGo < framesPerBuffer )
+ {
+ numFrames = data->sampsToGo;
+ finished = 1;
+ }
+ else
+ {
+ numFrames = framesPerBuffer;
+ finished = 0;
+ }
+ for( i=0; i<numFrames; i++ )
+ {
+ *out++ = GeneratePinkNoise( &data->leftPink );
+ *out++ = GeneratePinkNoise( &data->rightPink );
+ }
+ data->sampsToGo -= numFrames;
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int totalSamps;
+ /* Initialize two pink noise signals with different numbers of rows. */
+ InitializePinkNoise( &data.leftPink, 12 );
+ InitializePinkNoise( &data.rightPink, 16 );
+ /* Look at a few values. */
+ {
+ int i;
+ float pink;
+ for( i=0; i<20; i++ )
+ {
+ pink = GeneratePinkNoise( &data.leftPink );
+ printf("Pink = %f\n", pink );
+ }
+ }
+ data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ /* Open a stereo PortAudio stream so we can hear the result. */
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ 44100.,
+ 2048, /* 46 msec buffers */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for sound to finish.\n");
+ while( Pa_StreamActive( stream ) )
+ {
+ Pa_Sleep(100); /* SPIN! */
+ }
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+#ifdef PINK_MEASURE
+ printf("Pink min = %f, max = %f\n", pinkMin, pinkMax );
+#endif
+ Pa_Terminate();
+ return 0;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return 0;
+}
+#endif /* PINK_TEST */
--- /dev/null
+/*
+ * $Id$
+ * patest_record.c
+ * Record input into an array.
+ * Save array to a file.
+ * Playback recorded data.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "portaudio.h"
+
+/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */
+#define SAMPLE_RATE (44100)
+#define NUM_SECONDS (5)
+
+/* Select sample format. */
+#if 0
+#define PA_SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#elif 0
+#define PA_SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#else
+#define PA_SAMPLE_TYPE paUInt8
+typedef unsigned char SAMPLE;
+#endif
+
+typedef struct
+{
+ int frameIndex; /* Index into sample array. */
+ int maxFrameIndex;
+ int samplesPerFrame;
+ SAMPLE *recordedSamples;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int recordCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ SAMPLE *rptr = (SAMPLE*)inputBuffer;
+ SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+ long framesToCalc;
+ long i;
+ int finished;
+ unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
+
+ (void) outputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ framesToCalc = framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ finished = 0;
+ }
+ if( inputBuffer == NULL )
+ {
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *wptr++ = 0; /* left */
+ *wptr++ = 0; /* right */
+ }
+ }
+ else
+ {
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ }
+ data->frameIndex += framesToCalc;
+ return finished;
+}
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int playCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
+ SAMPLE *wptr = (SAMPLE*)outputBuffer;
+ unsigned int i;
+ int finished;
+ unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
+ (void) inputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ /* final buffer... */
+ for( i=0; i<framesLeft; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = 0; /* left */
+ *wptr++ = 0; /* right */
+ }
+ data->frameIndex += framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = *rptr++; /* left */
+ *wptr++ = *rptr++; /* right */
+ }
+ data->frameIndex += framesPerBuffer;
+ finished = 0;
+ }
+ return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalFrames;
+ int numSamples;
+ int numBytes;
+ SAMPLE max, average, val;
+ printf("patest_record.c\n"); fflush(stdout);
+
+ data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
+ data.frameIndex = 0;
+ data.samplesPerFrame = 2;
+ numSamples = totalFrames * data.samplesPerFrame;
+
+ numBytes = numSamples * sizeof(SAMPLE);
+ data.recordedSamples = (SAMPLE *) malloc( numBytes );
+ if( data.recordedSamples == NULL )
+ {
+ printf("Could not allocate record array.\n");
+ exit(1);
+ }
+ for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
+
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ /* Record some audio. -------------------------------------------- */
+ err = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(),
+ data.samplesPerFrame, /* stereo input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ paNoDevice,
+ 0,
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ 1024, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ recordCallback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Now recording!!\n"); fflush(stdout);
+
+ while( Pa_StreamActive( stream ) )
+ {
+ Pa_Sleep(1000);
+ printf("index = %d\n", data.frameIndex ); fflush(stdout);
+ }
+
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+
+ /* Measure maximum peak amplitude. */
+ max = 0;
+ average = 0;
+ for( i=0; i<numSamples; i++ )
+ {
+ val = data.recordedSamples[i];
+ if( val < 0 ) val = -val; /* ABS */
+ if( val > max )
+ {
+ max = val;
+ }
+ average += val;
+ }
+ printf("sample max amplitude = %d\n", max );
+ average = average / numSamples;
+ printf("sample average = %d\n", average );
+
+ /* Write recorded data to a file. */
+#if 0
+ {
+ FILE *fid;
+ fid = fopen("recorded.raw", "wb");
+ if( fid == NULL )
+ {
+ printf("Could not open file.");
+ }
+ else
+ {
+ fwrite( data.recordedSamples, data.samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
+ fclose( fid );
+ printf("Wrote data to 'recorded.raw'\n");
+ }
+ }
+#endif
+
+ /* Playback recorded data. -------------------------------------------- */
+ data.frameIndex = 0;
+ printf("Begin playback.\n"); fflush(stdout);
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,
+ 0, /* NO input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(),
+ data.samplesPerFrame, /* stereo output */
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ 1024, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ playCallback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ if( stream )
+ {
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for playback to finish.\n"); fflush(stdout);
+
+ while( Pa_StreamActive( stream ) ) Pa_Sleep(100);
+
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Done.\n"); fflush(stdout);
+ }
+ free( data.recordedSamples );
+
+ Pa_Terminate();
+ return 0;
+
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+/* $Id$ */
+
+#include "stdio.h"
+#include "portaudio.h"
+/* This will be called asynchronously by the PortAudio engine. */
+static int myCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
+{
+ float *out = (float *) outputBuffer;
+ float *in = (float *) inputBuffer;
+ float leftInput, rightInput;
+ unsigned int i;
+ if( inputBuffer == NULL ) return 0;
+ /* Read input buffer, process data, and fill output buffer. */
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ leftInput = *in++; /* Get interleaved samples from input buffer. */
+ rightInput = *in++;
+ *out++ = leftInput * rightInput; /* ring modulation */
+ *out++ = 0.5f * (leftInput + rightInput); /* mix */
+ }
+ return 0;
+}
+/* Open a PortAudioStream to input and output audio data. */
+int main(void)
+{
+ PortAudioStream *stream;
+ Pa_Initialize();
+ Pa_OpenDefaultStream(
+ &stream,
+ 2, 2, /* stereo input and output */
+ paFloat32, 44100.0,
+ 64, 0, /* 64 frames per buffer, let PA determine numBuffers */
+ myCallback, NULL );
+ Pa_StartStream( stream );
+ Pa_Sleep( 10000 ); /* Sleep for 10 seconds while processing. */
+ Pa_StopStream( stream );
+ Pa_CloseStream( stream );
+ Pa_Terminate();
+ return 0;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_saw.c
+ * Play a simple sawtooth wave.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (4)
+#define SAMPLE_RATE (44100)
+typedef struct
+{
+ float left_phase;
+ float right_phase;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ /* Cast data passed through stream to our structure. */
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned int i;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->left_phase; /* left */
+ *out++ = data->right_phase; /* right */
+ /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+ data->left_phase += 0.01f;
+ /* When signal reaches top, drop back down. */
+ if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+ /* higher pitch so we can distinguish left and right. */
+ data->right_phase += 0.03f;
+ if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+ }
+ return 0;
+}
+/*******************************************************************/
+static paTestData data;
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ printf("PortAudio Test: output sawtooth wave.\n");
+ /* Initialize our data for use by callback. */
+ data.left_phase = data.right_phase = 0.0;
+ /* Initialize library before making any other calls. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ /* Open an audio I/O stream. */
+ err = Pa_OpenDefaultStream(
+ &stream,
+ 0, /* no input channels */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ SAMPLE_RATE,
+ 256, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ /* Sleep for several seconds. */
+ Pa_Sleep(NUM_SECONDS*1000);
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * $Id$
+ * patest_sine.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS (5)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (64)
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Play for %d seconds.\n", NUM_SECONDS );
+ Pa_Sleep( NUM_SECONDS * 1000 );
+
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_sine8.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Test 8 bit data.
+ *
+ * Author: Ross Bencina <rossb@audiomulch.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (8)
+#define SAMPLE_RATE (44100)
+#define TEST_UNSIGNED (1)
+#if TEST_UNSIGNED
+#define TEST_FORMAT paUInt8
+#else
+#define TEST_FORMAT paInt8
+#endif
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct
+{
+#if TEST_UNSIGNED
+ unsigned char sine[TABLE_SIZE];
+#else
+ char sine[TABLE_SIZE];
+#endif
+ int left_phase;
+ int right_phase;
+ unsigned int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ char *out = (char*)outputBuffer;
+ int i;
+ int framesToCalc;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( data->framesToGo < framesPerBuffer )
+ {
+ framesToCalc = data->framesToGo;
+ data->framesToGo = 0;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ data->framesToGo -= framesPerBuffer;
+ }
+
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ /* zero remainder of final buffer */
+ for( ; i<(int)framesPerBuffer; i++ )
+ {
+#if TEST_UNSIGNED
+ *out++ = (unsigned char) 0x80; /* left */
+ *out++ = (unsigned char) 0x80; /* right */
+#else
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+#endif
+
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+#if TEST_UNSIGNED
+ printf("PortAudio Test: output UNsigned 8 bit sine wave.\n");
+#else
+ printf("PortAudio Test: output signed 8 bit sine wave.\n");
+#endif
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+#if TEST_UNSIGNED
+ data.sine[i] += (unsigned char) 0x80;
+#endif
+
+ }
+ data.left_phase = data.right_phase = 0;
+ data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ TEST_FORMAT,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ TEST_FORMAT,
+ NULL,
+ SAMPLE_RATE,
+ 256, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ /* Watch until sound is halfway finished. */
+ while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
+ /* Stop sound until ENTER hit. */
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Pause for 2 seconds.\n");
+ Pa_Sleep( 2000 );
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Waiting for sound to finish.\n");
+ while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_sine_formats.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Test various data formats.
+ *
+ * Author: Phil Burk
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS (20)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */
+#define RIGHT_FREQ (500.0)
+#define AMPLITUDE (1.0)
+
+/* Select ONE format for testing. */
+#define TEST_UINT8 (0)
+#define TEST_INT8 (0)
+#define TEST_INT16 (1)
+#define TEST_FLOAT32 (0)
+
+#if TEST_UINT8
+#define TEST_FORMAT paUInt8
+typedef unsigned char SAMPLE_t;
+#define SAMPLE_ZERO (0x80)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
+#define FORMAT_NAME "Unsigned 8 Bit"
+#endif
+
+#if TEST_INT8
+#define TEST_FORMAT paInt8
+typedef char SAMPLE_t;
+#define SAMPLE_ZERO (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
+#define FORMAT_NAME "Signed 8 Bit"
+#endif
+
+#if TEST_INT16
+#define TEST_FORMAT paInt16
+typedef short SAMPLE_t;
+#define SAMPLE_ZERO (0)
+#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767.0 * (x)))
+#define FORMAT_NAME "Signed 16 Bit"
+#endif
+
+#if TEST_FLOAT32
+#define TEST_FORMAT paFloat32
+typedef float SAMPLE_t;
+#define SAMPLE_ZERO (0.0)
+#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x))
+#define FORMAT_NAME "Float 32 Bit"
+#endif
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+
+typedef struct
+{
+ double left_phase;
+ double right_phase;
+ unsigned int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
+ int i;
+ int framesToCalc;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( data->framesToGo < framesPerBuffer )
+ {
+ framesToCalc = data->framesToGo;
+ data->framesToGo = 0;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ data->framesToGo -= framesPerBuffer;
+ }
+
+ for( i=0; i<framesToCalc; i++ )
+ {
+ data->left_phase += (LEFT_FREQ / SAMPLE_RATE);
+ if( data->left_phase > 1.0) data->left_phase -= 1.0;
+ *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. )));
+
+ data->right_phase += (RIGHT_FREQ / SAMPLE_RATE);
+ if( data->right_phase > 1.0) data->right_phase -= 1.0;
+ *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. )));
+ }
+ /* zero remainder of final buffer */
+ for( ; i<(int)framesPerBuffer; i++ )
+ {
+ *out++ = SAMPLE_ZERO; /* left */
+ *out++ = SAMPLE_ZERO; /* right */
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int totalSamps;
+
+ printf("PortAudio Test: output " FORMAT_NAME "\n");
+
+
+ data.left_phase = data.right_phase = 0.0;
+ data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ TEST_FORMAT,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ TEST_FORMAT,
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+
+ printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS );
+ while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+
+ printf("PortAudio Test Finished: " FORMAT_NAME "\n");
+
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_sine_time.c
+ * Play a sine wave using the Portable Audio api for several seconds.
+ * Pausing in the middle.
+ * use the Pa_StreamTime() and Pa_StreamActive() calls.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_SECONDS (8)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (64)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+ int framesToGo;
+}
+paTestData;
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ int i;
+ int framesToCalc;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ if( data->framesToGo < framesPerBuffer )
+ {
+ framesToCalc = data->framesToGo;
+ data->framesToGo = 0;
+ finished = 1;
+ }
+ else
+ {
+ framesToCalc = framesPerBuffer;
+ data->framesToGo -= framesPerBuffer;
+ }
+
+ for( i=0; i<framesToCalc; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+ /* zero remainder of final buffer */
+ for( ; i<(int)framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ int totalSamps;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = 0;
+ data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ /* Watch until sound is halfway finished. */
+ printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout);
+ while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
+ /* Stop sound until ENTER hit. */
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Pause for 2 seconds.\n"); fflush(stdout);
+ Pa_Sleep( 2000 );
+
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Play until sound is finished.\n"); fflush(stdout);
+ while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_stop.c
+ *
+ * Test the three ways of stopping audio:
+ * calling Pa_StopStream(),
+ * calling Pa_AbortStream(),
+ * and returning a 1 from the callback function.
+ *
+ * A long latency is set up so that you can hear the difference.
+ * Then a simple 8 note sequence is repeated twice.
+ * The program will print what you should hear.
+ *
+ * Author: Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
+#define SLEEP_DUR (200)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#define LATENCY_MSEC (3000)
+#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#define FRAMES_PER_NOTE (SAMPLE_RATE/2)
+#define MAX_REPEATS (2)
+#define FUNDAMENTAL (400.0f / SAMPLE_RATE)
+#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f)
+#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f)
+#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f)
+#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f)
+#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f)
+#define MODE_FINISH (0)
+#define MODE_STOP (1)
+#define MODE_ABORT (2)
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+#define TABLE_SIZE (400)
+typedef struct
+{
+ float waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation
+ float phase_increment;
+ float phase;
+ float *tune;
+ int notesPerTune;
+ int frameCounter;
+ int noteCounter;
+ int repeatCounter;
+ PaTimestamp outTime;
+ int stopMode;
+ int done;
+}
+paTestData;
+/************* Prototypes *****************************/
+int TestStopMode( paTestData *data );
+float LookupWaveform( paTestData *data, float phase );
+/******************************************************
+ * Convert phase between 0.0 and 1.0 to waveform value
+ * using linear interpolation.
+ */
+float LookupWaveform( paTestData *data, float phase )
+{
+ float fIndex = phase*TABLE_SIZE;
+ int index = (int) fIndex;
+ float fract = fIndex - index;
+ float lo = data->waveform[index];
+ float hi = data->waveform[index+1];
+ float val = lo + fract*(hi-lo);
+ return val;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ float value;
+ unsigned int i = 0;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+
+ data->outTime = outTime;
+ if( !data->done )
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ /* Are we done with this note? */
+ if( data->frameCounter >= FRAMES_PER_NOTE )
+ {
+ data->noteCounter += 1;
+ data->frameCounter = 0;
+ /* Are we done with this tune? */
+ if( data->noteCounter >= data->notesPerTune )
+ {
+ data->noteCounter = 0;
+ data->repeatCounter += 1;
+ /* Are we totally done? */
+ if( data->repeatCounter >= MAX_REPEATS )
+ {
+ data->done = 1;
+ if( data->stopMode == MODE_FINISH )
+ {
+ finished = 1;
+ break;
+ }
+ }
+ }
+ data->phase_increment = data->tune[data->noteCounter];
+ }
+ value = LookupWaveform(data, data->phase);
+ *out++ = value; /* left */
+ *out++ = value; /* right */
+ data->phase += data->phase_increment;
+ if( data->phase >= 1.0f ) data->phase -= 1.0f;
+
+ data->frameCounter += 1;
+ }
+ }
+ /* zero remainder of final buffer */
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ return finished;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ paTestData DATA;
+ int i;
+ float simpleTune[] = { NOTE_0, NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_3, NOTE_2, NOTE_1 };
+ printf("PortAudio Test: play song and test stopping. ask for %d buffers\n", NUM_BUFFERS );
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ DATA.waveform[i] = (float) (
+ (0.2 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. )) +
+ (0.2 * sin( ((double)(3*i)/(double)TABLE_SIZE) * M_PI * 2. )) +
+ (0.1 * sin( ((double)(5*i)/(double)TABLE_SIZE) * M_PI * 2. ))
+ );
+ }
+ DATA.waveform[TABLE_SIZE] = DATA.waveform[0]; // set guard point
+ DATA.tune = &simpleTune[0];
+ DATA.notesPerTune = sizeof(simpleTune) / sizeof(float);
+ printf("Test MODE_FINISH - callback returns 1.\n");
+ printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
+ DATA.stopMode = MODE_FINISH;
+ if( TestStopMode( &DATA ) != paNoError )
+ {
+ printf("Test of MODE_FINISH failed!\n");
+ goto error;
+ }
+ printf("Test MODE_STOP - stop when song is done.\n");
+ printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
+ DATA.stopMode = MODE_STOP;
+ if( TestStopMode( &DATA ) != paNoError )
+ {
+ printf("Test of MODE_STOP failed!\n");
+ goto error;
+ }
+
+ printf("Test MODE_ABORT - abort immediately.\n");
+ printf("Should hear last repetition cut short by %d msec.\n", LATENCY_MSEC);
+ DATA.stopMode = MODE_ABORT;
+ if( TestStopMode( &DATA ) != paNoError )
+ {
+ printf("Test of MODE_ABORT failed!\n");
+ goto error;
+ }
+ return 0;
+error:
+ return 1;
+}
+
+int TestStopMode( paTestData *data )
+{
+ PortAudioStream *stream;
+ PaError err;
+ data->done = 0;
+ data->phase = 0.0;
+ data->frameCounter = 0;
+ data->noteCounter = 0;
+ data->repeatCounter = 0;
+ data->phase_increment = data->tune[data->noteCounter];
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ OUTPUT_DEVICE,
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ if( data->stopMode == MODE_FINISH )
+ {
+ while( Pa_StreamActive( stream ) )
+ {
+ /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
+ data->noteCounter, data->repeatCounter );
+ fflush(stdout); /**/
+ Pa_Sleep( SLEEP_DUR );
+ }
+ }
+ else
+ {
+ while( data->repeatCounter < MAX_REPEATS )
+ {
+ /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
+ data->noteCounter, data->repeatCounter );
+ fflush(stdout); /**/
+ Pa_Sleep( SLEEP_DUR );
+ }
+ }
+ if( data->stopMode == MODE_ABORT )
+ {
+ printf("Call Pa_AbortStream()\n");
+ err = Pa_AbortStream( stream );
+ }
+ else
+ {
+ printf("Call Pa_StopStream()\n");
+ err = Pa_StopStream( stream );
+ }
+ if( err != paNoError ) goto error;
+ printf("Call Pa_CloseStream()\n"); fflush(stdout);
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_sync.c
+ * Test time stamping and synchronization of audio and video.
+ * A high latency is used so we can hear the difference in time.
+ * Random durations are used so we know we are hearing the right beep
+ * and not the one before or after.
+ *
+ * Sequence of events:
+ * Foreground requests a beep.
+ * Background randomly schedules a beep.
+ * Foreground waits for the beep to be heard based on Pa_StreamTime().
+ * Foreground outputs video (printf) in sync with audio.
+ * Repeat.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+#define NUM_BEEPS (6)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (256)
+#define BEEP_DURATION (1000)
+#define LATENCY_MSEC (2000)
+#define SLEEP_MSEC (10)
+#define TIMEOUT_MSEC ((3 * LATENCY_MSEC) / (2 * SLEEP_MSEC))
+#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
+#define STATE_BKG_IDLE (0)
+#define STATE_BKG_PENDING (1)
+#define STATE_BKG_BEEPING (2)
+typedef struct
+{
+ float left_phase;
+ float right_phase;
+ int state;
+ int requestBeep; /* Set by foreground, cleared by background. */
+ PaTimestamp beepTime;
+ int beepCount;
+}
+paTestData;
+static unsigned long GenerateRandomNumber( void );
+/************************************************************/
+/* Calculate pseudo-random 32 bit number based on linear congruential method. */
+static unsigned long GenerateRandomNumber( void )
+{
+ static unsigned long randSeed = 22222; /* Change this for different random sequences. */
+ randSeed = (randSeed * 196314165) + 907633515;
+ return randSeed;
+}
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ /* Cast data passed through stream to our structure. */
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned int i;
+ (void) inputBuffer;
+
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ switch( data->state )
+ {
+ case STATE_BKG_IDLE:
+ /* Schedule beep at some random time in the future. */
+ if( data->requestBeep )
+ {
+ int random = GenerateRandomNumber() >> 14;
+ data->beepTime = outTime + (i + random + (SAMPLE_RATE/4));
+ data->state = STATE_BKG_PENDING;
+ data->requestBeep = 0;
+ data->left_phase = data->right_phase = 0.0;
+ }
+ *out++ = 0.0; /* left */
+ *out++ = 0.0; /* right */
+ break;
+ case STATE_BKG_PENDING:
+ if( (outTime + i) >= data->beepTime )
+ {
+ data->state = STATE_BKG_BEEPING;
+ data->beepCount = BEEP_DURATION;
+ }
+ *out++ = 0.0; /* left */
+ *out++ = 0.0; /* right */
+ break;
+ case STATE_BKG_BEEPING:
+ if( data->beepCount <= 0 )
+ {
+ data->state = STATE_BKG_IDLE;
+ *out++ = 0.0; /* left */
+ *out++ = 0.0; /* right */
+ }
+ else
+ {
+ /* Play sawtooth wave. */
+ *out++ = data->left_phase; /* left */
+ *out++ = data->right_phase; /* right */
+ /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+ data->left_phase += 0.01f;
+ /* When signal reaches top, drop back down. */
+ if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+ /* higher pitch so we can distinguish left and right. */
+ data->right_phase += 0.03f;
+ if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+ }
+ data->beepCount -= 1;
+ break;
+ default:
+ data->state = STATE_BKG_IDLE;
+ break;
+ }
+ }
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData DATA;
+ int i, timeout;
+ PaTimestamp previousTime;
+ printf("PortAudio Test: you should see BEEP at the same time you hear it.\n");
+ printf("Wait for a few seconds random delay between BEEPs.\n");
+ printf("BEEP %d times.\n", NUM_BEEPS );
+ /* Initialize our DATA for use by callback. */
+ DATA.left_phase = DATA.right_phase = 0.0;
+ DATA.state = STATE_BKG_IDLE;
+ DATA.requestBeep = 0;
+ /* Initialize library before making any other calls. */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ /* Open an audio I/O stream. */
+ err = Pa_OpenDefaultStream(
+ &stream,
+ 0, /* no input channels */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ NUM_BUFFERS,
+ patestCallback,
+ &DATA );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ previousTime = Pa_StreamTime( stream );
+ for( i=0; i<NUM_BEEPS; i++ )
+ {
+ /* Request a beep from background. */
+ DATA.requestBeep = 1;
+ /* Wait for background to acknowledge request. */
+ timeout = TIMEOUT_MSEC;
+ while( (DATA.requestBeep == 1) && (timeout-- > 0 ) ) Pa_Sleep(SLEEP_MSEC);
+ if( timeout <= 0 )
+ {
+ fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" );
+ goto error;
+ }
+ /* Wait for scheduled beep time. */
+ timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC);
+ while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
+ {
+ Pa_Sleep(SLEEP_MSEC);
+ }
+ if( timeout <= 0 )
+ {
+ fprintf( stderr, "Timed out waiting for time. Now = %g, Beep for %g.\n",
+ Pa_StreamTime( stream ), DATA.beepTime );
+ goto error;
+ }
+ /* Beep should be sounding now so print synchronized BEEP. */
+ printf("BEEP");
+ fflush(stdout);
+ printf(" at %d, delta = %d\n",
+ (long) DATA.beepTime, (long) (DATA.beepTime - previousTime) );
+ fflush(stdout);
+ previousTime = DATA.beepTime;
+ }
+ err = Pa_StopStream( stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_underflow.c
+ * Simulate an output buffer underflow condition.
+ * Tests whether the stream can be stopped when underflowing buffers.
+ *
+ * Authors:
+ * Ross Bencina <rossb@audiomulch.com>
+ * Phil Burk <philburk@softsynth.com>
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS (20)
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (2048)
+#define MSEC_PER_BUFFER ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE )
+
+#ifndef M_PI
+#define M_PI (3.14159265)
+#endif
+
+#define TABLE_SIZE (200)
+typedef struct
+{
+ float sine[TABLE_SIZE];
+ int left_phase;
+ int right_phase;
+ int sleepTime;
+}
+paTestData;
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int patestCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ paTestData *data = (paTestData*)userData;
+ float *out = (float*)outputBuffer;
+ unsigned long i;
+ int finished = 0;
+ (void) outTime; /* Prevent unused variable warnings. */
+ (void) inputBuffer;
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = data->sine[data->left_phase]; /* left */
+ *out++ = data->sine[data->right_phase]; /* right */
+ data->left_phase += 1;
+ if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
+ data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
+ if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
+ }
+
+ /* Cause underflow to occur. */
+ if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime );
+ data->sleepTime += 1;
+
+ return finished;
+}
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ paTestData data;
+ int i;
+ printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
+ /* initialise sinusoidal wavetable */
+ for( i=0; i<TABLE_SIZE; i++ )
+ {
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
+ }
+ data.left_phase = data.right_phase = data.sleepTime = 0;
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ err = Pa_OpenStream(
+ &stream,
+ paNoDevice,/* default input device */
+ 0, /* no input */
+ paFloat32, /* 32 bit floating point input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ paFloat32, /* 32 bit floating point output */
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ patestCallback,
+ &data );
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+
+ while( data.sleepTime < (2 * MSEC_PER_BUFFER) )
+ {
+ printf("SleepTime = %d\n", data.sleepTime );
+ Pa_Sleep( data.sleepTime );
+ }
+
+ printf("Try to stop stream.\n");
+ err = Pa_StopStream( stream ); /* */
+ err = Pa_AbortStream( stream ); /* */
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Test finished.\n");
+ return err;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return err;
+}
--- /dev/null
+/*
+ * $Id$
+ * patest_wire.c
+ *
+ * Pass input directly to output.
+ * Note that some HW devices, for example many ISA audio cards
+ * on PCs, do NOT support full duplex! For a PC, you normally need
+ * a PCI based audio card such as the SBLive.
+ *
+ * Author: Phil Burk http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <math.h>
+#include "portaudio.h"
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (44100)
+#define FRAMES_PER_BUFFER (64)
+#if 1
+#define PA_SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define PA_SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+static int wireCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may be called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int wireCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ SAMPLE *out = (SAMPLE*)outputBuffer;
+ SAMPLE *in = (SAMPLE*)inputBuffer;
+ unsigned int i;
+ int finished = 0;
+ (void) outTime;
+
+ /* This may get called with NULL inputBuffer during initial setup. */
+ if( inputBuffer == NULL )
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = 0; /* left */
+ *out++ = 0; /* right */
+ }
+ }
+ else
+ {
+ for( i=0; i<framesPerBuffer; i++ )
+ {
+ *out++ = *in++; /* left */
+ *out++ = *in++; /* right */
+ }
+ }
+
+ return 0;
+}
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ PortAudioStream *stream;
+ PaError err;
+ int numDevices = Pa_CountDevices();
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+ printf("PortAudio Test: input device = %d\n", Pa_GetDefaultInputDeviceID() );
+ printf("PortAudio Test: output device = %d\n", Pa_GetDefaultOutputDeviceID() );
+ err = Pa_OpenStream(
+ &stream,
+ Pa_GetDefaultInputDeviceID(), /* default output device */
+ 2, /* stereo input */
+ PA_SAMPLE_TYPE,
+ NULL,
+ Pa_GetDefaultOutputDeviceID(), /* default output device */
+ 2, /* stereo output */
+ PA_SAMPLE_TYPE,
+ NULL,
+ SAMPLE_RATE,
+ FRAMES_PER_BUFFER, /* frames per buffer */
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ wireCallback,
+ NULL ); /* no data */
+ if( err != paNoError ) goto error;
+ err = Pa_StartStream( stream );
+ if( err != paNoError ) goto error;
+ printf("Full duplex sound test in progress.\n");
+ printf("Hit ENTER to exit test.\n");
+ getchar();
+ err = Pa_CloseStream( stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+ printf("Full duplex sound test complete.\n"); fflush(stdout);
+ return 0;
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+# Make PortAudio for Linux
+
+LIBS = -lm -lpthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+PASRC = ../pa_common/pa_lib.c pa_unix_oss.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work.
+TESTC = $(PASRC) ../pa_tests/patest_sine.c
+#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
+#TESTC = $(PASRC) ../pa_tests/patest_maxsines.c
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c
+#TESTC = $(PASRC) ../pa_tests/patest_many.c
+#TESTC = $(PASRC) ../pa_tests/patest_record.c
+#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
+
+# Tests that do not yet work.
+
+TESTH = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+ gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
--- /dev/null
+# Make PortAudio for FreeBSD
+
+LIBS = -lm -pthread
+
+CDEFINES = -I../pa_common
+CFLAGS = -g
+PASRC = ../pa_common/pa_lib.c pa_freebsd.c
+PAINC = ../pa_common/portaudio.h
+
+# Tests that work.
+#TESTC = $(PASRC) ../pa_tests/patest_sine.c
+TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
+#TESTC = $(PASRC) ../pa_tests/patest_stop.c
+#TESTC = $(PASRC) ../pa_tests/patest_sync.c
+#TESTC = $(PASRC) ../pa_tests/patest_pink.c
+#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
+#TESTC = $(PASRC) ../pa_tests/patest_clip.c
+#TESTC = $(PASRC) ../pa_tests/patest_dither.c
+#TESTC = $(PASRC) ../pa_tests/pa_devs.c
+#TESTC = $(PASRC) ../pa_tests/patest_many.c
+#TESTC = $(PASRC) ../pa_tests/patest_record.c
+#TESTC = $(PASRC) ../pa_tests/patest_wire.c
+#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
+
+# Tests that do not yet work.
+
+TESTH = $(PAINC)
+
+all: patest
+
+patest: $(TESTC) $(TESTH) Makefile
+ gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
+
+run: patest
+ ./patest
+
--- /dev/null
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * Linux OSS Implementation by douglas repetto and Phil Burk
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/*
+Modfication History
+ 1/2001 - Phil Burk - initial hack for Linux
+ 2/2001 - Douglas Repetto - many improvements, initial query support
+ 4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
+ 5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
+ use pthread_join() after thread shutdown.
+ 5/29/2001 - Phil - query for multiple devices, multiple formats,
+ input mode and input+output mode working,
+ Pa_GetCPULoad() implemented.
+ PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
+ SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
+ JH20010905 - Janos Haber - FreeBSD mods
+ 2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
+ added 24k and 16k to ratesToTry[]
+ fixed Pa_GetInternalDevice()
+ changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
+ handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
+ fixed Pa_StreamTime() for paqa_errs.c
+ fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
+ grep also for HP20010922 ...
+ PLB20010924 - Phil - merged Heiko's changes
+ removed sNumDevices and potential related bugs,
+ use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
+ simplify CPU Load calculation by comparing real-time to framesPerBuffer,
+ always close device when querying even if error occurs,
+ PLB20010927 - Phil - Improved negotiation for numChannels.
+ SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query.
+ DH20010115 - David Herring - fixed uninitialized handle.
+
+TODO
+O- change Pa_StreamTime() to query device (patest_sync.c)
+O- put semaphore lock around shared data?
+O- handle native formats better
+O- handle stereo-only device better ???
+O- what if input and output of a device capabilities differ (e.g. es1371) ???
+*/
+/*
+ PROPOSED - should we add this to "portaudio.h". Problem with
+ Pa_QueryDevice() not having same driver name os Pa_OpenStream().
+
+ A PaDriverInfo structure can be passed to the underlying device
+ on the Pa_OpenStream() call. The contents and interpretation of
+ the structure is determined by the PA implementation.
+*/
+typedef struct PaDriverInfo /* PROPOSED */
+{
+ /* Size of structure. Allows driver to extend the structure without breaking existing applications. */
+ int size;
+ /* Can be used to request a specific device name. */
+ const char *name;
+ unsigned long data;
+}
+PaDriverInfo;
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <math.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __linux__
+#include <linux/soundcard.h>
+#else
+#include <machine/soundcard.h> /* JH20010905 */
+#endif
+
+#include <sched.h>
+#include <pthread.h>
+
+/* Some versions of OSS do not define AFMT_S16_NE. Assume little endian. FIXME - check CPU*/
+#ifndef AFMT_S16_NE
+ #define AFMT_S16_NE AFMT_S16_LE
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define BAD_DEVICE_ID (-1)
+
+#define MIN_LATENCY_MSEC (100)
+#define MIN_TIMEOUT_MSEC (100)
+#define MAX_TIMEOUT_MSEC (1000)
+
+/************************************************* Definitions ********/
+#ifdef __linux__
+ #define DEVICE_NAME_BASE "/dev/dsp"
+#else
+ #define DEVICE_NAME_BASE "/dev/audio"
+#endif
+
+#define MAX_CHARS_DEVNAME (32)
+#define MAX_SAMPLE_RATES (10)
+typedef struct internalPortAudioDevice
+{
+ struct internalPortAudioDevice *pad_Next; /* Singly linked list. */
+ double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
+ char pad_DeviceName[MAX_CHARS_DEVNAME];
+ PaDeviceInfo pad_Info;
+}
+internalPortAudioDevice;
+
+/* Define structure to contain all OSS and Linux specific data. */
+typedef struct PaHostSoundControl
+{
+ int pahsc_OutputHandle;
+ int pahsc_InputHandle;
+ pthread_t pahsc_ThreadPID;
+ short *pahsc_NativeInputBuffer;
+ short *pahsc_NativeOutputBuffer;
+ unsigned int pahsc_BytesPerInputBuffer; /* native buffer size in bytes */
+ unsigned int pahsc_BytesPerOutputBuffer; /* native buffer size in bytes */
+ /* For measuring CPU utilization. */
+ struct timeval pahsc_EntryTime;
+ double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
+}
+PaHostSoundControl;
+
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sDeviceIndex = 0;
+static internalPortAudioDevice *sDeviceList = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sEnumerationError;
+static int sPaHostError = 0;
+
+/************************************************* Prototypes **********/
+
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
+static Pa_QueryDevices( void );
+static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad );
+static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate );
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ struct itimerval itimer;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ gettimeofday( &pahsc->pahsc_EntryTime, NULL );
+}
+
+static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
+{
+ long secs = timeA->tv_sec - timeB->tv_sec;
+ long usecs = secs * 1000000;
+ usecs += (timeA->tv_usec - timeB->tv_usec);
+ return usecs;
+}
+
+/******************************************************************************
+** Measure fractional CPU load based on real-time it took to calculate
+** buffers worth of output.
+*/
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ struct timeval currentTime;
+ long usecsElapsed;
+ double newUsage;
+
+#define LOWPASS_COEFFICIENT_0 (0.95)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+
+ if( gettimeofday( ¤tTime, NULL ) == 0 )
+ {
+ usecsElapsed = SubtractTime_AminusB( ¤tTime, &pahsc->pahsc_EntryTime );
+ /* Use inverse because it is faster than the divide. */
+ newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
+
+ past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+ (LOWPASS_COEFFICIENT_1 * newUsage);
+ }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+/*********************************************************************
+ * Try to open the named device.
+ * If it opens, try to set various rates and formats and fill in
+ * the device info structure.
+ */
+static PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad )
+{
+ int result = paHostError;
+ int numBytes;
+ int tempDevHandle;
+ int numChannels, maxNumChannels;
+ int format;
+ int numSampleRates;
+ int sampleRate;
+ int numRatesToTry;
+ int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
+ int i;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settins.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+
+ */
+ if ( (tempDevHandle = open(deviceName,O_WRONLY)) == -1 )
+ {
+ DBUG(("Pa_QueryDevice: could not open %s\n", deviceName ));
+ return paHostError;
+ }
+
+ /* Ask OSS what formats are supported by the hardware. */
+ pad->pad_Info.nativeSampleFormats = 0;
+ if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1)
+ {
+ ERR_RPT(("Pa_QueryDevice: could not get format info\n" ));
+ goto error;
+ }
+ if( format & AFMT_U8 ) pad->pad_Info.nativeSampleFormats |= paUInt8;
+ if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16;
+
+ /* Negotiate for the maximum number of channels for this device. PLB20010927
+ * Consider up to 16 as the upper number of channels.
+ * Variable numChannels should contain the actual upper limit after the call.
+ * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+ */
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
+ if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 )
+ {
+ /* ioctl() failed so bail out if we already have stereo */
+ if( numChannels > 2 ) break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) ) break;
+ DBUG(("Pa_QueryDevice: temp = %d\n", temp ))
+ if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+
+ /* The above negotiation may fail for an old driver so try this older technique. */
+ if( maxNumChannels < 1 )
+ {
+ int stereo = 1;
+ if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0)
+ {
+ maxNumChannels = 1;
+ }
+ else
+ {
+ maxNumChannels = (stereo) ? 2 : 1;
+ }
+ DBUG(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels ))
+ }
+
+ pad->pad_Info.maxOutputChannels = maxNumChannels;
+ DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
+
+ /* During channel negotiation, the last ioctl() may have failed. This can
+ * also cause sample rate negotiation to fail. Hence the following, to return
+ * to a supported number of channels. SG20011005 */
+ {
+ int temp = maxNumChannels;
+ if( temp > 2 ) temp = 2; /* use most reasonable default value */
+ ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp);
+ }
+
+ /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
+ * Eventually do separate queries for O_WRONLY and O_RDONLY
+ */
+ pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels;
+
+ DBUG(("Pa_QueryDevice: maxInputChannels = %d\n",
+ pad->pad_Info.maxInputChannels))
+
+
+ /* Determine available sample rates by trying each one and seeing result.
+ */
+ numSampleRates = 0;
+ numRatesToTry = sizeof(ratesToTry)/sizeof(int);
+ for (i = 0; i < numRatesToTry; i++)
+ {
+ sampleRate = ratesToTry[i];
+
+ if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) /* PLB20010817 */
+ {
+ if (sampleRate == ratesToTry[i])
+ {
+ DBUG(("Pa_QueryDevice: got sample rate: %d\n", sampleRate))
+ pad->pad_SampleRates[numSampleRates] = (float)ratesToTry[i];
+ numSampleRates++;
+ }
+ }
+ }
+
+ DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
+ if (numSampleRates==0) /* HP20010922 */
+ {
+ ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed).\n" ));
+ goto error;
+ }
+
+ pad->pad_Info.numSampleRates = numSampleRates;
+ pad->pad_Info.sampleRates = pad->pad_SampleRates;
+
+ pad->pad_Info.name = deviceName;
+
+ result = paNoError;
+
+error:
+ /* We MUST close the handle here or we won't be able to reopen it later!!! */
+ close(tempDevHandle);
+
+ return result;
+}
+
+/*********************************************************************
+ * Determines the number of available devices by trying to open
+ * each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
+ * Add each working device to a singly linked list of devices.
+ */
+static PaError Pa_QueryDevices( void )
+{
+ internalPortAudioDevice *pad, *lastPad;
+ int numBytes;
+ int go = 1;
+ int numDevices = 0;
+ PaError testResult;
+ PaError result = paNoError;
+
+ sDefaultInputDeviceID = paNoDevice;
+ sDefaultOutputDeviceID = paNoDevice;
+
+ lastPad = NULL;
+
+ while( go )
+ {
+ /* Allocate structure to hold device info. */
+ pad = PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
+ if( pad == NULL ) return paInsufficientMemory;
+ memset( pad, 0, sizeof(internalPortAudioDevice) );
+
+ /* Build name for device. */
+ if( numDevices == 0 )
+ {
+ sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
+ }
+ else
+ {
+ sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
+ }
+
+ DBUG(("Try device %s\n", pad->pad_DeviceName ));
+ testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
+ DBUG(("Pa_QueryDevice returned %d\n", testResult ));
+ if( testResult != paNoError )
+ {
+ if( lastPad == NULL )
+ {
+ result = testResult; /* No good devices! */
+ }
+ go = 0;
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ }
+ else
+ {
+ numDevices += 1;
+ /* Add to linked list of devices. */
+ if( lastPad )
+ {
+ lastPad->pad_Next = pad;
+ }
+ else
+ {
+ sDeviceList = pad; /* First element in linked list. */
+ }
+ lastPad = pad;
+ }
+ }
+
+ return result;
+
+}
+
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ int numDevices = 0;
+ internalPortAudioDevice *pad;
+
+ if( sDeviceList == NULL ) Pa_Initialize();
+ /* Count devices in list. */
+ pad = sDeviceList;
+ while( pad != NULL )
+ {
+ pad = pad->pad_Next;
+ numDevices++;
+ }
+
+ return numDevices;
+}
+
+/*************************************************************************/
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
+{
+ internalPortAudioDevice *pad;
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ pad = sDeviceList;
+ while( id > 0 )
+ {
+ pad = pad->pad_Next;
+ id--;
+ }
+ return pad;
+}
+
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ internalPortAudioDevice *pad;
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ pad = Pa_GetInternalDevice( id );
+ return &pad->pad_Info ;
+}
+
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sDeviceList == NULL )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ /* return paNoDevice; */
+ return 0;
+}
+
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ return 0;
+}
+
+/**********************************************************************
+** Make sure that we have queried the device capabilities.
+*/
+
+PaError PaHost_Init( void )
+{
+ return Pa_MaybeQueryDevices();
+}
+
+/*******************************************************************************************/
+static PaError Pa_AudioThreadProc( internalPortAudioStream *past )
+{
+ PaError result = 0;
+ PaHostSoundControl *pahsc;
+ short bytes_read = 0;
+
+#ifdef GNUSTEP
+ GSRegisterCurrentThread(); /* SB20010904 */
+#endif
+
+#if 1
+ {
+#define SCHEDULER_POLICY SCHED_RR
+
+ struct sched_param schp;
+ memset(&schp, 0, sizeof(schp));
+ schp.sched_priority = sched_get_priority_max(SCHEDULER_POLICY);
+
+ if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0)
+ {
+ perror("sched_setscheduler");
+ printf("PortAudio: only superuser can use real-time priority.\n");
+ }
+ else
+ {
+ printf("PortAudio: callback priority set to level %d!\n", schp.sched_priority);
+ }
+ }
+#endif
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+ past->past_IsActive = 1;
+ DBUG(("entering thread.\n"));
+
+ while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) )
+ {
+
+ DBUG(("go!\n"));
+ /* Read data from device */
+ if(pahsc->pahsc_NativeInputBuffer)
+ {
+ bytes_read = read(pahsc->pahsc_InputHandle,
+ (void *)pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_BytesPerInputBuffer);
+
+ DBUG(("bytes_read: %d\n", bytes_read));
+ }
+
+ /* Convert 16 bit native data to user data and call user routine. */
+ DBUG(("converting...\n"));
+ Pa_StartUsageCalculation( past );
+ result = Pa_CallConvertInt16( past,
+ pahsc->pahsc_NativeInputBuffer,
+ pahsc->pahsc_NativeOutputBuffer );
+ Pa_EndUsageCalculation( past );
+ if( result != 0)
+ {
+ DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n",
+ result));
+ break;
+ }
+
+ /* Write data to device. */
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+
+ write(pahsc->pahsc_OutputHandle,
+ (void *)pahsc->pahsc_NativeOutputBuffer,
+ pahsc->pahsc_BytesPerOutputBuffer);
+ }
+ }
+
+ past->past_IsActive = 0;
+ DBUG(("leaving thread.\n"));
+
+#ifdef GNUSTEP
+ GSUnregisterCurrentThread(); /* SB20010904 */
+#endif
+ return 0;
+}
+
+/*******************************************************************************************/
+static PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
+{
+ PaError result = paNoError;
+ int tmp;
+
+ /* Set format, channels, and rate in this order to keep OSS happy. */
+ /* Set data format. FIXME - handle more native formats. */
+ tmp = AFMT_S16_NE;
+ if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" ));
+ return paHostError;
+ }
+ if( tmp != AFMT_S16_NE)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" ));
+ return paHostError;
+ }
+
+
+ /* Set number of channels. */
+ tmp = numChannels;
+ if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" ));
+ return paHostError;
+ }
+ if( tmp != numChannels)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels ));
+ return paHostError;
+ }
+
+ /* Set playing frequency. 44100, 22050 and 11025 are safe bets. */
+ tmp = sampleRate;
+ if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" ));
+ return paHostError;
+ }
+ if( tmp != sampleRate)
+ {
+ ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate ));
+ return paHostError;
+ }
+
+ return result;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+ int log2 = 0;
+ while( (1<<log2) < n ) log2++;
+ return log2;
+}
+
+/*******************************************************************************************
+** Set number of fragments and size of fragments to achieve desired latency.
+*/
+static void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame )
+{
+ int tmp;
+ int numFrames , bufferSize, powerOfTwo;
+
+ /* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
+ while( numBuffers > 8 )
+ {
+ numBuffers = (numBuffers + 1) >> 1;
+ framesPerBuffer = framesPerBuffer << 1;
+ }
+
+ /* calculate size of buffers in bytes */
+ bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME */
+
+ /* Calculate next largest power of two */
+ powerOfTwo = CalcHigherLogTwo( bufferSize );
+ DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+ numBuffers, framesPerBuffer, powerOfTwo ));
+
+ /* Encode info into a single int */
+ tmp=(numBuffers<<16) + powerOfTwo;
+
+ if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1)
+ {
+ ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" ));
+ /* Don't return an error. Best to just continue and hope for the best. */
+ ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n",
+ numBuffers, framesPerBuffer, powerOfTwo ));
+ }
+}
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the cshrc file.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+
+int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond )
+{
+ int minBuffers;
+ int minLatencyMsec = MIN_LATENCY_MSEC;
+ char *minLatencyText = getenv(PA_LATENCY_ENV_NAME);
+ if( minLatencyText != NULL )
+ {
+ PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText ));
+ minLatencyMsec = atoi( minLatencyText );
+ if( minLatencyMsec < 1 ) minLatencyMsec = 1;
+ else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000;
+ }
+
+ minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer ));
+ if( minBuffers < 2 ) minBuffers = 2;
+ return minBuffers;
+}
+
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ int tmp;
+ int flags;
+ int numBytes, maxChannels;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *pad;
+ DBUG(("PaHost_OpenStream() called.\n" ));
+
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl));
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+
+ pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */
+ pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+
+ /* Allocate native buffers. */
+ pahsc->pahsc_BytesPerInputBuffer = past->past_FramesPerUserBuffer *
+ past->past_NumInputChannels * sizeof(short);
+ if( past->past_NumInputChannels > 0)
+ {
+ pahsc->pahsc_NativeInputBuffer = (short *) malloc(pahsc->pahsc_BytesPerInputBuffer);
+ if( pahsc->pahsc_NativeInputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+ pahsc->pahsc_BytesPerOutputBuffer = past->past_FramesPerUserBuffer *
+ past->past_NumOutputChannels * sizeof(short);
+ if( past->past_NumOutputChannels > 0)
+ {
+ pahsc->pahsc_NativeOutputBuffer = (short *) malloc(pahsc->pahsc_BytesPerOutputBuffer);
+ if( pahsc->pahsc_NativeOutputBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+
+ pahsc->pahsc_InverseMicrosPerBuffer = past->past_SampleRate / (1000000.0 * past->past_FramesPerUserBuffer);
+ DBUG(("pahsc_InverseMicrosPerBuffer = %g\n", pahsc->pahsc_InverseMicrosPerBuffer ));
+
+ /* ------------------------- OPEN DEVICE -----------------------*/
+
+ /* just output */
+ if (past->past_OutputDeviceID == past->past_InputDeviceID)
+ {
+
+ if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0) )
+ {
+ pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_RDWR\n", pad->pad_DeviceName ));
+ pahsc->pahsc_OutputHandle = pahsc->pahsc_InputHandle =
+ open(pad->pad_DeviceName,O_RDWR);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDWR\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_OutputHandle,
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumOutputChannels );
+ result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
+ past->past_NumOutputChannels, (int)past->past_SampleRate );
+ }
+ }
+ else
+ {
+ if (past->past_NumOutputChannels > 0)
+ {
+ pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_WRONLY\n", pad->pad_DeviceName ));
+ pahsc->pahsc_OutputHandle = open(pad->pad_DeviceName,O_WRONLY);
+ if(pahsc->pahsc_OutputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_WRONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_OutputHandle,
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumOutputChannels );
+ result = Pa_SetupDeviceFormat( pahsc->pahsc_OutputHandle,
+ past->past_NumOutputChannels, (int)past->past_SampleRate );
+ }
+
+ if (past->past_NumInputChannels > 0)
+ {
+ pad = Pa_GetInternalDevice( past->past_InputDeviceID );
+ DBUG(("PaHost_OpenStream: attempt to open %s for O_RDONLY\n", pad->pad_DeviceName ));
+ pahsc->pahsc_InputHandle = open(pad->pad_DeviceName,O_RDONLY);
+ if(pahsc->pahsc_InputHandle==-1)
+ {
+ ERR_RPT(("PaHost_OpenStream: could not open %s for O_RDONLY\n", pad->pad_DeviceName ));
+ result = paHostError;
+ goto error;
+ }
+ Pa_SetLatency( pahsc->pahsc_InputHandle, /* DH20010115 - was OutputHandle! */
+ past->past_NumUserBuffers, past->past_FramesPerUserBuffer,
+ past->past_NumInputChannels );
+ result = Pa_SetupDeviceFormat( pahsc->pahsc_InputHandle,
+ past->past_NumInputChannels, (int)past->past_SampleRate );
+ }
+ }
+
+
+ DBUG(("PaHost_OpenStream: SUCCESS - result = %d\n", result ));
+ return result;
+
+error:
+ ERR_RPT(("PaHost_OpenStream: ERROR - result = %d\n", result ));
+ PaHost_CloseStream( past );
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ int hres;
+
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ past->past_StopSoon = 0;
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+
+ /* Use pthread_create() instead of __clone() because:
+ * - pthread_create also works for other UNIX systems like Solaris,
+ * - the Java HotSpot VM crashes in pthread_setcanceltype() when using __clone()
+ */
+ hres = pthread_create(&(pahsc->pahsc_ThreadPID),
+ NULL /*pthread_attr_t * attr*/,
+ (void*)Pa_AudioThreadProc, past);
+ if( hres != 0 )
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ goto error;
+ }
+
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ int hres;
+ long timeOut;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ if( pahsc == NULL ) return paNoError;
+
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ past->past_StopSoon = 1;
+ /* If aborting, tell background thread to stop NOW! */
+ if( abort ) past->past_StopNow = 1;
+
+ /* Join thread to recover memory resources. */
+ if( pahsc->pahsc_ThreadPID != -1 )
+ {
+ /* This check is needed for GNUSTEP - SB20010904 */
+ if ( !pthread_equal( pahsc->pahsc_ThreadPID, pthread_self() ) )
+ {
+ hres = pthread_join( pahsc->pahsc_ThreadPID, NULL );
+ }
+ else
+ {
+ DBUG(("Play thread was stopped from itself - can't do pthread_join()\n"));
+ hres = 0;
+ }
+
+ if( hres != 0 )
+ {
+ result = paHostError;
+ sPaHostError = hres;
+ }
+ pahsc->pahsc_ThreadPID = -1;
+ }
+
+ past->past_IsActive = 0;
+
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ return paNoError;
+}
+
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ if( pahsc->pahsc_OutputHandle != BAD_DEVICE_ID )
+ {
+ int err;
+ DBUG(("PaHost_CloseStream: attempt to close output device handle = %d\n",
+ pahsc->pahsc_OutputHandle ));
+ err = close(pahsc->pahsc_OutputHandle);
+ if( err < 0 )
+ {
+ ERR_RPT(("PaHost_CloseStream: warning, closing output device failed.\n"));
+ }
+ }
+
+ if( (pahsc->pahsc_InputHandle != BAD_DEVICE_ID) &&
+ (pahsc->pahsc_InputHandle != pahsc->pahsc_OutputHandle) )
+ {
+ int err;
+ DBUG(("PaHost_CloseStream: attempt to close input device handle = %d\n",
+ pahsc->pahsc_InputHandle ));
+ err = close(pahsc->pahsc_InputHandle);
+ if( err < 0 )
+ {
+ ERR_RPT(("PaHost_CloseStream: warning, closing input device failed.\n"));
+ }
+ }
+ pahsc->pahsc_OutputHandle = BAD_DEVICE_ID;
+ pahsc->pahsc_InputHandle = BAD_DEVICE_ID;
+
+ if( pahsc->pahsc_NativeInputBuffer )
+ {
+ free( pahsc->pahsc_NativeInputBuffer );
+ pahsc->pahsc_NativeInputBuffer = NULL;
+ }
+ if( pahsc->pahsc_NativeOutputBuffer )
+ {
+ free( pahsc->pahsc_NativeOutputBuffer );
+ pahsc->pahsc_NativeOutputBuffer = NULL;
+ }
+
+ free( pahsc );
+ past->past_DeviceData = NULL;
+ return paNoError;
+}
+
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+ /* Free all of the linked devices. */
+ internalPortAudioDevice *pad, *nextPad;
+ pad = sDeviceList;
+ while( pad != NULL )
+ {
+ nextPad = pad->pad_Next;
+ DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName ));
+ PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
+ pad = nextPad;
+ }
+ sDeviceList = NULL;
+ return 0;
+}
+
+/*************************************************************************
+ * Sleep for the requested number of milliseconds.
+ */
+void Pa_Sleep( long msec )
+{
+#if 0
+ struct timeval timeout;
+ timeout.tv_sec = msec / 1000;
+ timeout.tv_usec = (msec % 1000) * 1000;
+ select( 0, NULL, NULL, NULL, &timeout );
+#else
+ long usecs = msec * 1000;
+ usleep( usecs );
+#endif
+}
+
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */
+ if( addr != NULL ) memset( addr, 0, numBytes );
+ return addr;
+}
+
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr != NULL ) free( addr );
+}
+
+
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) (past->past_IsActive != 0);
+}
+
+/***********************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ /* FIXME - return actual frames played, not frames generated.
+ ** Need to query the output device somehow.
+ */
+ if( past == NULL ) return paBadStreamPtr;
+ return past->past_FrameCount;
+}
+
+/***********************************************************************/
+long Pa_GetHostError( void )
+{
+ return (long) sPaHostError;
+}
--- /dev/null
+/*
+ * recplay.c
+ * Phil Burk
+ * Minimal record and playback test.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef __STDC__
+/* #include <getopt.h> */
+#endif /* __STDC__ */
+#include <fcntl.h>
+#ifdef __STDC__
+#include <string.h>
+#else /* __STDC__ */
+#include <strings.h>
+#endif /* __STDC__ */
+#include <sys/soundcard.h>
+
+#define NUM_BYTES (64*1024)
+#define BLOCK_SIZE (4*1024)
+
+#define AUDIO "/dev/dsp"
+
+char buffer[NUM_BYTES];
+
+int audioDev = 0;
+
+main (int argc, char *argv[])
+{
+ int numLeft;
+ char *ptr;
+ int num;
+ int samplesize;
+
+ /********** RECORD ********************/
+ /* Open audio device. */
+ audioDev = open (AUDIO, O_RDONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Record in blocks */
+ printf("Begin recording.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Read %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+
+ /********** PLAYBACK ********************/
+ /* Open audio device for writing. */
+ audioDev = open (AUDIO, O_WRONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Play in blocks */
+ printf("Begin playing.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Wrote %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+}
--- /dev/null
+/*
+ * $Id$
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info.
+#include <objbase.h>
+#include <unknwn.h>
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/************************************************************************************/
+void DSW_Term( DSoundWrapper *dsw )
+{
+ // Cleanup the sound buffers
+ if (dsw->dsw_OutputBuffer)
+ {
+ IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
+ dsw->dsw_OutputBuffer = NULL;
+ }
+#if SUPPORT_AUDIO_CAPTURE
+ if (dsw->dsw_InputBuffer)
+ {
+ IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
+ dsw->dsw_InputBuffer = NULL;
+ }
+ if (dsw->dsw_pDirectSoundCapture)
+ {
+ IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
+ dsw->dsw_pDirectSoundCapture = NULL;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ if (dsw->dsw_pDirectSound)
+ {
+ IDirectSound_Release( dsw->dsw_pDirectSound );
+ dsw->dsw_pDirectSound = NULL;
+ }
+}
+/************************************************************************************/
+HRESULT DSW_Init( DSoundWrapper *dsw )
+{
+ memset( dsw, 0, sizeof(DSoundWrapper) );
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ // Create the DS object
+ HRESULT hr = DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
+{
+ DWORD dwDataLen;
+ DWORD playCursor;
+ HRESULT result;
+ LPDIRECTSOUNDBUFFER pPrimaryBuffer;
+ HWND hWnd;
+ HRESULT hr;
+ WAVEFORMATEX wfFormat;
+ DSBUFFERDESC primaryDesc;
+ DSBUFFERDESC secondaryDesc;
+ unsigned char* pDSBuffData;
+ LARGE_INTEGER counterFrequency;
+ dsw->dsw_OutputSize = bytesPerBuffer;
+ dsw->dsw_OutputRunning = FALSE;
+ dsw->dsw_OutputUnderflows = 0;
+ dsw->dsw_FramesWritten = 0;
+ dsw->dsw_BytesPerFrame = nChannels * sizeof(short);
+ // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
+ // applications's window. Also if that window is closed before the Buffer is closed
+ // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
+ // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
+ // hWnd = GetForegroundWindow();
+ hWnd = GetDesktopWindow();
+ // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
+ // Exclusize also prevents unexpected sounds from other apps during a performance.
+ if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
+ hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
+ {
+ return hr;
+ }
+ // -----------------------------------------------------------------------
+ // Create primary buffer and set format just so we can specify our custom format.
+ // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
+ // Setup the primary buffer description
+ ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
+ primaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
+ primaryDesc.dwBufferBytes = 0;
+ primaryDesc.lpwfxFormat = NULL;
+ // Create the buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ // Set the primary buffer's format
+ if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
+ secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ secondaryDesc.dwBufferBytes = bytesPerBuffer;
+ secondaryDesc.lpwfxFormat = &wfFormat;
+ // Create the secondary buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
+ // Lock the DS buffer
+ if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
+ &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
+ // Zero the DS buffer
+ ZeroMemory(pDSBuffData, dwDataLen);
+ // Unlock the DS buffer
+ if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
+ if( QueryPerformanceFrequency( &counterFrequency ) )
+ {
+ int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
+ AddTraceMessage("dsw_CounterTicksPerBuffer = %d\n", dsw->dsw_CounterTicksPerBuffer.LowPart );
+ }
+ else
+ {
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
+ }
+ // Let DSound set the starting write position because if we set it to zero, it looks like the
+ // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerFrame;
+ /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartOutput( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
+ dsw->dsw_LastPlayCursor = 0;
+ dsw->dsw_FramesPlayed = 0;
+ hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ // Start the buffer playback in a loop.
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_OutputRunning = TRUE;
+ }
+
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_StopOutput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ dsw->dsw_OutputRunning = FALSE;
+ return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
+{
+ HRESULT hr;
+ DWORD playCursor;
+ DWORD writeCursor;
+ long numBytesEmpty;
+ long playWriteGap;
+ // Query to see how much room is in buffer.
+ // Note: Even though writeCursor is not used, it must be passed to prevent DirectSound from dieing
+ // under WinNT. The Microsoft documentation says we can pass NULL but apparently not.
+ // Thanks to Max Rheiner for the fix.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ AddTraceMessage("playCursor", playCursor);
+ AddTraceMessage("dsw_WriteOffset", dsw->dsw_WriteOffset);
+ // Determine size of gap between playIndex and WriteIndex that we cannot write into.
+ playWriteGap = writeCursor - playCursor;
+ if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
+ /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
+ /* Attempt to detect playCursor wrap-around and correct it. */
+ if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
+ {
+ /* How much time has elapsed since last check. */
+ LARGE_INTEGER currentTime;
+ LARGE_INTEGER elapsedTime;
+ long bytesPlayed;
+ long bytesExpected;
+ long buffersWrapped;
+ QueryPerformanceCounter( ¤tTime );
+ elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
+ dsw->dsw_LastPlayTime = currentTime;
+ /* How many bytes does DirectSound say have been played. */
+ bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
+ if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
+ dsw->dsw_LastPlayCursor = playCursor;
+ /* Calculate how many bytes we would have expected to been played by now. */
+ bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
+ buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
+ if( buffersWrapped > 0 )
+ {
+ AddTraceMessage("playCursor wrapped! bytesPlayed", bytesPlayed );
+ AddTraceMessage("playCursor wrapped! bytesExpected", bytesExpected );
+ playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+ bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+ }
+ /* Maintain frame output cursor. */
+ dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerFrame);
+ }
+ numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
+ if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
+ /* Have we underflowed? */
+ if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
+ {
+ if( dsw->dsw_OutputRunning )
+ {
+ dsw->dsw_OutputUnderflows += 1;
+ AddTraceMessage("underflow detected! numBytesEmpty", numBytesEmpty );
+ }
+ dsw->dsw_WriteOffset = writeCursor;
+ numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
+ }
+ *bytesEmpty = numBytesEmpty;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ long bytesEmpty;
+ hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
+ if (hr != DS_OK) return hr;
+ if( bytesEmpty == 0 ) return DS_OK;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ ZeroMemory(lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ ZeroMemory(lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ CopyMemory(lpbuf1, buf, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+ DWORD status;
+ if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+ return( DSERR_INVALIDPARAM );
+ else
+ return( status );
+}
+
+#if SUPPORT_AUDIO_CAPTURE
+/* These routines are used to support audio input.
+ * Do NOT compile these calls when using NT4 because it does
+ * not support the entry points.
+ */
+/************************************************************************************/
+HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ HRESULT hr = DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer )
+{
+ DSCBUFFERDESC captureDesc;
+ WAVEFORMATEX wfFormat;
+ HRESULT result;
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8);
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ dsw->dsw_InputSize = bytesPerBuffer;
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+ captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+ captureDesc.dwFlags = 0;
+ captureDesc.dwBufferBytes = bytesPerBuffer;
+ captureDesc.lpwfxFormat = &wfFormat;
+ // Create the capture buffer
+ if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
+ &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
+ dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartInput( DSoundWrapper *dsw )
+{
+ // Start the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_StopInput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
+{
+ HRESULT hr;
+ DWORD capturePos;
+ DWORD readPos;
+ long filled;
+ // Query to see how much data is in buffer.
+ // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
+ // so let's pass a pointer just to be safe.
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ filled = readPos - dsw->dsw_ReadOffset;
+ if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
+ *bytesFilled = filled;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy from DS to the buffer
+ CopyMemory( buf, lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
+ IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ }
+ return hr;
+}
+
+#endif /* SUPPORT_AUDIO_CAPTURE */
--- /dev/null
+#ifndef __DSOUND_WRAPPER_H
+#define __DSOUND_WRAPPER_H
+/*
+ * $Id$
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * For PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <DSound.h>
+#if !defined(BOOL)
+#define BOOL short
+#endif
+#ifndef SUPPORT_AUDIO_CAPTURE
+#define SUPPORT_AUDIO_CAPTURE (1)
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define DSW_NUM_POSITIONS (4)
+#define DSW_NUM_EVENTS (5)
+#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
+
+typedef struct
+{
+ /* Output */
+ LPDIRECTSOUND dsw_pDirectSound;
+ LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
+ DWORD dsw_WriteOffset; /* last write position */
+ INT dsw_OutputSize;
+ INT dsw_BytesPerFrame;
+ /* Try to detect play buffer underflows. */
+ LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
+ LARGE_INTEGER dsw_LastPlayTime;
+ UINT dsw_LastPlayCursor;
+ UINT dsw_OutputUnderflows;
+ BOOL dsw_OutputRunning;
+ /* use double which lets us can play for several thousand years with enough precision */
+ double dsw_FramesWritten;
+ double dsw_FramesPlayed;
+#if SUPPORT_AUDIO_CAPTURE
+ /* Input */
+ LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+ LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
+ UINT dsw_ReadOffset; /* last read position */
+ UINT dsw_InputSize;
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+}
+DSoundWrapper;
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ int nChannels, int bufSize );
+HRESULT DSW_StartOutput( DSoundWrapper *dsw );
+HRESULT DSW_StopOutput( DSoundWrapper *dsw );
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
+HRESULT DSW_Enumerate( DSoundWrapper *dsw );
+
+#if SUPPORT_AUDIO_CAPTURE
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ int nChannels, int bufSize );
+HRESULT DSW_StartInput( DSoundWrapper *dsw );
+HRESULT DSW_StopInput( DSoundWrapper *dsw );
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __DSOUND_WRAPPER_H */
--- /dev/null
+/*
+ * $Id$
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ *
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/* Modifications
+ * 7/19/01 Mike Berry - casts for compiling with __MWERKS__ CodeWarrior
+ * 9/27/01 Phil Burk - use number of frames instead of real-time for CPULoad calculation.
+ */
+/* Compiler flags:
+ SUPPORT_AUDIO_CAPTURE - define this flag if you want to SUPPORT_AUDIO_CAPTURE
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif //__MWERKS__
+#include <math.h>
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+#include "dsound_wrapper.h"
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+#define PA_USE_HIGH_LATENCY (0)
+#if PA_USE_HIGH_LATENCY
+#define PA_WIN_9X_LATENCY (500)
+#define PA_WIN_NT_LATENCY (600)
+#else
+#define PA_WIN_9X_LATENCY (140)
+#define PA_WIN_NT_LATENCY (280)
+#endif
+/* Trigger an underflow for testing purposes. Should normally be (0). */
+#define PA_SIMULATE_UNDERFLOW (0)
+#if PA_SIMULATE_UNDERFLOW
+static gUnderCallbackCounter = 0;
+#define UNDER_START_GAP (10)
+#define UNDER_STOP_GAP (UNDER_START_GAP + 4)
+#endif
+/************************************************* Definitions ********/
+typedef struct internalPortAudioStream internalPortAudioStream;
+typedef struct internalPortAudioDevice
+{
+ GUID pad_GUID;
+ GUID *pad_lpGUID;
+ double pad_SampleRates[10]; /* for pointing to from pad_Info FIXME?!*/
+ PaDeviceInfo pad_Info;
+}
+internalPortAudioDevice;
+/* Define structure to contain all DirectSound and Windows specific data. */
+typedef struct PaHostSoundControl
+{
+ DSoundWrapper pahsc_DSoundWrapper;
+ MMRESULT pahsc_TimerID;
+ BOOL pahsc_IfInsideCallback; /* Test for reentrancy. */
+ short *pahsc_NativeBuffer;
+ unsigned int pahsc_BytesPerBuffer; /* native buffer size in bytes */
+ double pahsc_ValidFramesWritten;
+ int pahsc_FramesPerDSBuffer;
+ /* For measuring CPU utilization. */
+ LARGE_INTEGER pahsc_EntryCount;
+ double pahsc_InverseTicksPerUserBuffer;
+}
+PaHostSoundControl;
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumDevices = 0;
+static int sDeviceIndex = 0;
+static internalPortAudioDevice *sDevices = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sEnumerationError;
+static int sPaHostError = 0;
+/************************************************* Prototypes **********/
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
+static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext );
+static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext );
+static Pa_QueryDevices( void );
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
+ DWORD dwUser, DWORD dw1, DWORD dw2);
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
+}
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ LARGE_INTEGER CurrentCount = { 0, 0 };
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /*
+ ** Measure CPU utilization during this callback. Note that this calculation
+ ** assumes that we had the processor the whole time.
+ */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+ if( QueryPerformanceCounter( &CurrentCount ) )
+ {
+ LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+ double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerUserBuffer;
+ past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+ (LOWPASS_COEFFICIENT_1 * newUsage);
+ }
+}
+/****************************************** END CPU UTILIZATION *******/
+static PaError Pa_QueryDevices( void )
+{
+ int numBytes;
+ sDefaultInputDeviceID = paNoDevice;
+ sDefaultOutputDeviceID = paNoDevice;
+ /* Enumerate once just to count devices. */
+ sNumDevices = 0; // for default device
+ DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
+#if SUPPORT_AUDIO_CAPTURE
+ DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ /* Allocate structures to hold device info. */
+ numBytes = sNumDevices * sizeof(internalPortAudioDevice);
+ sDevices = (internalPortAudioDevice *)PaHost_AllocateFastMemory( numBytes ); /* MEM */
+ if( sDevices == NULL ) return paInsufficientMemory;
+ /* Enumerate again to fill in structures. */
+ sDeviceIndex = 0;
+ sEnumerationError = 0;
+ DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)0 );
+#if SUPPORT_AUDIO_CAPTURE
+ if( sEnumerationError != paNoError ) return sEnumerationError;
+ sEnumerationError = 0;
+ DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)1 );
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ return sEnumerationError;
+}
+/************************************************************************************/
+long Pa_GetHostError()
+{
+ return sPaHostError;
+}
+/************************************************************************************
+** Just count devices so we know how much memory to allocate.
+*/
+static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext )
+{
+ sNumDevices++;
+ return TRUE;
+}
+/************************************************************************************
+** Extract capabilities info from each device.
+*/
+static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext )
+{
+ HRESULT hr;
+ LPDIRECTSOUND lpDirectSound;
+#if SUPPORT_AUDIO_CAPTURE
+ LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ int isInput = (int) lpContext; /* Passed from Pa_CountDevices() */
+ internalPortAudioDevice *pad;
+
+ if( sDeviceIndex >= sNumDevices )
+ {
+ sEnumerationError = paInternalError;
+ return FALSE;
+ }
+ pad = &sDevices[sDeviceIndex];
+ /* Copy GUID to static array. Set pointer. */
+ if( lpGUID == NULL )
+ {
+ pad->pad_lpGUID = NULL;
+ }
+ else
+ {
+ memcpy( &pad->pad_GUID, lpGUID, sizeof(GUID) );
+ pad->pad_lpGUID = &pad->pad_GUID;
+ }
+ pad->pad_Info.sampleRates = pad->pad_SampleRates; /* Point to array. */
+ /* Allocate room for descriptive name. */
+ if( lpszDesc != NULL )
+ {
+ int len = strlen(lpszDesc);
+ pad->pad_Info.name = (char *)malloc( len+1 );
+ if( pad->pad_Info.name == NULL )
+ {
+ sEnumerationError = paInsufficientMemory;
+ return FALSE;
+ }
+ memcpy( (void *) pad->pad_Info.name, lpszDesc, len+1 );
+ }
+#if SUPPORT_AUDIO_CAPTURE
+ if( isInput )
+ {
+ /********** Input ******************************/
+ DSCCAPS caps;
+ if( lpGUID == NULL ) sDefaultInputDeviceID = sDeviceIndex;
+ hr = DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
+ if( hr != DS_OK )
+ {
+ pad->pad_Info.maxInputChannels = 0;
+ DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr ));
+ }
+ else
+ {
+ /* Query device characteristics. */
+ caps.dwSize = sizeof(caps);
+ IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+ /* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */
+ pad->pad_Info.maxInputChannels = caps.dwChannels;
+ /* Determine sample rates from flags. */
+ if( caps.dwChannels == 2 )
+ {
+ int index = 0;
+ if( caps.dwFormats & WAVE_FORMAT_1S16) pad->pad_SampleRates[index++] = 11025.0;
+ if( caps.dwFormats & WAVE_FORMAT_2S16) pad->pad_SampleRates[index++] = 22050.0;
+ if( caps.dwFormats & WAVE_FORMAT_4S16) pad->pad_SampleRates[index++] = 44100.0;
+ pad->pad_Info.numSampleRates = index;
+ }
+ else if( caps.dwChannels == 1 )
+ {
+ int index = 0;
+ if( caps.dwFormats & WAVE_FORMAT_1M16) pad->pad_SampleRates[index++] = 11025.0;
+ if( caps.dwFormats & WAVE_FORMAT_2M16) pad->pad_SampleRates[index++] = 22050.0;
+ if( caps.dwFormats & WAVE_FORMAT_4M16) pad->pad_SampleRates[index++] = 44100.0;
+ pad->pad_Info.numSampleRates = index;
+ }
+ else pad->pad_Info.numSampleRates = 0;
+ IDirectSoundCapture_Release( lpDirectSoundCapture );
+ }
+ }
+ else
+#endif /* SUPPORT_AUDIO_CAPTURE */
+
+ {
+ /********** Output ******************************/
+ DSCAPS caps;
+ if( lpGUID == NULL ) sDefaultOutputDeviceID = sDeviceIndex;
+ /* Create interfaces for each object. */
+ hr = DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
+ if( hr != DS_OK )
+ {
+ pad->pad_Info.maxOutputChannels = 0;
+ DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr ));
+ }
+ else
+ {
+ /* Query device characteristics. */
+ caps.dwSize = sizeof(caps);
+ IDirectSound_GetCaps( lpDirectSound, &caps );
+ pad->pad_Info.maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+ /* Get sample rates. */
+ pad->pad_SampleRates[0] = (double) caps.dwMinSecondarySampleRate;
+ pad->pad_SampleRates[1] = (double) caps.dwMaxSecondarySampleRate;
+ if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) pad->pad_Info.numSampleRates = -1;
+ else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ {
+ if( caps.dwMinSecondarySampleRate == 0 )
+ {
+ /*
+ ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+ ** But it supports continuous sampling.
+ ** So fake range of rates, and hope it really supports it.
+ */
+ pad->pad_SampleRates[0] = 11025.0f;
+ pad->pad_SampleRates[1] = 48000.0f;
+ pad->pad_Info.numSampleRates = -1; /* continuous range */
+
+ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+ }
+ else
+ {
+ pad->pad_Info.numSampleRates = 1;
+ }
+ }
+ else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+ {
+ /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+ ** But we know that they really support a range of rates!
+ ** So when we see a ridiculous set of rates, assume it is a range.
+ */
+ pad->pad_Info.numSampleRates = -1;
+ DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+ }
+ else pad->pad_Info.numSampleRates = 2;
+ IDirectSound_Release( lpDirectSound );
+ }
+ }
+ pad->pad_Info.nativeSampleFormats = paInt16;
+ sDeviceIndex++;
+ return( TRUE );
+}
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ if( sNumDevices <= 0 ) Pa_Initialize();
+ return sNumDevices;
+}
+static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
+{
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ return &sDevices[id];
+}
+/*************************************************************************/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+ internalPortAudioDevice *pad;
+ if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
+ pad = Pa_GetInternalDevice( id );
+ return &pad->pad_Info ;
+}
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sNumDevices == 0 )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+** set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE (32)
+#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+ DWORD hresult;
+ char envbuf[PA_ENV_BUF_SIZE];
+ PaDeviceID recommendedID = paNoDevice;
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ recommendedID = atoi( envbuf );
+ }
+ return recommendedID;
+}
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultInputDeviceID;
+ }
+ return result;
+}
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultOutputDeviceID;
+ }
+ return result;
+}
+/**********************************************************************
+** Make sure that we have queried the device capabilities.
+*/
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+ PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+ return Pa_MaybeQueryDevices();
+}
+static PaError Pa_TimeSlice( internalPortAudioStream *past )
+{
+ PaError result = 0;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ long bytesToXfer = 0;
+ long numChunks;
+ HRESULT hresult;
+ PaHostSoundControl *pahsc;
+ short *nativeBufPtr;
+ past->past_NumCallbacks += 1;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ /* How much input data is available? */
+#if SUPPORT_AUDIO_CAPTURE
+ if( past->past_NumInputChannels > 0 )
+ {
+ DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled );
+ bytesToXfer = bytesFilled;
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ /* How much output room is available? */
+ if( past->past_NumOutputChannels > 0 )
+ {
+ DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty );
+ bytesToXfer = bytesEmpty;
+ }
+ AddTraceMessage( "bytesEmpty ", bytesEmpty );
+ /* Choose smallest value if both are active. */
+ if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) )
+ {
+ bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty;
+ }
+ /* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n",
+ bytesFilled, bytesEmpty, bytesToXfer);
+ */
+ /* Quantize to multiples of a buffer. */
+ numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer;
+ if( numChunks > (long)(past->past_NumUserBuffers/2) )
+ {
+ numChunks = (long)past->past_NumUserBuffers/2;
+ }
+ else if( numChunks < 0 )
+ {
+ numChunks = 0;
+ }
+ AddTraceMessage( "numChunks ", numChunks );
+ nativeBufPtr = pahsc->pahsc_NativeBuffer;
+ if( numChunks > 0 )
+ {
+ while( numChunks-- > 0 )
+ {
+ /* Measure usage based on time to process one user buffer. */
+ Pa_StartUsageCalculation( past );
+#if SUPPORT_AUDIO_CAPTURE
+ /* Get native data from DirectSound. */
+ if( past->past_NumInputChannels > 0 )
+ {
+ hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
+ if( hresult < 0 )
+ {
+ ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult));
+ sPaHostError = hresult;
+ break;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ /* Convert 16 bit native data to user data and call user routine. */
+ result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr );
+ if( result != 0) break;
+ /* Pass native data to DirectSound. */
+ if( past->past_NumOutputChannels > 0 )
+ {
+ /* static short DEBUGHACK = 0;
+ DEBUGHACK += 0x0049;
+ nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */
+ hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer );
+ if( hresult < 0 )
+ {
+ ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult));
+ sPaHostError = hresult;
+ break;
+ }
+ }
+ Pa_EndUsageCalculation( past );
+ }
+ }
+ return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+#if PA_SIMULATE_UNDERFLOW
+ gUnderCallbackCounter++;
+ if( (gUnderCallbackCounter >= UNDER_START_GAP) &&
+ (gUnderCallbackCounter <= UNDER_STOP_GAP) )
+ {
+ if( gUnderCallbackCounter == UNDER_START_GAP)
+ {
+ AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
+ }
+ if( gUnderCallbackCounter == UNDER_STOP_GAP)
+ {
+ AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter );
+ }
+ return;
+ }
+#endif
+ past = (internalPortAudioStream *) dwUser;
+ if( past == NULL ) return;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive )
+ {
+ if( past->past_StopNow )
+ {
+ past->past_IsActive = 0;
+ }
+ else if( past->past_StopSoon )
+ {
+ DSoundWrapper *dsw = &pahsc->pahsc_DSoundWrapper;
+ if( past->past_NumOutputChannels > 0 )
+ {
+ DSW_ZeroEmptySpace( dsw );
+ AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten );
+ AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed );
+ /* clear past_IsActive when all sound played */
+ if( dsw->dsw_FramesPlayed >= past->past_FrameCount )
+ {
+ past->past_IsActive = 0;
+ }
+ }
+ else
+ {
+ past->past_IsActive = 0;
+ }
+ }
+ else
+ {
+ pahsc->pahsc_IfInsideCallback = 1;
+ if( Pa_TimeSlice( past ) != 0) /* Call time slice independant of timing method. */
+ {
+ past->past_StopSoon = 1;
+ }
+ pahsc->pahsc_IfInsideCallback = 0;
+ }
+ }
+}
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ HRESULT hr;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ int numBytes, maxChannels;
+ unsigned int minNumBuffers;
+ internalPortAudioDevice *pad;
+ DSoundWrapper *dsw;
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+ pahsc->pahsc_TimerID = 0;
+ dsw = &pahsc->pahsc_DSoundWrapper;
+ DSW_Init( dsw );
+ /* Allocate native buffer. */
+ maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ?
+ past->past_NumOutputChannels : past->past_NumInputChannels;
+ pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short);
+ if( maxChannels > 0 )
+ {
+ pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */
+ if( pahsc->pahsc_NativeBuffer == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+ else
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+
+ DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer ));
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+ numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers;
+ if( numBytes < DSBSIZE_MIN )
+ {
+ result = paBufferTooSmall;
+ goto error;
+ }
+ if( numBytes > DSBSIZE_MAX )
+ {
+ result = paBufferTooBig;
+ goto error;
+ }
+ pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers;
+ {
+ int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate);
+ PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency ));
+ }
+ /* ------------------ OUTPUT */
+ if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) )
+ {
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+ pad = Pa_GetInternalDevice( past->past_OutputDeviceID );
+ hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
+ /* If this fails, then try each output device until we find one that works. */
+ if( hr != DS_OK )
+ {
+ int i;
+ ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n",
+ ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
+ for( i=0; i<Pa_CountDevices(); i++ )
+ {
+ pad = Pa_GetInternalDevice( i );
+ if( pad->pad_Info.maxOutputChannels >= past->past_NumOutputChannels )
+ {
+ DBUG(("Try device '%s' instead.\n", pad->pad_Info.name ));
+ hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL );
+ if( hr == DS_OK )
+ {
+ ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name ));
+ break;
+ }
+ }
+ }
+ }
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+ hr = DSW_InitOutputBuffer( dsw,
+ (unsigned long) (past->past_SampleRate + 0.5),
+ past->past_NumOutputChannels, numBytes );
+ DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+ past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten;
+ }
+#if SUPPORT_AUDIO_CAPTURE
+ /* ------------------ INPUT */
+ if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) )
+ {
+ pad = Pa_GetInternalDevice( past->past_InputDeviceID );
+ hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
+ /* If this fails, then try each input device until we find one that works. */
+ if( hr != DS_OK )
+ {
+ int i;
+ ERR_RPT(("Creation of requested Audio Capture device '%s' failed.\n",
+ ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) ));
+ for( i=0; i<Pa_CountDevices(); i++ )
+ {
+ pad = Pa_GetInternalDevice( i );
+ if( pad->pad_Info.maxInputChannels >= past->past_NumInputChannels )
+ {
+ PRINT(("Try device '%s' instead.\n", pad->pad_Info.name ));
+ hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr == DS_OK ) break;
+ }
+ }
+ }
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+ hr = DSW_InitInputBuffer( dsw,
+ (unsigned long) (past->past_SampleRate + 0.5),
+ past->past_NumInputChannels, numBytes );
+ DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+ }
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ /* Calculate scalar used in CPULoad calculation. */
+ {
+ LARGE_INTEGER frequency;
+ if( QueryPerformanceFrequency( &frequency ) == 0 )
+ {
+ pahsc->pahsc_InverseTicksPerUserBuffer = 0.0;
+ }
+ else
+ {
+ pahsc->pahsc_InverseTicksPerUserBuffer = past->past_SampleRate /
+ ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer );
+ DBUG(("pahsc_InverseTicksPerUserBuffer = %g\n", pahsc->pahsc_InverseTicksPerUserBuffer ));
+ }
+ }
+ return result;
+error:
+ PaHost_CloseStream( past );
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ HRESULT hr;
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ /* Give user callback a chance to pre-fill buffer. */
+ result = Pa_TimeSlice( past );
+ if( result != paNoError ) return result; // FIXME - what if finished?
+ hr = DSW_StartOutput( &pahsc->pahsc_DSoundWrapper );
+ DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+error:
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+#if SUPPORT_AUDIO_CAPTURE
+ HRESULT hr;
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ hr = DSW_StartInput( &pahsc->pahsc_DSoundWrapper );
+ DBUG(("Pa_StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paHostError;
+ sPaHostError = hr;
+ goto error;
+ }
+error:
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ past->past_StopNow = 0;
+ past->past_StopSoon = 0;
+ past->past_IsActive = 1;
+ /* Create timer that will wake us up so we can fill the DSound buffer. */
+ {
+ int msecPerBuffer;
+ int resolution;
+ int bufsPerInterrupt = past->past_NumUserBuffers/4;
+ if( bufsPerInterrupt < 1 ) bufsPerInterrupt = 1;
+ msecPerBuffer = 1000 * (bufsPerInterrupt * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate;
+ if( msecPerBuffer < 10 ) msecPerBuffer = 10;
+ else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
+ resolution = msecPerBuffer/4;
+ pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+ (DWORD) past, TIME_PERIODIC );
+ }
+ if( pahsc->pahsc_TimerID == 0 )
+ {
+ past->past_IsActive = 0;
+ result = paHostError;
+ sPaHostError = 0;
+ goto error;
+ }
+error:
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ int timeoutMsec;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ if( abort ) past->past_StopNow = 1;
+ past->past_StopSoon = 1;
+ /* Set timeout at 20% beyond maximum time we might wait. */
+ timeoutMsec = (int) (1200.0 * pahsc->pahsc_FramesPerDSBuffer / past->past_SampleRate);
+ while( past->past_IsActive && (timeoutMsec > 0) )
+ {
+ Sleep(10);
+ timeoutMsec -= 10;
+ }
+ if( pahsc->pahsc_TimerID != 0 )
+ {
+ timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */
+ pahsc->pahsc_TimerID = 0;
+ }
+ return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+#if SUPPORT_AUDIO_CAPTURE
+ HRESULT hr;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+ hr = DSW_StopInput( &pahsc->pahsc_DSoundWrapper );
+ DBUG(("DSW_StopInput() result is %x\n", hr));
+#endif /* SUPPORT_AUDIO_CAPTURE */
+ return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ HRESULT hr;
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+ hr = DSW_StopOutput( &pahsc->pahsc_DSoundWrapper );
+ DBUG(("DSW_StopOutput() result is %x\n", hr));
+ return paNoError;
+}
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ DSW_Term( &pahsc->pahsc_DSoundWrapper );
+ if( pahsc->pahsc_NativeBuffer )
+ {
+ PaHost_FreeFastMemory( pahsc->pahsc_NativeBuffer, pahsc->pahsc_BytesPerBuffer ); /* MEM */
+ pahsc->pahsc_NativeBuffer = NULL;
+ }
+ PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */
+ past->past_DeviceData = NULL;
+ return paNoError;
+}
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+ char envbuf[PA_ENV_BUF_SIZE];
+ DWORD hostVersion;
+ DWORD hresult;
+ int minLatencyMsec = 0;
+ double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+ int minBuffers;
+ /* Let user determine minimal latency by setting environment variable. */
+ hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ minLatencyMsec = atoi( envbuf );
+ }
+ else
+ {
+ /* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+ hostVersion = GetVersion();
+ /* High bit clear if NT */
+ minLatencyMsec = ( (hostVersion & 0x80000000) == 0 ) ? PA_WIN_NT_LATENCY : PA_WIN_9X_LATENCY ;
+#if PA_USE_HIGH_LATENCY
+ PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+ }
+ minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+ if( minBuffers < 2 ) minBuffers = 2;
+ return minBuffers;
+}
+/*************************************************************************/
+PaError PaHost_Term( void )
+{
+ int i;
+ /* Free names allocated during enumeration. */
+ for( i=0; i<sNumDevices; i++ )
+ {
+ if( sDevices[i].pad_Info.name != NULL )
+ {
+ free( (void *) sDevices[i].pad_Info.name );
+ sDevices[i].pad_Info.name = NULL;
+ }
+ }
+ if( sDevices != NULL )
+ {
+ PaHost_FreeFastMemory( sDevices, sNumDevices * sizeof(internalPortAudioDevice) ); /* MEM */
+ sDevices = NULL;
+ sNumDevices = 0;
+ }
+ return 0;
+}
+void Pa_Sleep( long msec )
+{
+ Sleep( msec );
+}
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = GlobalAlloc( GPTR, numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
+ return addr;
+}
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr != NULL ) GlobalFree( addr ); /* MEM */
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) (past->past_IsActive);
+}
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ DSoundWrapper *dsw;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ dsw = &pahsc->pahsc_DSoundWrapper;
+ return dsw->dsw_FramesPlayed;
+}
--- /dev/null
+LIBRARY PortAudio
+DESCRIPTION 'PortAudio Portable interface to audio HW'
+
+EXPORTS
+ ; Explicit exports can go here
+ Pa_Initialize @1
+ Pa_Terminate @2
+ Pa_GetHostError @3
+ Pa_GetErrorText @4
+ Pa_CountDevices @5
+ Pa_GetDefaultInputDeviceID @6
+ Pa_GetDefaultOutputDeviceID @7
+ Pa_GetDeviceInfo @8
+ Pa_OpenStream @9
+ Pa_OpenDefaultStream @10
+ Pa_CloseStream @11
+ Pa_StartStream @12
+ Pa_StopStream @13
+ Pa_StreamActive @14
+ Pa_StreamTime @15
+ Pa_GetCPULoad @16
+ Pa_GetMinNumBuffers @17
+ Pa_Sleep @18
+
+ ;123456789012345678901234567890123456
+ ;000000000111111111122222222223333333
+
+
--- /dev/null
+
+# Makefile for PortAudio on cygwin
+# Contributed by Bill Eldridge on 6/13/2001
+
+ARCH= pa_win_wmme
+
+TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
+
+.c.o:
+ -gcc -c -I./pa_common $< -o $*.o
+ -gcc $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm
+
+all: sharedlib tests
+
+sharedlib: ./pa_common/pa_lib.c
+ gcc -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o
+ gcc -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o
+ dlltool --export-all --output-def pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o
+ gcc -shared -Wl,--enable-auto-image-base -o pa_win_wmme/portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/lib/w32api -lwinmm
+ cp pa_win_wmme/portaudio.dll /usr/local/bin
+
+tests: $(TESTS:.c=.o)
+
+sine:
+ gcc -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o
+ gcc pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm
+
+clean:
+ -rm ./pa_tests/*.exe
+
+nothing:
+ gcc pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm
+
+
--- /dev/null
+/*
+ * $Id$
+ * pa_win_wmme.c
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtainingF
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/*
+ All memory allocations and frees are marked with MEM for quick review.
+*/
+
+/* Modification History:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
+ PLB20010413 - check for excessive numbers of channels
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+ including condition including of memory.h,
+ and explicit typecasting on memory allocation
+ PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
+ PLB20010816 - pass process instead of thread to SetPriorityClass()
+ PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
+ JM20020118 - prevent hung thread when buffers underflow.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif /* __MWERKS__ */
+#include "portaudio.h"
+#include "pa_host.h"
+#include "pa_trace.h"
+
+/************************************************* Constants ********/
+#define PA_USE_TIMER_CALLBACK (0) /* Select between two options for background task. 0=thread, 1=timer */
+#define PA_USE_HIGH_LATENCY (0) /* For debugging glitches. */
+/* Switches for debugging. */
+#define PA_SIMULATE_UNDERFLOW (0) /* Set to one to force an underflow of the output buffer. */
+/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
+#define PA_TRACE_RUN (0)
+#define PA_TRACE_START_STOP (1)
+#if PA_USE_HIGH_LATENCY
+ #define PA_MIN_MSEC_PER_HOST_BUFFER (100)
+ #define PA_MAX_MSEC_PER_HOST_BUFFER (300) /* Do not exceed unless user buffer exceeds */
+ #define PA_MIN_NUM_HOST_BUFFERS (4)
+ #define PA_MAX_NUM_HOST_BUFFERS (16) /* OK to exceed if necessary */
+ #define PA_WIN_9X_LATENCY (400)
+#else
+ #define PA_MIN_MSEC_PER_HOST_BUFFER (10)
+ #define PA_MAX_MSEC_PER_HOST_BUFFER (100) /* Do not exceed unless user buffer exceeds */
+ #define PA_MIN_NUM_HOST_BUFFERS (3)
+ #define PA_MAX_NUM_HOST_BUFFERS (16) /* OK to exceed if necessary */
+ #define PA_WIN_9X_LATENCY (200)
+#endif
+#define MIN_TIMEOUT_MSEC (1000)
+/*
+** Use higher latency for NT because it is even worse at real-time
+** operation than Win9x.
+*/
+#define PA_WIN_NT_LATENCY (PA_WIN_9X_LATENCY * 2)
+
+#if PA_SIMULATE_UNDERFLOW
+static gUnderCallbackCounter = 0;
+#define UNDER_SLEEP_AT (40)
+#define UNDER_SLEEP_FOR (500)
+#endif
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+/************************************************* Definitions ********/
+/**************************************************************
+ * Structure for internal host specific stream data.
+ * This is allocated on a per stream basis.
+ */
+typedef struct PaHostSoundControl
+{
+ /* Input -------------- */
+ HWAVEIN pahsc_HWaveIn;
+ WAVEHDR *pahsc_InputBuffers;
+ int pahsc_CurrentInputBuffer;
+ int pahsc_BytesPerHostInputBuffer;
+ int pahsc_BytesPerUserInputBuffer; /* native buffer size in bytes */
+ /* Output -------------- */
+ HWAVEOUT pahsc_HWaveOut;
+ WAVEHDR *pahsc_OutputBuffers;
+ int pahsc_CurrentOutputBuffer;
+ int pahsc_BytesPerHostOutputBuffer;
+ int pahsc_BytesPerUserOutputBuffer; /* native buffer size in bytes */
+ /* Run Time -------------- */
+ PaTimestamp pahsc_FramesPlayed;
+ long pahsc_LastPosition; /* used to track frames played. */
+ /* For measuring CPU utilization. */
+ LARGE_INTEGER pahsc_EntryCount;
+ double pahsc_InverseTicksPerHostBuffer;
+ /* Init Time -------------- */
+ int pahsc_NumHostBuffers;
+ int pahsc_FramesPerHostBuffer;
+ int pahsc_UserBuffersPerHostBuffer;
+ CRITICAL_SECTION pahsc_StreamLock; /* Mutext to prevent threads from colliding. */
+ INT pahsc_StreamLockInited;
+#if PA_USE_TIMER_CALLBACK
+ BOOL pahsc_IfInsideCallback; /* Test for reentrancy. */
+ MMRESULT pahsc_TimerID;
+#else
+ HANDLE pahsc_AbortEvent;
+ int pahsc_AbortEventInited;
+ HANDLE pahsc_BufferEvent;
+ int pahsc_BufferEventInited;
+ HANDLE pahsc_EngineThread;
+ DWORD pahsc_EngineThreadID;
+#endif
+}
+PaHostSoundControl;
+/************************************************* Shared Data ********/
+/* FIXME - put Mutex around this shared data. */
+static int sNumInputDevices = 0;
+static int sNumOutputDevices = 0;
+static int sNumDevices = 0;
+static PaDeviceInfo **sDevicePtrs = NULL;
+static int sDefaultInputDeviceID = paNoDevice;
+static int sDefaultOutputDeviceID = paNoDevice;
+static int sPaHostError = 0;
+static const char sMapperSuffixInput[] = " - Input";
+static const char sMapperSuffixOutput[] = " - Output";
+/************************************************* Macros ********/
+/* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
+#define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
+/************************************************* Prototypes **********/
+static Pa_QueryDevices( void );
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
+ DWORD dwUser, DWORD dw1, DWORD dw2);
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past );
+static PaError PaHost_UpdateStreamTime( PaHostSoundControl *pahsc );
+static PaError PaHost_BackgroundManager( internalPortAudioStream *past );
+
+/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
+static void Pa_StartUsageCalculation( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /* Query system timer for usage analysis and to prevent overuse of CPU. */
+ QueryPerformanceCounter( &pahsc->pahsc_EntryCount );
+}
+static void Pa_EndUsageCalculation( internalPortAudioStream *past )
+{
+ LARGE_INTEGER CurrentCount = { 0, 0 };
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ /*
+ ** Measure CPU utilization during this callback. Note that this calculation
+ ** assumes that we had the processor the whole time.
+ */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+ if( QueryPerformanceCounter( &CurrentCount ) )
+ {
+ LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart;
+ double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerHostBuffer;
+ past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
+ (LOWPASS_COEFFICIENT_1 * newUsage);
+ }
+}
+/****************************************** END CPU UTILIZATION *******/
+
+static PaError Pa_QueryDevices( void )
+{
+ int numBytes;
+ /* Count the devices and add one extra for the WAVE_MAPPER */
+ sNumInputDevices = waveInGetNumDevs() + 1;
+ sDefaultInputDeviceID = 0;
+ sNumOutputDevices = waveOutGetNumDevs() + 1;
+ sDefaultOutputDeviceID = sNumInputDevices;
+ sNumDevices = sNumInputDevices + sNumOutputDevices;
+ /* Allocate structures to hold device info. */
+ /* PLB20010402 - was allocating too much memory. */
+ /* numBytes = sNumDevices * sizeof(PaDeviceInfo); // PLB20010402 */
+ numBytes = sNumDevices * sizeof(PaDeviceInfo *); /* PLB20010402 */
+ sDevicePtrs = (PaDeviceInfo **) GlobalAlloc( GPTR, numBytes ); /* MEM */
+ if( sDevicePtrs == NULL ) return paInsufficientMemory;
+ return paNoError;
+}
+/************************************************************************************/
+long Pa_GetHostError()
+{
+ return sPaHostError;
+}
+/*************************************************************************/
+int Pa_CountDevices()
+{
+ if( sNumDevices <= 0 ) Pa_Initialize();
+ return sNumDevices;
+}
+/*************************************************************************
+** If a PaDeviceInfo structure has not already been created,
+** then allocate one and fill it in for the selected device.
+**
+** We create one extra input and one extra output device for the WAVE_MAPPER.
+** [Does anyone know how to query the default device and get its name?]
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
+{
+#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */
+#define NUM_CUSTOMSAMPLINGRATES 5 /* must be the same number of elements as in the array below */
+#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
+ static DWORD customSamplingRates[] = { 32000, 48000, 64000, 88200, 96000 };
+ PaDeviceInfo *deviceInfo;
+ double *sampleRates; /* non-const ptr */
+ int i;
+ char *s;
+
+ if( id < 0 || id >= sNumDevices )
+ return NULL;
+ if( sDevicePtrs[ id ] != NULL )
+ {
+ return sDevicePtrs[ id ];
+ }
+ deviceInfo = (PaDeviceInfo *)GlobalAlloc( GPTR, sizeof(PaDeviceInfo) ); /* MEM */
+ if( deviceInfo == NULL ) return NULL;
+ deviceInfo->structVersion = 1;
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->numSampleRates = 0;
+ sampleRates = (double*)GlobalAlloc( GPTR, MAX_NUMSAMPLINGRATES * sizeof(double) ); /* MEM */
+ deviceInfo->sampleRates = sampleRates;
+ deviceInfo->nativeSampleFormats = paInt16; /* should query for higher bit depths below */
+ if( id < sNumInputDevices )
+ {
+ /* input device */
+ int inputMmID = id - 1; /* WAVE_MAPPER is -1 so we start with WAVE_MAPPER */
+ WAVEINCAPS wic;
+ if( waveInGetDevCaps( inputMmID, &wic, sizeof( WAVEINCAPS ) ) != MMSYSERR_NOERROR )
+ goto error;
+
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ if( inputMmID == WAVE_MAPPER )
+ {
+ s = (char *) GlobalAlloc( GMEM_FIXED, strlen( wic.szPname ) + 1 + sizeof(sMapperSuffixInput) ); /* MEM */
+ strcpy( s, wic.szPname );
+ strcat( s, sMapperSuffixInput );
+ }
+ else
+ {
+ s = (char *) GlobalAlloc( GMEM_FIXED, strlen( wic.szPname ) + 1 ); /* MEM */
+ strcpy( s, wic.szPname );
+ }
+ deviceInfo->name = s;
+ deviceInfo->maxInputChannels = wic.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ ** This happened with an SBLive card on a Windows ME box.
+ ** If that happens, then force it to 2 channels. PLB20010413
+ */
+ if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
+ {
+ ERR_RPT(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+ deviceInfo->maxInputChannels = 2;
+ }
+ /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+ * based on the format flags. */
+ if( wic.dwFormats & WAVE_FORMAT_1M16 ||wic.dwFormats & WAVE_FORMAT_1S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
+ if( wic.dwFormats & WAVE_FORMAT_2M16 ||wic.dwFormats & WAVE_FORMAT_2S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
+ if( wic.dwFormats & WAVE_FORMAT_4M16 ||wic.dwFormats & WAVE_FORMAT_4S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
+ /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+ * based on opening the device successfully. */
+ for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
+ {
+ WAVEFORMATEX wfx;
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nSamplesPerSec = customSamplingRates[i];
+ wfx.wBitsPerSample = 16;
+ wfx.cbSize = 0; /* ignored */
+ wfx.nChannels = (WORD)deviceInfo->maxInputChannels;
+ wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
+ wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
+ if( waveInOpen( NULL, inputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
+ {
+ sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
+ }
+ }
+
+ }
+ else if( id - sNumInputDevices < sNumOutputDevices )
+ {
+ /* output device */
+ int outputMmID = id - sNumInputDevices - 1;
+ WAVEOUTCAPS woc;
+ if( waveOutGetDevCaps( outputMmID, &woc, sizeof( WAVEOUTCAPS ) ) != MMSYSERR_NOERROR )
+ goto error;
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ if( outputMmID == WAVE_MAPPER )
+ {
+ s = (char *) GlobalAlloc( GMEM_FIXED, strlen( woc.szPname ) + 1 + sizeof(sMapperSuffixOutput) ); /* MEM */
+ strcpy( s, woc.szPname );
+ strcat( s, sMapperSuffixOutput );
+ }
+ else
+ {
+ s = (char *) GlobalAlloc( GMEM_FIXED, strlen( woc.szPname ) + 1 ); /* MEM */
+ strcpy( s, woc.szPname );
+ }
+ deviceInfo->name = s;
+ deviceInfo->maxOutputChannels = woc.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ ** This happened with an SBLive card on a Windows ME box.
+ ** If that happens, then force it to 2 channels. PLB20010413
+ */
+ if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+ {
+ ERR_RPT(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+ deviceInfo->maxOutputChannels = 2;
+ }
+ /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+ * based on the format flags. */
+ if( woc.dwFormats & WAVE_FORMAT_1M16 ||woc.dwFormats & WAVE_FORMAT_1S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
+ if( woc.dwFormats & WAVE_FORMAT_2M16 ||woc.dwFormats & WAVE_FORMAT_2S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
+ if( woc.dwFormats & WAVE_FORMAT_4M16 ||woc.dwFormats & WAVE_FORMAT_4S16 )
+ sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
+ /* Add a sample rate to the list if we can do stereo 16 bit at that rate
+ * based on opening the device successfully. */
+ for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
+ {
+ WAVEFORMATEX wfx;
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nSamplesPerSec = customSamplingRates[i];
+ wfx.wBitsPerSample = 16;
+ wfx.cbSize = 0; /* ignored */
+ wfx.nChannels = (WORD)deviceInfo->maxOutputChannels;
+ wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
+ wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
+ if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
+ {
+ sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
+ }
+ }
+ }
+ sDevicePtrs[ id ] = deviceInfo;
+ return deviceInfo;
+error:
+ GlobalFree( sampleRates ); /* MEM */
+ GlobalFree( deviceInfo ); /* MEM */
+
+ return NULL;
+}
+/*************************************************************************
+** Returns recommended device ID.
+** On the PC, the recommended device can be specified by the user by
+** setting an environment variable. For example, to use device #1.
+**
+** set PA_RECOMMENDED_OUTPUT_DEVICE=1
+**
+** The user should first determine the available device ID by using
+** the supplied application "pa_devs".
+*/
+#define PA_ENV_BUF_SIZE (32)
+#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
+{
+ DWORD hresult;
+ char envbuf[PA_ENV_BUF_SIZE];
+ PaDeviceID recommendedID = paNoDevice;
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ recommendedID = atoi( envbuf );
+ }
+ return recommendedID;
+}
+static PaError Pa_MaybeQueryDevices( void )
+{
+ if( sNumDevices == 0 )
+ {
+ return Pa_QueryDevices();
+ }
+ return 0;
+}
+/**********************************************************************
+** Check for environment variable, else query devices and use result.
+*/
+PaDeviceID Pa_GetDefaultInputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultInputDeviceID;
+ }
+ return result;
+}
+PaDeviceID Pa_GetDefaultOutputDeviceID( void )
+{
+ PaError result;
+ result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
+ if( result < 0 )
+ {
+ result = Pa_MaybeQueryDevices();
+ if( result < 0 ) return result;
+ result = sDefaultOutputDeviceID;
+ }
+ return result;
+}
+/**********************************************************************
+** Initialize Host dependant part of API.
+*/
+PaError PaHost_Init( void )
+{
+#if PA_SIMULATE_UNDERFLOW
+ PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
+#endif
+ return Pa_MaybeQueryDevices();
+}
+
+/**********************************************************************
+** Check WAVE buffers to see if they are done.
+** Fill any available output buffers and use any available
+** input buffers by calling user callback.
+**
+** This routine will loop until:
+** user callback returns !=0 OR
+** all output buffers are filled OR
+** past->past_StopSoon is set OR
+** an error occurs when calling WMME.
+**
+** Returns >0 when user requests a stop, <0 on error.
+**
+*/
+static PaError Pa_TimeSlice( internalPortAudioStream *past )
+{
+ PaError result = 0;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ long buffersEmpty = 0;
+ MMRESULT mresult;
+ char *inBufPtr;
+ char *outBufPtr;
+ int gotInput = 0;
+ int gotOutput = 0;
+ int i;
+ int buffersProcessed = 0;
+ int done = 0;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+
+ past->past_NumCallbacks += 1;
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks );
+#endif
+
+ /* JM20020118 - prevent hung thread when buffers underflow. */
+ /* while( !done ) /* BAD */
+ while( !done && !past->past_StopSoon ) /* GOOD */
+ {
+#if PA_SIMULATE_UNDERFLOW
+ if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
+ {
+ Sleep(UNDER_SLEEP_FOR);
+ }
+#endif
+
+ /* If we are using output, then we need an empty output buffer. */
+ gotOutput = 0;
+ outBufPtr = NULL;
+ if( past->past_NumOutputChannels > 0 )
+ {
+ if((pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].dwFlags & WHDR_DONE) == 0)
+ {
+ break; /* If none empty then bail and try again later. */
+ }
+ else
+ {
+ outBufPtr = pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ].lpData;
+ gotOutput = 1;
+ }
+ }
+ /* Use an input buffer if one is available. */
+ gotInput = 0;
+ inBufPtr = NULL;
+ if( ( past->past_NumInputChannels > 0 ) &&
+ (pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].dwFlags & WHDR_DONE) )
+ {
+ inBufPtr = pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ].lpData;
+ gotInput = 1;
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr );
+ AddTraceMessage("Pa_TimeSlice: got input buffer # ", pahsc->pahsc_CurrentInputBuffer );
+#endif
+
+ }
+ /* If we can't do anything then bail out. */
+ if( !gotInput && !gotOutput ) break;
+ buffersProcessed += 1;
+ /* Each Wave buffer contains multiple user buffers so do them all now. */
+ /* Base Usage on time it took to process one host buffer. */
+ Pa_StartUsageCalculation( past );
+ for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
+ {
+ if( done )
+ {
+ if( gotOutput )
+ {
+ /* Clear remainder of wave buffer if we are waiting for stop. */
+ AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
+ memset( outBufPtr, 0, pahsc->pahsc_BytesPerUserOutputBuffer );
+ }
+ }
+ else
+ {
+ /* Convert 16 bit native data to user data and call user routine. */
+ result = Pa_CallConvertInt16( past, (short *) inBufPtr, (short *) outBufPtr );
+ if( result != 0) done = 1;
+ }
+ if( gotInput ) inBufPtr += pahsc->pahsc_BytesPerUserInputBuffer;
+ if( gotOutput) outBufPtr += pahsc->pahsc_BytesPerUserOutputBuffer;
+ }
+ Pa_EndUsageCalculation( past );
+ /* Send WAVE buffer to Wave Device to be refilled. */
+ if( gotInput )
+ {
+ mresult = waveInAddBuffer( pahsc->pahsc_HWaveIn,
+ &pahsc->pahsc_InputBuffers[ pahsc->pahsc_CurrentInputBuffer ],
+ sizeof(WAVEHDR) );
+ if( mresult != MMSYSERR_NOERROR )
+ {
+ sPaHostError = mresult;
+ result = paHostError;
+ break;
+ }
+ pahsc->pahsc_CurrentInputBuffer = (pahsc->pahsc_CurrentInputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
+ 0 : pahsc->pahsc_CurrentInputBuffer+1;
+ }
+ /* Write WAVE buffer to Wave Device. */
+ if( gotOutput )
+ {
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "Pa_TimeSlice: writing buffer ", pahsc->pahsc_CurrentOutputBuffer );
+#endif
+ mresult = waveOutWrite( pahsc->pahsc_HWaveOut,
+ &pahsc->pahsc_OutputBuffers[ pahsc->pahsc_CurrentOutputBuffer ],
+ sizeof(WAVEHDR) );
+ if( mresult != MMSYSERR_NOERROR )
+ {
+ sPaHostError = mresult;
+ result = paHostError;
+ break;
+ }
+ pahsc->pahsc_CurrentOutputBuffer = (pahsc->pahsc_CurrentOutputBuffer+1 >= pahsc->pahsc_NumHostBuffers) ?
+ 0 : pahsc->pahsc_CurrentOutputBuffer+1;
+ }
+
+ }
+
+#if PA_TRACE_RUN
+ AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
+#endif
+ return (result != 0) ? result : done;
+}
+
+/*******************************************************************/
+static PaError PaHost_BackgroundManager( internalPortAudioStream *past )
+{
+ PaError result = 0;
+ int i;
+ int numQueuedOutputBuffers = 0;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+
+ /* Has someone asked us to abort by calling Pa_AbortStream()? */
+ if( past->past_StopNow )
+ {
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ /* Has someone asked us to stop by calling Pa_StopStream()
+ * OR has a user callback returned '1' to indicate finished.
+ */
+ else if( past->past_StopSoon )
+ {
+ /* Poll buffer and when all have played then exit thread. */
+ /* Count how many output buffers are queued. */
+ numQueuedOutputBuffers = 0;
+ if( past->past_NumOutputChannels > 0 )
+ {
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ if( !( pahsc->pahsc_OutputBuffers[ i ].dwFlags & WHDR_DONE) )
+ {
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "WinMMPa_OutputThreadProc: waiting for buffer ", i );
+#endif
+ numQueuedOutputBuffers++;
+ }
+ }
+ }
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "WinMMPa_OutputThreadProc: numQueuedOutputBuffers ", numQueuedOutputBuffers );
+#endif
+ if( numQueuedOutputBuffers == 0 )
+ {
+ past->past_IsActive = 0; /* Will cause thread to return. */
+ }
+ }
+ else
+ {
+ /* Process full input buffer and fill up empty output buffers. */
+ if( (result = Pa_TimeSlice( past )) != 0)
+ {
+ /* User callback has asked us to stop. */
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "WinMMPa_OutputThreadProc: TimeSlice() returned ", result );
+#endif
+ past->past_StopSoon = 1; /* Request that audio play out then stop. */
+ result = paNoError;
+ }
+ }
+
+ PaHost_UpdateStreamTime( pahsc );
+ return result;
+}
+
+#if PA_USE_TIMER_CALLBACK
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+ PaError result;
+ past = (internalPortAudioStream *) dwUser;
+ if( past == NULL ) return;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return;
+ if( pahsc->pahsc_IfInsideCallback )
+ {
+ if( pahsc->pahsc_TimerID != 0 )
+ {
+ timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */
+ pahsc->pahsc_TimerID = 0;
+ }
+ return;
+ }
+ pahsc->pahsc_IfInsideCallback = 1;
+ /* Manage flags and audio processing. */
+ result = PaHost_BackgroundManager( past );
+ if( result != paNoError )
+ {
+ past->past_IsActive = 0;
+ }
+ pahsc->pahsc_IfInsideCallback = 0;
+}
+#else /* PA_USE_TIMER_CALLBACK */
+/*******************************************************************/
+static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg )
+{
+ internalPortAudioStream *past;
+ PaHostSoundControl *pahsc;
+ void *inputBuffer=NULL;
+ HANDLE events[2];
+ int numEvents = 0;
+ DWORD result = 0;
+ DWORD waitResult;
+ DWORD numTimeouts = 0;
+ DWORD timeOut;
+ past = (internalPortAudioStream *) pArg;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod );
+ AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", past->past_NumUserBuffers );
+#endif
+ /* Calculate timeOut as half the time it would take to play all buffers. */
+ timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+ /* Get event(s) ready for wait. */
+ events[numEvents++] = pahsc->pahsc_BufferEvent;
+ if( pahsc->pahsc_AbortEventInited ) events[numEvents++] = pahsc->pahsc_AbortEvent;
+ /* Stay in this thread as long as we are "active". */
+ while( past->past_IsActive )
+ {
+ /*******************************************************************/
+ /******** WAIT here for an event from WMME or PA *******************/
+ /*******************************************************************/
+ waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut );
+ /* Error? */
+ if( waitResult == WAIT_FAILED )
+ {
+ sPaHostError = GetLastError();
+ result = paHostError;
+ past->past_IsActive = 0;
+ }
+ /* Timeout? Don't stop. Just keep polling for DONE.*/
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedOutputBuffers );
+#endif
+ numTimeouts += 1;
+ }
+ /* Manage flags and audio processing. */
+ result = PaHost_BackgroundManager( past );
+ if( result != paNoError )
+ {
+ past->past_IsActive = 0;
+ }
+ }
+ return result;
+}
+#endif
+
+/*******************************************************************/
+PaError PaHost_OpenInputStream( internalPortAudioStream *past )
+{
+ MMRESULT mr;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ int i;
+ int inputMmId;
+ int bytesPerInputFrame;
+ WAVEFORMATEX wfx;
+ const PaDeviceInfo *pad;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID));
+ pad = Pa_GetDeviceInfo( past->past_InputDeviceID );
+ if( pad == NULL ) return paInternalError;
+ switch( pad->nativeSampleFormats )
+ {
+ case paInt32:
+ case paFloat32:
+ bytesPerInputFrame = sizeof(float) * past->past_NumInputChannels;
+ break;
+ default:
+ bytesPerInputFrame = sizeof(short) * past->past_NumInputChannels;
+ break;
+ }
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nChannels = (WORD) past->past_NumInputChannels;
+ wfx.nSamplesPerSec = (DWORD) past->past_SampleRate;
+ wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * past->past_SampleRate);
+ wfx.nBlockAlign = (WORD)bytesPerInputFrame;
+ wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/past->past_NumInputChannels) * 8);
+ wfx.cbSize = 0;
+ inputMmId = PaDeviceIdToWinId( past->past_InputDeviceID );
+#if PA_USE_TIMER_CALLBACK
+ mr = waveInOpen( &pahsc->pahsc_HWaveIn, inputMmId, &wfx,
+ 0, 0, CALLBACK_NULL );
+#else
+ mr = waveInOpen( &pahsc->pahsc_HWaveIn, inputMmId, &wfx,
+ (DWORD)pahsc->pahsc_BufferEvent, (DWORD) past, CALLBACK_EVENT );
+#endif
+ if( mr != MMSYSERR_NOERROR )
+ {
+ ERR_RPT(("PortAudio: PaHost_OpenInputStream() failed!\n"));
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ /* Allocate an array to hold the buffer pointers. */
+ pahsc->pahsc_InputBuffers = (WAVEHDR *) GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(WAVEHDR)*pahsc->pahsc_NumHostBuffers ); /* MEM */
+ if( pahsc->pahsc_InputBuffers == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ /* Allocate each buffer. */
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ pahsc->pahsc_InputBuffers[i].lpData = (char *)GlobalAlloc( GMEM_FIXED, pahsc->pahsc_BytesPerHostInputBuffer ); /* MEM */
+ if( pahsc->pahsc_InputBuffers[i].lpData == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ pahsc->pahsc_InputBuffers[i].dwBufferLength = pahsc->pahsc_BytesPerHostInputBuffer;
+ pahsc->pahsc_InputBuffers[i].dwUser = i;
+ if( ( mr = waveInPrepareHeader( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ }
+ return result;
+error:
+ return result;
+}
+/*******************************************************************/
+PaError PaHost_OpenOutputStream( internalPortAudioStream *past )
+{
+ MMRESULT mr;
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ int i;
+ int outputMmID;
+ int bytesPerOutputFrame;
+ WAVEFORMATEX wfx;
+ const PaDeviceInfo *pad;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID));
+ pad = Pa_GetDeviceInfo( past->past_OutputDeviceID );
+ if( pad == NULL ) return paInternalError;
+ switch( pad->nativeSampleFormats )
+ {
+ case paInt32:
+ case paFloat32:
+ bytesPerOutputFrame = sizeof(float) * past->past_NumOutputChannels;
+ break;
+ default:
+ bytesPerOutputFrame = sizeof(short) * past->past_NumOutputChannels;
+ break;
+ }
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nChannels = (WORD) past->past_NumOutputChannels;
+ wfx.nSamplesPerSec = (DWORD) past->past_SampleRate;
+ wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * past->past_SampleRate);
+ wfx.nBlockAlign = (WORD)bytesPerOutputFrame;
+ wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/past->past_NumOutputChannels) * 8);
+ wfx.cbSize = 0;
+ outputMmID = PaDeviceIdToWinId( past->past_OutputDeviceID );
+#if PA_USE_TIMER_CALLBACK
+ mr = waveOutOpen( &pahsc->pahsc_HWaveOut, outputMmID, &wfx,
+ 0, 0, CALLBACK_NULL );
+#else
+
+ pahsc->pahsc_AbortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( pahsc->pahsc_AbortEvent == NULL )
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();
+ goto error;
+ }
+ pahsc->pahsc_AbortEventInited = 1;
+ mr = waveOutOpen( &pahsc->pahsc_HWaveOut, outputMmID, &wfx,
+ (DWORD)pahsc->pahsc_BufferEvent, (DWORD) past, CALLBACK_EVENT );
+#endif
+ if( mr != MMSYSERR_NOERROR )
+ {
+ ERR_RPT(("PortAudio: PaHost_OpenOutputStream() failed!\n"));
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ /* Allocate an array to hold the buffer pointers. */
+ pahsc->pahsc_OutputBuffers = (WAVEHDR *) GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof(WAVEHDR)*pahsc->pahsc_NumHostBuffers ); /* MEM */
+ if( pahsc->pahsc_OutputBuffers == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ /* Allocate each buffer. */
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ pahsc->pahsc_OutputBuffers[i].lpData = (char *) GlobalAlloc( GMEM_FIXED, pahsc->pahsc_BytesPerHostOutputBuffer ); /* MEM */
+ if( pahsc->pahsc_OutputBuffers[i].lpData == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ pahsc->pahsc_OutputBuffers[i].dwBufferLength = pahsc->pahsc_BytesPerHostOutputBuffer;
+ pahsc->pahsc_OutputBuffers[i].dwUser = i;
+ if( (mr = waveOutPrepareHeader( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ }
+ return result;
+error:
+ return result;
+}
+/*******************************************************************/
+PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ return pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer;
+}
+/*******************************************************************
+* Determine number of WAVE Buffers
+* and how many User Buffers we can put into each WAVE buffer.
+*/
+static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ unsigned int minNumBuffers;
+ int minFramesPerHostBuffer;
+ int maxFramesPerHostBuffer;
+ int minTotalFrames;
+ int userBuffersPerHostBuffer;
+ int framesPerHostBuffer;
+ int numHostBuffers;
+ /* Calculate minimum and maximum sizes based on timing and sample rate. */
+ minFramesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+ minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
+ DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
+ maxFramesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * past->past_SampleRate / 1000.0);
+ maxFramesPerHostBuffer = (maxFramesPerHostBuffer + 7) & ~7;
+ DBUG(("PaHost_CalcNumHostBuffers: maxFramesPerHostBuffer = %d\n", maxFramesPerHostBuffer ));
+ /* Determine number of user buffers based on minimum latency. */
+ minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate );
+ past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+ minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
+ /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
+ if( (int) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
+ {
+ userBuffersPerHostBuffer =
+ (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
+ past->past_FramesPerUserBuffer;
+ }
+ else
+ {
+ userBuffersPerHostBuffer = 1;
+ }
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ /* Make sure we have anough WAVE buffers. */
+ if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
+ {
+ numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
+ }
+ else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
+ ((int) past->past_FramesPerUserBuffer < (maxFramesPerHostBuffer/2) ) )
+ {
+ /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
+ while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
+ {
+ userBuffersPerHostBuffer += 1;
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ /* If we have gone too far, back up one. */
+ if( (framesPerHostBuffer > maxFramesPerHostBuffer) ||
+ (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
+ {
+ userBuffersPerHostBuffer -= 1;
+ framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
+ numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
+ break;
+ }
+ }
+ }
+
+ pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
+ pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
+ pahsc->pahsc_NumHostBuffers = numHostBuffers;
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
+ DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
+ DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
+}
+/*******************************************************************/
+PaError PaHost_OpenStream( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc;
+ /* Allocate and initialize host data. */
+ pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */
+ if( pahsc == NULL )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ memset( pahsc, 0, sizeof(PaHostSoundControl) );
+ past->past_DeviceData = (void *) pahsc;
+ /* Figure out how user buffers fit into WAVE buffers. */
+ PaHost_CalcNumHostBuffers( past );
+ {
+ int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate);
+ DBUG(("PortAudio on WMME - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency ));
+ }
+ InitializeCriticalSection( &pahsc->pahsc_StreamLock );
+ pahsc->pahsc_StreamLockInited = 1;
+
+#if (PA_USE_TIMER_CALLBACK == 0)
+ pahsc->pahsc_BufferEventInited = 0;
+ pahsc->pahsc_BufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( pahsc->pahsc_BufferEvent == NULL )
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();
+ goto error;
+ }
+ pahsc->pahsc_BufferEventInited = 1;
+#endif /* (PA_USE_TIMER_CALLBACK == 0) */
+ /* ------------------ OUTPUT */
+ pahsc->pahsc_BytesPerUserOutputBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels * sizeof(short);
+ pahsc->pahsc_BytesPerHostOutputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserOutputBuffer;
+ if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) )
+ {
+ result = PaHost_OpenOutputStream( past );
+ if( result < 0 ) goto error;
+ }
+ /* ------------------ INPUT */
+ pahsc->pahsc_BytesPerUserInputBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels * sizeof(short);
+ pahsc->pahsc_BytesPerHostInputBuffer = pahsc->pahsc_UserBuffersPerHostBuffer * pahsc->pahsc_BytesPerUserInputBuffer;
+ if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) )
+ {
+ result = PaHost_OpenInputStream( past );
+ if( result < 0 ) goto error;
+ }
+ /* Calculate scalar used in CPULoad calculation. */
+ {
+ LARGE_INTEGER frequency;
+ if( QueryPerformanceFrequency( &frequency ) == 0 )
+ {
+ pahsc->pahsc_InverseTicksPerHostBuffer = 0.0;
+ }
+ else
+ {
+ pahsc->pahsc_InverseTicksPerHostBuffer = past->past_SampleRate /
+ ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer * pahsc->pahsc_UserBuffersPerHostBuffer );
+ DBUG(("pahsc_InverseTicksPerHostBuffer = %g\n", pahsc->pahsc_InverseTicksPerHostBuffer ));
+ }
+ }
+ return result;
+error:
+ PaHost_CloseStream( past );
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartOutput( internalPortAudioStream *past )
+{
+ MMRESULT mr;
+ PaHostSoundControl *pahsc;
+ PaError result = paNoError;
+ int i;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( past->past_OutputDeviceID != paNoDevice )
+ {
+ if( (mr = waveOutPause( pahsc->pahsc_HWaveOut )) != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ ZeroMemory( pahsc->pahsc_OutputBuffers[i].lpData, pahsc->pahsc_OutputBuffers[i].dwBufferLength );
+ mr = waveOutWrite( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) );
+ if( mr != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ past->past_FrameCount += pahsc->pahsc_FramesPerHostBuffer;
+ }
+ pahsc->pahsc_CurrentOutputBuffer = 0;
+ if( (mr = waveOutRestart( pahsc->pahsc_HWaveOut )) != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ }
+ DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+error:
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartInput( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ MMRESULT mr;
+ int i;
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( past->past_InputDeviceID != paNoDevice )
+ {
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ mr = waveInAddBuffer( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) );
+ if( mr != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ }
+ pahsc->pahsc_CurrentInputBuffer = 0;
+ mr = waveInStart( pahsc->pahsc_HWaveIn );
+ DBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", hr));
+ if( mr != MMSYSERR_NOERROR )
+ {
+ result = paHostError;
+ sPaHostError = mr;
+ goto error;
+ }
+ }
+error:
+ return result;
+}
+/*************************************************************************/
+PaError PaHost_StartEngine( internalPortAudioStream *past )
+{
+ PaError result = paNoError;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+#if PA_USE_TIMER_CALLBACK
+ int resolution;
+ int bufsPerTimerCallback;
+ int msecPerBuffer;
+#endif /* PA_USE_TIMER_CALLBACK */
+
+ past->past_StopSoon = 0;
+ past->past_StopNow = 0;
+ past->past_IsActive = 1;
+ pahsc->pahsc_FramesPlayed = 0.0;
+ pahsc->pahsc_LastPosition = 0;
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
+#endif
+#if PA_USE_TIMER_CALLBACK
+ /* Create timer that will wake us up so we can fill the DSound buffer. */
+ bufsPerTimerCallback = pahsc->pahsc_NumHostBuffers/4;
+ if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
+ if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
+ msecPerBuffer = (1000 * bufsPerTimerCallback *
+ pahsc->pahsc_UserBuffersPerHostBuffer *
+ past->past_FramesPerUserBuffer ) / (int) past->past_SampleRate;
+ if( msecPerBuffer < 10 ) msecPerBuffer = 10;
+ else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
+ resolution = msecPerBuffer/4;
+ pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution,
+ (LPTIMECALLBACK) Pa_TimerCallback,
+ (DWORD) past, TIME_PERIODIC );
+ if( pahsc->pahsc_TimerID == 0 )
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();;
+ goto error;
+ }
+#else /* PA_USE_TIMER_CALLBACK */
+ ResetEvent( pahsc->pahsc_AbortEvent );
+ /* Create thread that waits for audio buffers to be ready for processing. */
+ pahsc->pahsc_EngineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, past, 0, &pahsc->pahsc_EngineThreadID );
+ if( pahsc->pahsc_EngineThread == NULL )
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();;
+ goto error;
+ }
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StartEngine: thread ", (int) pahsc->pahsc_EngineThread );
+#endif
+ /* I used to pass the thread which was failing. I now pass GetCurrentProcess().
+ ** This fix could improve latency for some applications. It could also result in CPU
+ ** starvation if the callback did too much processing.
+ ** I also added result checks, so we might see more failures at initialization.
+ ** Thanks to Alberto di Bene for spotting this.
+ */
+ if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();;
+ goto error;
+ }
+ if( !SetThreadPriority( pahsc->pahsc_EngineThread, THREAD_PRIORITY_HIGHEST ) )
+ {
+ result = paHostError;
+ sPaHostError = GetLastError();;
+ goto error;
+ }
+#endif
+error:
+ return result;
+}
+
+/*************************************************************************/
+PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
+{
+ int timeOut;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ past->past_StopSoon = 1;
+ /* If aborting, tell background thread to stop NOW! */
+ if( abort ) past->past_StopNow = 1;
+
+ /* Calculate timeOut longer than longest time it could take to play all buffers. */
+ timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate);
+ if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
+
+#if PA_USE_TIMER_CALLBACK
+ if( (past->past_OutputDeviceID != paNoDevice) &&
+ past->past_IsActive &&
+ (pahsc->pahsc_TimerID != 0) )
+ {
+ /* Wait for IsActive to drop. */
+ while( (past->past_IsActive) && (timeOut > 0) )
+ {
+ Sleep(10);
+ timeOut -= 10;
+ }
+ timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */
+ pahsc->pahsc_TimerID = 0;
+ }
+#else /* PA_USE_TIMER_CALLBACK */
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StopEngine: thread ", (int) pahsc->pahsc_EngineThread );
+#endif
+ if( (past->past_OutputDeviceID != paNoDevice) &&
+ (past->past_IsActive) &&
+ (pahsc->pahsc_EngineThread != NULL) )
+ {
+ DWORD got;
+ /* Tell background thread to stop generating more data and to let current data play out. */
+ DBUG(("PaHost_StopEngine: waiting for background thread.\n"));
+ got = WaitForSingleObject( pahsc->pahsc_EngineThread, timeOut );
+ if( got == WAIT_TIMEOUT )
+ {
+ ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n"));
+ return paTimedOut;
+ }
+ CloseHandle( pahsc->pahsc_EngineThread );
+ pahsc->pahsc_EngineThread = NULL;
+ }
+#endif /* PA_USE_TIMER_CALLBACK */
+
+ past->past_IsActive = 0;
+ return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
+{
+ MMRESULT mr;
+ PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+ if( pahsc->pahsc_HWaveIn != NULL )
+ {
+ mr = waveInReset( pahsc->pahsc_HWaveIn );
+ if( mr != MMSYSERR_NOERROR )
+ {
+ sPaHostError = mr;
+ return paHostError;
+ }
+ }
+ return paNoError;
+}
+/*************************************************************************/
+PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
+{
+ MMRESULT mr;
+ PaHostSoundControl *pahsc;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+ (void) abort;
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+ if( pahsc->pahsc_HWaveOut != NULL )
+ {
+ mr = waveOutReset( pahsc->pahsc_HWaveOut );
+ if( mr != MMSYSERR_NOERROR )
+ {
+ sPaHostError = mr;
+ return paHostError;
+ }
+ }
+ return paNoError;
+}
+/*******************************************************************/
+PaError PaHost_CloseStream( internalPortAudioStream *past )
+{
+ int i;
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paNoError;
+#if PA_TRACE_START_STOP
+ AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut );
+#endif
+ /* Free data and device for output. */
+ if( pahsc->pahsc_HWaveOut )
+ {
+ if( pahsc->pahsc_OutputBuffers )
+ {
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ waveOutUnprepareHeader( pahsc->pahsc_HWaveOut, &pahsc->pahsc_OutputBuffers[i], sizeof(WAVEHDR) );
+ GlobalFree( pahsc->pahsc_OutputBuffers[i].lpData ); /* MEM */
+ }
+ GlobalFree( pahsc->pahsc_OutputBuffers ); /* MEM */
+ }
+ waveOutClose( pahsc->pahsc_HWaveOut );
+ }
+ /* Free data and device for input. */
+ if( pahsc->pahsc_HWaveIn )
+ {
+ if( pahsc->pahsc_InputBuffers )
+ {
+ for( i=0; i<pahsc->pahsc_NumHostBuffers; i++ )
+ {
+ waveInUnprepareHeader( pahsc->pahsc_HWaveIn, &pahsc->pahsc_InputBuffers[i], sizeof(WAVEHDR) );
+ GlobalFree( pahsc->pahsc_InputBuffers[i].lpData ); /* MEM */
+ }
+ GlobalFree( pahsc->pahsc_InputBuffers ); /* MEM */
+ }
+ waveInClose( pahsc->pahsc_HWaveIn );
+ }
+#if (PA_USE_TIMER_CALLBACK == 0)
+ if( pahsc->pahsc_AbortEventInited ) CloseHandle( pahsc->pahsc_AbortEvent );
+ if( pahsc->pahsc_BufferEventInited ) CloseHandle( pahsc->pahsc_BufferEvent );
+#endif
+ if( pahsc->pahsc_StreamLockInited )
+ DeleteCriticalSection( &pahsc->pahsc_StreamLock );
+ PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */
+ past->past_DeviceData = NULL;
+ return paNoError;
+}
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
+{
+ char envbuf[PA_ENV_BUF_SIZE];
+ DWORD hostVersion;
+ DWORD hresult;
+ int minLatencyMsec = 0;
+ double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
+ int minBuffers;
+ /* Let user determine minimal latency by setting environment variable. */
+ hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ minLatencyMsec = atoi( envbuf );
+ }
+ else
+ {
+ /* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+ hostVersion = GetVersion();
+ /* High bit clear if NT */
+ minLatencyMsec = ( (hostVersion & 0x80000000) == 0 ) ? PA_WIN_NT_LATENCY : PA_WIN_9X_LATENCY ;
+#if PA_USE_HIGH_LATENCY
+ PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+ }
+ DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+ minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
+ if( minBuffers < 2 ) minBuffers = 2;
+ return minBuffers;
+}
+/*************************************************************************
+** Cleanup device info.
+*/
+PaError PaHost_Term( void )
+{
+ int i;
+ if( sNumDevices > 0 )
+ {
+ if( sDevicePtrs != NULL )
+ {
+ for( i=0; i<sNumDevices; i++ )
+ {
+ if( sDevicePtrs[i] != NULL )
+ {
+ GlobalFree( (char*)sDevicePtrs[i]->name ); /* MEM */
+ GlobalFree( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */
+ GlobalFree( sDevicePtrs[i] ); /* MEM */
+ }
+ }
+ GlobalFree( sDevicePtrs ); /* MEM */
+ sDevicePtrs = NULL;
+ }
+ sNumDevices = 0;
+ }
+ return paNoError;
+}
+/*************************************************************************/
+void Pa_Sleep( long msec )
+{
+ Sleep( msec );
+}
+/*************************************************************************
+ * Allocate memory that can be accessed in real-time.
+ * This may need to be held in physical memory so that it is not
+ * paged to virtual memory.
+ * This call MUST be balanced with a call to PaHost_FreeFastMemory().
+ * Memory will be set to zero.
+ */
+void *PaHost_AllocateFastMemory( long numBytes )
+{
+ void *addr = GlobalAlloc( GPTR, numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
+ return addr;
+}
+/*************************************************************************
+ * Free memory that could be accessed in real-time.
+ * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
+ */
+void PaHost_FreeFastMemory( void *addr, long numBytes )
+{
+ if( addr != NULL ) GlobalFree( addr ); /* MEM */
+}
+/***********************************************************************/
+PaError PaHost_StreamActive( internalPortAudioStream *past )
+{
+ PaHostSoundControl *pahsc;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ if( pahsc == NULL ) return paInternalError;
+ return (PaError) past->past_IsActive;
+}
+/*************************************************************************
+ * This must be called periodically because mmtime.u.sample
+ * is a DWORD and can wrap and lose sync after a few hours.
+ */
+static PaError PaHost_UpdateStreamTime( PaHostSoundControl *pahsc )
+{
+ MMRESULT mr;
+ MMTIME mmtime;
+ mmtime.wType = TIME_SAMPLES;
+ if( pahsc->pahsc_HWaveOut != NULL )
+ {
+ mr = waveOutGetPosition( pahsc->pahsc_HWaveOut, &mmtime, sizeof(mmtime) );
+ }
+ else
+ {
+ mr = waveInGetPosition( pahsc->pahsc_HWaveIn, &mmtime, sizeof(mmtime) );
+ }
+ if( mr != MMSYSERR_NOERROR )
+ {
+ sPaHostError = mr;
+ return paHostError;
+ }
+ /* This data has two variables and is shared by foreground and background. */
+ /* So we need to make it thread safe. */
+ EnterCriticalSection( &pahsc->pahsc_StreamLock );
+ pahsc->pahsc_FramesPlayed += ((long)mmtime.u.sample) - pahsc->pahsc_LastPosition;
+ pahsc->pahsc_LastPosition = (long)mmtime.u.sample;
+ LeaveCriticalSection( &pahsc->pahsc_StreamLock );
+ return paNoError;
+}
+/*************************************************************************/
+PaTimestamp Pa_StreamTime( PortAudioStream *stream )
+{
+ PaHostSoundControl *pahsc;
+ internalPortAudioStream *past = (internalPortAudioStream *) stream;
+ if( past == NULL ) return paBadStreamPtr;
+ pahsc = (PaHostSoundControl *) past->past_DeviceData;
+ PaHost_UpdateStreamTime( pahsc );
+ return pahsc->pahsc_FramesPlayed;
+}
--- /dev/null
+README for PABLIO
+Portable Audio Blocking I/O Library
+Author: Phil Burk
+
+PABLIO is a simplified interface to PortAudio that provide
+read/write style blocking I/O.
+
+Please see the .DOC file for documentation.
+
+/*
+ * More information on PortAudio at: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
--- /dev/null
+/*
+ * $Id$
+ * pablio.c
+ * Portable Audio Blocking Input/Output utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include "pablio.h"
+#include <string.h>
+
+/************************************************************************/
+/******** Constants *****************************************************/
+/************************************************************************/
+
+#define FRAMES_PER_BUFFER (256)
+
+/************************************************************************/
+/******** Prototypes ****************************************************/
+/************************************************************************/
+
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData );
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
+
+/************************************************************************/
+/******** Functions *****************************************************/
+/************************************************************************/
+
+/* Called from PortAudio.
+ * Read and write data only if there is room in FIFOs.
+ */
+static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData )
+{
+ PABLIO_Stream *data = (PABLIO_Stream*)userData;
+ long numBytes = data->bytesPerFrame * framesPerBuffer;
+ (void) outTime;
+
+ /* This may get called with NULL inputBuffer during initial setup. */
+ if( inputBuffer != NULL )
+ {
+ RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
+ }
+ if( outputBuffer != NULL )
+ {
+ int i;
+ int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
+ /* Zero out remainder of buffer if we run out of data. */
+ for( i=numRead; i<numBytes; i++ )
+ {
+ ((char *)outputBuffer)[i] = 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Allocate buffer. */
+static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
+{
+ long numBytes = numFrames * bytesPerFrame;
+ char *buffer = (char *) malloc( numBytes );
+ if( buffer == NULL ) return paInsufficientMemory;
+ memset( buffer, 0, numBytes );
+ return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
+}
+
+/* Free buffer. */
+static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
+{
+ if( rbuf->buffer ) free( rbuf->buffer );
+ rbuf->buffer = NULL;
+ return paNoError;
+}
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesWritten;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
+ numBytes -= bytesWritten;
+ p += bytesWritten;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
+{
+ long bytesRead;
+ char *p = (char *) data;
+ long numBytes = aStream->bytesPerFrame * numFrames;
+ while( numBytes > 0)
+ {
+ bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
+ numBytes -= bytesRead;
+ p += bytesRead;
+ if( numBytes > 0) Pa_Sleep(10);
+ }
+ return numFrames;
+}
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream )
+{
+ int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ return bytesEmpty / aStream->bytesPerFrame;
+}
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream )
+{
+ int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
+ return bytesFull / aStream->bytesPerFrame;
+}
+
+/************************************************************/
+static unsigned long RoundUpToNextPowerOf2( unsigned long n )
+{
+ long numBits = 0;
+ if( ((n-1) & n) == 0) return n; /* Already Power of two. */
+ while( n > 0 )
+ {
+ n= n>>1;
+ numBits++;
+ }
+ return (1<<numBits);
+}
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ * and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
+ PaSampleFormat format, long flags )
+{
+ long bytesPerSample;
+ long doRead = 0;
+ long doWrite = 0;
+ PaError err;
+ PABLIO_Stream *aStream;
+ long minNumBuffers;
+ long numFrames;
+
+ /* Allocate PABLIO_Stream structure for caller. */
+ aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
+ if( aStream == NULL ) return paInsufficientMemory;
+ memset( aStream, 0, sizeof(PABLIO_Stream) );
+
+ /* Determine size of a sample. */
+ bytesPerSample = Pa_GetSampleSize( format );
+ if( bytesPerSample < 0 )
+ {
+ err = (PaError) bytesPerSample;
+ goto error;
+ }
+ aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
+ aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
+
+ /* Initialize PortAudio */
+ err = Pa_Initialize();
+ if( err != paNoError ) goto error;
+
+ /* Warning: numFrames must be larger than amount of data processed per interrupt
+ * inside PA to prevent glitches. Just to be safe, adjust size upwards.
+ */
+ minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
+ numFrames = minNumBuffers * FRAMES_PER_BUFFER;
+ numFrames = RoundUpToNextPowerOf2( numFrames );
+
+ /* Initialize Ring Buffers */
+ doRead = ((flags & PABLIO_READ) != 0);
+ doWrite = ((flags & PABLIO_WRITE) != 0);
+ if(doRead)
+ {
+ err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ }
+ if(doWrite)
+ {
+ long numBytes;
+ err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
+ if( err != paNoError ) goto error;
+ /* Make Write FIFO appear full initially. */
+ numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
+ }
+
+ /* Open a PortAudio stream that we will use to communicate with the underlying
+ * audio drivers. */
+ err = Pa_OpenStream(
+ &aStream->stream,
+ (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice),
+ (doRead ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
+ (doWrite ? aStream->samplesPerFrame : 0 ),
+ format,
+ NULL,
+ sampleRate,
+ FRAMES_PER_BUFFER,
+ minNumBuffers,
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ blockingIOCallback,
+ aStream );
+ if( err != paNoError ) goto error;
+
+ err = Pa_StartStream( aStream->stream );
+ if( err != paNoError ) goto error;
+
+ *rwblPtr = aStream;
+ return paNoError;
+
+error:
+ CloseAudioStream( aStream );
+ *rwblPtr = NULL;
+ return err;
+}
+
+/************************************************************/
+PaError CloseAudioStream( PABLIO_Stream *aStream )
+{
+ PaError err;
+ int bytesEmpty;
+ int byteSize = aStream->outFIFO.bufferSize;
+
+ /* If we are writing data, make sure we play everything written. */
+ if( byteSize > 0 )
+ {
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ while( bytesEmpty < byteSize )
+ {
+ Pa_Sleep( 10 );
+ bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
+ }
+ }
+
+ err = Pa_StopStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ err = Pa_CloseStream( aStream->stream );
+ if( err != paNoError ) goto error;
+ Pa_Terminate();
+
+error:
+ PABLIO_TermFIFO( &aStream->inFIFO );
+ PABLIO_TermFIFO( &aStream->outFIFO );
+ free( aStream );
+ return err;
+}
--- /dev/null
+LIBRARY PABLIO
+DESCRIPTION 'PABLIO Portable Audio Blocking I/O'
+
+EXPORTS
+ ; Explicit exports can go here
+ Pa_Initialize @1
+ Pa_Terminate @2
+ Pa_GetHostError @3
+ Pa_GetErrorText @4
+ Pa_CountDevices @5
+ Pa_GetDefaultInputDeviceID @6
+ Pa_GetDefaultOutputDeviceID @7
+ Pa_GetDeviceInfo @8
+ Pa_OpenStream @9
+ Pa_OpenDefaultStream @10
+ Pa_CloseStream @11
+ Pa_StartStream @12
+ Pa_StopStream @13
+ Pa_StreamActive @14
+ Pa_StreamTime @15
+ Pa_GetCPULoad @16
+ Pa_GetMinNumBuffers @17
+ Pa_Sleep @18
+
+ OpenAudioStream @19
+ CloseAudioStream @20
+ WriteAudioStream @21
+ ReadAudioStream @22
+
+ Pa_GetSampleSize @23
+
+ ;123456789012345678901234567890123456
+ ;000000000111111111122222222223333333
+
+
--- /dev/null
+#ifndef _PABLIO_H
+#define _PABLIO_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * PABLIO.h
+ * Portable Audio Blocking read/write utility.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * Include file for PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "portaudio.h"
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ RingBuffer inFIFO;
+ RingBuffer outFIFO;
+ PortAudioStream *stream;
+ int bytesPerFrame;
+ int samplesPerFrame;
+}
+PABLIO_Stream;
+
+/* Values for flags for OpenAudioStream(). */
+#define PABLIO_READ (1<<0)
+#define PABLIO_WRITE (1<<1)
+#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE)
+#define PABLIO_MONO (1<<2)
+#define PABLIO_STEREO (1<<3)
+
+/************************************************************
+ * Write data to ring buffer.
+ * Will not return until all the data has been written.
+ */
+long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Read data from ring buffer.
+ * Will not return until all the data has been read.
+ */
+long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames );
+
+/************************************************************
+ * Return the number of frames that could be written to the stream without
+ * having to wait.
+ */
+long GetAudioStreamWriteable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Return the number of frames that are available to be read from the
+ * stream without having to wait.
+ */
+long GetAudioStreamReadable( PABLIO_Stream *aStream );
+
+/************************************************************
+ * Opens a PortAudio stream with default characteristics.
+ * Allocates PABLIO_Stream structure.
+ *
+ * flags parameter can be an ORed combination of:
+ * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
+ * and either PABLIO_MONO or PABLIO_STEREO
+ */
+PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate,
+ PaSampleFormat format, long flags );
+
+PaError CloseAudioStream( PABLIO_Stream *aStream );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _PABLIO_H */
--- /dev/null
+/*
+ * $Id$
+ * ringbuffer.c
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+/***************************************************************************
+ * Initialize FIFO.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
+{
+ if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
+ rbuf->bufferSize = numBytes;
+ rbuf->buffer = (char *)dataPtr;
+ RingBuffer_Flush( rbuf );
+ rbuf->bigMask = (numBytes*2)-1;
+ rbuf->smallMask = (numBytes)-1;
+ return 0;
+}
+/***************************************************************************
+** Return number of bytes available for reading. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
+{
+ return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
+}
+/***************************************************************************
+** Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
+{
+ return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
+}
+
+/***************************************************************************
+** Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf )
+{
+ rbuf->writeIndex = rbuf->readIndex = 0;
+}
+
+/***************************************************************************
+** Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetWriteAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if write is not contiguous. */
+ index = rbuf->writeIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+
+
+/***************************************************************************
+*/
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 )
+{
+ long index;
+ long available = RingBuffer_GetReadAvailable( rbuf );
+ if( numBytes > available ) numBytes = available;
+ /* Check to see if read is not contiguous. */
+ index = rbuf->readIndex & rbuf->smallMask;
+ if( (index + numBytes) > rbuf->bufferSize )
+ {
+ /* Write data in two blocks that wrap the buffer. */
+ long firstHalf = rbuf->bufferSize - index;
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = firstHalf;
+ *dataPtr2 = &rbuf->buffer[0];
+ *sizePtr2 = numBytes - firstHalf;
+ }
+ else
+ {
+ *dataPtr1 = &rbuf->buffer[index];
+ *sizePtr1 = numBytes;
+ *dataPtr2 = NULL;
+ *sizePtr2 = 0;
+ }
+ return numBytes;
+}
+/***************************************************************************
+*/
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
+{
+ return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
+}
+
+/***************************************************************************
+** Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numWritten;
+ void *data1, *data2;
+ numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+
+ memcpy( data1, data, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data2, data, size2 );
+ }
+ else
+ {
+ memcpy( data1, data, size1 );
+ }
+ RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
+ return numWritten;
+}
+
+/***************************************************************************
+** Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
+{
+ long size1, size2, numRead;
+ void *data1, *data2;
+ numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
+ if( size2 > 0 )
+ {
+ memcpy( data, data1, size1 );
+ data = ((char *)data) + size1;
+ memcpy( data, data2, size2 );
+ }
+ else
+ {
+ memcpy( data, data1, size1 );
+ }
+ RingBuffer_AdvanceReadIndex( rbuf, numRead );
+ return numRead;
+}
--- /dev/null
+#ifndef _RINGBUFFER_H
+#define _RINGBUFFER_H
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/*
+ * $Id$
+ * ringbuffer.h
+ * Ring Buffer utility..
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program is distributed with the PortAudio Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "ringbuffer.h"
+#include <string.h>
+
+typedef struct
+{
+ long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */
+ long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */
+ long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */
+ long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */
+ long smallMask; /* Used for fitting indices to buffer. */
+ char *buffer;
+}
+RingBuffer;
+/*
+ * Initialize Ring Buffer.
+ * numBytes must be power of 2, returns -1 if not.
+ */
+long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr );
+
+/* Clear buffer. Should only be called when buffer is NOT being read. */
+void RingBuffer_Flush( RingBuffer *rbuf );
+
+/* Return number of bytes available for writing. */
+long RingBuffer_GetWriteAvailable( RingBuffer *rbuf );
+/* Return number of bytes available for read. */
+long RingBuffer_GetReadAvailable( RingBuffer *rbuf );
+/* Return bytes written. */
+long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes );
+/* Return bytes read. */
+long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes );
+
+/* Get address of region(s) to which we can write data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes );
+
+/* Get address of region(s) from which we can read data.
+** If the region is contiguous, size2 will be zero.
+** If non-contiguous, size2 will be the size of second region.
+** Returns room available to be written or numBytes, whichever is smaller.
+*/
+long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
+ void **dataPtr1, long *sizePtr1,
+ void **dataPtr2, long *sizePtr2 );
+
+long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* _RINGBUFFER_H */
--- /dev/null
+/*
+ * $Id$
+ * test_rw.c
+ * Read input from one stream and write it to another.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "pablio.h"
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (15)
+#define SAMPLES_PER_FRAME (2)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i;
+ SAMPLE samples[SAMPLES_PER_FRAME];
+ PaError err;
+ PABLIO_Stream *aStream;
+
+ printf("Full duplex sound test using PortAudio and RingBuffers\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_READ_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Process samples in the foreground. */
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+ {
+ /* Read one frame of data into sample array from audio input. */
+ ReadAudioStream( aStream, samples, 1 );
+ /* Write that same frame of data to output. */
+ /* samples[1] = (i&256) * (1.0f/256.0f); /* sawtooth */
+ WriteAudioStream( aStream, samples, 1 );
+ }
+
+ CloseAudioStream( aStream );
+
+ printf("Full duplex sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ Pa_Terminate();
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+/*
+ * $Id$
+ * test_rw_echo.c
+ * Echo delayed input to output.
+ *
+ * Author: Phil Burk, http://www.softsynth.com/portaudio/
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * Note that if you need low latency, you should not use PABLIO.
+ * Use the PA_OpenStream callback technique which is lower level
+ * than PABLIO.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+/*
+** Note that many of the older ISA sound cards on PCs do NOT support
+** full duplex audio (simultaneous record and playback).
+** And some only support full duplex at lower sample rates.
+*/
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (20)
+#define SAMPLES_PER_FRAME (2)
+
+/* Select whether we will use floats or shorts. */
+#if 1
+#define SAMPLE_TYPE paFloat32
+typedef float SAMPLE;
+#else
+#define SAMPLE_TYPE paInt16
+typedef short SAMPLE;
+#endif
+
+#define NUM_ECHO_FRAMES (2*SAMPLE_RATE)
+SAMPLE samples[NUM_ECHO_FRAMES][SAMPLES_PER_FRAME] = {0.0};
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i;
+ PaError err;
+ PABLIO_Stream *aInStream;
+ PABLIO_Stream *aOutStream;
+ int index;
+
+ printf("Full duplex sound test using PABLIO\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ /* Open input first so it can start to fill buffers. */
+ err = OpenAudioStream( &aInStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_READ | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+ /* printf("opened input\n"); fflush(stdout); /**/
+
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, SAMPLE_TYPE,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+ /* printf("opened output\n"); fflush(stdout); /**/
+
+ /* Process samples in the foreground. */
+ index = 0;
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+ {
+ /* Write old frame of data to output. */
+ /* samples[index][1] = (i&256) * (1.0f/256.0f); /* sawtooth */
+ WriteAudioStream( aOutStream, &samples[index][0], 1 );
+
+ /* Read one frame of data into sample array for later output. */
+ ReadAudioStream( aInStream, &samples[index][0], 1 );
+ index += 1;
+ if( index >= NUM_ECHO_FRAMES ) index = 0;
+
+ if( (i & 0xFFFF) == 0 ) printf("i = %d\n", i ); fflush(stdout); /**/
+ }
+
+ CloseAudioStream( aOutStream );
+ CloseAudioStream( aInStream );
+
+ printf("R/W echo sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PortAudio\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+/*
+ * $Id$
+ * test_w_saw.c
+ * Generate stereo sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE (44100)
+#define NUM_SECONDS (6)
+#define SAMPLES_PER_FRAME (2)
+
+#define FREQUENCY (220.0f)
+#define PHASE_INCREMENT (2.0f * FREQUENCY / SAMPLE_RATE)
+#define FRAMES_PER_BLOCK (100)
+
+float samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+float phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ PaError err;
+ PABLIO_Stream *aOutStream;
+
+ printf("Generate sawtooth waves using PABLIO.\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paFloat32,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Initialize oscillator phases. */
+ phases[0] = 0.0;
+ phases[1] = 0.0;
+
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Generate sawtooth waveforms in a block for efficiency. */
+ for( j=0; j<FRAMES_PER_BLOCK; j++ )
+ {
+ /* Generate a sawtooth wave by incrementing a variable. */
+ phases[0] += PHASE_INCREMENT;
+ /* The signal range is -1.0 to +1.0 so wrap around if we go over. */
+ if( phases[0] > 1.0f ) phases[0] -= 2.0f;
+ samples[j][0] = phases[0];
+
+ /* On the second channel, generate a sawtooth wave a fifth higher. */
+ phases[1] += PHASE_INCREMENT * (3.0f / 2.0f);
+ if( phases[1] > 1.0f ) phases[1] -= 2.0f;
+ samples[j][1] = phases[1];
+ }
+
+ /* Write samples to output. */
+ WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aOutStream );
+
+ printf("Sawtooth sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PABLIO\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}
--- /dev/null
+/*
+ * $Id$
+ * test_w_saw8.c
+ * Generate stereo 8 bit sawtooth waveforms.
+ *
+ * Author: Phil Burk, http://www.softsynth.com
+ *
+ * This program uses PABLIO, the Portable Audio Blocking I/O Library.
+ * PABLIO is built on top of PortAudio, the Portable Audio Library.
+ *
+ * For more information see: http://www.audiomulch.com/portaudio/
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define SAMPLE_RATE (22050)
+#define NUM_SECONDS (6)
+#define SAMPLES_PER_FRAME (2)
+
+
+#define FRAMES_PER_BLOCK (100)
+
+unsigned char samples[FRAMES_PER_BLOCK][SAMPLES_PER_FRAME];
+unsigned char phases[SAMPLES_PER_FRAME];
+
+/*******************************************************************/
+int main(void);
+int main(void)
+{
+ int i,j;
+ PaError err;
+ PABLIO_Stream *aOutStream;
+
+ printf("Generate unsigned 8 bit sawtooth waves using PABLIO.\n");
+ fflush(stdout);
+
+ /* Open simplified blocking I/O layer on top of PortAudio. */
+ err = OpenAudioStream( &aOutStream, SAMPLE_RATE, paUInt8,
+ (PABLIO_WRITE | PABLIO_STEREO) );
+ if( err != paNoError ) goto error;
+
+ /* Initialize oscillator phases to "ground" level for paUInt8. */
+ phases[0] = 128;
+ phases[1] = 128;
+
+ for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i += FRAMES_PER_BLOCK )
+ {
+ /* Generate sawtooth waveforms in a block for efficiency. */
+ for( j=0; j<FRAMES_PER_BLOCK; j++ )
+ {
+ /* Generate a sawtooth wave by incrementing a variable. */
+ phases[0] += 1;
+ /* We don't have to do anything special to wrap when using paUint8 because
+ * 8 bit arithmetic automatically wraps. */
+ samples[j][0] = phases[0];
+
+ /* On the second channel, generate a higher sawtooth wave. */
+ phases[1] += 3;
+ samples[j][1] = phases[1];
+ }
+
+ /* Write samples to output. */
+ WriteAudioStream( aOutStream, samples, FRAMES_PER_BLOCK );
+ }
+
+ CloseAudioStream( aOutStream );
+
+ printf("Sawtooth sound test complete.\n" );
+ fflush(stdout);
+ return 0;
+
+error:
+ fprintf( stderr, "An error occured while using PABLIO\n" );
+ fprintf( stderr, "Error number: %d\n", err );
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+ return -1;
+}