aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/pa_mac_sm
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/pa_mac_sm')
-rw-r--r--pd/portaudio/pa_mac_sm/pa_mac_sm.c1656
1 files changed, 0 insertions, 1656 deletions
diff --git a/pd/portaudio/pa_mac_sm/pa_mac_sm.c b/pd/portaudio/pa_mac_sm/pa_mac_sm.c
deleted file mode 100644
index 59457ded..00000000
--- a/pd/portaudio/pa_mac_sm/pa_mac_sm.c
+++ /dev/null
@@ -1,1656 +0,0 @@
-/*
- * $Id: pa_mac_sm.c,v 1.1.2.1 2002/06/07 21:20:48 rossb Exp $
- * Portable Audio I/O Library for Macintosh
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Phil Burk
- *
- * Special thanks to Chris Rolfe for his many helpful suggestions, bug fixes,
- * and code contributions.
- * Thanks also to Tue Haste Andersen, Alberto Ricci, Nico Wald,
- * Roelf Toxopeus and Tom Erbe for testing the code and making
- * numerous suggestions.
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-/* Modification History
- PLB20010415 - ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID
- PLB20010415 - Device Scan was crashing for anything other than siBadSoundInDevice, but some Macs may return other errors!
- PLB20010420 - Fix TIMEOUT in record mode.
- PLB20010420 - Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON
- PLB20010907 - Pass unused event to WaitNextEvent to prevent Mac OSX crash. Thanks Dominic Mazzoni.
- PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni.
- PLB20011009 - Use NewSndCallBackUPP() for CARBON
- PLB20020417 - I used to call Pa_GetMinNumBuffers() which doesn't take into account the
- variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will
- give lower latency when virtual memory is turned off.
- Thanks Kristoffer Jensen and Georgios Marentakis for spotting this bug.
- PLB20020423 - Use new method to calculate CPU load similar to other ports. Based on num frames calculated.
- Fixed Pa_StreamTime(). Now estimates how many frames have played based on MicroSecond timer.
- Added PA_MAX_USAGE_ALLOWED to prevent Mac form hanging when CPU load approaches 100%.
- PLB20020424 - Fixed return value in Pa_StreamTime
-*/
-
-/*
-COMPATIBILITY
-This Macintosh implementation is designed for use with Mac OS 7, 8 and
-9 on PowerMacs, and OS X if compiled with CARBON
-
-OUTPUT
-A circular array of CmpSoundHeaders is used as a queue. For low latency situations
-there will only be two small buffers used. For higher latency, more and larger buffers
-may be used.
-To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followed
-by a callbackCmd which informs us when the buffer has been processsed.
-
-INPUT
-The SndInput Manager SPBRecord call is used for sound input. If only
-input is used, then the PA user callback is called from the Input completion proc.
-For full-duplex, or output only operation, the PA callback is called from the
-HostBuffer output completion proc. In that case, input sound is passed to the
-callback by a simple FIFO.
-
-TODO:
-O- Add support for native sample data formats other than int16.
-O- Review buffer sizing. Should it be based on result of siDeviceBufferInfo query?
-O- Determine default devices somehow.
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <memory.h>
-#include <math.h>
-
-/* Mac specific includes */
-#include "OSUtils.h"
-#include <MacTypes.h>
-#include <Math64.h>
-#include <Errors.h>
-#include <Sound.h>
-#include <SoundInput.h>
-#include <SoundComponents.h>
-#include <Devices.h>
-#include <DateTimeUtils.h>
-#include <Timer.h>
-#include <Gestalt.h>
-
-#include "portaudio.h"
-#include "pa_host.h"
-#include "pa_trace.h"
-
-#ifndef FALSE
- #define FALSE (0)
- #define TRUE (!FALSE)
-#endif
-
-/* #define TARGET_API_MAC_CARBON (1) */
-
-/*
- * Define maximum CPU load that will be allowed. User callback will
- * be skipped if load exceeds this limit. This is to prevent the Mac
- * from hanging when the CPU is hogged by the sound thread.
- * On my PowerBook G3, the mac hung when I used 94% of CPU ( usage = 0.94 ).
- */
-#define PA_MAX_USAGE_ALLOWED (0.92)
-
-/* Debugging output macros. */
-#define PRINT(x) { printf x; fflush(stdout); }
-#define ERR_RPT(x) PRINT(x)
-#define DBUG(x) /* PRINT(x) /**/
-#define DBUGX(x) /* PRINT(x) /**/
-
-#define MAC_PHYSICAL_FRAMES_PER_BUFFER (512) /* Minimum number of stereo frames per SoundManager double buffer. */
-#define MAC_VIRTUAL_FRAMES_PER_BUFFER (4096) /* Need this many when using Virtual Memory for recording. */
-#define PA_MIN_NUM_HOST_BUFFERS (2)
-#define PA_MAX_NUM_HOST_BUFFERS (16) /* Do not exceed!! */
-#define PA_MAX_DEVICE_INFO (32)
-
-/* Conversions for 16.16 fixed point code. */
-#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0))
-#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16)))
-
-/************************************************************************************/
-/****************** Structures ******************************************************/
-/************************************************************************************/
-/* Use for passing buffers from input callback to output callback for processing. */
-typedef struct MultiBuffer
-{
- char *buffers[PA_MAX_NUM_HOST_BUFFERS];
- int numBuffers;
- int nextWrite;
- int nextRead;
-}
-MultiBuffer;
-
-/* Define structure to contain all Macintosh specific data. */
-typedef struct PaHostSoundControl
-{
- UInt64 pahsc_EntryCount;
- double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */
-
- /* Use char instead of Boolean for atomic operation. */
- volatile char pahsc_IsRecording; /* Recording in progress. Set by foreground. Cleared by background. */
- volatile char pahsc_StopRecording; /* Signal sent to background. */
- volatile char pahsc_IfInsideCallback;
- /* Input */
- SPB pahsc_InputParams;
- SICompletionUPP pahsc_InputCompletionProc;
- MultiBuffer pahsc_InputMultiBuffer;
- int32 pahsc_BytesPerInputHostBuffer;
- int32 pahsc_InputRefNum;
- /* Output */
- CmpSoundHeader pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS];
- int32 pahsc_BytesPerOutputHostBuffer;
- SndChannelPtr pahsc_Channel;
- SndCallBackUPP pahsc_OutputCompletionProc;
- int32 pahsc_NumOutsQueued;
- int32 pahsc_NumOutsPlayed;
- PaTimestamp pahsc_NumFramesDone;
- UInt64 pahsc_WhenFramesDoneIncremented;
- /* Init Time -------------- */
- int32 pahsc_NumHostBuffers;
- int32 pahsc_FramesPerHostBuffer;
- int32 pahsc_UserBuffersPerHostBuffer;
- int32 pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */
-}
-PaHostSoundControl;
-
-/* Mac specific device information. */
-typedef struct internalPortAudioDevice
-{
- long pad_DeviceRefNum;
- long pad_DeviceBufferSize;
- Component pad_Identifier;
- PaDeviceInfo pad_Info;
-}
-internalPortAudioDevice;
-
-/************************************************************************************/
-/****************** Data ************************************************************/
-/************************************************************************************/
-static int sNumDevices = 0;
-static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 };
-static int32 sPaHostError = 0;
-static int sDefaultOutputDeviceID;
-static int sDefaultInputDeviceID;
-
-/************************************************************************************/
-/****************** Prototypes ******************************************************/
-/************************************************************************************/
-static PaError PaMac_TimeSlice( internalPortAudioStream *past, int16 *macOutputBufPtr );
-static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr );
-static PaError PaMac_RecordNext( internalPortAudioStream *past );
-static void PaMac_StartLoadCalculation( internalPortAudioStream *past );
-static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate );
-static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h );
-static PaError PaMac_ScanInputDevices( void );
-static PaError PaMac_ScanOutputDevices( void );
-static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad );
-static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad );
-static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader );
-static void PaMac_EndLoadCalculation( internalPortAudioStream *past );
-static void PaMac_PlayNext ( internalPortAudioStream *past, int index );
-static long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index );
-static pascal void PaMac_InputCompletionProc(SPBPtr recParams);
-static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd);
-static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index );
-long PaHost_GetTotalBufferFrames( internalPortAudioStream *past );
-static int Mac_IsVirtualMemoryOn( void );
-static void PToCString(unsigned char* inString, char* outString);
-char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf );
-char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf );
-int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf );
-int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf );
-int MultiBuffer_IsWriteable( MultiBuffer *mbuf );
-int MultiBuffer_IsReadable( MultiBuffer *mbuf );
-void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf );
-void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf );
-
-/*************************************************************************
-** Simple FIFO index control for multiple buffers.
-** Read and Write indices range from 0 to 2N-1.
-** This allows us to distinguish between full and empty.
-*/
-char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf )
-{
- return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers];
-}
-char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf )
-{
- return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers];
-}
-int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf )
-{
- return mbuf->nextRead % mbuf->numBuffers;
-}
-int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf )
-{
- return mbuf->nextWrite % mbuf->numBuffers;
-}
-
-int MultiBuffer_IsWriteable( MultiBuffer *mbuf )
-{
- int bufsFull = mbuf->nextWrite - mbuf->nextRead;
- if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
- return (bufsFull < mbuf->numBuffers);
-}
-int MultiBuffer_IsReadable( MultiBuffer *mbuf )
-{
- int bufsFull = mbuf->nextWrite - mbuf->nextRead;
- if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers);
- return (bufsFull > 0);
-}
-void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf )
-{
- int temp = mbuf->nextRead + 1;
- mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
-}
-void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf )
-{
- int temp = mbuf->nextWrite + 1;
- mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp;
-}
-
-/*************************************************************************
-** String Utility by Chris Rolfe
-*/
-static void PToCString(unsigned char* inString, char* outString)
-{
- long i;
- for(i=0; i<inString[0]; i++) /* convert Pascal to C string */
- outString[i] = inString[i+1];
- outString[i]=0;
-}
-
-/*************************************************************************/
-PaError PaHost_Term( void )
-{
- int i;
- PaDeviceInfo *dev;
- double *rates;
- /* Free any allocated sample rate arrays. */
- for( i=0; i<sNumDevices; i++ )
- {
- dev = &sDevices[i].pad_Info;
- rates = (double *) dev->sampleRates;
- if( (rates != NULL) ) free( rates ); /* MEM_011 */
- dev->sampleRates = NULL;
- if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */
- dev->name = NULL;
- }
- sNumDevices = 0;
- return paNoError;
-}
-
-/*************************************************************************
- PaHost_Init() is the library initialization function - call this before
- using the library.
-*/
-PaError PaHost_Init( void )
-{
- PaError err;
- NumVersionVariant version;
-
- version.parts = SndSoundManagerVersion();
- DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole));
-
- /* Have we already initialized the device info? */
- err = (PaError) Pa_CountDevices();
- if( err < 0 ) return err;
- else return paNoError;
-}
-
-/*************************************************************************
- PaMac_ScanOutputDevices() queries the properties of all output devices.
-*/
-static PaError PaMac_ScanOutputDevices( void )
-{
- PaError err;
- Component identifier=0;
- ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 };
- long numComponents, i;
-
- /* Search the system linked list for output components */
- numComponents = CountComponents (&criteria);
- identifier = 0;
- sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */
- for (i = 0; i < numComponents; i++)
- {
- /* passing nil returns first matching component. */
- identifier = FindNextComponent( identifier, &criteria);
- sDevices[sNumDevices].pad_Identifier = identifier;
-
- /* Set up for default OUTPUT devices. */
- err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] );
- if( err < 0 ) return err;
- else sNumDevices++;
-
- }
-
- return paNoError;
-}
-
-/*************************************************************************
- PaMac_ScanInputDevices() queries the properties of all input devices.
-*/
-static PaError PaMac_ScanInputDevices( void )
-{
- Str255 deviceName;
- int count;
- Handle iconHandle;
- PaError err;
- OSErr oserr;
- count = 1;
- sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */
- while(true)
- {
- /* Thanks Chris Rolfe and Alberto Ricci for this trick. */
- oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle);
- DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr ));
-#if 1
- /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */
- if(oserr != noErr) break; /* Some type of error is expected when count > devices */
-#else
- if(oserr == siBadSoundInDevice)
- { /* it's expected when count > devices */
- oserr = noErr;
- break;
- }
- if(oserr != noErr)
- {
- ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr ));
- sPaHostError = oserr;
- return paHostError;
- }
-#endif
- DisposeHandle(iconHandle); /* Don't need the icon */
-
- err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] );
- DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err ));
- if( err < 0 ) return err;
- else if( err == 1 ) sNumDevices++;
- }
-
- return paNoError;
-}
-
-/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */
-/* Thanks to Chris Rolfe for help with this query. */
-#pragma options align=mac68k
-typedef struct
-{
- int16 numRates;
- UnsignedFixed (**rates)[]; /* Handle created by SPBGetDeviceInfo */
-}
-SRateInfo;
-#pragma options align=reset
-
-/*************************************************************************
-** PaMac_QueryOutputDeviceInfo()
-** Query information about a named output device.
-** Clears contents of ipad and writes info based on queries.
-** Return one if OK,
-** zero if device cannot be used,
-** or negative error.
-*/
-static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad )
-{
- int len;
- OSErr err;
- PaDeviceInfo *dev = &ipad->pad_Info;
- SRateInfo srinfo = {0};
- int numRates;
- ComponentDescription tempD;
- Handle nameH=nil, infoH=nil, iconH=nil;
-
- memset( ipad, 0, sizeof(internalPortAudioDevice) );
-
- dev->structVersion = 1;
- dev->maxInputChannels = 0;
- dev->maxOutputChannels = 2;
- dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */
-
- /* Get sample rates supported. */
- err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo);
- if(err != noErr)
- {
- ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err ));
- goto error;
- }
- numRates = srinfo.numRates;
- DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = 0x%x\n", srinfo.numRates ));
- if( numRates == 0 )
- {
- dev->numSampleRates = -1;
- numRates = 2;
- }
- else
- {
- dev->numSampleRates = numRates;
- }
- dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
- /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
- DisposeHandle((Handle) srinfo.rates);
-
- /* Device name */
- /* we pass an existing handle for the component name;
- we don't care about the info (type, subtype, etc.) or icon, so set them to nil */
- infoH = nil;
- iconH = nil;
- nameH = NewHandle(0);
- if(nameH == nil) return paInsufficientMemory;
- err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err ));
- goto error;
- }
- len = (*nameH)[0] + 1;
- dev->name = (char *) malloc(len); /* MEM_010 */
- if( dev->name == NULL )
- {
- DisposeHandle(nameH);
- return paInsufficientMemory;
- }
- else
- {
- PToCString((unsigned char *)(*nameH), (char *) dev->name);
- DisposeHandle(nameH);
- }
-
- DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name ));
- return paNoError;
-
-error:
- sPaHostError = err;
- return paHostError;
-
-}
-
-/*************************************************************************
-** PaMac_QueryInputDeviceInfo()
-** Query information about a named input device.
-** Clears contents of ipad and writes info based on queries.
-** Return one if OK,
-** zero if device cannot be used,
-** or negative error.
-*/
-static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad )
-{
- PaError result = paNoError;
- int len;
- OSErr err;
- long mRefNum = 0;
- long tempL;
- int16 tempS;
- Fixed tempF;
- PaDeviceInfo *dev = &ipad->pad_Info;
- SRateInfo srinfo = {0};
- int numRates;
-
- memset( ipad, 0, sizeof(internalPortAudioDevice) );
- dev->maxOutputChannels = 0;
-
- /* Open device based on name. If device is in use, it may not be able to open in write mode. */
- err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum);
- if (err)
- {
- /* If device is in use, it may not be able to open in write mode so try read mode. */
- err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err ));
- sPaHostError = err;
- return paHostError;
- }
- }
-
- /* Define macros for printing out device info. */
-#define PrintDeviceInfo(selector,var) \
- err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \
- if (err) { \
- DBUG(("query %s failed\n", #selector )); \
- }\
- else { \
- DBUG(("query %s = 0x%x\n", #selector, var )); \
- }
-
- PrintDeviceInfo( siContinuous, tempS );
- PrintDeviceInfo( siAsync, tempS );
- PrintDeviceInfo( siNumberChannels, tempS );
- PrintDeviceInfo( siSampleSize, tempS );
- PrintDeviceInfo( siSampleRate, tempF );
- PrintDeviceInfo( siChannelAvailable, tempS );
- PrintDeviceInfo( siActiveChannels, tempL );
- PrintDeviceInfo( siDeviceBufferInfo, tempL );
-
- err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
- if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL ));
- /* Can we use this device? */
- err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err ));
- goto error;
- }
- if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */
-
- err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err ));
- goto error;
- }
- dev->maxInputChannels = tempS;
-
- /* Get sample rates supported. */
- err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err ));
- goto error;
- }
-
- numRates = srinfo.numRates;
- DBUG(("numRates = 0x%x\n", numRates ));
- if( numRates == 0 )
- {
- dev->numSampleRates = -1;
- numRates = 2;
- }
- else
- {
- dev->numSampleRates = numRates;
- }
- dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates );
- /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */
- DisposeHandle((Handle) srinfo.rates);
-
- /* Get size of device buffer. */
- err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err ));
- goto error;
- }
- ipad->pad_DeviceBufferSize = tempL;
- DBUG(("siDeviceBufferInfo = %d\n", tempL ));
-
- /* Set format based on sample size. */
- err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS);
- if (err)
- {
- ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err ));
- goto error;
- }
- switch( tempS )
- {
- case 0x0020:
- dev->nativeSampleFormats = paInt32; /* FIXME - warning, code probably won't support this! */
- break;
- case 0x0010:
- default: /* FIXME - What about other formats? */
- dev->nativeSampleFormats = paInt16;
- break;
- }
- DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats ));
-
- /* Device name */
- len = deviceName[0] + 1; /* Get length of Pascal string */
- dev->name = (char *) malloc(len); /* MEM_010 */
- if( dev->name == NULL )
- {
- result = paInsufficientMemory;
- goto cleanup;
- }
- PToCString(deviceName, (char *) dev->name);
- DBUG(("deviceName = %s\n", dev->name ));
- result = (PaError) 1;
- /* All done so close up device. */
-cleanup:
- if( mRefNum ) SPBCloseDevice(mRefNum);
- return result;
-
-error:
- if( mRefNum ) SPBCloseDevice(mRefNum);
- sPaHostError = err;
- return paHostError;
-
-useless:
- if( mRefNum ) SPBCloseDevice(mRefNum);
- return (PaError) 0;
-}
-
-/*************************************************************************
-** Allocate a double array and fill it with listed sample rates.
-*/
-static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h )
-{
- OSErr err = noErr;
- SInt8 hState;
- int i;
- UnsignedFixed *fixedRates;
- double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */
- if( rates == NULL ) return NULL;
- /* Save and restore handle state as suggested by TechNote at:
- http://developer.apple.com/technotes/tn/tn1122.html
- */
- hState = HGetState (h);
- if (!(err = MemError ()))
- {
- HLock (h);
- if (!(err = MemError ( )))
- {
- fixedRates = (UInt32 *) *h;
- for( i=0; i<numRates; i++ )
- {
- rates[i] = UnsignedFixedToDouble(fixedRates[i]);
- }
-
- HSetState (h,hState);
- err = MemError ( );
- }
- }
- if( err )
- {
- free( rates );
- ERR_RPT(("Error in PaMac_GetSampleRatesFromHandle = %d\n", err ));
- }
- return rates;
-}
-
-/*************************************************************************/
-int Pa_CountDevices()
-{
- PaError err;
- DBUG(("Pa_CountDevices()\n"));
- /* If no devices, go find some. */
- if( sNumDevices <= 0 )
- {
- err = PaMac_ScanOutputDevices();
- if( err != paNoError ) goto error;
- err = PaMac_ScanInputDevices();
- if( err != paNoError ) goto error;
- }
- return sNumDevices;
-
-error:
- PaHost_Term();
- DBUG(("Pa_CountDevices: returns %d\n", err ));
- return err;
-
-}
-
-/*************************************************************************/
-const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
-{
- if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
- return &sDevices[id].pad_Info;
-}
-/*************************************************************************/
-PaDeviceID Pa_GetDefaultInputDeviceID( void )
-{
- return sDefaultInputDeviceID;
-}
-
-/*************************************************************************/
-PaDeviceID Pa_GetDefaultOutputDeviceID( void )
-{
- return sDefaultOutputDeviceID;
-}
-
-/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
-static void PaMac_StartLoadCalculation( internalPortAudioStream *past )
-{
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- UnsignedWide widePad;
- if( pahsc == NULL ) return;
- /* Query system timer for usage analysis and to prevent overuse of CPU. */
- Microseconds( &widePad );
- pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad );
-}
-
-/******************************************************************************
-** Measure fractional CPU load based on real-time it took to calculate
-** buffers worth of output.
-*/
-/**************************************************************************/
-static void PaMac_EndLoadCalculation( internalPortAudioStream *past )
-{
- UnsignedWide widePad;
- UInt64 currentCount;
- long usecsElapsed;
- double newUsage;
- 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.95)
-#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
- Microseconds( &widePad );
- currentCount = UnsignedWideToUInt64( widePad );
-
- usecsElapsed = (long) U64Subtract(currentCount, pahsc->pahsc_EntryCount);
-
- /* 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);
-
-}
-
-/***********************************************************************
-** Called by Pa_StartStream()
-*/
-PaError PaHost_StartInput( internalPortAudioStream *past )
-{
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- pahsc->pahsc_IsRecording = 0;
- pahsc->pahsc_StopRecording = 0;
- pahsc->pahsc_InputMultiBuffer.nextWrite = 0;
- pahsc->pahsc_InputMultiBuffer.nextRead = 0;
- return PaMac_RecordNext( past );
-}
-
-/***********************************************************************
-** Called by Pa_StopStream().
-** May be called during error recovery or cleanup code
-** so protect against NULL pointers.
-*/
-PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
-{
- int32 timeOutMsec;
- PaError result = paNoError;
- OSErr err = 0;
- long mRefNum;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- if( pahsc == NULL ) return paNoError;
-
- (void) abort;
-
- mRefNum = pahsc->pahsc_InputRefNum;
-
- DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum ));
- if( mRefNum )
- {
- DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
- if( pahsc->pahsc_IsRecording )
- {
- /* PLB20010420 - Fix TIMEOUT in record mode. */
- pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */
- err = SPBStopRecording(mRefNum);
- DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording ));
-
- /* Calculate timeOut longer than longest time it could take to play one buffer. */
- timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate);
- /* Keep querying sound channel until it is no longer busy playing. */
- while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0))
- {
- Pa_Sleep(20);
- timeOutMsec -= 20;
- }
- if( timeOutMsec <= 0 )
- {
- ERR_RPT(("PaHost_StopInput: timed out!\n"));
- return paTimedOut;
- }
- }
- }
- if( err )
- {
- sPaHostError = err;
- result = paHostError;
- }
-
- DBUG(("PaHost_StopInput: finished.\n", mRefNum ));
- return result;
-}
-
-/***********************************************************************/
-static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader )
-{
- sndHeader->numChannels = past->past_NumOutputChannels;
- sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate);
- sndHeader->loopStart = 0;
- sndHeader->loopEnd = 0;
- sndHeader->encode = cmpSH;
- sndHeader->baseFrequency = kMiddleC;
- sndHeader->markerChunk = nil;
- sndHeader->futureUse2 = nil;
- sndHeader->stateVars = nil;
- sndHeader->leftOverSamples = nil;
- sndHeader->compressionID = 0;
- sndHeader->packetSize = 0;
- sndHeader->snthID = 0;
- sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day;
- sndHeader->sampleArea[0] = 0;
- sndHeader->format = kSoundNotCompressed;
-}
-
-static void SetFramesDone( PaHostSoundControl *pahsc, PaTimestamp framesDone )
-{
- UnsignedWide now;
- Microseconds( &now );
- pahsc->pahsc_NumFramesDone = framesDone;
- pahsc->pahsc_WhenFramesDoneIncremented = UnsignedWideToUInt64( now );
-}
-
-/***********************************************************************/
-PaError PaHost_StartOutput( internalPortAudioStream *past )
-{
- SndCommand pauseCommand;
- SndCommand resumeCommand;
- int i;
- OSErr error;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- if( pahsc == NULL ) return paInternalError;
- if( pahsc->pahsc_Channel == NULL ) return paInternalError;
-
- past->past_StopSoon = 0;
- past->past_IsActive = 1;
- pahsc->pahsc_NumOutsQueued = 0;
- pahsc->pahsc_NumOutsPlayed = 0;
-
- SetFramesDone( pahsc, 0.0 );
-
- /* Pause channel so it does not do back ground processing while we are still filling the queue. */
- pauseCommand.cmd = pauseCmd;
- pauseCommand.param1 = pauseCommand.param2 = 0;
- error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true);
- if (noErr != error) goto exit;
-
- /* Queue all of the buffers so we start off full. */
- for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
- {
- PaMac_PlayNext( past, i );
- }
-
- /* Resume channel now that the queue is full. */
- resumeCommand.cmd = resumeCmd;
- resumeCommand.param1 = resumeCommand.param2 = 0;
- error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand );
- if (noErr != error) goto exit;
-
- return paNoError;
-exit:
- past->past_IsActive = 0;
- sPaHostError = error;
- ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error ));
- return paHostError;
-}
-
-/*******************************************************************/
-long PaHost_GetTotalBufferFrames( internalPortAudioStream *past )
-{
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer);
-}
-
-/***********************************************************************
-** Called by Pa_StopStream().
-** May be called during error recovery or cleanup code
-** so protect against NULL pointers.
-*/
-PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
-{
- int32 timeOutMsec;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- if( pahsc == NULL ) return paNoError;
- if( pahsc->pahsc_Channel == NULL ) return paNoError;
-
- DBUG(("PaHost_StopOutput()\n"));
- if( past->past_IsActive == 0 ) return paNoError;
-
- /* Set flags for callback function to see. */
- if( abort ) past->past_StopNow = 1;
- past->past_StopSoon = 1;
- /* Calculate timeOut longer than longest time it could take to play all buffers. */
- timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate);
- /* Keep querying sound channel until it is no longer busy playing. */
- while( past->past_IsActive && (timeOutMsec > 0))
- {
- Pa_Sleep(20);
- timeOutMsec -= 20;
- }
- if( timeOutMsec <= 0 )
- {
- ERR_RPT(("PaHost_StopOutput: timed out!\n"));
- return paTimedOut;
- }
- else return paNoError;
-}
-
-/***********************************************************************/
-PaError PaHost_StartEngine( internalPortAudioStream *past )
-{
- (void) past; /* Prevent unused variable warnings. */
- return paNoError;
-}
-
-/***********************************************************************/
-PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
-{
- (void) past; /* Prevent unused variable warnings. */
- (void) abort; /* Prevent unused variable warnings. */
- return paNoError;
-}
-/***********************************************************************/
-PaError PaHost_StreamActive( internalPortAudioStream *past )
-{
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording );
-}
-int Mac_IsVirtualMemoryOn( void )
-{
- long attr;
- OSErr result = Gestalt( gestaltVMAttr, &attr );
- DBUG(("gestaltVMAttr : 0x%x\n", attr ));
- return ((attr >> gestaltVMHasPagingControl ) & 1);
-}
-
-/*******************************************************************
-* Determine number of host Buffers
-* and how many User Buffers we can put into each host buffer.
-*/
-static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past )
-{
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- int32 minNumBuffers;
- int32 minFramesPerHostBuffer;
- int32 minTotalFrames;
- int32 userBuffersPerHostBuffer;
- int32 framesPerHostBuffer;
- int32 numHostBuffers;
-
- minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer;
- minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7;
- DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer ));
-
- /* Determine number of user buffers based on minimum latency. */
- /* PLB20020417 I used to call Pa_GetMinNumBuffers() which doesn't take into account the
- ** variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will
- ** gove lower latency when virtual memory is turned off. */
- /* minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); WRONG */
- minNumBuffers = PaMac_GetMinNumBuffers( minFramesPerHostBuffer, past->past_FramesPerUserBuffer, past->past_SampleRate );
-
- past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers;
- DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
- minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer;
-
- /* We cannot make the buffers too small because they may not get serviced quickly enough. */
- if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer )
- {
- userBuffersPerHostBuffer =
- (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) /
- past->past_FramesPerUserBuffer;
- }
- else
- {
- userBuffersPerHostBuffer = 1;
- }
- framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
-
- /* Calculate number of host buffers needed. Round up to cover minTotalFrames. */
- numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
- /* Make sure we have enough host buffers. */
- if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
- {
- numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
- }
- else
- {
- /* If we have too many host buffers, try to put more user buffers in a host buffer. */
- while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
- {
- userBuffersPerHostBuffer += 1;
- framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
- numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
- }
- }
-
- pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer;
- pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer;
- pahsc->pahsc_NumHostBuffers = numHostBuffers;
- DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer ));
- DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers ));
- DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer ));
- DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers ));
-}
-
-/*******************************************************************/
-PaError PaHost_OpenStream( internalPortAudioStream *past )
-{
- OSErr err;
- PaError result = paHostError;
- PaHostSoundControl *pahsc;
- int i;
- /* Allocate and initialize host data. */
- pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
- if( pahsc == NULL )
- {
- return paInsufficientMemory;
- }
- past->past_DeviceData = (void *) pahsc;
-
- /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */
- if( (past->past_NumInputChannels > 0) && Mac_IsVirtualMemoryOn() )
- {
- pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER;
- }
- else
- {
- pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER;
- }
-
- PaHost_CalcNumHostBuffers( past );
-
- /* Setup constants for CPU load measurement. */
- pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer);
-
- /* ------------------ OUTPUT */
- if( past->past_NumOutputChannels > 0 )
- {
- /* Create sound channel to which we can send commands. */
- pahsc->pahsc_Channel = 0L;
- err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */
- if(err != 0)
- {
- ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err ));
- goto error;
- }
-
- /* Install our callback function pointer straight into the sound channel structure */
- /* Use new CARBON name for callback procedure. */
-#if TARGET_API_MAC_CARBON
- pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc);
-#else
- pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc);
-#endif
-
- pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc;
-
- pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16);
- for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
- {
- char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer);
- if (buf == NULL)
- {
- ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer ));
- goto memerror;
- }
-
- PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] );
- pahsc->pahsc_SoundHeaders[i].samplePtr = buf;
- pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer;
-
- }
- }
-#ifdef SUPPORT_AUDIO_CAPTURE
- /* ------------------ INPUT */
- /* Use double buffer scheme that matches output. */
- if( past->past_NumInputChannels > 0 )
- {
- int16 tempS;
- long tempL;
- Fixed tempF;
- long mRefNum;
- unsigned char noname = 0; /* FIXME - use real device names. */
-#if TARGET_API_MAC_CARBON
- pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc);
-#else
- pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc);
-#endif
- pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16);
- for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
- {
- char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer);
- if ( buf == NULL )
- {
- ERR_RPT(("PaHost_OpenStream: could not allocate input buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer ));
- goto memerror;
- }
- pahsc->pahsc_InputMultiBuffer.buffers[i] = buf;
- }
- pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers;
-
- err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); /* FIXME - use name so we get selected device */
- // FIXME err = SPBOpenDevice( (const unsigned char *) sDevices[past->past_InputDeviceID].pad_Info.name, siWritePermission, &mRefNum);
- if (err) goto error;
- pahsc->pahsc_InputRefNum = mRefNum;
- DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum ));
-
- /* Set input device characteristics. */
- tempS = 1;
- err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS);
- if (err)
- {
- ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err ));
- goto error;
- }
-
- tempL = 0x03;
- err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL);
- if (err)
- {
- DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err ));
- }
-
- /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */
- tempS = past->past_NumInputChannels;
- err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS);
- if (err)
- {
- ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err ));
- goto error;
- }
-
- tempF = ((unsigned long)past->past_SampleRate) << 16;
- err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF);
- if (err)
- {
- ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err ));
- goto error;
- }
-
- /* Setup record-parameter block */
- pahsc->pahsc_InputParams.inRefNum = mRefNum;
- pahsc->pahsc_InputParams.milliseconds = 0; // not used
- pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc;
- pahsc->pahsc_InputParams.interruptRoutine = 0;
- pahsc->pahsc_InputParams.userLong = (long) past;
- pahsc->pahsc_InputParams.unused1 = 0;
- }
-#endif /* SUPPORT_AUDIO_CAPTURE */
- DBUG(("PaHost_OpenStream: complete.\n"));
- return paNoError;
-
-error:
- PaHost_CloseStream( past );
- ERR_RPT(("PaHost_OpenStream: sPaHostError = 0x%x.\n", err ));
- sPaHostError = err;
- return paHostError;
-
-memerror:
- PaHost_CloseStream( past );
- return paInsufficientMemory;
-}
-
-/***********************************************************************
-** Called by Pa_CloseStream().
-** May be called during error recovery or cleanup code
-** so protect against NULL pointers.
-*/
-PaError PaHost_CloseStream( internalPortAudioStream *past )
-{
- PaError result = paNoError;
- OSErr err = 0;
- int i;
- PaHostSoundControl *pahsc;
-
- DBUG(("PaHost_CloseStream( 0x%x )\n", past ));
-
- if( past == NULL ) return paBadStreamPtr;
-
- pahsc = (PaHostSoundControl *) past->past_DeviceData;
- if( pahsc == NULL ) return paNoError;
-
- if( past->past_NumOutputChannels > 0 )
- {
- /* TRUE means flush now instead of waiting for quietCmd to be processed. */
- if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE);
- {
- for (i = 0; i<pahsc->pahsc_NumHostBuffers; i++)
- {
- Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr;
- if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer );
- }
- }
- }
-
- if( past->past_NumInputChannels > 0 )
- {
- if( pahsc->pahsc_InputRefNum )
- {
- err = SPBCloseDevice(pahsc->pahsc_InputRefNum);
- pahsc->pahsc_InputRefNum = 0;
- if( err )
- {
- sPaHostError = err;
- result = paHostError;
- }
- }
- {
- for (i = 0; i<pahsc->pahsc_InputMultiBuffer.numBuffers; i++)
- {
- Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i];
- if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer );
- }
- }
- }
-
- past->past_DeviceData = NULL;
- PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) );
-
- DBUG(("PaHost_CloseStream: complete.\n", past ));
- return result;
-}
-/*************************************************************************/
-int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate )
-{
-/* We use the MAC_VIRTUAL_FRAMES_PER_BUFFER because we might be recording.
-** This routine doesn't have enough information to determine the best value
-** and is being depracated. */
- return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate );
-}
-/*************************************************************************/
-static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate )
-{
- int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer;
- int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost;
- if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS;
- (void) sampleRate;
- return numBufs;
-}
-
-/*************************************************************************/
-void Pa_Sleep( int32 msec )
-{
- EventRecord event;
- int32 sleepTime, endTime;
- /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */
- sleepTime = ((msec * 60) + 999) / 1000;
- if( sleepTime < 1 ) sleepTime = 1;
- endTime = TickCount() + sleepTime;
- do
- {
- DBUGX(("Sleep for %d ticks.\n", sleepTime ));
- /* Use WaitNextEvent() to sleep without getting events. */
- /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent
- * Mac OSX crash. Thanks Dominic Mazzoni. */
- WaitNextEvent( 0, &event, sleepTime, NULL );
- sleepTime = endTime - TickCount();
- }
- while( sleepTime > 0 );
-}
-/*************************************************************************/
-int32 Pa_GetHostError( void )
-{
- int32 err = sPaHostError;
- sPaHostError = 0;
- return err;
-}
-
-/*************************************************************************
- * Allocate memory that can be accessed in real-time.
- * This may need to be held in physical memory so that it is not
- * paged to virtual memory.
- * This call MUST be balanced with a call to PaHost_FreeFastMemory().
- */
-void *PaHost_AllocateFastMemory( long numBytes )
-{
- void *addr = NewPtrClear( numBytes );
- if( (addr == NULL) || (MemError () != 0) ) return NULL;
-
-#if (TARGET_API_MAC_CARBON == 0)
- if( HoldMemory( addr, numBytes ) != noErr )
- {
- DisposePtr( (Ptr) addr );
- return NULL;
- }
-#endif
- return addr;
-}
-
-/*************************************************************************
- * Free memory that could be accessed in real-time.
- * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
- */
-void PaHost_FreeFastMemory( void *addr, long numBytes )
-{
- if( addr == NULL ) return;
-#if TARGET_API_MAC_CARBON
- (void) numBytes;
-#else
- UnholdMemory( addr, numBytes );
-#endif
- DisposePtr( (Ptr) addr );
-}
-
-/*************************************************************************/
-PaTimestamp Pa_StreamTime( PortAudioStream *stream )
-{
- PaTimestamp framesDone1;
- PaTimestamp framesDone2;
- UInt64 whenIncremented;
- UnsignedWide now;
- UInt64 now64;
- long microsElapsed;
- long framesElapsed;
-
- PaHostSoundControl *pahsc;
- internalPortAudioStream *past = (internalPortAudioStream *) stream;
- if( past == NULL ) return paBadStreamPtr;
- pahsc = (PaHostSoundControl *) past->past_DeviceData;
-
-/* Capture information from audio thread.
- * We have to be careful that we don't get interrupted in the middle.
- * So we grab the pahsc_NumFramesDone twice and make sure it didn't change.
- */
- do
- {
- framesDone1 = pahsc->pahsc_NumFramesDone;
- whenIncremented = pahsc->pahsc_WhenFramesDoneIncremented;
- framesDone2 = pahsc->pahsc_NumFramesDone;
- } while( framesDone1 != framesDone2 );
-
- /* Calculate how many microseconds have elapsed and convert to frames. */
- Microseconds( &now );
- now64 = UnsignedWideToUInt64( now );
- microsElapsed = U64Subtract( now64, whenIncremented );
- framesElapsed = microsElapsed * past->past_SampleRate * 0.000001;
-
- return framesDone1 + framesElapsed;
-}
-
-/**************************************************************************
-** Callback for Input, SPBRecord()
-*/
-int gRecordCounter = 0;
-int gPlayCounter = 0;
-pascal void PaMac_InputCompletionProc(SPBPtr recParams)
-{
- PaError result = paNoError;
- int finished = 1;
- internalPortAudioStream *past;
- PaHostSoundControl *pahsc;
-
- gRecordCounter += 1; /* debug hack to see if engine running */
-
- /* Get our PA data from Mac structure. */
- past = (internalPortAudioStream *) recParams->userLong;
- if( past == NULL ) return;
-
- if( past->past_Magic != PA_MAGIC )
- {
- AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past );
- AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic );
- goto error;
- }
- pahsc = (PaHostSoundControl *) past->past_DeviceData;
- past->past_NumCallbacks += 1;
-
- /* Have we been asked to stop recording? */
- if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error;
-
- /* If there are no output channels, then we need to call the user callback function from here.
- * Otherwise we will call the user code during the output completion routine.
- */
- if(past->past_NumOutputChannels == 0)
- {
- SetFramesDone( pahsc,
- pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer );
- result = PaMac_CallUserLoop( past, NULL );
- }
-
- /* Did user code ask us to stop? If not, issue another recording request. */
- if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) )
- {
- result = PaMac_RecordNext( past );
- if( result != paNoError ) pahsc->pahsc_IsRecording = 0;
- }
- else goto error;
-
- return;
-
-error:
- pahsc->pahsc_IsRecording = 0;
- pahsc->pahsc_StopRecording = 0;
- return;
-}
-
-/***********************************************************************
-** Called by either input or output completion proc.
-** Grabs input data if any present, and calls PA conversion code,
-** that in turn calls user code.
-*/
-static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr )
-{
- PaError result = paNoError;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- int16 *inPtr = NULL;
- int i;
-
-
- /* Advance read index for sound input FIFO here, independantly of record/write process. */
- if(past->past_NumInputChannels > 0)
- {
- if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) )
- {
- inPtr = (int16 *) MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer );
- MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer );
- }
- }
-
- /* Call user code enough times to fill buffer. */
- if( (inPtr != NULL) || (outPtr != NULL) )
- {
- PaMac_StartLoadCalculation( past ); /* CPU usage */
-
-#ifdef PA_MAX_USAGE_ALLOWED
- /* If CPU usage exceeds limit, skip user callback to prevent hanging CPU. */
- if( past->past_Usage > PA_MAX_USAGE_ALLOWED )
- {
- past->past_FrameCount += (PaTimestamp) pahsc->pahsc_FramesPerHostBuffer;
- }
- else
-#endif
- {
-
- for( i=0; i<pahsc->pahsc_UserBuffersPerHostBuffer; i++ )
- {
- result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr );
- if( result != 0)
- {
- /* Recording might be in another process, so tell it to stop with a flag. */
- pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording;
- break;
- }
- /* Advance sample pointers. */
- if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels;
- if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
- }
- }
-
- PaMac_EndLoadCalculation( past );
- }
- return result;
-}
-
-/***********************************************************************
-** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager.
-*/
-static PaError PaMac_RecordNext( internalPortAudioStream *past )
-{
- PaError result = paNoError;
- OSErr err;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- /* Get pointer to next buffer to record into. */
- pahsc->pahsc_InputParams.bufferPtr = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer );
-
- /* Advance write index if there is room. Otherwise keep writing same buffer. */
- if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) )
- {
- MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer );
- }
-
- AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr );
- AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite );
-
- /* Setup parameters and issue an asynchronous recording request. */
- pahsc->pahsc_InputParams.bufferLength = pahsc->pahsc_BytesPerInputHostBuffer;
- pahsc->pahsc_InputParams.count = pahsc->pahsc_BytesPerInputHostBuffer;
- err = SPBRecord(&pahsc->pahsc_InputParams, true);
- if( err )
- {
- AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err );
- sPaHostError = err;
- result = paHostError;
- }
- else
- {
- pahsc->pahsc_IsRecording = 1;
- }
- return result;
-}
-
-/**************************************************************************
-** Callback for Output Playback()
-** Return negative error, 0 to continue, 1 to stop.
-*/
-long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index )
-{
- PaHostSoundControl *pahsc;
- long result = 0;
- int finished = 1;
- char *outPtr;
-
- gPlayCounter += 1; /* debug hack */
-
- past->past_NumCallbacks += 1;
- pahsc = (PaHostSoundControl *) past->past_DeviceData;
- if( pahsc == NULL ) return -1;
- /* Are we nested?! */
- if( pahsc->pahsc_IfInsideCallback ) return 0;
- pahsc->pahsc_IfInsideCallback = 1;
- /* Get pointer to buffer to fill. */
- outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr;
- /* Combine with any sound input, and call user callback. */
- result = PaMac_CallUserLoop( past, (int16 *) outPtr );
-
- pahsc->pahsc_IfInsideCallback = 0;
- return result;
-}
-
-/*************************************************************************************
-** Called by SoundManager when ready for another buffer.
-*/
-static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd)
-{
- internalPortAudioStream *past;
- PaHostSoundControl *pahsc;
- (void) theChannel;
- (void) theCallBackCmd;
-
- /* Get our data from Mac structure. */
- past = (internalPortAudioStream *) theCallBackCmd->param2;
- if( past == NULL ) return;
-
- pahsc = (PaHostSoundControl *) past->past_DeviceData;
- pahsc->pahsc_NumOutsPlayed += 1;
-
- SetFramesDone( pahsc,
- pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer );
-
- PaMac_BackgroundManager( past, theCallBackCmd->param1 );
-}
-
-/*******************************************************************/
-static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index )
-{
- PaError result = paNoError;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
- /* Has someone asked us to abort by calling Pa_AbortStream()? */
- if( past->past_StopNow )
- {
- SndCommand command;
- /* Clear the queue of any pending commands. */
- command.cmd = flushCmd;
- command.param1 = command.param2 = 0;
- SndDoImmediate( pahsc->pahsc_Channel, &command );
- /* Then stop currently playing buffer, if any. */
- command.cmd = quietCmd;
- SndDoImmediate( pahsc->pahsc_Channel, &command );
- past->past_IsActive = 0;
- }
- /* Has someone asked us to stop by calling Pa_StopStream()
- * OR has a user callback returned '1' to indicate finished.
- */
- else if( past->past_StopSoon )
- {
- if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 )
- {
- past->past_IsActive = 0; /* We're finally done. */
- }
- }
- else
- {
- PaMac_PlayNext( past, index );
- }
- return result;
-}
-
-/*************************************************************************************
-** Fill next buffer with sound and queue it for playback.
-*/
-static void PaMac_PlayNext ( internalPortAudioStream *past, int index )
-{
- OSErr error;
- long result;
- SndCommand playCmd;
- SndCommand callbackCmd;
- PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
-
- /* If this was the last buffer, or abort requested, then just be done. */
- if ( past->past_StopSoon ) goto done;
-
- /* Load buffer with sound. */
- result = PaMac_FillNextOutputBuffer ( past, index );
- if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */
- else if( result < 0 ) goto done;
-
- /* Play the next buffer. */
- playCmd.cmd = bufferCmd;
- playCmd.param1 = 0;
- playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ];
- error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true );
- if( error != noErr ) goto gotError;
-
- /* Ask for a callback when it is done. */
- callbackCmd.cmd = callBackCmd;
- callbackCmd.param1 = index;
- callbackCmd.param2 = (long)past;
- error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true );
- if( error != noErr ) goto gotError;
- pahsc->pahsc_NumOutsQueued += 1;
-
- return;
-
-gotError:
- sPaHostError = error;
-done:
- return;
-}