?? pa_mac.c
字號:
/* * $Id: pa_mac.c,v 1.7 2003/03/02 08:01:36 dmazzoni 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 from hanging when CPU load approaches 100%. PLB20020424 - Fixed return value in Pa_StreamTime PLB20020612 - Fix allocation error on Mac 8600 by casting *nameH as uchar* so that we get a proper Str255 length.*//*COMPATIBILITYThis Macintosh implementation is designed for use with Mac OS 7, 8 and9 on PowerMacs, and OS X if compiled with CARBON OUTPUTA circular array of CmpSoundHeaders is used as a queue. For low latency situationsthere will only be two small buffers used. For higher latency, more and larger buffersmay be used.To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followedby a callbackCmd which informs us when the buffer has been processsed. INPUTThe SndInput Manager SPBRecord call is used for sound input. If onlyinput 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 theHostBuffer output completion proc. In that case, input sound is passed to thecallback 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);static void CToPString(char *inString, unsigned 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;}/*************************************************************************** String Utility by Dominic Mazzoni*/static void CToPString(char* inString, unsigned char* outString){ long len = strlen(inString); long i; if (len > 255) len = 255; /* Length is stored in first char of Pascal string */ outString[0] = (unsigned char)len; for(i=0; i<len; i++) outString[i+1] = inString[i];}/*************************************************************************/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();
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -