📄 maccoreaudio.cxx
字号:
/* * maccoreaudio.cxx * * Copyright (c) 2004 Network for Educational Technology ETH * * Written by Hannes Friederich, Andreas Fenkart. * Based on work of Shawn Pai-Hsiang Hsiao * * The contents of this file are subject to the Mozilla Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * */ #pragma implementation "maccoreaudio.h" #include <ptlib/unix/ptlib/maccoreaudio.h> namespace PWLibStupidOSXHacks{ int loadCoreAudioStuff;}PCREATE_SOUND_PLUGIN(CoreAudio, PSoundChannelCoreAudio);/************** util *******************/#ifdef PTRACING// should also produce output when ptracing is disabled#define checkStatus( err ) \ if(err) {\ OSStatus error = static_cast<OSStatus>(err);\ PTRACE(1, "CoreAudio Error " << __func__ << " " \ << error << "(" << (char*)&err << ")" ); \ } ostream& operator<<(ostream &os, AudioStreamBasicDescription &inDesc){ os << "- - - - - - - - - - - - - - - - - - - -\n"; os << " Sample Rate: " << inDesc.mSampleRate << endl; os << " Format ID: " << (char*)&inDesc.mFormatID << endl; os << " Format Flags: " << hex << inDesc.mFormatFlags << dec << endl; os << " Bytes per Packet: " << inDesc.mBytesPerPacket << endl; os << " Frames per Packet: " << inDesc.mFramesPerPacket << endl; os << " Bytes per Frame: " << inDesc.mBytesPerFrame << endl; os << " Channels per Frame: " << inDesc.mChannelsPerFrame << endl; os << " Bits per Channel: " << inDesc.mBitsPerChannel << endl; os << "- - - - - - - - - - - - - - - - - - - -\n"; return os;}ostream& operator<<(ostream &os, PSoundChannel::Directions &dir){ if(dir == PSoundChannel::Player) os << " Player "; else if(dir == PSoundChannel::Recorder) os << " Recorder "; else os << " Unknown direction "; return os;}ostream& operator<<(ostream &os, AudioValueRange range){ os << range.mMinimum << " " << range.mMaximum ; return os;}ostream& operator<<(ostream &os, PSoundChannelCoreAudio::State &state){ switch(state){ case PSoundChannelCoreAudio::init_: os << "init"; break; case PSoundChannelCoreAudio::open_: os << "open"; break; case PSoundChannelCoreAudio::setformat_: os << "setformat"; break; case PSoundChannelCoreAudio::setbuffer_: os << "setbuffer"; break; case PSoundChannelCoreAudio::running_: os << "running"; break; case PSoundChannelCoreAudio::destroy_: os << "destroy"; break; default: os << " Unknown state "; } return os;}#endif/***** PSound implementation *****/PSound::PSound(unsigned channels, unsigned sampleRate, unsigned bitsPerSample, PINDEX bufferSize, const BYTE *data){ PAssert(0, PUnimplementedFunction); }PSound::PSound(const PFilePath & filename){ PAssert(0, PUnimplementedFunction); }PSound & PSound::operator=(const PBYTEArray & data){ PAssert(0, PUnimplementedFunction); return *this;}BOOL PSound::Load(const PFilePath & filename){ PAssert(0, PUnimplementedFunction); return false;}BOOL PSound::Save(const PFilePath & filename){ PAssert(0, PUnimplementedFunction); return false;}BOOL PSound::Play(){ PAssert(0, PUnimplementedFunction); return false;}void PSound::SetFormat(unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample){ this->numChannels = numChannels; this->sampleRate = sampleRate; this->sampleSize = bitsPerSample; formatInfo.SetSize(0);}BOOL PSound::PlayFile(const PFilePath & file, BOOL wait){ PAssert(0, PUnimplementedFunction); return false;}void PSound::Beep(){ PAssert(0, PUnimplementedFunction); }#include "maccoreaudio/circular_buffer.inl"/***** PSoundChannel implementation *****/void PSoundChannelCoreAudio::Init(){ PWLIB_StaticLoader_CoreAudio_PSoundChannel();}PSoundChannelCoreAudio::PSoundChannelCoreAudio() : state(init_), mCircularBuffer(NULL), converter_buffer(NULL), mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL){ CommonConstruct();}PSoundChannelCoreAudio::PSoundChannelCoreAudio(const PString & device, Directions dir, unsigned numChannels, unsigned sampleRate, unsigned bitsPerSample) : mCircularBuffer(NULL), converter_buffer(NULL), mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL){ CommonConstruct(); Open(device, dir, numChannels, sampleRate, bitsPerSample);}PSoundChannelCoreAudio::~PSoundChannelCoreAudio(){ OSStatus err = noErr; PTRACE(2, __func__ << " destructor " << direction << " " << state ); State curr(state); state = destroy_; usleep(1000*20); // about the intervall between two callbacks // ensures that current callback has terminated /* OutputUnit also for input device, * Stop everything before deallocating buffers */ switch(curr) { case running_: err = AudioOutputUnitStop(mAudioUnit); checkStatus(err); PTRACE(1, direction << " AudioUnit stopped" ); usleep(1000*20); // ensure that all callbacks terminated /* fall through */ case setbuffer_: /* check for all buffers unconditionally */ err = AudioUnitUninitialize(mAudioUnit); checkStatus(err); /* fall through */ case setformat_: err = AudioConverterDispose(converter); checkStatus(err); /* fall through */ case open_: err = CloseComponent(mAudioUnit); checkStatus(err); /* fall through */ case init_: case destroy_: /* nop */; } /* now free all buffers */ if(this->converter_buffer != NULL){ free(this->converter_buffer); this->converter_buffer = NULL; } if(this->mCircularBuffer != NULL){ delete this->mCircularBuffer; this->mCircularBuffer = NULL; } if(this->mInputCircularBuffer !=NULL) { delete this->mInputCircularBuffer; this->mInputCircularBuffer = NULL; } if(this->mInputBufferList != NULL){ free(this->mInputBufferList); this->mInputBufferList = NULL; } if(this->mOutputBufferList != NULL){ free(this->mOutputBufferList); this->mOutputBufferList = NULL; } // tell PChannel::IsOpen() that the channel is closed. os_handle = -1; }unsigned PSoundChannelCoreAudio::GetChannels() const{ if(state >= setformat_) return pwlibASBD.mChannelsPerFrame; else return 0;}unsigned PSoundChannelCoreAudio::GetSampleRate() const{ if(state >= setformat_) return (unsigned)(pwlibASBD.mSampleRate); else return 0;}unsigned PSoundChannelCoreAudio::GetSampleSize() const{ if(state >= setformat_) return (pwlibASBD.mBitsPerChannel); else return 0;}/* * Functions for retrieving AudioDevice list */#include "maccoreaudio/maccoreaudio_devices.inl"PString PSoundChannelCoreAudio::GetDefaultDevice(Directions dir){ OSStatus err = noErr; UInt32 theSize; AudioDeviceID theID; theSize = sizeof(AudioDeviceID); if (dir == Player) { err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &theSize, &theID); } else { err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &theSize, &theID); } if (err == 0) { return CADeviceName(theID); } else { return CA_DUMMY_DEVICE_NAME; }}PStringList PSoundChannelCoreAudio::GetDeviceNames(Directions dir){ PStringList devices; int numDevices; AudioDeviceID *deviceList; bool isInput = (dir == Recorder); numDevices = CADeviceList(&deviceList); for (int i = 0; i < numDevices; i++) { PString s = CADeviceName(deviceList[i]); if (CADeviceSupportDirection(deviceList[i], isInput) > 0) { devices.AppendString(s); } } devices.AppendString(CA_DUMMY_DEVICE_NAME); if(deviceList != NULL) { free(deviceList); deviceList = NULL; } return devices;}/* * Functions responsible for converting 8kHz to 44k1Hz. It works like this. * The AudioHardware is abstracted by an AudioUnit(AUHAL). Each time the device * has more data available or needs new data a callback function is called. * These are PlayRenderProc and RecordProc. * * The user data is stored in a format a set by ::SetFormat. Usually 8kHz, mono, * 16bit unsigned. The AudioUnit usually provides 44,1kHz, stereo, 32bit float. * So conversion is needed. The conversion is done by the AUConverter from * the AudioToolbox framework. * * Currently inside the callback functions from the AUHAL, we pass the request * to the Converter, that in turn uses another callback function to grab some * of data in the input format. All this is done on-the-fly, which means inside * the thread managing the AudioHardware. The callback functions of the * converter are ComplexBufferFillPlayback, ComplexbufferFillRecord. * * The problem we have that 44,1kHz is not a multiple of 8kHz, so we can never * be sure about how many data the converter is going to ask exactly, * sometimes it might be 102, 106.. This is especially true in case of the * first request, where it might ask some additional data depending on * PrimeMethod that garantues smoth resampling at the border. * * To summarize, when the AudioUnit device is ready to handle more data. It * calls its callback function, within these functions the data is processed or * prepared by pulling through AUConverter. The converter in turn calls its * callback function to request more input data. Depending on whether we talk * about Read or Write, this includes more or less complex buffering. *//* * Callback function called by the converter to request more data for * playback. * * outDataPacketDesc is unused, because all our packets have the same * format and do not need individual description */OSStatus PSoundChannelCoreAudio::ComplexBufferFillPlayback( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDesc, void *inUserData){ OSStatus err = noErr; PSoundChannelCoreAudio *This = static_cast<PSoundChannelCoreAudio*>(inUserData); AudioStreamBasicDescription pwlibASBD = This->pwlibASBD; CircularBuffer* circBuf = This->mCircularBuffer; // output might stop in case there is a complete buffer underrun!!! UInt32 minPackets = MIN(*ioNumberDataPackets, circBuf->size() / pwlibASBD.mBytesPerPacket ); UInt32 outBytes = minPackets* pwlibASBD.mBytesPerPacket;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -