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

📄 pa_mac_core.c

📁 一个任天堂掌上游戏机NDS的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 * $Id: pa_mac_core.c,v 1.8.4.12 2003/04/16 19:06:01 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-2002 Ross Bencina and Phil Burk
 *
 * Theory of Operation
 *
 * This code uses the HAL (Hardware Access Layer) of the Apple CoreAudio library.
 * This is the layer closes to the hardware.
 * The HAL layer only supports the native HW supported sample rates.
 * So if the chip only supports 44100 Hz, then the HAL only supports 44100.
 * To provide other rates we use the handy Apple AudioConverter which provides
 * sample rate conversion, mono-to-stereo conversion, and buffer size adaptation.
 *
 * There are four modes of operation:
 *    PA_MODE_OUTPUT_ONLY,
 *    PA_MODE_INPUT_ONLY,
 *    PA_MODE_IO_ONE_DEVICE,
 *    PA_MODE_IO_TWO_DEVICES
 *    
 * The processing pipeline for PA_MODE_IO_ONE_DEVICE is in one thread:
 *
 * PaOSX_CoreAudioIOCallback() input buffers -> RingBuffer -> input.AudioConverter ->
 *    PortAudio callback -> output.AudioConverter -> PaOSX_CoreAudioIOCallback() output buffers
 *
 * For two separate devices, we have to use two separate callbacks.
 * We pass data between them using a RingBuffer FIFO.
 * The processing pipeline for PA_MODE_IO_TWO_DEVICES is split into two threads:
 *
 * PaOSX_CoreAudioInputCallback() input buffers -> RingBuffer
 *
 * RingBuffer -> input.AudioConverter ->
 *    PortAudio callback -> output.AudioConverter -> PaOSX_CoreAudioIOCallback() output buffers
 *
 * License
 *
 * 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. [Supported 10.12.2002]
            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_LATENCY_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.
 10.12.2002 - Phil Burk - Use AudioConverter to allow wide range of sample rates, and mono.
              Use FIFO (from pablio/rinbuffer.h) so that we can pull data through converter.
              Added PaOSX_FixVolumeScalar() to make iMic audible.
 10.17.2002 - Phil Burk - Support full duplex between two different devices.
              Name internal functions PaOSX_*
              Dumped useless PA_MIN_LATENCY_MSEC environment variable.
              Use kAudioDevicePropertyStreamFormatMatch to determine max channels.
 02.03.2003 - Phil Burk - always use AudioConverters so that we can adapt when format changes.
              Synchronize with device when format changes.
 02.13.2003 - Phil Burk - scan for maxChannels because FormatMatch won't tell us.
 03.05.2003 - Phil Burk and Dominic Mazzoni - interleave and deinterleave multiple
              CoreAudio buffers. Needed for MOTU828 and some other N>2 channel devices.
              See code related to "streamInterleavingBuffer".
 03.06.2003 - Phil Burk and Ryan Francesconi - fixed numChannels query for MOTU828.
              Handle fact that MOTU828 gives you 8 channels even when you ask for 2!
 04.06.2003 - Phil Burk - Combine Dominic Mazzoni's technique of using Configuration to query maxChannels
              with old technique of scanning for mormat.
              Increase channel scan by 1 to handle mono USB microphones.
              Do not merge or split channels in AudioConverter to handle 2+2 channels
              of Quattro which has a format of 2 channels.
 04.07.2003 - Phil Burk - use AudioGetCurrentHostTime instead of getrusage() which can lock threads.
 04.10.2003 - Phil Burk - fixed pointer bug with input deinterleaving loop.
              Detect and ignore NULL inputData and outputData in CodeAudio callback.
              Overlap creation and deletion of AudioConverters to prevent thread death when device rate changes.
 04.16.2003 - Phil Burk - Fixed input channel scrambling when numChannels != 2^N. Caused by alignment
              error when filling RingBuffer with 2^N zero bytes.
*/

#include <CoreServices/CoreServices.h>
#include <CoreAudio/CoreAudio.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/DefaultAudioOutput.h>
#include <AudioToolbox/AudioConverter.h>
#include <CoreAudio/HostTime.h>

#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
#include "ringbuffer.h"

/************************************************* Configuration ********/
#define PA_ENABLE_LOAD_MEASUREMENT  (1)

/************************************************* Constants ********/
#define SET_DEVICE_BUFFER_SIZE   (1)

/* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
#define PA_TRACE_RUN             (0)
#define PA_TRACE_START_STOP      (0)

#define PA_MIN_LATENCY_MSEC      (20) /* FIXME */
#define MIN_TIMEOUT_MSEC         (3000)

#define PRINT(x) { printf x; fflush(stdout); }
#define PRINT_ERR( msg, err ) PRINT(( msg ": error = 0x%0lX = '%s'\n", (err), ErrorToString(err)) )
#define DBUG(x)    /* PRINT(x) */
#define DBUGBACK(x) /* if( sMaxBackgroundErrorMessages-- > 0 ) PRINT(x) */
#define DBUGX(x)

// define value of isInput passed to CoreAudio routines
#define IS_INPUT    (true)
#define IS_OUTPUT   (false)

typedef enum PaDeviceMode
{
    PA_MODE_OUTPUT_ONLY,
    PA_MODE_INPUT_ONLY,
    PA_MODE_IO_ONE_DEVICE,
    PA_MODE_IO_TWO_DEVICES
} PaDeviceMode;

#define PA_USING_OUTPUT   (pahsc->mode != PA_MODE_INPUT_ONLY)
#define PA_USING_INPUT    (pahsc->mode != PA_MODE_OUTPUT_ONLY)

/**************************************************************
 * Information needed by PortAudio specific to a CoreAudio device.
 */
typedef struct PaHostInOut_s
{
    AudioDeviceID      audioDeviceID; /* CoreAudio specific ID */
    int                bytesPerUserNativeBuffer; /* User buffer size in native host format. Depends on numChannels. */
    AudioConverterRef  converter;
    void              *converterBuffer;
    int                numChannels;
    /** Used for interleaving or de-interleaving multiple streams for devices like MOTU828. */
    int                streamInterleavingBufferLen; /**< size in bytes */
    Float32           *streamInterleavingBuffer; 
} PaHostInOut;

/**************************************************************
 * Structure for internal host specific stream data.
 * This is allocated on a per stream basis.
 */
typedef struct PaHostSoundControl
{
    PaHostInOut        input;
    PaHostInOut        output;
    AudioDeviceID      primaryDeviceID;
    PaDeviceMode       mode;
    RingBuffer         ringBuffer;
    char              *ringBufferData;
    Boolean            formatListenerCalled;
    /* For measuring CPU utilization. */
    UInt64             entryTime;
    double             inverseHostTicksPerBuffer; /* 1/Ticks of real-time audio per user buffer. */
} PaHostSoundControl;

/**************************************************************
 * Structure for internal extended device info query.
 * 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 int sNumCoreDevices = 0;
static AudioDeviceID *sCoreDeviceIDs;   // Array of Core AudioDeviceIDs
static PaHostDeviceInfo *sDeviceInfos = NULL;
static int sDefaultInputDeviceID = paNoDevice;
static int sDefaultOutputDeviceID = paNoDevice;
static int sSavedHostError = 0;

static const double supportedSampleRateRange[] = { 8000.0, 96000.0 }; /* FIXME - go to double HW rate. */
static const char sMapperSuffixInput[] = " - Input";
static const char sMapperSuffixOutput[] = " - Output";

/* Debug support. */
//static int sMaxBackgroundErrorMessages = 100;
//static int sCoverageCounter = 1; // used to check code coverage during validation

/* 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 PaOSX_QueryDevices( void );
static int PaOSX_ScanDevices( Boolean isInput );
static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput );
static PaDeviceID PaOSX_QueryDefaultInputDevice( void );
static PaDeviceID PaOSX_QueryDefaultOutputDevice( void );
static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past );

static OSStatus PAOSX_DevicePropertyListener (AudioDeviceID					inDevice,
								UInt32							inChannel,
								Boolean							isInput,
								AudioDevicePropertyID			inPropertyID,
								void*							inClientData);
                                
/**********************************************************************/
/* OS X errors are 4 character ID that can be printed.
 * Note that uses a static pad so result must be printed immediately.
 */
static OSStatus statusText[2] = { 0, 0 };
static const char *ErrorToString( OSStatus err )
{
    const char *str;

    switch (err)
    {
    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:
        statusText[0] = err;
    	str = (const char *)statusText;
        break;
    }

    return str;
}

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

/********************************* 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. */
    pahsc->entryTime = AudioGetCurrentHostTime();
}

/******************************************************************************
** Measure fractional CPU load based on real-time it took to calculate
** buffers worth of output.
*/
static void Pa_EndUsageCalculation( internalPortAudioStream   *past )
{
    UInt64   exitTime;
    UInt64   ticksElapsed;
    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;
    
    exitTime = AudioGetCurrentHostTime();
    
    ticksElapsed = exitTime - pahsc->entryTime;

    /* Use inverse because it is faster than the divide. */
	newUsage =  ticksElapsed * pahsc->inverseHostTicksPerBuffer;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -