📄 pa_lib.c
字号:
/*
* $Id: pa_lib.c,v 1.2 2006/06/27 10:15:03 sumomo Exp $
* Portable Audio I/O Library
* Host Independant Layer
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2000 Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
/* Modification History:
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
PLB20010820 - fix dither and shift for recording PaUInt8 format
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifdef _WIN32
#ifndef __MWERKS__
#include <memory.h>
#endif /* __MWERKS__ */
#else /* !_WIN32 */
#include <memory.h>
#endif /* _WIN32 */
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
/* The reason we might NOT want to validate the rate before opening the stream
* is because many DirectSound drivers lie about the rates they actually support.
*/
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
/*
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
*/
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
/***********************************************************************/
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
{
double err, minErr = allowableError;
int i, bestFit = -1;
for( i=0; i<numRates; i++ )
{
err = fabs( frameRate - rateTable[i] );
if( err < minErr )
{
minErr = err;
bestFit = i;
}
}
return bestFit;
}
/**************************************************************************
** Make sure sample rate is legal and also convert to enumeration for driver.
*/
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
double *closestFrameRatePtr )
{
long bestRateIndex;
const PaDeviceInfo *pdi;
pdi = Pa_GetDeviceInfo( id );
if( pdi == NULL )
{
return paInvalidDeviceId;
}
if( pdi->numSampleRates == -1 )
{
/* Is it out of range? */
if( (requestedFrameRate < pdi->sampleRates[0]) ||
(requestedFrameRate > pdi->sampleRates[1]) )
{
return paInvalidSampleRate;
}
*closestFrameRatePtr = requestedFrameRate;
}
else
{
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
if( bestRateIndex < 0 ) return paInvalidSampleRate;
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
}
return paNoError;
}
/*************************************************************************/
PaError Pa_OpenStream(
PortAudioStream** streamPtrPtr,
PaDeviceID inputDeviceID,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDeviceID,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
unsigned long streamFlags,
PortAudioCallback *callback,
void *userData )
{
internalPortAudioStream *past = NULL;
PaError result = paNoError;
int bitsPerInputSample;
int bitsPerOutputSample;
/* Print passed parameters. */
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
streamPtrPtr, inputDeviceID, numInputChannels,
inputSampleFormat, inputDriverInfo ));
DBUG((" %d, %d, %d, %p, /* output */\n",
outputDeviceID, numOutputChannels,
outputSampleFormat, outputDriverInfo ));
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
sampleRate, framesPerBuffer, numberOfBuffers,
streamFlags, userData ));
/* Check for parameter errors. */
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
if( streamPtrPtr == NULL ) return paBadStreamPtr;
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
{
return paInvalidDeviceId;
}
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
#if SUPPORT_AUDIO_CAPTURE
if( inputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( inputSampleFormat );
if( size < 0 ) return size;
bitsPerInputSample = 8 * size;
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
}
#else
if( inputDeviceID >= 0 )
{
return paInvalidChannelCount;
}
#endif /* SUPPORT_AUDIO_CAPTURE */
else
{
if( numInputChannels > 0 ) return paInvalidChannelCount;
bitsPerInputSample = 0;
}
if( outputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( outputSampleFormat );
if( size < 0 ) return size;
bitsPerOutputSample = 8 * size;
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
}
else
{
if( numOutputChannels > 0 ) return paInvalidChannelCount;
bitsPerOutputSample = 0;
}
if( callback == NULL ) return paNullCallback;
/* Allocate and clear stream structure. */
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
if( past == NULL ) return paInsufficientMemory;
memset( past, 0, sizeof(internalPortAudioStream) );
AddTraceMessage("Pa_OpenStream: past", (long) past );
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
past->past_FramesPerUserBuffer = framesPerBuffer;
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() MUST CHECK FOR ZERO! */
past->past_Callback = callback;
past->past_UserData = userData;
past->past_OutputSampleFormat = outputSampleFormat;
past->past_InputSampleFormat = inputSampleFormat;
past->past_OutputDeviceID = outputDeviceID;
past->past_InputDeviceID = inputDeviceID;
past->past_NumInputChannels = numInputChannels;
past->past_NumOutputChannels = numOutputChannels;
past->past_Flags = streamFlags;
/* Check for absurd sample rates. */
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
{
result = paInvalidSampleRate;
goto cleanup;
}
/* Allocate buffers that may be used for format conversion from user to native buffers. */
if( numInputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
/* Allocate single Input buffer for passing formatted samples to user callback. */
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
if( past->past_InputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_InputBuffer = NULL;
}
/* Allocate single Output buffer. */
if( numOutputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
if( past->past_OutputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_OutputBuffer = NULL;
}
result = PaHost_OpenStream( past );
if( result < 0 ) goto cleanup;
*streamPtrPtr = (void *) past;
return result;
cleanup:
if( past != NULL ) Pa_CloseStream( past );
*streamPtrPtr = NULL;
return result;
}
/*************************************************************************/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData )
{
return Pa_OpenStream(
stream,
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
numInputChannels, sampleFormat, NULL,
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
numOutputChannels, sampleFormat, NULL,
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
}
/*************************************************************************/
PaError Pa_CloseStream( PortAudioStream* stream)
{
PaError result;
internalPortAudioStream *past;
DBUG(("Pa_CloseStream()\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
Pa_AbortStream( past );
result = PaHost_CloseStream( past );
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
return result;
}
/*************************************************************************/
PaError Pa_StartStream( PortAudioStream *stream )
{
PaError result = paHostError;
internalPortAudioStream *past;
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
past->past_FrameCount = 0.0;
if( past->past_NumInputChannels > 0 )
{
result = PaHost_StartInput( past );
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
if( past->past_NumOutputChannels > 0 )
{
result = PaHost_StartOutput( past );
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
result = PaHost_StartEngine( past );
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
if( result < 0 ) goto error;
return paNoError;
error:
return result;
}
/*************************************************************************/
PaError Pa_StopStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 0 );
}
/*************************************************************************/
PaError Pa_AbortStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 1 );
}
/*************************************************************************/
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
{
PaError result = paNoError;
internalPortAudioStream *past;
DBUG(("Pa_StopStream().\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
{
result = PaHost_StopEngine( past, abort );
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -