⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pa_mac.c

📁 ppciaxclient softphone
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * $Id: pa_mac.c,v 1.4.4.2 2002/10/15 03:14:08 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.
*/

/*
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);
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 + -