diff options
author | Guenter Geiger <ggeiger@users.sourceforge.net> | 2002-07-29 17:06:19 +0000 |
---|---|---|
committer | Guenter Geiger <ggeiger@users.sourceforge.net> | 2002-07-29 17:06:19 +0000 |
commit | 57045df5fe3ec557e57dc7434ac1a07b5521bffc (patch) | |
tree | 7174058b41b73c808107c7090d9a4e93ee202341 /pd/portaudio | |
parent | da38b3424229e59f956252c3d89895e43e84e278 (diff) |
This commit was generated by cvs2svn to compensate for changes in r58,
which included commits to RCS files with non-trunk default branches.
svn path=/trunk/; revision=59
Diffstat (limited to 'pd/portaudio')
40 files changed, 10002 insertions, 0 deletions
diff --git a/pd/portaudio/LICENSE.txt b/pd/portaudio/LICENSE.txt new file mode 100644 index 00000000..105da3f7 --- /dev/null +++ b/pd/portaudio/LICENSE.txt @@ -0,0 +1,65 @@ +Portable header file to contain: +/* + * 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. + * + */ + + +Implementation files to contain: +/* + * PortAudio Portable Real-Time Audio Library + * Latest version at: http://www.audiomulch.com/portaudio/ + * <platform> Implementation + * Copyright (c) 1999-2000 <author(s)> + * + * 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. + * + */
\ No newline at end of file diff --git a/pd/portaudio/MSP-README.txt b/pd/portaudio/MSP-README.txt new file mode 100644 index 00000000..e29de09f --- /dev/null +++ b/pd/portaudio/MSP-README.txt @@ -0,0 +1,3 @@ +This is not the full distribution, just what's needed to get Pd running +on Mac OSX and ASIO (Windows.) I changed some code in pablio.c as marked. +-MSP diff --git a/pd/portaudio/README.txt b/pd/portaudio/README.txt new file mode 100644 index 00000000..d1e5d7d6 --- /dev/null +++ b/pd/portaudio/README.txt @@ -0,0 +1,81 @@ +README for PortAudio +Implementations for PC DirectSound and Mac SoundManager + +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com// + * + * Copyright (c) 1999-2000 Phil Burk and Ross Bencina + * + * 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. + * + */ + +PortAudio is a portable audio I/O library designed for cross-platform +support of audio. It uses a callback mechanism to request audio processing. +Audio can be generated in various formats, including 32 bit floating point, +and will be converted to the native format internally. + +Documentation: + See "pa_common/portaudio.h" for API spec. + See docs folder for a tutorial. + Also see http://www.portaudio.com/docs/ + And see "pa_tests/patest_saw.c" for an example. + +For information on compiling programs with PortAudio, please see the +tutorial at: + + http://www.portaudio.com/docs/pa_tutorial.html + +Important Files and Folders: + pa_common/ = platform independant code + pa_common/portaudio.h = header file for PortAudio API. Specifies API. + pa_common/pa_lib.c = host independant code for all implementations. + + pablio = simple blocking read/write interface + +Platform Implementations + pa_asio = ASIO for Windows and Macintosh + pa_beos = BeOS + pa_mac = Macintosh Sound Manager for OS 8,9 and Carbon + pa_mac_core = Macintosh Core Audio for OS X + pa_sgi = Silicon Graphics AL + pa_unix_oss = OSS implementation for various Unixes + pa_win_ds = Windows Direct Sound + pa_win_wmme = Windows MME (most widely supported) + +Test Programs + pa_tests/pa_fuzz.c = guitar fuzz box + pa_tests/pa_devs.c = print a list of available devices + pa_tests/pa_minlat.c = determine minimum latency for your machine + pa_tests/paqa_devs.c = self test that opens all devices + pa_tests/paqa_errs.c = test error detection and reporting + pa_tests/patest_clip.c = hear a sine wave clipped and unclipped + pa_tests/patest_dither.c = hear effects of dithering (extremely subtle) + pa_tests/patest_pink.c = fun with pink noise + pa_tests/patest_record.c = record and playback some audio + pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad(). + pa_tests/patest_sine.c = output a sine wave in a simple PA app + pa_tests/patest_sync.c = test syncronization of audio and video + pa_tests/patest_wire.c = pass input to output, wire simulator diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp new file mode 100644 index 00000000..baed8ed4 --- /dev/null +++ b/pd/portaudio/pa_asio/pa_asio.cpp @@ -0,0 +1,2998 @@ +/* + * $Id: pa_asio.cpp,v 1.7 2002/04/30 12:33:04 stephane Exp $ + * 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 + 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz + 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz + 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz + 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk + 13-04-02 Removes another compiler warning : Stephane Letz + 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz + + TO DO : + + - Check Pa_StopSteam and Pa_AbortStream + - Optimization for Input only or Ouput only (really necessary ??) +*/ + + +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +#include "asiosys.h" +#include "asio.h" +#include "asiodrivers.h" + + +#if MAC +#include <Devices.h> +#include <Timer.h> +#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; + + +//---------------------------------------------------------- +#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 +static void bufferSwitch(long index, ASIOBool processNow); +static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow); +static void sampleRateChanged(ASIOSampleRate sRate); +static long asioMessages(long selector, long value, void* message, double* opt); +static void Pa_StartUsageCalculation( internalPortAudioStream *past ); +static void Pa_EndUsageCalculation( internalPortAudioStream *past ); + +static void Pa_ASIO_Convert_Inter_Input( + ASIOBufferInfo* nativeBuffer, + void* inputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index); + +static void Pa_ASIO_Convert_Inter_Output( + ASIOBufferInfo* nativeBuffer, + void* outputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index); + +static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, + ASIOSampleType nativeFormat, + long NumInputChannels, + long NumOuputChannels, + long index, + long hostFrameOffset, + long frames); + +static void Pa_ASIO_Callback_Input(long index); +static void Pa_ASIO_Callback_Output(long index, long framePerBuffer); +static void Pa_ASIO_Callback_End(); +static void Pa_ASIO_Clear_User_Buffers(); + +// Some external references +extern AsioDrivers* asioDrivers ; +bool loadAsioDriver(char *name); +unsigned long get_sys_reference_time(); + + +/************************************************************************************/ +/****************** 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 + + +static bool Pa_ASIO_loadAsioDriver(char *name) +{ + #ifdef WINDOWS + CoInitialize(0); + #endif + return loadAsioDriver(name); +} + + + +// Utilities for alignement buffer size computation +static int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);} +static 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 +static 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); + /* check memory */ + if(!names[i]) return paInsufficientMemory; + } + + /* MUST BE CHECKED : to force fragments loading on Mac */ + Pa_ASIO_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 ( !Pa_ASIO_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[sNumDevices].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)); + /* check memory */ + if (!sampleRates) { + ASIOExit(); + return paInsufficientMemory; + } + 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 */ + 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: +static 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 + + +static 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 + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Input_Int16_Float32 (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; + } + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Input_Int32_Float32 (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 +static void Input_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap) +{ + unsigned long temp; + int i,j; + + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned 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 = (float)temp; + userBufPtr += NumInputChannels; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 +static void Input_Float32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap) +{ + unsigned long temp; + int i,j; + + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned 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 = (long)((float)temp * MAX_INT32_FP); // Is temp a value between -1.0 and 1.0 ?? + userBufPtr += NumInputChannels; + } + } +} + + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 = (short)temp; + userBufPtr += NumInputChannels; + } + } +} + + //------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 = (short)(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 = (short)temp; + userBufPtr += NumInputChannels; + } + } + + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +// MUST BE TESTED +static void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned 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 = (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++ ) { + unsigned long *asioBufPtr = &((unsigned long*)nativeBuffer[j].buffers[index])[hostFrameOffset]; + short *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 = (short)(((float)temp * MAX_INT16_FP) + dither); + temp = ClipShort(temp); + *userBufPtr = (short)temp; + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 + +static void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned 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++ ) { + unsigned long *asioBufPtr = &((unsigned 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 = (char)temp; + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 + +static void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned 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)(((float)temp*MAX_INT8_FP) + 0x80); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j<NumInputChannels; j++ ) { + unsigned long *asioBufPtr = &((unsigned long*)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 = 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 +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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] = (short)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] = (short)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] = (short)temp; + userBufPtr += NumOuputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 + + static 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; + } + } + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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] = (short)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] = (short)temp; + userBufPtr += NumOuputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 + +static 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; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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] = (short)temp; + userBufPtr += NumOuputChannels; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 +static 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; + } + } +} +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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] = (short)temp; + userBufPtr += NumOuputChannels; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 +static 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; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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] = (short)temp; + userBufPtr += NumOuputChannels; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 + +static 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; + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Clear_Output_16 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long 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; } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Clear_Output_32 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long 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; } + } +} + + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 ??) +static 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; + } + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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; + } +} +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Callback_End() + { + /* Empty ASIO ouput : write offset */ + asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; + } + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static 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 ); + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- + static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, + ASIOSampleType nativeFormat, + long NumInputChannels, + long NumOuputChannels, + long index, + long hostFrameOffset, + long 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; + } +} + + +//--------------------------------------------------------------------------------------- +static void Pa_ASIO_Convert_Inter_Input( + ASIOBufferInfo* nativeBuffer, + void* inputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long 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_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); + break; + case ASIOSTInt32LSB: + Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); + break; + case ASIOSTInt32MSB: + Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Float32(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; + } + } +} + + +//--------------------------------------------------------------------------------------- +static void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer, + void* outputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long 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 (!Pa_ASIO_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? */ + PaError err = (PaError) Pa_CountDevices(); + return ( err < 0 ) ? err : paNoError; +} + +/***********************************************************************/ +PaError PaHost_Term( void ) +{ + int i; + PaDeviceInfo *dev; + double *rates; + PaError result = paNoError; + + if (sNumDevices > 0) { + + /* 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; + + /* Check if a stream already runs */ + if (asioDriverInfo.past != NULL) return paHostError; + + /* 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 + + + + diff --git a/pd/portaudio/pa_common/pa_convert.c b/pd/portaudio/pa_common/pa_convert.c new file mode 100644 index 00000000..377a9554 --- /dev/null +++ b/pd/portaudio/pa_common/pa_convert.c @@ -0,0 +1,402 @@ +/* + * pa_conversions.c + * portaudio + * + * Created by Phil Burk on Mon Mar 18 2002. + * + */ +#include <stdio.h> + +#include "portaudio.h" +#include "pa_host.h" + +#define CLIP( val, min, max ) { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); } + +/*************************************************************************/ +static void PaConvert_Float32_Int16( + float *sourceBuffer, int sourceStride, + short *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + short samp = (short) (*sourceBuffer * (32767.0f)); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int16_Clip( + float *sourceBuffer, int sourceStride, + short *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + long samp = (long) (*sourceBuffer * (32767.0f)); + CLIP( samp, -0x8000, 0x7FFF ); + *targetBuffer = (short) samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int16_ClipDither( + float *sourceBuffer, int sourceStride, + short *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + // use smaller scaler to prevent overflow when we add the dither + float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; + float dithered = (*sourceBuffer * (32766.0f)) + dither; + long samp = (long) dithered; + CLIP( samp, -0x8000, 0x7FFF ); + *targetBuffer = (short) samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int16_Dither( + float *sourceBuffer, int sourceStride, + short *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + // use smaller scaler to prevent overflow when we add the dither + float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; + float dithered = (*sourceBuffer * (32766.0f)) + dither; + *targetBuffer = (short) dithered; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + + +/*************************************************************************/ +static void PaConvert_Int16_Float32( + short *sourceBuffer, int sourceStride, + float *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + float samp = *sourceBuffer * (1.0f / 32768.0f); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int8( + float *sourceBuffer, int sourceStride, + char *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + char samp = (char) (*sourceBuffer * (127.0)); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + + +/*************************************************************************/ +static void PaConvert_Float32_Int8_Clip( + float *sourceBuffer, int sourceStride, + char *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + long samp = *sourceBuffer * 127.0f; + CLIP( samp, -0x80, 0x7F ); + *targetBuffer = (char) samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int8_ClipDither( + float *sourceBuffer, int sourceStride, + char *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + // use smaller scaler to prevent overflow when we add the dither + float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; + float dithered = (*sourceBuffer * (126.0f)) + dither; + long samp = (long) dithered; + CLIP( samp, -0x80, 0x7F ); + *targetBuffer = (char) samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_Int8_Dither( + float *sourceBuffer, int sourceStride, + char *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + // use smaller scaler to prevent overflow when we add the dither + float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; //FIXME + float dithered = (*sourceBuffer * (126.0f)) + dither; + long samp = (long) dithered; + *targetBuffer = (char) samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Int8_Float32( + char *sourceBuffer, int sourceStride, + float *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + float samp = *sourceBuffer * (1.0f / 128.0f); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_Float32_UInt8( + float *sourceBuffer, int sourceStride, + unsigned char *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + unsigned char samp = 128 + (unsigned char) (*sourceBuffer * (127.0)); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static void PaConvert_UInt8_Float32( + unsigned char *sourceBuffer, int sourceStride, + float *targetBuffer, int targetStride, + int numSamples ) +{ + int i; + for( i=0; i<numSamples; i++ ) + { + float samp = (*sourceBuffer - 128) * (1.0f / 128.0f); + *targetBuffer = samp; + sourceBuffer += sourceStride; + targetBuffer += targetStride; + } +} + +/*************************************************************************/ +static PortAudioConverter *PaConvert_SelectProc( PaSampleFormat sourceFormat, + PaSampleFormat targetFormat, int ifClip, int ifDither ) +{ + PortAudioConverter *proc = NULL; + switch( sourceFormat ) + { + case paUInt8: + switch( targetFormat ) + { + case paFloat32: + proc = (PortAudioConverter *) PaConvert_UInt8_Float32; + break; + default: + break; + } + break; + case paInt8: + switch( targetFormat ) + { + case paFloat32: + proc = (PortAudioConverter *) PaConvert_Int8_Float32; + break; + default: + break; + } + break; + case paInt16: + switch( targetFormat ) + { + case paFloat32: + proc = (PortAudioConverter *) PaConvert_Int16_Float32; + break; + default: + break; + } + break; + case paFloat32: + switch( targetFormat ) + { + case paUInt8: + proc = (PortAudioConverter *) PaConvert_Float32_UInt8; + break; + case paInt8: + if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_ClipDither; + else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Clip; + else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Dither; + else proc = (PortAudioConverter *) PaConvert_Float32_Int8; + break; + case paInt16: + if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_ClipDither; + else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Clip; + else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Dither; + else proc = (PortAudioConverter *) PaConvert_Float32_Int16; + break; + default: + break; + } + break; + default: + break; + } + return proc; + +} + +/*************************************************************************/ +PaError PaConvert_SetupInput( internalPortAudioStream *past, + PaSampleFormat nativeInputSampleFormat ) +{ + past->past_NativeInputSampleFormat = nativeInputSampleFormat; + past->past_InputConversionSourceStride = 1; + past->past_InputConversionTargetStride = 1; + + if( nativeInputSampleFormat != past->past_InputSampleFormat ) + { + int ifDither = (past->past_Flags & paDitherOff) == 0; + past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat, + past->past_InputSampleFormat, 0, ifDither ); + if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported; + } + else + { + past->past_InputConversionProc = NULL; /* no conversion necessary */ + } + + return paNoError; +} + +/*************************************************************************/ +PaError PaConvert_SetupOutput( internalPortAudioStream *past, + PaSampleFormat nativeOutputSampleFormat ) +{ + + past->past_NativeOutputSampleFormat = nativeOutputSampleFormat; + past->past_OutputConversionSourceStride = 1; + past->past_OutputConversionTargetStride = 1; + + if( nativeOutputSampleFormat != past->past_OutputSampleFormat ) + { + int ifDither = (past->past_Flags & paDitherOff) == 0; + int ifClip = (past->past_Flags & paClipOff) == 0; + + past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat, + nativeOutputSampleFormat, ifClip, ifDither ); + if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported; + } + else + { + past->past_OutputConversionProc = NULL; /* no conversion necessary */ + } + + return paNoError; +} + +/************************************************************************* +** Called by host code. +** Convert input from native format to user format, +** call user code, +** then convert output to native format. +** Returns result from user callback. +*/ +long PaConvert_Process( internalPortAudioStream *past, + void *nativeInputBuffer, + void *nativeOutputBuffer ) +{ + int userResult; + void *inputBuffer = NULL; + void *outputBuffer = NULL; + + /* Get native input data. */ + if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) + { + if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat ) + { + /* Already in native format so just read directly from native buffer. */ + inputBuffer = nativeInputBuffer; + } + else + { + inputBuffer = past->past_InputBuffer; + /* Convert input data to user format. */ + (*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride, + inputBuffer, past->past_InputConversionTargetStride, + past->past_FramesPerUserBuffer * past->past_NumInputChannels ); + } + } + + /* Are we doing output? */ + if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) + { + outputBuffer = (past->past_OutputConversionProc == NULL) ? + 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 ); + + /* Advance frame counter for timestamp. */ + past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here? + + /* Convert to native format if necessary. */ + if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) ) + { + (*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride, + nativeOutputBuffer, past->past_OutputConversionTargetStride, + past->past_FramesPerUserBuffer * past->past_NumOutputChannels ); + } + + return userResult; +} diff --git a/pd/portaudio/pa_common/pa_host.h b/pd/portaudio/pa_common/pa_host.h new file mode 100644 index 00000000..d9cd71ab --- /dev/null +++ b/pd/portaudio/pa_common/pa_host.h @@ -0,0 +1,185 @@ +#ifndef PA_HOST_H +#define PA_HOST_H + +/* + * $Id: pa_host.h,v 1.1.1.1 2002-07-29 17:06:18 ggeiger Exp $ + * 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. + * + */ + +#include "portaudio.h" + +#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 + +/* Used to convert between various sample formats. */ +typedef void (PortAudioConverter)( + void *inputBuffer, int inputStride, + void *outputBuffer, int outputStride, + int numSamples ); + +#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_NativeInputSampleFormat; + PaSampleFormat past_InputSampleFormat; + PaSampleFormat past_NativeOutputSampleFormat; + 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; + /* Format Conversion */ + /* These are setup by PaConversion_Setup() */ + PortAudioConverter *past_InputConversionProc; + int past_InputConversionSourceStride; + int past_InputConversionTargetStride; + PortAudioConverter *past_OutputConversionProc; + int past_OutputConversionSourceStride; + int past_OutputConversionTargetStride; +} +internalPortAudioStream; + +/************************************************************************************/ +/******** These functions must be provided by a platform implementation. ************/ +/************************************************************************************/ + +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 ); + +void *PaHost_AllocateFastMemory( long numBytes ); +void PaHost_FreeFastMemory( void *addr, long numBytes ); + +/* This only called if PA_VALIDATE_RATE IS CALLED. */ +PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate, + double *closestFrameRatePtr ); + +/**********************************************************************/ +/************ Common Utility Routines provided by PA ******************/ +/**********************************************************************/ + +/* PaHost_IsInitialized() returns non-zero if PA is initialized, 0 otherwise */ +int PaHost_IsInitialized( void ); + +internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream ); + +int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, + int numRates, double frameRate ); + +long Pa_CallConvertInt16( internalPortAudioStream *past, + short *nativeInputBuffer, + short *nativeOutputBuffer ); + +/* Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit 1.31 fixed point value prior to >>15. +** Range of output is +/- 65535 +** Multiply by PA_DITHER_SCALE to get a float between -2.0 and 2.0. */ +#define PA_DITHER_BITS (15) +#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1)) +long PaConvert_TriangularDither( void ); + +PaError PaConvert_SetupInput( internalPortAudioStream *past, + PaSampleFormat nativeInputSampleFormat ); + +PaError PaConvert_SetupOutput( internalPortAudioStream *past, + PaSampleFormat nativeOutputSampleFormat ); + +long PaConvert_Process( internalPortAudioStream *past, + void *nativeInputBuffer, + void *nativeOutputBuffer ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_HOST_H */ diff --git a/pd/portaudio/pa_common/pa_lib.c b/pd/portaudio/pa_common/pa_lib.c new file mode 100644 index 00000000..ade2d82b --- /dev/null +++ b/pd/portaudio/pa_common/pa_lib.c @@ -0,0 +1,806 @@ +/* + * $Id: pa_lib.c,v 1.1.1.1 2002-07-29 17:06:18 ggeiger Exp $ + * 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() MUST 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 for passing formatted samples to user callback. */ + 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; +} + +/*************************************************************************/ +internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream ) +{ + internalPortAudioStream* result = (internalPortAudioStream*) stream; + + if( result == NULL || result->past_Magic != PA_MAGIC ) + return NULL; + else + return result; +} + +/************************************************************* +** Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit integer prior to >>15. +** Range of output is +/- 32767 +*/ +#define PA_DITHER_BITS (15) +#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1)) +long PaConvert_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. + * Shift before adding to prevent overflow which would skew the distribution. + * Also shift an extra bit for the high pass filter. + */ +#define DITHER_SHIFT ((32 - PA_DITHER_BITS) + 1) + current = (((long)randSeed1)>>DITHER_SHIFT) + (((long)randSeed2)>>DITHER_SHIFT); + /* 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; + 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 += PaConvert_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 += PaConvert_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 = PaConvert_TriangularDither()*PA_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) + PaConvert_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; +} + +/*************************************************************************/ +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; +} + +int PaHost_IsInitialized() +{ + return gInitCount; +} + +/*************************************************************************/ +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; +} + + diff --git a/pd/portaudio/pa_common/pa_trace.c b/pd/portaudio/pa_common/pa_trace.c new file mode 100644 index 00000000..d55a6d37 --- /dev/null +++ b/pd/portaudio/pa_common/pa_trace.c @@ -0,0 +1,83 @@ +/* + * $Id: pa_trace.c,v 1.1.1.1 2002/01/22 00:52:11 phil Exp $ + * 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 diff --git a/pd/portaudio/pa_common/pa_trace.h b/pd/portaudio/pa_common/pa_trace.h new file mode 100644 index 00000000..d0fc904c --- /dev/null +++ b/pd/portaudio/pa_common/pa_trace.h @@ -0,0 +1,67 @@ +#ifndef PA_TRACE_H +#define PA_TRACE_H +/* + * $Id: pa_trace.h,v 1.1.1.1 2002/01/22 00:52:11 phil Exp $ + * 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 */ diff --git a/pd/portaudio/pa_common/portaudio.h b/pd/portaudio/pa_common/portaudio.h new file mode 100644 index 00000000..06f7079b --- /dev/null +++ b/pd/portaudio/pa_common/portaudio.h @@ -0,0 +1,463 @@ +#ifndef PORT_AUDIO_H +#define PORT_AUDIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: portaudio.h,v 1.5 2002/03/26 18:04:22 philburk Exp $ + * 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 ); + +/* + Pa_GetHostError() returns a host specific error code. + This can be called after receiving a PortAudio error code of paHostError. + +*/ + +long Pa_GetHostError( void ); + +/* + Pa_GetErrorText() translates the supplied PortAudio 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 + PortAudio implementations. + + The floating point representation (paFloat32) uses +1.0 and -1.0 as the + maximum and minimum respectively. + + paUInt8 is an unsigned 8 bit format where 128 is considered "ground" + +*/ + +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)) +#define paCustomFormat ((PaSampleFormat) (1<<16)) + +/* + Device enumeration mechanism. + + Device ids range from 0 to Pa_CountDevices()-1. + + Devices may support input, output or both. + +*/ + +typedef int PaDeviceID; +#define paNoDevice -1 + +int Pa_CountDevices( void ); + +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; + +/* + Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the + default device ids for input and output respectively, or paNoDevice if + no device is available. + 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 ids by using + the supplied application "pa_devs". + +*/ + +PaDeviceID Pa_GetDefaultInputDeviceID( void ); +PaDeviceID Pa_GetDefaultOutputDeviceID( void ); + + + +/* + Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure + for the device specified. + If the device parameter is out of range the function returns NULL. + + PortAudio manages the memory referenced by the returned pointer, the client + must not manipulate or free the memory. The pointer is only guaranteed to be + valid between calls to Pa_Initialize() and Pa_Terminate(). + +*/ + +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device ); + +/* + PaTimestamp is used to represent a continuous sample clock with arbitrary + start time that can be used for syncronization. The type is used for the + outTime argument to the PortAudioCallback and as the result of Pa_StreamTime() + +*/ + +typedef double PaTimestamp; + +/* + PortAudioCallback is implemented by PortAudio clients. + + 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 non-zero 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 + non-zero 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. + +*/ + +#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; + +/* + 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 + PaDeviceInfo record for the device specified by the inputDevice parameter. + If inputDevice is paNoDevice numInputChannels is ignored. + + inputSampleFormat is the sample 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 device's 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 full-duplex streams it is the + sample rate for both 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 behaviour 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: + Upon 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 non-zero 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 output 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. This has the same effect 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 (1) when the stream is active (ie playing + or recording audio), zero (0) 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 in samples for the stream. + This time may be used as a time reference (for example synchronizing audio to + MIDI). + +*/ + +PaTimestamp Pa_StreamTime( PortAudioStream *stream ); + +/* + Pa_GetCPULoad() returns the CPU Load for the stream. + The "CPU Load" is a fraction of total CPU time consumed by the stream's + audio processing routines including, but not limited to the client supplied + callback. + 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 ); + +/* + Pa_GetMinNumBuffers() returns the minimum number of buffers required by + 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 ); + +/* + Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds. + You may sleep longer than the requested time so don't rely on this for + accurate musical timing. + + Pa_Sleep() is provided as a convenience for authors of portable code (such as + the tests and examples in the PortAudio distribution.) + +*/ + +void Pa_Sleep( long msec ); + +/* + Pa_GetSampleSize() returns the size in bytes of a single sample in the + supplied PaSampleFormat, or paSampleFormatNotSupported if the format is + no supported. + +*/ + +PaError Pa_GetSampleSize( PaSampleFormat format ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_AUDIO_H */ diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt new file mode 100644 index 00000000..c79b90e6 --- /dev/null +++ b/pd/portaudio/pa_mac_core/notes.txt @@ -0,0 +1,34 @@ +Notes on Core Audio Implementation of PortAudio + +by Phil Burk and Darren Gibbs + +Document last updated March 20, 2002 + +WHAT WORKS + +Output with very low latency, <10 msec. +Half duplex input or output. +Full duplex on the same CoreAudio device. +The paFLoat32, paInt16, paInt8, paUInt8 sample formats. +Pa_GetCPULoad() +Pa_StreamTime() + +KNOWN BUGS OR LIMITATIONS + +We do not yet support simultaneous input and output on different +devices. Note that some CoreAudio devices like the Roland UH30 look +like one device but are actually two different CoreAudio devices. The +BuiltIn audio is typically one CoreAudio device. + +Mono doesn't work. + +DEVICE MAPPING + +CoreAudio devices can support both input and output. But the sample +rates supported may be different. So we have map one or two PortAudio +device to each CoreAudio device depending on whether it supports +input, output or both. + +When we query devices, we first get a list of CoreAudio devices. Then +we scan the list and add a PortAudio device for each CoreAudio device +that supports input. Then we make a scan for output devices. diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c new file mode 100644 index 00000000..9a4b1488 --- /dev/null +++ b/pd/portaudio/pa_mac_core/pa_mac_core.c @@ -0,0 +1,1261 @@ +/* + * $Id: pa_mac_core.c,v 1.8 2002/04/12 18:07:20 philburk Exp $ + * 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 + 2.7.2002 - Darren and Phil - fixed isInput so GetProperty works better, + fixed device queries for numChannels and sampleRates, + one CoreAudio device now maps to separate input and output PaDevices, + audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices). + 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7 + 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file. + Return error if opened in mono mode cuz not supported. + Add support for Pa_GetCPULoad(); + Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!) + Check for invalid sample rates and return an error. + Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally. + Better error checking for invalid channel counts and invalid devices. + 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers. + 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation. + +TODO: +O- how do mono output? +O- FIFO between input and output callbacks if different devices, like in pa_mac.c +*/ + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +/************************************************* Constants ********/ + +/* 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_LATENCY_MSEC (8) +#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) /**/ + +// define value of isInput passed to CoreAudio routines +#define IS_INPUT (true) +#define IS_OUTPUT (false) + +/************************************************************** + * Structure for internal host specific stream data. + * This is allocated on a per stream basis. + */ +typedef struct PaHostSoundControl +{ + AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now. + /* Input -------------- */ + int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */ + /* Output -------------- */ + int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */ + /* Init Time -------------- */ + int pahsc_FramesPerHostBuffer; + int pahsc_UserBuffersPerHostBuffer; + /* For measuring CPU utilization. */ + struct rusage pahsc_EntryRusage; + double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ +} +PaHostSoundControl; + +/************************************************************** + * Structure for internal extended device info. + * There will be one or two PortAudio devices for each Core Audio device: + * one input and or one output. + */ +typedef struct PaHostDeviceInfo +{ + PaDeviceInfo paInfo; + AudioDeviceID audioDeviceID; +} +PaHostDeviceInfo; + +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static int sNumPaDevices = 0; /* Total number of PaDeviceInfos */ +static int sNumInputDevices = 0; /* Total number of input PaDeviceInfos */ +static int sNumOutputDevices = 0; +static PaHostDeviceInfo *sDeviceInfos = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sPaHostError = 0; + +static int sNumCoreDevices = 0; +static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs + +static const char sMapperSuffixInput[] = " - Input"; +static const char sMapperSuffixOutput[] = " - Output"; + +/* We index the input devices first, then the output devices. */ +#define LOWEST_INPUT_DEVID (0) +#define HIGHEST_INPUT_DEVID (sNumInputDevices - 1) +#define LOWEST_OUTPUT_DEVID (sNumInputDevices) +#define HIGHEST_OUTPUT_DEVID (sNumPaDevices - 1) + +/************************************************* Macros ********/ + +/************************************************* Prototypes **********/ + +static PaError Pa_QueryDevices( void ); +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); + +static int PaHost_ScanDevices( Boolean isInput ); +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); + +static PaDeviceID Pa_QueryDefaultInputDevice( void ); +static PaDeviceID Pa_QueryDefaultOutputDevice( void ); +static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ); + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */ + getrusage( RUSAGE_SELF, &pahsc->pahsc_EntryRusage ); +} + +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 rusage currentRusage; + 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( getrusage( RUSAGE_SELF, ¤tRusage ) == 0 ) + { + usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime ); + + /* Use inverse because it is faster than the divide. */ + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; + + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + } +} +/****************************************** END CPU UTILIZATION *******/ + +/************************************************************************/ +static PaDeviceID Pa_QueryDefaultInputDevice( void ) +{ + OSStatus err = noErr; + UInt32 count; + int i; + AudioDeviceID tempDeviceID = kAudioDeviceUnknown; + PaDeviceID defaultDeviceID = paNoDevice; + + // get the default output device for the HAL + // it is required to pass the size of the data to be returned + count = sizeof(tempDeviceID); + err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID); + if (err != noErr) goto Bail; + + // scan input devices to see which one matches this device + defaultDeviceID = paNoDevice; + for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ ) + { + DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); + if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) + { + defaultDeviceID = i; + break; + } + } +Bail: + return defaultDeviceID; +} + +/************************************************************************/ +static PaDeviceID Pa_QueryDefaultOutputDevice( void ) +{ + OSStatus err = noErr; + UInt32 count; + int i; + AudioDeviceID tempDeviceID = kAudioDeviceUnknown; + PaDeviceID defaultDeviceID = paNoDevice; + + // get the default output device for the HAL + // it is required to pass the size of the data to be returned + count = sizeof(tempDeviceID); + err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID); + if (err != noErr) goto Bail; + + // scan output devices to see which one matches this device + defaultDeviceID = paNoDevice; + for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ ) + { + DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); + if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) + { + defaultDeviceID = i; + break; + } + } +Bail: + return defaultDeviceID; +} + +/******************************************************************/ +static PaError Pa_QueryDevices( void ) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + int numBytes; + + // find out how many Core 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 + sNumCoreDevices = outSize / sizeof(AudioDeviceID); + + // Bail if there aren't any devices + if (sNumCoreDevices < 1) + ERR_RPT(("No Devices Available\n")); + + // make space for the devices we are about to get + sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize); + + // get an array of AudioDeviceIDs + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs); + if (err != noErr) + ERR_RPT(("Couldn't get list of audio device IDs\n")); + + // Allocate structures to hold device info pointers. + // There will be a maximum of two Pa devices per Core Audio device, input and/or output. + numBytes = sNumCoreDevices * 2 * sizeof(PaHostDeviceInfo); + sDeviceInfos = (PaHostDeviceInfo *) PaHost_AllocateFastMemory( numBytes ); + if( sDeviceInfos == NULL ) return paInsufficientMemory; + + // Scan all the Core Audio devices to see which support input and allocate a + // PaHostDeviceInfo structure for each one. + PaHost_ScanDevices( IS_INPUT ); + sNumInputDevices = sNumPaDevices; + // Now scan all the output devices. + PaHost_ScanDevices( IS_OUTPUT ); + sNumOutputDevices = sNumPaDevices - sNumInputDevices; + + // Figure out which of the devices that we scanned is the default device. + sDefaultInputDeviceID = Pa_QueryDefaultInputDevice(); + sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice(); + + return paNoError; +} + +/************************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumPaDevices <= 0 ) Pa_Initialize(); + return sNumPaDevices; +} + +/*************************************************************************/ +/* Allocate a string containing the device name. */ +static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 outSize; + Boolean outWritable; + char *deviceName = nil; + + // query size of name + err = AudioDeviceGetPropertyInfo(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, &outWritable); + if (err == noErr) + { + deviceName = (char*)malloc( outSize + 1); + if( deviceName ) + { + err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName); + if (err != noErr) + ERR_RPT(("Couldn't get audio device name.\n")); + } + } + + return deviceName; +} + +/*************************************************************************/ +// 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, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 outSize; + + outSize = sizeof(*desc); + err = AudioDeviceGetProperty(deviceID, 0, isInput, 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 = "Unknown CoreAudio Error!"; + break; + } + + return str; +} + +/************************************************************************* +** PaDeviceInfo structures have already been created +** so just return the pointer. +** +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( id < 0 || id >= sNumPaDevices ) + return NULL; + + return &sDeviceInfos[id].paInfo; +} + +/************************************************************************* +** Scan all of the Core Audio devices to see which support input or output. +** Changes sNumDevices, and fills in sDeviceInfos. +*/ +static int PaHost_ScanDevices( Boolean isInput ) +{ + int coreDeviceIndex; + int result; + PaHostDeviceInfo *hostDeviceInfo; + int numAdded = 0; + + for( coreDeviceIndex=0; coreDeviceIndex<sNumCoreDevices; coreDeviceIndex++ ) + { + // try to fill in next PaHostDeviceInfo + hostDeviceInfo = &sDeviceInfos[sNumPaDevices]; + result = PaHost_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); + DBUG(("PaHost_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, hostDeviceInfo->audioDeviceID )); + if( result > 0 ) + { + sNumPaDevices += 1; // bump global counter if we got one + numAdded += 1; + } + else if( result < 0 ) return result; + } + return numAdded; +} + +/************************************************************************* +** Try to fill in the device info for this device. +** Return 1 if a good device that PA can use. +** Return 0 if not appropriate +** or return negative error. +** +*/ +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) +{ + OSErr err; + int index; + UInt32 outSize; + AudioStreamBasicDescription formatDesc; + Boolean result; + AudioDeviceID devID; + double *sampleRates = NULL; /* non-const ptr */ + + PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0}; + int maxNumSampleRates = sizeof( possibleSampleRates ) / sizeof( double ); + + deviceInfo->structVersion = 1; + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + deviceInfo->numSampleRates = -1; + + devID = sCoreDeviceIDs[ coreDeviceIndex ]; + hostDeviceInfo->audioDeviceID = devID; + + // Figure out supported sample rates + // Make room in case device supports all rates. + sampleRates = (double*)PaHost_AllocateFastMemory( maxNumSampleRates * sizeof(double) ); + if( sampleRates == NULL ) return paInsufficientMemory; + + 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 < maxNumSampleRates; index ++) + { + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mSampleRate = possibleSampleRates[index]; + result = deviceDoesSupportFormat( devID, &formatDesc, isInput ); + + if (result == true) + { + deviceInfo->numSampleRates += 1; + *sampleRates = possibleSampleRates[index]; + sampleRates++; + } + } + // If no sample rates supported, then not a very good device. + if( deviceInfo->numSampleRates == 0 ) goto error; + + // Get data format info from the device. + outSize = sizeof(formatDesc); + err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); + + // If no channels supported, then not a very good device. + if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; + + if( isInput ) + { + deviceInfo->maxInputChannels = formatDesc.mChannelsPerFrame; + } + else + { + 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. + 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; + } + + // Get the device name + deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput ); + return 1; + +error: + if( sampleRates != NULL ) free( sampleRates ); + 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 ) +{ +#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( sNumPaDevices == 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 ) +{ + 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 *inputNativeBufferfPtr = NULL; + char *outputNativeBufferfPtr = NULL; + 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 + + Pa_StartUsageCalculation( past ); + + /* If we are using output, then we need an empty output buffer. */ + if( past->past_NumOutputChannels > 0 ) + { + outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; + } + + /* If we are using input, then we need a full input buffer. */ + if( past->past_NumInputChannels > 0 ) + { + inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; + } + + 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( outputNativeBufferfPtr ) + { + /* Clear remainder of wave buffer if we are waiting for stop. */ + AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i ); + memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer ); + } + } + else + { + /* Convert 32 bit native data to user data and call user routine. */ + result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); + if( result != 0) done = 1; + } + if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer; + if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer; + } + + 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; + +// 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 + { + /* use time stamp from CoreAudio if valid */ + if( inOutputTime->mFlags & kAudioTimeStampSampleTimeValid) + { + past->past_FrameCount = inOutputTime->mSampleTime; + } + + /* 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; +} + +#if 0 +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; +} +#endif + + +/*******************************************************************/ +/* Attempt to set device sample rate. */ +static PaError PaHost_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) +{ + AudioStreamBasicDescription formatDesc; + OSStatus err; + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mSampleRate = sampleRate; + err = AudioDeviceSetProperty( devID, 0, 0, + isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + + if (err != kAudioHardwareNoError) return paInvalidSampleRate; + else return paNoError; +} + +/*******************************************************************/ +PaError PaHost_OpenInputStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + const PaHostDeviceInfo *hostDeviceInfo; + PaError result = paNoError; + UInt32 bytesPerHostBuffer; + UInt32 dataSize; + OSStatus err = noErr; + int bytesPerInputFrame; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID)); + if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) || + (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) ) + { + return paInvalidDeviceId; + } + hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID]; + + /* Try to set sample rate. */ + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_INPUT, past->past_SampleRate ); + if( result != paNoError ) return result; + + if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels ) + { +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else +FIXME - should this be set on a stream basis? Is it possible to change? + /* Attempt to set number of channels. */ + AudioStreamBasicDescription formatDesc; + OSStatus err; + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mChannelsPerFrame = past->past_NumInputChannels; + + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, + IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if (err != kAudioHardwareNoError) + { + result = paInvalidChannelCount; + goto error; + } +#endif + } + + // calculate native buffer sizes in bytes + bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; + pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(UInt32); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); + if( err != noErr ) + { + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; + } + + // setup conversion procedure + result = PaConvert_SetupInput( past, paFloat32 ); + + return result; + +error: + return result; +} + +/*******************************************************************/ +PaError PaHost_OpenOutputStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + const PaHostDeviceInfo *hostDeviceInfo; + PaError result = paNoError; + UInt32 bytesPerHostBuffer; + UInt32 dataSize; + OSStatus err = noErr; + int bytesPerOutputFrame; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); + if( (past->past_OutputDeviceID < LOWEST_OUTPUT_DEVID) || + (past->past_OutputDeviceID > HIGHEST_OUTPUT_DEVID) ) + { + return paInvalidDeviceId; + } + hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID]; + + /* Try to set sample rate. */ + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_OUTPUT, past->past_SampleRate ); + if( result != paNoError ) return result; + + if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels ) + { +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else + /* Attempt to set number of channels. */ + AudioStreamBasicDescription formatDesc; + OSStatus err; + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mChannelsPerFrame = past->past_NumOutputChannels; + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, + IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + + if (err != kAudioHardwareNoError) + { + result = paInvalidChannelCount; + goto error; + } +#endif + } + + // calculate buffer sizes in bytes + bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; + pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(bytesPerHostBuffer); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); + if( err != noErr ) + { + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; + } + + // setup conversion procedure + result = PaConvert_SetupOutput( past, paFloat32 ); + + return result; + +error: + return result; +} + +/*******************************************************************/ +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return pahsc->pahsc_FramesPerHostBuffer; +} + + +/******************************************************************* +* Determine how many User Buffers we can put into our CoreAudio stream buffer. +* Uses: +* past->past_FramesPerUserBuffer, etc. +* Sets: +* past->past_NumUserBuffers +* pahsc->pahsc_UserBuffersPerHostBuffer +* pahsc->pahsc_FramesPerHostBuffer +*/ +static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData; + unsigned int minNumUserBuffers; + + // Determine number of user buffers based on minimum latency. + minNumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + // Compare to user requested number in user wants more than the minimum. + past->past_NumUserBuffers = ( minNumUserBuffers > past->past_NumUserBuffers ) ? + minNumUserBuffers : past->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + + // For CoreAudio, we only have one Host buffer, so... + pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers; + // Calculate size of CoreAudio buffer. + pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + + DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); +} + +/*******************************************************************/ +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; + + // If we are using both input and out, then they must be on the same CoreAudio device, + // until we implement a FIFO between two devices. + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_InputDeviceID != paNoDevice) ) + { + AudioDeviceID inputID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + AudioDeviceID outputID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + if( inputID != outputID ) + { + ERR_RPT(("PortAudio: input and output must use same CoreAudio device!\n")); + return paInvalidDeviceId; + } + } + + PaHost_CalcHostBufferSize( past ); + + { + int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate); + PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency )); + } + + /* Setup constants for CPU load measurement. */ + pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); + + /* ------------------ OUTPUT */ + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) ) + { + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + result = PaHost_OpenOutputStream( past ); + if( result < 0 ) goto error; + } + + /* ------------------ INPUT */ + if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) ) + { + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + result = PaHost_OpenInputStream( past ); + if( result < 0 ) goto error; + } + + return result; + +error: + PaHost_CloseStream( past ); + return result; +} + +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + return 0; +} + +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + return 0; +} + +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + OSStatus err = noErr; + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + past->past_StopSoon = 0; + past->past_StopNow = 0; + past->past_IsActive = 1; + + // Associate an IO proc with the device and pass a pointer to the audio data context + err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past); + if (err != noErr) goto error; + + // start playing sound through the device + err = AudioDeviceStart(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto error; + return result; + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result ); +#endif + +error: + return paHostError; // FIXME - save host error +} + +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + OSStatus err = noErr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + + /* 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; + past->past_IsActive = 0; + +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StopOutput: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); +#endif + + // FIXME - we should ask proc to stop instead of stopping abruptly + err = AudioDeviceStop(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; + + err = AudioDeviceRemoveIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; + + return paNoError; + +Bail: + return paHostError; // FIXME - save err +} + +/*************************************************************************/ +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 PA_TRACE_START_STOP + AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); +#endif + + 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 cshrc file. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +#if 1 +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) +{ + int minBuffers; + int denominator; + int minLatencyMsec = PA_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; + } + + denominator = 1000.0 * framesPerBuffer; + minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); + if( minBuffers < 1 ) minBuffers = 1; + return minBuffers; +} +#else +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. +*/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ + int minUserBuffers; + int minFramesPerHostBuffer; + + // Calculate minimum and maximum sizes based on timing and sample rate. + minFramesPerHostBuffer = (int) (PA_MIN_LATENCY_MSEC * sampleRate / 1000.0); + // round up to nearest multiple of 8 + minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; + DBUG(("Pa_GetMinNumBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); + + minUserBuffers = (minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; + if( minUserBuffers < 1 ) minUserBuffers = 1; + + return minUserBuffers; +} +#endif + +/************************************************************************* +** Cleanup device info. +*/ +PaError PaHost_Term( void ) +{ + int i; + + if( sDeviceInfos != NULL ) + { + for( i=0; i<sNumPaDevices; i++ ) + { + if( sDeviceInfos[i].paInfo.name != NULL ) free( (char*)sDeviceInfos[i].paInfo.name ); + if( sDeviceInfos[i].paInfo.sampleRates != NULL ) free( (void*)sDeviceInfos[i].paInfo.sampleRates ); + } + free( sDeviceInfos ); + sDeviceInfos = NULL; + } + + sNumPaDevices = 0; + return paNoError; +} + +/*************************************************************************/ +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 + usleep( msec * 1000 ); +#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; +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + AudioTimeStamp timeStamp; + PaTimestamp streamTime; + PaHostSoundControl *pahsc; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp); + + streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ? + timeStamp.mSampleTime : past->past_FrameCount; + + return streamTime; +} diff --git a/pd/portaudio/pablio/README.txt b/pd/portaudio/pablio/README.txt new file mode 100644 index 00000000..99c7d146 --- /dev/null +++ b/pd/portaudio/pablio/README.txt @@ -0,0 +1,39 @@ +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. + * + */ + + diff --git a/pd/portaudio/pablio/pablio.c b/pd/portaudio/pablio/pablio.c new file mode 100644 index 00000000..53dec058 --- /dev/null +++ b/pd/portaudio/pablio/pablio.c @@ -0,0 +1,307 @@ +/* + * $Id: pablio.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $ + * 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; +} diff --git a/pd/portaudio/pablio/pablio.def b/pd/portaudio/pablio/pablio.def new file mode 100644 index 00000000..a10f9529 --- /dev/null +++ b/pd/portaudio/pablio/pablio.def @@ -0,0 +1,35 @@ +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 + + diff --git a/pd/portaudio/pablio/pablio.h b/pd/portaudio/pablio/pablio.h new file mode 100644 index 00000000..9060c560 --- /dev/null +++ b/pd/portaudio/pablio/pablio.h @@ -0,0 +1,108 @@ +#ifndef _PABLIO_H +#define _PABLIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: pablio.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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, + */ +PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, + PaSampleFormat format, long flags ); + +PaError CloseAudioStream( PABLIO_Stream *aStream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PABLIO_H */ diff --git a/pd/portaudio/pablio/pablio_pd.c b/pd/portaudio/pablio/pablio_pd.c new file mode 100644 index 00000000..e7105e9b --- /dev/null +++ b/pd/portaudio/pablio/pablio_pd.c @@ -0,0 +1,335 @@ +/* + * $Id: pablio_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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. + * + */ + + /* changes by Miller Puckette to support Pd: device selection, + settable audio buffer size, and settable number of channels. + LATER also fix it to poll for input and output fifo fill points. */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "portaudio.h" +#include "ringbuffer.h" +#include "pablio_pd.h" /* MSP */ +#include <string.h> + + /* MSP -- FRAMES_PER_BUFFER constant removed */ + +/************************************************************************/ +/******** 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 + */ +PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, + PaSampleFormat format, long flags, int nchannels, + int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno) /* MSP */ +{ + long bytesPerSample; + long doRead = 0; + long doWrite = 0; + PaError err; + PABLIO_Stream *aStream; + long minNumBuffers; + long numFrames; + + /* fprintf(stderr, + "open %lf fmt %d flags %d ch: %d fperbuf: %d nbuf: %d devs: %d %d\n", + sampleRate, format, flags, nchannels, + framesperbuf, nbuffers, indeviceno, outdeviceno); */ + + if (indeviceno < 0) /* MSP... */ + { + indeviceno = Pa_GetDefaultInputDeviceID(); + fprintf(stderr, "using default input device number: %d\n", indeviceno); + } + if (outdeviceno < 0) + { + outdeviceno = Pa_GetDefaultOutputDeviceID(); + fprintf(stderr, "using default output device number: %d\n", outdeviceno); + } + nbuffers = RoundUpToNextPowerOf2(nbuffers); + fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", + nchannels, flags, nbuffers, framesperbuf); + /* ...MSP */ + + /* 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 = nchannels; /* MSP */ + 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. */ /* MSP */ + minNumBuffers = Pa_GetMinNumBuffers(framesperbuf, sampleRate); + if (minNumBuffers > nbuffers) + fprintf(stderr, + "warning: number of buffers %d less than recommended minimum %d\n", + (int)nbuffers, (int)minNumBuffers); + numFrames = nbuffers * framesperbuf; + + /* 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 ? indeviceno : paNoDevice), /* MSP */ + (doRead ? aStream->samplesPerFrame : 0 ), + format, + NULL, + (doWrite ? outdeviceno : paNoDevice), /* MSP */ + (doWrite ? aStream->samplesPerFrame : 0 ), + format, + NULL, + sampleRate, + framesperbuf, /* MSP */ + nbuffers, /* MSP */ + paNoFlag, /* MSP -- portaudio will clip for us */ + blockingIOCallback, + aStream ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( aStream->stream ); + if( err != paNoError ) /* MSP */ + { + fprintf(stderr, "Pa_StartStream failed; closing audio stream...\n"); + CloseAudioStream( aStream ); + goto error; + } + + *rwblPtr = aStream; + return paNoError; + +error: + *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; +} diff --git a/pd/portaudio/pablio/pablio_pd.h b/pd/portaudio/pablio/pablio_pd.h new file mode 100644 index 00000000..b87b9f5a --- /dev/null +++ b/pd/portaudio/pablio/pablio_pd.h @@ -0,0 +1,110 @@ +#ifndef _PABLIO_H +#define _PABLIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: pablio_pd.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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, + */ +PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, + PaSampleFormat format, long flags, int nchannels, + int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno); /* MSP */ + +PaError CloseAudioStream( PABLIO_Stream *aStream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PABLIO_H */ diff --git a/pd/portaudio/pablio/ringbuffer.c b/pd/portaudio/pablio/ringbuffer.c new file mode 100644 index 00000000..b8cc691f --- /dev/null +++ b/pd/portaudio/pablio/ringbuffer.c @@ -0,0 +1,199 @@ +/* + * $Id: ringbuffer.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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; +} diff --git a/pd/portaudio/pablio/ringbuffer.h b/pd/portaudio/pablio/ringbuffer.h new file mode 100644 index 00000000..1bf78e3a --- /dev/null +++ b/pd/portaudio/pablio/ringbuffer.h @@ -0,0 +1,101 @@ +#ifndef _RINGBUFFER_H +#define _RINGBUFFER_H +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: ringbuffer.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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 */ diff --git a/pd/portaudio/pablio/ringbuffer_pd.c b/pd/portaudio/pablio/ringbuffer_pd.c new file mode 100644 index 00000000..16890d65 --- /dev/null +++ b/pd/portaudio/pablio/ringbuffer_pd.c @@ -0,0 +1,214 @@ +/* + * $Id: ringbuffer_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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. + * + */ +/* + * modified 2002/07/13 by olaf.matthes@gmx.de to allow any number if channels + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "ringbuffer.h" +#include <string.h> + +/*************************************************************************** + * Initialize FIFO. + */ +long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ) +{ + rbuf->bufferSize = numBytes; + rbuf->buffer = (char *)dataPtr; + RingBuffer_Flush( rbuf ); + return 0; +} +/*************************************************************************** +** Return number of bytes available for reading. */ +long RingBuffer_GetReadAvailable( RingBuffer *rbuf ) +{ + long ret = (rbuf->writeIndex - rbuf->readIndex) + rbuf->bufferSize; + if (ret >= 2 * rbuf->bufferSize) + ret -= 2 * rbuf->bufferSize; + return ( ret ); +} +/*************************************************************************** +** 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; + while (index >= rbuf->bufferSize) + index -= rbuf->bufferSize; + 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 ) +{ + long ret = (rbuf->writeIndex + numBytes); + if ( ret > 2 * rbuf->bufferSize) + ret -= 2 * rbuf->bufferSize; /* check for end of buffer */ + return rbuf->writeIndex = ret; +} + +/*************************************************************************** +** 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; + while (index > rbuf->bufferSize) + index -= rbuf->bufferSize; + + 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 ) +{ + long ret = (rbuf->readIndex + numBytes); + if( ret > 2 * rbuf->bufferSize) + ret -= 2 * rbuf->bufferSize; + return rbuf->readIndex = ret; +} + +/*************************************************************************** +** 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; +} diff --git a/pd/portaudio/pablio/test_rw.c b/pd/portaudio/pablio/test_rw.c new file mode 100644 index 00000000..cf54427d --- /dev/null +++ b/pd/portaudio/pablio/test_rw.c @@ -0,0 +1,99 @@ +/* + * $Id: test_rw.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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 (44100) +#define NUM_SECONDS (5) +#define SAMPLES_PER_FRAME (2) +#define FRAMES_PER_BLOCK (64) + +/* 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 * FRAMES_PER_BLOCK]; + 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 += FRAMES_PER_BLOCK ) + { + /* Read one block of data into sample array from audio input. */ + ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK ); + /* Write that same block of data to output. */ + WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK ); + } + + 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; +} diff --git a/pd/portaudio/pablio/test_rw_echo.c b/pd/portaudio/pablio/test_rw_echo.c new file mode 100644 index 00000000..9b27e3c2 --- /dev/null +++ b/pd/portaudio/pablio/test_rw_echo.c @@ -0,0 +1,123 @@ +/* + * $Id: test_rw_echo.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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; +} diff --git a/pd/portaudio/pablio/test_w_saw.c b/pd/portaudio/pablio/test_w_saw.c new file mode 100644 index 00000000..b8e3e71a --- /dev/null +++ b/pd/portaudio/pablio/test_w_saw.c @@ -0,0 +1,108 @@ +/* + * $Id: test_w_saw.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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; +} diff --git a/pd/portaudio/pablio/test_w_saw8.c b/pd/portaudio/pablio/test_w_saw8.c new file mode 100644 index 00000000..b876bd9f --- /dev/null +++ b/pd/portaudio/pablio/test_w_saw8.c @@ -0,0 +1,106 @@ +/* + * $Id: test_w_saw8.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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; +} diff --git a/pd/portaudio/pablio/test_w_saw_pd.c b/pd/portaudio/pablio/test_w_saw_pd.c new file mode 100644 index 00000000..be95d245 --- /dev/null +++ b/pd/portaudio/pablio/test_w_saw_pd.c @@ -0,0 +1,108 @@ +/* + * $Id: test_w_saw_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * 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_pd.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, 2, 512, 8, -1, -1 ); + 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; +} diff --git a/pd/portaudio/portmidi-macosx/Makefile b/pd/portaudio/portmidi-macosx/Makefile new file mode 100644 index 00000000..d8667355 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/Makefile @@ -0,0 +1,24 @@ +CC = cc +CFLAGS = -Wmost +LDFLAGS = -framework Carbon -framework CoreMIDI +OBJS = ptdarwin.o pmutil.o pmmacosx.o pmdarwin.o portmidi.o +LIBS = + +all: libportmidi.a pmtest + +libportmidi.a: portmidi.h porttime.h pminternal.h $(OBJS) + rm -f libportmidi.a + ar rv libportmidi.a $(OBJS) + ranlib libportmidi.a + +pmtest: pmtest.c libportmidi.a + $(CC) $(CFLAGS) pmtest.c $(OBJS) -o pmtest $(LDFLAGS) $(LIBS) + +pmmacosx.o: pmmacosx.c portmidi.h pminternal.h pmmacosx.h porttime.h +pmdarwin.o: pmdarwin.c portmidi.h pmmacosx.h +pmutil.o: pmutil.c portmidi.h pmutil.h pminternal.h +portmidi.o: portmidi.c portmidi.h pminternal.h +ptdarwin.o: ptdarwin.c porttime.h portmidi.h + +clean: + rm -f pmtest *.o diff --git a/pd/portaudio/portmidi-macosx/README b/pd/portaudio/portmidi-macosx/README new file mode 100644 index 00000000..89c0e6fa --- /dev/null +++ b/pd/portaudio/portmidi-macosx/README @@ -0,0 +1,12 @@ +PortMidi for MacOS X / Darwin +Jon Parise <jparise@cmu.edu> +$Date: 2002-07-29 17:06:16 $ + +This is the MacOS X / Darwin port of the PortMidi library from the Carnegie +Mellon Computer Music Group. It is based on the Apple CoreAudio MIDI +interface. + +This port was finished in early 2002. At this point, I consider the code +base complete. + +- Jon diff --git a/pd/portaudio/portmidi-macosx/pmdarwin.c b/pd/portaudio/portmidi-macosx/pmdarwin.c new file mode 100644 index 00000000..510339c3 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmdarwin.c @@ -0,0 +1,36 @@ +/* + * PortMidi OS-dependent interface for Darwin (MacOS X) + * Jon Parise <jparise@cmu.edu> + * + * $Id: pmdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $ + */ + +/* + * This file only needs to implement pm_init(), which calls various + * routines to register the available midi devices. This file must + * be separate from the main portmidi.c file because it is system + * dependent, and it is separate from, say, pmwinmm.c, because it + * might need to register devices for winmm, directx, and others. + */ + +#include <stdlib.h> +#include "portmidi.h" +#include "pmmacosx.h" + +PmError pm_init() +{ + return pm_macosx_init(); +} + +PmError pm_term() +{ + return pm_macosx_term(); +} + +PmDeviceID Pm_GetDefaultInputDeviceID() { return 0; }; +PmDeviceID Pm_GetDefaultOutputDeviceID() { return 0; }; + +void *pm_alloc(size_t s) { return malloc(s); } + +void pm_free(void *ptr) { free(ptr); } + diff --git a/pd/portaudio/portmidi-macosx/pminternal.h b/pd/portaudio/portmidi-macosx/pminternal.h new file mode 100644 index 00000000..2a92e16d --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pminternal.h @@ -0,0 +1,100 @@ +/* pminternal.h -- header for interface implementations */ + +/* this file is included by files that implement library internals */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +/* these are defined in system-specific file */ +void *pm_alloc(size_t s); +void pm_free(void *ptr); + +struct pm_internal_struct; + +/* these do not use PmInternal because it is not defined yet... */ +typedef PmError (*pm_write_fn)(struct pm_internal_struct *midi, + PmEvent *buffer, long length); +typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi, + void *driverInfo); +typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi); +typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi); + +typedef struct { + pm_write_fn write; + pm_open_fn open; + pm_abort_fn abort; + pm_close_fn close; +} pm_fns_node, *pm_fns_type; + +/* when open fails, the dictionary gets this set of functions: */ +extern pm_fns_node pm_none_dictionary; + +typedef struct { + PmDeviceInfo pub; + void *descriptor; /* system-specific data to open device */ + pm_fns_type dictionary; +} descriptor_node, *descriptor_type; + + +#define pm_descriptor_max 32 +extern descriptor_node descriptors[pm_descriptor_max]; +extern int descriptor_index; + + +typedef unsigned long (*time_get_proc_type)(void *time_info); + +typedef struct pm_internal_struct { + short write_flag; /* MIDI_IN, or MIDI_OUT */ + int device_id; /* which device is open (index to descriptors) */ + PmTimeProcPtr time_proc; /* where to get the time */ + void *time_info; /* pass this to get_time() */ + PmEvent *buffer; /* input or output buffer */ + long buffer_len; /* how big is the buffer */ + long latency; /* time delay in ms between timestamps and actual output */ + /* set to zero to get immediate, simple blocking output */ + /* if latency is zero, timestamps will be ignored */ + int overflow; /* set to non-zero if input is dropped */ + int flush; /* flag to drop incoming sysex data because of overflow */ + int sysex_in_progress; /* use for overflow management */ + struct pm_internal_struct *thru; + PmTimestamp last_msg_time; /* timestamp of last message */ + long head; + long tail; + pm_fns_type dictionary; /* implementation functions */ + void *descriptor; /* system-dependent state */ +} PmInternal; + + +typedef struct { + long head; + long tail; + long len; + long msg_size; + long overflow; + char *buffer; +} PmQueueRep; + + +PmError pm_init(void); /* defined in a system-specific file */ +PmError pm_term(void); /* defined in a system-specific file */ +int pm_in_device(int n, char *interf, char *device); +int pm_out_device(int n, char *interf, char *device); +PmError none_write(PmInternal *midi, PmEvent *buffer, long length); +PmError pm_success_fn(PmInternal *midi); +PmError pm_fail_fn(PmInternal *midi); +long pm_in_poll(PmInternal *midi); +long pm_out_poll(PmInternal *midi); + +PmError pm_add_device(char *interf, char *name, int input, void *descriptor, + pm_fns_type dictionary); + +void pm_enqueue(PmInternal *midi, PmEvent *event); + + +#ifdef __cplusplus +} +#endif + diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.c b/pd/portaudio/portmidi-macosx/pmmacosx.c new file mode 100644 index 00000000..0aafcf7f --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmmacosx.c @@ -0,0 +1,336 @@ +/* + * Platform interface to the MacOS X CoreMIDI framework + * + * Jon Parise <jparise@cmu.edu> + * + * $Id: pmmacosx.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $ + */ + +#include "portmidi.h" +#include "pminternal.h" +#include "porttime.h" +#include "pmmacosx.h" + +#include <stdio.h> +#include <string.h> + +#include <CoreServices/CoreServices.h> +#include <CoreMIDI/MIDIServices.h> + +#define PACKET_BUFFER_SIZE 1024 + +static MIDIClientRef client = NULL; /* Client handle to the MIDI server */ +static MIDIPortRef portIn = NULL; /* Input port handle */ +static MIDIPortRef portOut = NULL; /* Output port handle */ + +extern pm_fns_node pm_macosx_in_dictionary; +extern pm_fns_node pm_macosx_out_dictionary; + +static int +midi_length(long msg) +{ + int status, high, low; + static int high_lengths[] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ + 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ + }; + static int low_lengths[] = { + 1, 1, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ + 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ + }; + + status = msg & 0xFF; + high = status >> 4; + low = status & 15; + + return (high != 0xF0) ? high_lengths[high] : low_lengths[low]; +} + +static PmTimestamp +get_timestamp(PmInternal *midi) +{ + PmTimeProcPtr time_proc; + + /* Set the time procedure accordingly */ + time_proc = midi->time_proc; + if (time_proc == NULL) { + time_proc = Pt_Time; + } + + return (*time_proc)(midi->time_info); +} + +/* called when MIDI packets are received */ +static void +readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon) +{ + PmInternal *midi; + PmEvent event; + MIDIPacket *packet; + unsigned int packetIndex; + + /* Retrieve the context for this connection */ + midi = (PmInternal *) connRefCon; + + packet = (MIDIPacket *) &newPackets->packet[0]; + for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { + + /* Build the PmMessage for the PmEvent structure */ + switch (packet->length) { + case 1: + event.message = Pm_Message(packet->data[0], 0, 0); + break; + case 2: + event.message = Pm_Message(packet->data[0], packet->data[1], 0); + break; + case 3: + event.message = Pm_Message(packet->data[0], packet->data[1], + packet->data[2]); + break; + default: + /* Skip packets that are too large to fit in a PmMessage */ + continue; + } + + /* Set the timestamp and dispatch this message */ + event.timestamp = get_timestamp(midi); + pm_enqueue(midi, &event); + + /* Advance to the next packet in the packet list */ + packet = MIDIPacketNext(packet); + } +} + +static PmError +midi_in_open(PmInternal *midi, void *driverInfo) +{ + MIDIEndpointRef endpoint; + + endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; + if (endpoint == NULL) { + return pmInvalidDeviceId; + } + + if (MIDIPortConnectSource(portIn, endpoint, midi) != noErr) { + return pmHostError; + } + + return pmNoError; +} + +static PmError +midi_in_close(PmInternal *midi) +{ + MIDIEndpointRef endpoint; + + endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; + if (endpoint == NULL) { + return pmInvalidDeviceId; + } + + if (MIDIPortDisconnectSource(portIn, endpoint) != noErr) { + return pmHostError; + } + + return pmNoError; +} + +static PmError +midi_out_open(PmInternal *midi, void *driverInfo) +{ + /* + * MIDISent() only requires an output port (portOut) and a valid MIDI + * endpoint (which we've already created and stored in the PmInternal + * structure). Therefore, no additional work needs to be done here to + * open the device for output. + */ + + return pmNoError; +} + +static PmError +midi_out_close(PmInternal *midi) +{ + return pmNoError; +} + +static PmError +midi_abort(PmInternal *midi) +{ + return pmNoError; +} + +static PmError +midi_write(PmInternal *midi, PmEvent *events, long length) +{ + Byte packetBuffer[PACKET_BUFFER_SIZE]; + MIDIEndpointRef endpoint; + MIDIPacketList *packetList; + MIDIPacket *packet; + MIDITimeStamp timestamp; + PmTimeProcPtr time_proc; + PmEvent event; + unsigned int pm_time; + unsigned int eventIndex; + unsigned int messageLength; + Byte message[3]; + + endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; + if (endpoint == NULL) { + return pmInvalidDeviceId; + } + + /* Make sure the packetBuffer is large enough */ + if (length > PACKET_BUFFER_SIZE) { + return pmHostError; + } + + /* + * Initialize the packet list. Each packet contains bytes that are to + * be played at the same time. + */ + packetList = (MIDIPacketList *) packetBuffer; + if ((packet = MIDIPacketListInit(packetList)) == NULL) { + return pmHostError; + } + + /* Set the time procedure accordingly */ + time_proc = midi->time_proc; + if (time_proc == NULL) { + time_proc = Pt_Time; + } + + /* Extract the event data and pack it into the message buffer */ + for (eventIndex = 0; eventIndex < length; eventIndex++) { + event = events[eventIndex]; + + /* Compute the timestamp */ + pm_time = (*time_proc)(midi->time_info); + timestamp = pm_time + midi->latency; + + messageLength = midi_length(event.message); + message[0] = Pm_MessageStatus(event.message); + message[1] = Pm_MessageData1(event.message); + message[2] = Pm_MessageData2(event.message); + + /* Add this message to the packet list */ + packet = MIDIPacketListAdd(packetList, sizeof(packetBuffer), packet, + timestamp, messageLength, message); + if (packet == NULL) { + return pmHostError; + } + } + + if (MIDISend(portOut, endpoint, packetList) != noErr) { + return pmHostError; + } + + return pmNoError; +} + +pm_fns_node pm_macosx_in_dictionary = { + none_write, + midi_in_open, + midi_abort, + midi_in_close +}; + +pm_fns_node pm_macosx_out_dictionary = { + midi_write, + midi_out_open, + midi_abort, + midi_out_close +}; + +PmError +pm_macosx_init(void) +{ + OSStatus status; + ItemCount numDevices, numInputs, numOutputs; + MIDIEndpointRef endpoint; + CFStringEncoding defaultEncoding; + CFStringRef deviceName; + char nameBuf[256]; + int i; + + /* Determine the number of MIDI devices on the system */ + numDevices = MIDIGetNumberOfDevices(); + numInputs = MIDIGetNumberOfSources(); + numOutputs = MIDIGetNumberOfDestinations(); + + /* Return prematurely if no devices exist on the system */ + if (numDevices <= 0) { + return pmHostError; + } + + /* Determine the default system character encording */ + defaultEncoding = CFStringGetSystemEncoding(); + + /* Iterate over the MIDI input devices */ + for (i = 0; i < numInputs; i++) { + endpoint = MIDIGetSource(i); + if (endpoint == NULL) { + continue; + } + + /* Get the name of this device */ + MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName); + CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding); + CFRelease(deviceName); + + /* Register this device with PortMidi */ + pm_add_device("CoreMIDI", nameBuf, TRUE, (void *)endpoint, + &pm_macosx_in_dictionary); + } + + /* Iterate over the MIDI output devices */ + for (i = 0; i < numOutputs; i++) { + endpoint = MIDIGetDestination(i); + if (endpoint == NULL) { + continue; + } + + /* Get the name of this device */ + MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &deviceName); + CFStringGetCString(deviceName, nameBuf, 256, defaultEncoding); + CFRelease(deviceName); + + /* Register this device with PortMidi */ + pm_add_device("CoreMIDI", nameBuf, FALSE, (void *)endpoint, + &pm_macosx_out_dictionary); + } + + /* Initialize the client handle */ + status = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client); + if (status != noErr) { + fprintf(stderr, "Could not initialize client: %d\n", (int)status); + return pmHostError; + } + + /* Create the input port */ + status = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, NULL, + &portIn); + if (status != noErr) { + fprintf(stderr, "Could not create input port: %d\n", (int)status); + return pmHostError; + } + + /* Create the output port */ + status = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); + if (status != noErr) { + fprintf(stderr, "Could not create output port: %d\n", (int)status); + return pmHostError; + } + + return pmNoError; +} + +PmError +pm_macosx_term(void) +{ + if (client != NULL) MIDIClientDispose(client); + if (portIn != NULL) MIDIPortDispose(portIn); + if (portOut != NULL) MIDIPortDispose(portOut); + + return pmNoError; +} diff --git a/pd/portaudio/portmidi-macosx/pmmacosx.h b/pd/portaudio/portmidi-macosx/pmmacosx.h new file mode 100644 index 00000000..15e9551d --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmmacosx.h @@ -0,0 +1,4 @@ +/* system-specific definitions */ + +PmError pm_macosx_init(void); +PmError pm_macosx_term(void); diff --git a/pd/portaudio/portmidi-macosx/pmtest b/pd/portaudio/portmidi-macosx/pmtest Binary files differnew file mode 100644 index 00000000..8adc5334 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmtest diff --git a/pd/portaudio/portmidi-macosx/pmtest.c b/pd/portaudio/portmidi-macosx/pmtest.c new file mode 100644 index 00000000..5628d25e --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmtest.c @@ -0,0 +1,136 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "portmidi.h" +#include "porttime.h" +#include "pminternal.h" + +#define LATENCY 0 +#define NUM_ECHOES 10 + +int +main() +{ + int i = 0; + int n = 0; + PmStream *midi_in; + PmStream *midi_out; + PmError err; + char line[80]; + PmEvent buffer[NUM_ECHOES]; + int transpose; + int delay; + int status, data1, data2; + int statusprefix; + + + + /* always start the timer before you start midi */ + Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */ + + + for (i = 0; i < Pm_CountDevices(); i++) { + const PmDeviceInfo *info = Pm_GetDeviceInfo(i); + printf("%d: %s, %s", i, info->interf, info->name); + if (info->input) printf(" (input)"); + if (info->output) printf(" (output)"); + printf("\n"); + } + + /* OPEN INPUT DEVICE */ + + printf("Type input number: "); + while (n != 1) { + n = scanf("%d", &i); + gets(line); + } + + err = Pm_OpenInput(&midi_in, i, NULL, 100, NULL, NULL, NULL); + if (err) { + printf("could not open midi device: %s\n", Pm_GetErrorText(err)); + exit(1); + } + printf("Midi Input opened.\n"); + + /* OPEN OUTPUT DEVICE */ + + printf("Type output number: "); + n = 0; + while (n != 1) { + n = scanf("%d", &i); + gets(line); + } + + err = Pm_OpenOutput(&midi_out, i, NULL, 0, NULL, NULL, LATENCY); + if (err) { + printf("could not open midi device: %s\n", Pm_GetErrorText(err)); + exit(1); + } + printf("Midi Output opened with %d ms latency.\n", LATENCY); + + + + /* Get input from user for parameters */ + printf("Type number of milliseconds for echoes: "); + n = 0; + while (n != 1) { + n = scanf("%d", &delay); + gets(line); + } + + printf("Type number of semitones to transpose up: "); + n = 0; + while (n != 1) { + n = scanf("%d", &transpose); + gets(line); + } + + + + /* loop, echoing input back transposed with multiple taps */ + + printf("Press C2 on the keyboard (2 octaves below middle C) to quit.\nWaiting for MIDI input...\n"); + + do { + err = Pm_Read(midi_in, buffer, 1); + if (err == 0) continue; /* no bytes read. */ + + /* print a hash mark for each event read. */ + printf("#"); + fflush(stdout); + + status = Pm_MessageStatus(buffer[0].message); + data1 = Pm_MessageData1(buffer[0].message); + data2 = Pm_MessageData2(buffer[0].message); + statusprefix = status >> 4; + + /* ignore messages other than key-down and key-up */ + if ((statusprefix != 0x9) && (statusprefix != 0x8)) continue; + + printf("\nReceived key message = %X %X %X, at time %ld\n", status, data1, data2, buffer[0].timestamp); + fflush(stdout); + + /* immediately send the echoes to PortMIDI */ + for (i = 1; i < NUM_ECHOES; i++) { + buffer[i].message = Pm_Message(status, data1 + transpose, data2 >> i); + buffer[i].timestamp = buffer[0].timestamp + (i * delay); + } + Pm_Write(midi_out, buffer, NUM_ECHOES); + } while (data1 != 36); /* quit when C2 is pressed */ + + printf("Key C2 pressed. Exiting...\n"); + fflush(stdout); + + /* Give the echoes time to finish before quitting. */ + sleep(((NUM_ECHOES * delay) / 1000) + 1); + + Pm_Close(midi_in); + Pm_Close(midi_out); + + printf("Done.\n"); + return 0; +} + + + diff --git a/pd/portaudio/portmidi-macosx/pmutil.c b/pd/portaudio/portmidi-macosx/pmutil.c new file mode 100644 index 00000000..f3582a42 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmutil.c @@ -0,0 +1,86 @@ +/* pmutil.c -- some helpful utilities for building midi + applications that use PortMidi + */ +#include "stdlib.h" +#include "memory.h" +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" + + +PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg) +{ + PmQueueRep *queue = (PmQueueRep *) malloc(sizeof(PmQueueRep)); + if (!queue) return NULL; + queue->len = num_msgs * bytes_per_msg; + queue->buffer = malloc(queue->len); + if (!queue->buffer) { + free(queue); + return NULL; + } + queue->head = 0; + queue->tail = 0; + queue->msg_size = bytes_per_msg; + queue->overflow = FALSE; + return queue; +} + + +PmError Pm_QueueDestroy(PmQueue *q) +{ + PmQueueRep *queue = (PmQueueRep *) q; + if (!queue || !queue->buffer) return pmBadPtr; + free(queue->buffer); + free(queue); + return pmNoError; +} + + +PmError Pm_Dequeue(PmQueue *q, void *msg) +{ + long head; + PmQueueRep *queue = (PmQueueRep *) q; + if (queue->overflow) { + queue->overflow = FALSE; + return pmBufferOverflow; + } + head = queue->head; /* make sure this is written after access */ + if (head == queue->tail) return 0; + memcpy(msg, queue->buffer + head, queue->msg_size); + head += queue->msg_size; + if (head == queue->len) head = 0; + queue->head = head; + return 1; /* success */ +} + + +/* source should not enqueue data if overflow is set */ +/**/ +PmError Pm_Enqueue(PmQueue *q, void *msg) +{ + PmQueueRep *queue = (PmQueueRep *) q; + long tail = queue->tail; + memcpy(queue->buffer + tail, msg, queue->msg_size); + tail += queue->msg_size; + if (tail == queue->len) tail = 0; + if (tail == queue->head) { + queue->overflow = TRUE; + /* do not update tail, so message is lost */ + return pmBufferOverflow; + } + queue->tail = tail; + return pmNoError; +} + + +int Pm_QueueFull(PmQueue *q) +{ + PmQueueRep *queue = (PmQueueRep *) q; + long tail = queue->tail; + tail += queue->msg_size; + if (tail == queue->len) { + tail = 0; + } + return (tail == queue->head); +} + diff --git a/pd/portaudio/portmidi-macosx/pmutil.h b/pd/portaudio/portmidi-macosx/pmutil.h new file mode 100644 index 00000000..b6268ed3 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/pmutil.h @@ -0,0 +1,44 @@ +/* pmutil.h -- some helpful utilities for building midi + applications that use PortMidi + */ + +typedef void PmQueue; + +/* + A single-reader, single-writer queue is created by + Pm_QueueCreate(), which takes the number of messages and + the message size as parameters. The queue only accepts + fixed sized messages. Returns NULL if memory cannot be allocated. + + Pm_QueueDestroy() destroys the queue and frees its storage. + */ + +PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg); +PmError Pm_QueueDestroy(PmQueue *queue); + +/* + Pm_Dequeue() removes one item from the queue, copying it into msg. + Returns 1 if successful, and 0 if the queue is empty. + Returns pmBufferOverflow and clears the overflow flag if + the flag is set. + */ +PmError Pm_Dequeue(PmQueue *queue, void *msg); + + +/* + Pm_Enqueue() inserts one item into the queue, copying it from msg. + Returns pmNoError if successful and pmBufferOverflow if the queue was + already full. If pmBufferOverflow is returned, the overflow flag is set. + */ +PmError Pm_Enqueue(PmQueue *queue, void *msg); + + +/* + Pm_QueueFull() returns non-zero if the queue is full + Pm_QueueEmpty() returns non-zero if the queue is empty + + Either condition may change immediately because a parallel + enqueue or dequeue operation could be in progress. + */ +int Pm_QueueFull(PmQueue *queue); +#define Pm_QueueEmpty(m) (m->head == m->tail) diff --git a/pd/portaudio/portmidi-macosx/portmidi.c b/pd/portaudio/portmidi-macosx/portmidi.c new file mode 100644 index 00000000..c2a32ae7 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/portmidi.c @@ -0,0 +1,358 @@ +#include "stdlib.h" +#include "portmidi.h" +#include "pminternal.h" + +#define is_empty(midi) ((midi)->tail == (midi)->head) + +static int pm_initialized = FALSE; + +int descriptor_index = 0; +descriptor_node descriptors[pm_descriptor_max]; + + +/* pm_add_device -- describe interface/device pair to library + * + * This is called at intialization time, once for each + * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) + * The strings are retained but NOT COPIED, so do not destroy them! + * + * returns pmInvalidDeviceId if device memory is exceeded + * otherwise returns pmNoError + */ +PmError pm_add_device(char *interf, char *name, int input, + void *descriptor, pm_fns_type dictionary) +{ + if (descriptor_index >= pm_descriptor_max) { + return pmInvalidDeviceId; + } + descriptors[descriptor_index].pub.interf = interf; + descriptors[descriptor_index].pub.name = name; + descriptors[descriptor_index].pub.input = input; + descriptors[descriptor_index].pub.output = !input; + descriptors[descriptor_index].descriptor = descriptor; + descriptors[descriptor_index].dictionary = dictionary; + descriptor_index++; + return pmNoError; +} + + +PmError Pm_Initialize( void ) +{ + if (!pm_initialized) { + PmError err = pm_init(); /* defined by implementation specific file */ + if (err) return err; + pm_initialized = TRUE; + } + return pmNoError; +} + + +PmError Pm_Terminate( void ) +{ + PmError err = pmNoError; + if (pm_initialized) { + err = pm_term(); /* defined by implementation specific file */ + /* note that even when pm_term() fails, we mark portmidi as + not initialized */ + pm_initialized = FALSE; + } + return err; +} + + +int Pm_CountDevices( void ) +{ + PmError err = Pm_Initialize(); + if (err) return err; + + return descriptor_index; +} + + +const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) +{ + PmError err = Pm_Initialize(); + if (err) return NULL; + + if (id >= 0 && id < descriptor_index) { + return &descriptors[id].pub; + } + return NULL; +} + + +/* failure_fn -- "noop" function pointer */ +/**/ +PmError failure_fn(PmInternal *midi) +{ + return pmBadPtr; +} + + +/* pm_success_fn -- "noop" function pointer */ +/**/ +PmError pm_success_fn(PmInternal *midi) +{ + return pmNoError; +} + + +PmError none_write(PmInternal *midi, PmEvent *buffer, long length) +{ + return length; /* if we return 0, caller might get into a loop */ +} + +PmError pm_fail_fn(PmInternal *midi) +{ + return pmBadPtr; +} + +static PmError none_open(PmInternal *midi, void *driverInfo) +{ + return pmBadPtr; +} + +#define none_abort pm_fail_fn + +#define none_close pm_fail_fn + + +pm_fns_node pm_none_dictionary = { + none_write, none_open, + none_abort, none_close }; + + +/* Pm_Read -- read up to length longs from source into buffer */ +/* + * returns number of longs actually read, or error code + When the reader wants data: + if overflow_flag: + do not get anything + empty the buffer (read_ptr = write_ptr) + clear overflow_flag + return pmBufferOverflow + get data + return number of messages + + + */ +PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length) +{ + PmInternal *midi = (PmInternal *) stream; + int n = 0; + long head = midi->head; + while (head != midi->tail && n < length) { + *buffer++ = midi->buffer[head++]; + if (head == midi->buffer_len) head = 0; + n++; + } + midi->head = head; + if (midi->overflow) { + midi->head = midi->tail; + midi->overflow = FALSE; + return pmBufferOverflow; + } + return n; +} + + +PmError Pm_Poll( PortMidiStream *stream ) +{ + PmInternal *midi = (PmInternal *) stream; + return midi->head != midi->tail; +} + + +PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length) +{ + PmInternal *midi = (PmInternal *) stream; + return (*midi->dictionary->write)(midi, buffer, length); +} + + +PmError Pm_WriteShort( PortMidiStream *stream, long when, long msg) +{ + PmEvent event; + event.timestamp = when; + event.message = msg; + return Pm_Write(stream, &event, 1); +} + + +PmError Pm_OpenInput( PortMidiStream** stream, + PmDeviceID inputDevice, + void *inputDriverInfo, + long bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + PmStream *thru) +{ + PmInternal *midi; + + PmError err = Pm_Initialize(); + if (err) return err; + + if (inputDevice < 0 || inputDevice >= descriptor_index) { + return pmInvalidDeviceId; + } + + if (!descriptors[inputDevice].pub.input) { + return pmInvalidDeviceId; + } + + midi = (PmInternal *) malloc(sizeof(PmInternal)); + *stream = midi; + if (!midi) return pmInsufficientMemory; + + midi->head = 0; + midi->tail = 0; + midi->dictionary = &pm_none_dictionary; + midi->overflow = FALSE; + midi->flush = FALSE; + midi->sysex_in_progress = FALSE; + midi->buffer_len = bufferSize; + midi->buffer = (PmEvent *) pm_alloc(sizeof(PmEvent) * midi->buffer_len); + if (!midi->buffer) return pmInsufficientMemory; + midi->latency = 0; + midi->thru = thru; + midi->time_proc = time_proc; + midi->time_info = time_info; + midi->device_id = inputDevice; + midi->dictionary = descriptors[inputDevice].dictionary; + midi->write_flag = FALSE; + err = (*midi->dictionary->open)(midi, inputDriverInfo); + if (err) { + pm_free(midi->buffer); + *stream = NULL; + } + return err; +} + + +PmError Pm_OpenOutput( PortMidiStream** stream, + PmDeviceID outputDevice, + void *outputDriverInfo, + long bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + long latency ) +{ + PmInternal *midi; + + PmError err = Pm_Initialize(); + if (err) return err; + + if (outputDevice < 0 || outputDevice >= descriptor_index) { + return pmInvalidDeviceId; + } + + if (!descriptors[outputDevice].pub.output) { + return pmInvalidDeviceId; + } + + midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); + *stream = midi; + if (!midi) return pmInsufficientMemory; + + midi->head = 0; + midi->tail = 0; + midi->buffer_len = bufferSize; + midi->buffer = NULL; + midi->device_id = outputDevice; + midi->dictionary = descriptors[outputDevice].dictionary; + midi->time_proc = time_proc; + midi->time_info = time_info; + midi->latency = latency; + midi->write_flag = TRUE; + err = (*midi->dictionary->open)(midi, outputDriverInfo); + if (err) { + *stream = NULL; + pm_free(midi); // Fixed by Ning Hu, Sep.2001 + } + return err; +} + + +PmError Pm_Abort( PortMidiStream* stream ) +{ + PmInternal *midi = (PmInternal *) stream; + return (*midi->dictionary->abort)(midi); +} + + +PmError Pm_Close( PortMidiStream *stream ) +{ + PmInternal *midi = (PmInternal *) stream; + return (*midi->dictionary->close)(midi); +} + + +const char *Pm_GetErrorText( PmError errnum ) +{ + const char *msg; + + switch(errnum) + { + case pmNoError: msg = "Success"; break; + case pmHostError: msg = "Host error."; break; + case pmInvalidDeviceId: msg = "Invalid device ID."; break; + case pmInsufficientMemory: msg = "Insufficient memory."; break; + case pmBufferTooSmall: msg = "Buffer too small."; break; + case pmBadPtr: msg = "Bad pointer."; break; + case pmInternalError: msg = "Internal PortMidi Error."; break; + default: msg = "Illegal error number."; break; + } + return msg; +} + + +long pm_next_time(PmInternal *midi) +{ + return midi->buffer[midi->head].timestamp; +} + + +/* source should not enqueue data if overflow is set */ +/* + When producer has data to enqueue: + if buffer is full: + set overflow_flag and flush_flag + return + else if overflow_flag: + return + else if flush_flag: + if sysex message is in progress: + return + else: + clear flush_flag + // fall through to enqueue data + enqueue the data + + */ +void pm_enqueue(PmInternal *midi, PmEvent *event) +{ + long tail = midi->tail; + midi->buffer[tail++] = *event; + if (tail == midi->buffer_len) tail = 0; + if (tail == midi->head || midi->overflow) { + midi->overflow = TRUE; + midi->flush = TRUE; + return; + } + if (midi->flush) { + if (midi->sysex_in_progress) return; + else midi->flush = FALSE; + } + midi->tail = tail; +} + + +int pm_queue_full(PmInternal *midi) +{ + long tail = midi->tail + 1; + if (tail == midi->buffer_len) tail = 0; + return tail == midi->head; +} + + + diff --git a/pd/portaudio/portmidi-macosx/portmidi.h b/pd/portaudio/portmidi-macosx/portmidi.h new file mode 100644 index 00000000..3e648c90 --- /dev/null +++ b/pd/portaudio/portmidi-macosx/portmidi.h @@ -0,0 +1,338 @@ +#ifndef PORT_MIDI_H +#define PORT_MIDI_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * PortMidi Portable Real-Time Audio Library + * PortMidi API Header File + * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * Copyright (c) 2001 Roger B. Dannenberg + * + * 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. + * + */ + +/* CHANGELOG FOR PORTMIDI -- THIS VERSION IS 1.0 + * + * 21Jan02 RBD Added tests in Pm_OpenInput() and Pm_OpenOutput() to + * prevent opening an input as output and vice versa. + * Added comments and documentation. + * Implemented Pm_Terminate(). + */ + +#ifndef FALSE + #define FALSE 0 +#endif +#ifndef TRUE + #define TRUE 1 +#endif + + +typedef enum { + pmNoError = 0, + + pmHostError = -10000, + pmInvalidDeviceId, /* out of range or + output device when input is requested or + input device when output is requested */ + //pmInvalidFlag, + pmInsufficientMemory, + pmBufferTooSmall, + pmBufferOverflow, + pmBadPtr, + pmInternalError +} PmError; + +/* + Pm_Initialize() is the library initialisation function - call this before + using the library. +*/ + +PmError Pm_Initialize( void ); + +/* + Pm_Terminate() is the library termination function - call this after + using the library. +*/ + +PmError Pm_Terminate( void ); + +/* + Return host specific error number. All host-specific errors are translated + to the single error class pmHostError. To find out the original error + number, call Pm_GetHostError(). + This can be called after a function returns a PmError equal to pmHostError. +*/ +int Pm_GetHostError(); + +/* + Translate the error number into a human readable message. +*/ +const char *Pm_GetErrorText( PmError errnum ); + + +/* + Device enumeration mechanism. + + Device ids range from 0 to Pm_CountDevices()-1. + + Devices may support input, output or both. Device 0 is always the "default" + device. Other platform specific devices are specified by positive device + ids. +*/ + +typedef int PmDeviceID; +#define pmNoDevice -1 + +typedef struct { + int structVersion; + const char *interf; + const char *name; + int input; /* true iff input is available */ + int output; /* true iff output is available */ +} PmDeviceInfo; + + +int Pm_CountDevices( void ); +/* + Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() + + Return the default device ID or pmNoDevice if there is no devices. + The result can be passed to Pm_OpenMidi(). + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PM_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "pm_devs". +*/ +PmDeviceID Pm_GetDefaultInputDeviceID( void ); +PmDeviceID Pm_GetDefaultOutputDeviceID( void ); + +/* + PmTimestamp is used to represent a millisecond clock with arbitrary + start time. The type is used for all MIDI timestampes and clocks. +*/ + +typedef long PmTimestamp; + +/* TRUE if t1 before t2? */ +#define PmBefore(t1,t2) ((t1-t2) < 0) + + +/* + Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo 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 PortMidi implementation and must + not be manipulated or freed. The pointer is guaranteed to be valid + between calls to Pm_Initialize() and Pm_Terminate(). +*/ + +const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); + + +/* + A single PortMidiStream is a descriptor for an open MIDI device. +*/ + +typedef void PortMidiStream; +#define PmStream PortMidiStream + +typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); + + +/* + Pm_Open() opens a device; for either input or output. + + Port is the address of a PortMidiStream pointer which will receive + a pointer to the newly opened stream. + + inputDevice is the id of the device used for input (see PmDeviceID above.) + + inputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or handle 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 PmDeviceID above.) + + outputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or handle processing. + outputDriverInfo is never required for correct operation. If not used + outputDriverInfo should be NULL. + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. + + time_proc is a pointer to a procedure that returns time in milliseconds. It + may be NULL, in which case a default millisecond timebase is used. + + time_info is a pointer passed to time_proc. + + thru points to a PmMidi descriptor opened for output; Midi input will be + copied to this output. To disable Midi thru, use NULL. + + return value: + Upon success Pm_Open() returns PmNoError and places a pointer to a + valid PortMidiStream in the stream argument. + If a call to Pm_Open() fails a nonzero error code is returned (see + PMError above) and the value of port is invalid. + +*/ + +PmError Pm_OpenInput( PortMidiStream** stream, + PmDeviceID inputDevice, + void *inputDriverInfo, + long bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + PmStream* thru ); + + +PmError Pm_OpenOutput( PortMidiStream** stream, + PmDeviceID outputDevice, + void *outputDriverInfo, + long bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + long latency ); + + +/* + Pm_Abort() terminates outgoing messages immediately + */ +PmError Pm_Abort( PortMidiStream* stream ); + +/* + Pm_Close() closes a midi stream, flushing any pending buffers. +*/ + +PmError Pm_Close( PortMidiStream* stream ); + + +/* + Pm_Message() encodes a short Midi message into a long word. If data1 + and/or data2 are not present, use zero. The port parameter is the + index of the Midi port if the device supports more than one. + + Pm_MessagePort(), Pm_MessageStatus(), Pm_MessageData1(), and + Pm_MessageData2() extract fields from a long-encoded midi message. +*/ + +#define Pm_Message(status, data1, data2) \ + ((((data2) << 16) & 0xFF0000) | \ + (((data1) << 8) & 0xFF00) | \ + ((status) & 0xFF)) + +#define Pm_MessageStatus(msg) ((msg) & 0xFF) +#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) +#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) + +/* All midi data comes in the form of PmEvent structures. A sysex + message is encoded as a sequence of PmEvent structures, with each + structure carrying 4 bytes of the message, i.e. only the first + PmEvent carries the status byte. + + When receiving sysex messages, the sysex message is terminated + by either an EOX status byte (anywhere in the 4 byte message) or + by a non-real-time status byte in the low order byte of message. + If you get a non-real-time status byte, it means the sysex message + was somehow truncated. It is permissible to interleave real-time + messages within sysex messages. + */ + +typedef long PmMessage; + +typedef struct { + PmMessage message; + PmTimestamp timestamp; +} PmEvent; + + +/* + Pm_Read() retrieves midi data into a buffer, and returns the number + of events read. Result is a non-negative number unless an error occurs, + in which case a PmError value will be returned. + + Buffer Overflow + + The problem: if an input overflow occurs, data will be lost, ultimately + because there is no flow control all the way back to the data source. + When data is lost, the receiver should be notified and some sort of + graceful recovery should take place, e.g. you shouldn't resume receiving + in the middle of a long sysex message. + + With a lock-free fifo, which is pretty much what we're stuck with to + enable portability to the Mac, it's tricky for the producer and consumer + to synchronously reset the buffer and resume normal operation. + + Solution: the buffer managed by PortMidi will be flushed when an overflow + occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) + and ordinary processing resumes as soon as a new message arrives. The + remainder of a partial sysex message is not considered to be a "new + message" and will be flushed as well. + +*/ + +PmError Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ); + +/* + Pm_Poll() tests whether input is available, returning TRUE, FALSE, or + an error value. +*/ + +PmError Pm_Poll( PortMidiStream *stream); + +/* + Pm_Write() writes midi data from a buffer. This may contain short + messages or sysex messages that are converted into a sequence of PmEvent + structures. Use Pm_WriteSysEx() to write a sysex message stored as a + contiguous array of bytes. +*/ + +PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ); + +/* + Pm_WriteShort() writes a timestamped non-system-exclusive midi message. +*/ + +PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); + +/* + Pm_WriteSysEx() writes a timestamped system-exclusive midi message. +*/ +PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, char *msg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_MIDI_H */ diff --git a/pd/portaudio/portmidi-macosx/porttime.h b/pd/portaudio/portmidi-macosx/porttime.h new file mode 100644 index 00000000..8592106d --- /dev/null +++ b/pd/portaudio/portmidi-macosx/porttime.h @@ -0,0 +1,30 @@ +/* porttime.h -- portable interface to millisecond timer */ + +/* Should there be a way to choose the source of time here? */ + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + ptNoError = 0, + ptHostError = -10000, + ptAlreadyStarted, + ptAlreadyStopped +} PtError; + + +typedef long PtTimestamp; + +typedef int (PtCallback)( PtTimestamp timestamp, void *userData ); + + +PtError Pt_Start(int resolution, PtCallback *callback, void *userData); +PtError Pt_Stop(); +int Pt_Started(); +PtTimestamp Pt_Time(); + +#ifdef __cplusplus +} +#endif diff --git a/pd/portaudio/portmidi-macosx/ptdarwin.c b/pd/portaudio/portmidi-macosx/ptdarwin.c new file mode 100644 index 00000000..51cf5fde --- /dev/null +++ b/pd/portaudio/portmidi-macosx/ptdarwin.c @@ -0,0 +1,58 @@ +/* + * Portable timer implementation for Darwin / MacOS X + * + * Jon Parise <jparise@cmu.edu> + * + * $Id: ptdarwin.c,v 1.1.1.1 2002-07-29 17:06:16 ggeiger Exp $ + */ + +#include <stdio.h> +#include <sys/time.h> +#include "porttime.h" + +#define TRUE 1 +#define FALSE 0 + +static int time_started_flag = FALSE; +static struct timeval time_offset; + +PtError Pt_Start(int resolution, PtCallback *callback, void *userData) +{ + struct timezone tz; + + if (callback) printf("error in porttime: callbacks not implemented\n"); + time_started_flag = TRUE; + gettimeofday(&time_offset, &tz); + + return ptNoError; +} + + +PtError Pt_Stop() +{ + time_started_flag = FALSE; + return ptNoError; +} + + +int Pt_Started() +{ + return time_started_flag; +} + + +PtTimestamp Pt_Time() +{ + long seconds, milliseconds; + struct timeval now; + struct timezone tz; + + gettimeofday(&now, &tz); + seconds = now.tv_sec - time_offset.tv_sec; + milliseconds = (now.tv_usec - time_offset.tv_usec) / 1000; + + return (seconds * 1000 + milliseconds); +} + + + |