📄 maccoreaudio.cxx
字号:
PTRACE(2, __func__ << " requested " << *ioNumberDataPackets << " packets, " << " fetching " << minPackets << " packets"); if(outBytes > This->converter_buffer_size){ PTRACE(1, This->direction << " Converter buffer too small"); // doesn't matter converter will ask right again for remaining data // converter buffer multiple of packet size outBytes = This->converter_buffer_size; } // dequeue data from circular buffer, without locking(false) outBytes = circBuf->Drain(This->converter_buffer, outBytes, false); UInt32 reqBytes = *ioNumberDataPackets * pwlibASBD.mBytesPerPacket; if(outBytes < reqBytes && outBytes < This->converter_buffer_size) { reqBytes = MIN(reqBytes, This->converter_buffer_size); PTRACE(1, "Buffer underrun, filling up with silence " << (reqBytes - outBytes) << " bytes "); bzero(This->converter_buffer + outBytes, reqBytes - outBytes ); outBytes = reqBytes; } // fill structure that gets returned to converter ioData->mBuffers[0].mData = (char*)This->converter_buffer; ioData->mBuffers[0].mDataByteSize = outBytes; *ioNumberDataPackets = outBytes / pwlibASBD.mBytesPerPacket; 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(1, __func__ << ", frames " << inNumberFrames); err = AudioConverterFillComplexBuffer(This->converter, PSoundChannelCoreAudio::ComplexBufferFillPlayback, This, &inNumberFrames, // should be packets ioData, NULL /*outPacketDescription*/); checkStatus(err); // make fake stereo from mono //ioData->mBuffers[1] = ioData->mBuffers[0]; /* 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(1, __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(1, __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 input device if device is unknown if(in == 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. // 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. * */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -