📄 maccoreaudio.cxx
字号:
return err;
}
/*
* CoreAudio Player callback function
*/
OSStatus PSoundChannelCoreAudio::PlayRenderProc(
void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const struct AudioTimeStamp* TimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
struct AudioBufferList* ioData)
{
OSStatus err = noErr;
PSoundChannelCoreAudio *This =
static_cast<PSoundChannelCoreAudio *>(inRefCon);
if( This->state != running_ || This->mCircularBuffer->Empty() ) {
// PTRACE(1, __func__ << " terminating");
return noErr;
}
//PTRACE(4, __func__ << ", frames " << inNumberFrames);
err = AudioConverterFillComplexBuffer(This->converter,
PSoundChannelCoreAudio::ComplexBufferFillPlayback,
This,
&inNumberFrames, // should be packets
ioData,
NULL /*outPacketDescription*/);
checkStatus(err);
/* now that cpu intensive work is done, make stereo from mono
* assume non-interleaved ==> 1 buffer per channel */
UInt32 len = ioData->mBuffers[0].mDataByteSize;
if(len > 0 && This->state == running_){
unsigned i = 1;
while(i < ioData->mNumberBuffers) {
memcpy(ioData->mBuffers[i].mData, ioData->mBuffers[0].mData, len);
ioData->mBuffers[i].mDataByteSize = len;
i++;
}
}
return err;
}
OSStatus PSoundChannelCoreAudio::RecordProc(
void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
//PTRACE(2, __func__ << ", frames " << inNumberFrames );
OSStatus err = noErr;
PSoundChannelCoreAudio *This =
static_cast<PSoundChannelCoreAudio *>(inRefCon);
CircularBuffer* inCircBuf = This->mInputCircularBuffer;
AudioStreamBasicDescription asbd = This->hwASBD;
if(This->state != running_){
return noErr;
}
if( This->mRecordInputBufferSize < inNumberFrames * asbd.mFramesPerPacket){
PTRACE(1, "Allocated ABL RecordBuffer is too small ");
inNumberFrames = This->mRecordInputBufferSize / asbd.mFramesPerPacket;
}
/* fetch the data from the microphone or other input device */
AudioBufferList* inputData = This->mInputBufferList;
err= AudioUnitRender(This->mAudioUnit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames, //# of frames requested
inputData);// Audio Buffer List to hold data
checkStatus(err);
/* in any case reduce to mono by taking only the first buffer */
AudioBuffer *audio_buf = &inputData->mBuffers[0];
inCircBuf->Fill((char *)audio_buf->mData, audio_buf->mDataByteSize,
false, true); // do not wait, overwrite oldest frames
/*
* Sample Rate Conversion(SRC)
*/
unsigned int frames = inCircBuf->size() / This->hwASBD.mBytesPerFrame;
/* given the number of Microphone frames how many 8kHz frames are
* to expect, keeping a minimum buffer fill of MIN_INPUT_FILL frames to
* have some data handy in case the converter requests more Data */
if(frames > MIN_INPUT_FILL){
UInt32 pullFrames = int(float(frames-MIN_INPUT_FILL)/This->rateTimes8kHz);
UInt32 pullBytes = MIN( This->converter_buffer_size,
pullFrames * This->pwlibASBD.mBytesPerFrame);
UInt32 pullPackets = pullBytes / This->pwlibASBD.mBytesPerPacket;
PTRACE(5, __func__ << " going to pull " << pullPackets << " packets");
/* now pull the frames through the converter */
AudioBufferList* outputData = This->mOutputBufferList;
err = AudioConverterFillComplexBuffer(This->converter,
PSoundChannelCoreAudio::ComplexBufferFillRecord,
This,
&pullPackets,
outputData,
NULL /*outPacketDescription*/);
checkStatus(err);
/* put the converted data into the main CircularBuffer for later
* fetching by the public Read function */
audio_buf = &outputData->mBuffers[0];
This->mCircularBuffer->Fill((char*)audio_buf->mData,
audio_buf->mDataByteSize,
false, true); // do not wait, overwrite oldest frames
}
return err;
}
/**
* Callback function called by the converter to fetch more date
*/
OSStatus PSoundChannelCoreAudio::ComplexBufferFillRecord(
AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDesc,
void *inUserData)
{
OSStatus err = noErr;
PSoundChannelCoreAudio *This =
static_cast<PSoundChannelCoreAudio *>(inUserData);
CircularBuffer* inCircBuf = This->mInputCircularBuffer;
AudioStreamBasicDescription& hwASBD = This->hwASBD;
// make sure it's always a multiple of packets
UInt32 minPackets = MIN(*ioNumberDataPackets,
inCircBuf->size() / hwASBD.mBytesPerPacket );
UInt32 ioBytes = minPackets * hwASBD.mBytesPerPacket;
//PTRACE(5, __func__ << " " << *ioNumberDataPackets << " requested "
// << " fetching " << minPackets << " packets");
if(ioBytes > This->converter_buffer_size){
PTRACE(1, "converter_buffer too small " << ioBytes << " requested "
<< " but only " << This->converter_buffer_size << " fit in");
ioBytes = This->converter_buffer_size;
}
ioBytes = inCircBuf->Drain((char*)This->converter_buffer, ioBytes, false);
if(ioBytes != minPackets * hwASBD.mBytesPerPacket) {
// no more a multiple of packet problably !!!
PTRACE(1, "Failed to fetch the computed number of packets");
}
ioData->mBuffers[0].mData = This->converter_buffer;
ioData->mBuffers[0].mDataByteSize = ioBytes;
// assuming non-interleaved or mono
*ioNumberDataPackets = ioBytes / hwASBD.mBytesPerPacket;
return err;
}
OSStatus PSoundChannelCoreAudio::CallbackSetup(){
OSStatus err = noErr;
AURenderCallbackStruct callback;
callback.inputProcRefCon = this;
if (direction == Recorder) {
callback.inputProc = RecordProc;
/* kAudioOutputUnit stands for both Microphone/Speaker */
err = AudioUnitSetProperty(mAudioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callback,
sizeof(callback));
}
else {
callback.inputProc = PlayRenderProc;
err = AudioUnitSetProperty(mAudioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callback,
sizeof(callback));
}
checkStatus(err);
return err;
}
/********* Function for configuring & initialization of audio units *********/
/**
* Functions to open an AUHAL component and assign it the device indicated
* by deviceID. Conigures the unit for match user desired format as close as
* possible while not assuming special hardware. (able to change sampling rate)
*/
OSStatus PSoundChannelCoreAudio::SetupInputUnit(AudioDeviceID in)
{
OSStatus err = noErr;
Component comp;
ComponentDescription desc;
//There are several different types of Audio Units.
//Some audio units serve as Outputs, Mixers, or DSP
//units. See AUComponent.h for listing
desc.componentType = kAudioUnitType_Output;
//Every Component has a subType, which will give a clearer picture
//of what this components function will be.
desc.componentSubType = kAudioUnitSubType_HALOutput;
//all Audio Units in AUComponent.h must use
//"kAudioUnitManufacturer_Apple" as the Manufacturer
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
//Finds a component that meets the desc spec's
comp = FindNextComponent(NULL, &desc);
if (comp == NULL) return kAudioCodecUnspecifiedError;
//gains access to the services provided by the component
err = OpenAComponent(comp, &mAudioUnit);
checkStatus(err);
err = EnableIO();
checkStatus(err);
err= SetDeviceAsCurrent(in);
checkStatus(err);
return err;
}
/**
* By default all units are configured for output. If we want to use a
* unit for input we must configure it, before assigning the corresponding
* device to it. This to make sure that it asks the device driver for the ASBD
* of the input direction.
*/
OSStatus PSoundChannelCoreAudio::EnableIO()
{
OSStatus err = noErr;
UInt32 enableIO;
///////////////
//ENABLE IO (INPUT)
//You must enable the Audio Unit (AUHAL) for input and disable output
//BEFORE setting the AUHAL's current device.
//Enable input on the AUHAL
enableIO = 1;
err = AudioUnitSetProperty(mAudioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1, // input element
&enableIO,
sizeof(enableIO));
checkStatus(err);
//disable Output on the AUHAL
enableIO = 0;
err = AudioUnitSetProperty(mAudioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
0, //output element
&enableIO,
sizeof(enableIO));
return err;
}
/*
* Functions to open an AUHAL component and assign it the device indicated
* by deviceID. The builtin converter is configured to accept non-interleaved
* data.
*/
OSStatus PSoundChannelCoreAudio::SetupOutputUnit(AudioDeviceID out){
OSStatus err;
//An Audio Unit is a OS component
//The component description must be setup, then used to
//initialize an AudioUnit
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
//desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
//Finds an component that meets the desc spec's
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL) return kAudioCodecUnspecifiedError;
//gains access to the services provided by the component
err = OpenAComponent(comp, &mAudioUnit);
checkStatus(err);
//enableIO not needed, because output is default
err = SetDeviceAsCurrent(out);
return err;
}
OSStatus PSoundChannelCoreAudio::SetDeviceAsCurrent(AudioDeviceID id)
{
UInt32 size = sizeof(AudioDeviceID);
OSStatus err = noErr;
//get the default device if the device id not specified // bogus
if(id == kAudioDeviceUnknown)
{
if(direction == Recorder) {
err = AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultOutputDevice, &size, &id);
} else {
err = AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultInputDevice, &size, &id);
}
checkStatus(err);
}
mDeviceID = id;
// Set the Current Device to the AUHAL.
// this should be done only after IO has been enabled on the AUHAL.
// This means the direction selected, to make sure the ASBD for the proper
// direction is requested
err = AudioUnitSetProperty(mAudioUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&mDeviceID,
sizeof(mDeviceID));
checkStatus(err);
return err;
}
/*
* The major task of Open() is to find the matching device ID.
*
*/
BOOL PSoundChannelCoreAudio::Open(const PString & deviceName,
Directions dir,
unsigned numChannels,
unsigned sampleRate,
unsigned bitsPerSample)
{
OSStatus err;
/* Save whether this is a Player or Recorder */
this->direction = dir;
/*
* Init the AudioUnit and assign it to the requested AudioDevice
*/
if (strcmp(deviceName, CA_DUMMY_DEVICE_NAME) == 0) {
/* Dummy device */
PTRACE(6, "Dummy device " << direction);
mDeviceID = kAudioDeviceUnknown;
} else {
AudioDeviceID deviceID = GetDeviceID(deviceName, direction == Recorder);
if(direction == Player)
err = SetupOutputUnit(deviceID);
else
err = SetupInputUnit(deviceID);
checkStatus(err);
}
/*
* Add a listener to print current volume setting in case it is changed
*/
err = AudioDeviceAddPropertyListener(mDeviceID, 1,
kAudioPropertyWildcardSection, kAudioDevicePropertyVolumeScalar,
VolumeChangePropertyListener, (void *)this);
checkStatus(err);
//os_handle = mDeviceID; // tell PChanne::IsOpen() that the channel is open.
os_handle = 8; // tell PChannel::IsOpen() that the channel is open.
state = open_;
return SetFormat(numChannels, sampleRate, bitsPerSample);
}
/*
* Audio Unit for the Hardware Abstraction Layer(AUHAL) have builtin
* converters. It would be nice if we could configure it to spit out/consume
* the data in the format the data are passed by Read/Write function calls.
*
* Unfortunately this is not possible for the microphone, because this
* converter does not have a buffer inside, so it cannot do any Sample
* Rate Conversion(SRC). We would have to set the device nominal sample
* rate itself to 8kHz. Unfortunately not all microphones can do that,
* so this is not an option. Maybe there will be some change in the future
* by Apple, so we leave it here.
*
* For the output we have the problem that we do not know currently how
* to configure the channel map so that a mono input channel gets copied
* to all output channels, so we still have to do the conversion ourselves
* to copy the result onto all output channels.
*
* Still the builtin converters can be used for something useful, such as
* converting from interleaved -> non-interleaved and to reduce the number of
* bits per sample to save space and time while copying
*/
/*
* Configure the builtin AudioConverter to accept non-interleaved data.
* Turn off SRC by setting the same sample rate at both ends.
* See also general notes above
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -