Fixed DevStudio 2003 build with memory check code.
[pwlib.git] / src / ptlib / unix / maccoreaudio.cxx
blob029f21be69136cf2cf4744d170c45df6e5936b74
1 /*
2 * maccoreaudio.cxx
4 * Copyright (c) 2004 Network for Educational Technology ETH
6 * Written by Hannes Friederich, Andreas Fenkart.
7 * Based on work of Shawn Pai-Hsiang Hsiao
9 * The contents of this file are subject to the Mozilla Public License
10 * Version 1.0 (the "License"); you may not use this file except in
11 * compliance with the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS"
15 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
16 * the License for the specific language governing rights and limitations
17 * under the License.
21 #pragma implementation "maccoreaudio.h"
23 #include <ptlib/unix/ptlib/maccoreaudio.h>
24 #include <iostream> // used for Volume Listener
28 PCREATE_SOUND_PLUGIN(CoreAudio, PSoundChannelCoreAudio);
30 namespace PWLibStupidOSXHacks
32 int loadCoreAudioStuff;
36 /************** util *******************/
39 #ifdef PTRACING
41 // should also produce output when ptracing is disabled
42 #define checkStatus( err ) \
43 if(err) {\
44 OSStatus error = static_cast<OSStatus>(err);\
45 PTRACE(1, "CoreAudio Error " << __func__ << " " \
46 << error << "(" << (char*)&err << ")" ); \
47 error = err; \
50 ostream& operator<<(ostream &os, AudioStreamBasicDescription &inDesc)
52 os << "- - - - - - - - - - - - - - - - - - - -\n";
53 os << " Sample Rate: " << inDesc.mSampleRate << endl;
54 os << " Format ID: " << (char*)&inDesc.mFormatID << endl;
55 os << " Format Flags: " << hex << inDesc.mFormatFlags << dec << endl;
56 os << " Bytes per Packet: " << inDesc.mBytesPerPacket << endl;
57 os << " Frames per Packet: " << inDesc.mFramesPerPacket << endl;
58 os << " Bytes per Frame: " << inDesc.mBytesPerFrame << endl;
59 os << " Channels per Frame: " << inDesc.mChannelsPerFrame << endl;
60 os << " Bits per Channel: " << inDesc.mBitsPerChannel << endl;
61 os << "- - - - - - - - - - - - - - - - - - - -\n";
62 return os;
66 ostream& operator<<(ostream &os, PSoundChannel::Directions &dir)
68 if(dir == PSoundChannel::Player)
69 os << " Player ";
70 else if(dir == PSoundChannel::Recorder)
71 os << " Recorder ";
72 else
73 os << " Unknown direction ";
74 return os;
77 ostream& operator<<(ostream &os, AudioValueRange range)
79 os << range.mMinimum << " " << range.mMaximum ;
80 return os;
84 ostream& operator<<(ostream &os, PSoundChannelCoreAudio::State &state)
86 switch(state){
87 case PSoundChannelCoreAudio::init_:
88 os << "init";
89 break;
90 case PSoundChannelCoreAudio::open_:
91 os << "open";
92 break;
93 case PSoundChannelCoreAudio::setformat_:
94 os << "setformat";
95 break;
96 case PSoundChannelCoreAudio::setbuffer_:
97 os << "setbuffer";
98 break;
99 case PSoundChannelCoreAudio::running_:
100 os << "running";
101 break;
102 case PSoundChannelCoreAudio::destroy_:
103 os << "destroy";
104 break;
105 default:
106 os << " Unknown state ";
108 return os;
111 #else
113 #define checkStatus( err ) \
114 if(err) {\
115 OSStatus error = static_cast<OSStatus>(err);\
116 cout << "CoreAudio Error " << __func__ << " " \
117 << error << "(" << (char*)&err << ")" << endl; \
119 #endif
122 /***** PSound implementation *****/
124 PSound::PSound(unsigned channels,
125 unsigned sampleRate,
126 unsigned bitsPerSample,
127 PINDEX bufferSize,
128 const BYTE *data)
130 PAssert(0, PUnimplementedFunction);
133 PSound::PSound(const PFilePath & filename)
135 PAssert(0, PUnimplementedFunction);
138 PSound & PSound::operator=(const PBYTEArray & data)
140 PAssert(0, PUnimplementedFunction);
141 return *this;
144 BOOL PSound::Load(const PFilePath & filename)
146 PAssert(0, PUnimplementedFunction);
147 return false;
150 BOOL PSound::Save(const PFilePath & filename)
152 PAssert(0, PUnimplementedFunction);
153 return false;
156 BOOL PSound::Play()
158 PAssert(0, PUnimplementedFunction);
159 return false;
162 void PSound::SetFormat(unsigned numChannels,
163 unsigned sampleRate,
164 unsigned bitsPerSample)
166 this->numChannels = numChannels;
167 this->sampleRate = sampleRate;
168 this->sampleSize = bitsPerSample;
169 formatInfo.SetSize(0);
172 BOOL PSound::PlayFile(const PFilePath & file,
173 BOOL wait)
175 PAssert(0, PUnimplementedFunction);
176 return false;
179 void PSound::Beep()
181 PAssert(0, PUnimplementedFunction);
185 #include "maccoreaudio/circular_buffer.inl"
187 /***** PSoundChannel implementation *****/
189 PSoundChannelCoreAudio::PSoundChannelCoreAudio()
190 : state(init_), mCircularBuffer(NULL), converter_buffer(NULL),
191 mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL)
193 CommonConstruct();
197 PSoundChannelCoreAudio::PSoundChannelCoreAudio(const PString & device,
198 Directions dir,
199 unsigned numChannels,
200 unsigned sampleRate,
201 unsigned bitsPerSample)
202 : state(init_), mCircularBuffer(NULL), converter_buffer(NULL),
203 mInputCircularBuffer(NULL), mInputBufferList(NULL), mOutputBufferList(NULL)
205 CommonConstruct();
206 Open(device, dir, numChannels, sampleRate, bitsPerSample);
211 PSoundChannelCoreAudio::~PSoundChannelCoreAudio()
213 OSStatus err = noErr;
214 State curr(state);
215 state = destroy_;
216 usleep(1000*20); // about the intervall between two callbacks
217 // ensures that current callback has terminated
219 /* OutputUnit also for input device,
220 * Stop everything before deallocating buffers */
221 switch(curr) {
222 case running_:
223 err = AudioOutputUnitStop(mAudioUnit);
224 checkStatus(err);
225 PTRACE(1, direction << " AudioUnit stopped" );
226 usleep(1000*20); // ensure that all callbacks terminated
227 /* fall through */
228 case mute_:
229 case setbuffer_:
230 /* check for all buffers unconditionally */
231 err = AudioUnitUninitialize(mAudioUnit);
232 checkStatus(err);
233 /* fall through */
234 case setformat_:
235 err = AudioConverterDispose(converter);
236 checkStatus(err);
237 /* fall through */
238 case open_:
239 err = CloseComponent(mAudioUnit);
240 checkStatus(err);
241 err = AudioDeviceRemovePropertyListener(mDeviceID, 1,
242 kAudioPropertyWildcardSection, kAudioDevicePropertyVolumeScalar,
243 VolumeChangePropertyListener);
244 checkStatus(err);
245 /* fall through */
246 case init_:
247 case destroy_:
248 /* nop */;
251 /* now free all buffers */
252 if(this->converter_buffer != NULL){
253 free(this->converter_buffer);
254 this->converter_buffer = NULL;
256 if(this->mCircularBuffer != NULL){
257 delete this->mCircularBuffer;
258 this->mCircularBuffer = NULL;
260 if(this->mInputCircularBuffer !=NULL) {
261 delete this->mInputCircularBuffer;
262 this->mInputCircularBuffer = NULL;
264 if(this->mInputBufferList != NULL){
265 free(this->mInputBufferList);
266 this->mInputBufferList = NULL;
268 if(this->mOutputBufferList != NULL){
269 free(this->mOutputBufferList);
270 this->mOutputBufferList = NULL;
273 // tell PChannel::IsOpen() that the channel is closed.
274 os_handle = -1;
278 unsigned PSoundChannelCoreAudio::GetChannels() const
280 if(state >= setformat_)
281 return pwlibASBD.mChannelsPerFrame;
282 else
283 return 0;
286 unsigned PSoundChannelCoreAudio::GetSampleRate() const
288 if(state >= setformat_)
289 return (unsigned)(pwlibASBD.mSampleRate);
290 else
291 return 0;
294 unsigned PSoundChannelCoreAudio::GetSampleSize() const
296 if(state >= setformat_)
297 return (pwlibASBD.mBitsPerChannel);
298 else
299 return 0;
304 * Functions for retrieving AudioDevice list
306 #include "maccoreaudio/maccoreaudio_devices.inl"
309 PString PSoundChannelCoreAudio::GetDefaultDevice(Directions dir)
311 OSStatus err = noErr;
312 UInt32 theSize;
313 AudioDeviceID theID;
315 theSize = sizeof(AudioDeviceID);
317 if (dir == Player) {
318 err = AudioHardwareGetProperty(
319 kAudioHardwarePropertyDefaultOutputDevice,
320 &theSize, &theID);
322 else {
323 err = AudioHardwareGetProperty(
324 kAudioHardwarePropertyDefaultInputDevice,
325 &theSize, &theID);
328 if (err == 0) {
329 return CADeviceName(theID);
330 } else {
331 return CA_DUMMY_DEVICE_NAME;
335 PStringList PSoundChannelCoreAudio::GetDeviceNames(Directions dir)
337 PStringList devices;
339 int numDevices;
340 AudioDeviceID *deviceList;
341 bool isInput = (dir == Recorder);
343 numDevices = CADeviceList(&deviceList);
345 for (int i = 0; i < numDevices; i++) {
346 PString s = CADeviceName(deviceList[i]);
347 if (CADeviceSupportDirection(deviceList[i], isInput) > 0) {
348 devices.AppendString(s);
351 devices.AppendString(CA_DUMMY_DEVICE_NAME);
353 if(deviceList != NULL) {
354 free(deviceList);
355 deviceList = NULL;
358 return devices;
362 * Functions responsible for converting 8kHz to 44k1Hz. It works like this.
363 * The AudioHardware is abstracted by an AudioUnit(AUHAL). Each time the device
364 * has more data available or needs new data a callback function is called.
365 * These are PlayRenderProc and RecordProc.
367 * The user data is stored in a format a set by ::SetFormat. Usually 8kHz, mono,
368 * 16bit unsigned. The AudioUnit usually provides 44,1kHz, stereo, 32bit float.
369 * So conversion is needed. The conversion is done by the AUConverter from
370 * the AudioToolbox framework.
372 * Currently inside the callback functions from the AUHAL, we pass the request
373 * to the Converter, that in turn uses another callback function to grab some
374 * of data in the input format. All this is done on-the-fly, which means inside
375 * the thread managing the AudioHardware. The callback functions of the
376 * converter are ComplexBufferFillPlayback, ComplexbufferFillRecord.
378 * The problem we have that 44,1kHz is not a multiple of 8kHz, so we can never
379 * be sure about how many data the converter is going to ask exactly,
380 * sometimes it might be 102, 106.. This is especially true in case of the
381 * first request, where it might ask some additional data depending on
382 * PrimeMethod that garantuees smoth resampling at the border.
384 * To summarize, when the AudioUnit device is ready to handle more data. It
385 * calls its callback function, within these functions the data is processed or
386 * prepared by pulling through AUConverter. The converter in turn calls its
387 * callback function to request more input data. Depending on whether we talk
388 * about Read or Write, this includes more or less complex buffering.
394 * Callback function called by the converter to request more data for
395 * playback.
397 * outDataPacketDesc is unused, because all our packets have the same
398 * format and do not need individual description
400 OSStatus PSoundChannelCoreAudio::ComplexBufferFillPlayback(
401 AudioConverterRef inAudioConverter,
402 UInt32 *ioNumberDataPackets,
403 AudioBufferList *ioData,
404 AudioStreamPacketDescription **outDataPacketDesc,
405 void *inUserData)
407 OSStatus err = noErr;
408 PSoundChannelCoreAudio *This =
409 static_cast<PSoundChannelCoreAudio*>(inUserData);
410 AudioStreamBasicDescription pwlibASBD = This->pwlibASBD;
411 CircularBuffer* circBuf = This->mCircularBuffer;
413 // output might stop in case there is a complete buffer underrun!!!
414 UInt32 minPackets = MIN(*ioNumberDataPackets,
415 circBuf->size() / pwlibASBD.mBytesPerPacket );
416 UInt32 outBytes = minPackets* pwlibASBD.mBytesPerPacket;
418 //PTRACE(2, __func__ << " requested " << *ioNumberDataPackets <<
419 // " packets, " << " fetching " << minPackets << " packets");
421 if(outBytes > This->converter_buffer_size){
422 PTRACE(1, This->direction << " Converter buffer too small");
424 // doesn't matter converter will ask right again for remaining data
425 // converter buffer multiple of packet size
426 outBytes = This->converter_buffer_size;
429 // dequeue data from circular buffer, without locking(false)
430 outBytes = circBuf->Drain(This->converter_buffer, outBytes, false);
432 UInt32 reqBytes = *ioNumberDataPackets * pwlibASBD.mBytesPerPacket;
433 if(outBytes < reqBytes && outBytes < This->converter_buffer_size) {
434 reqBytes = MIN(reqBytes, This->converter_buffer_size);
435 PTRACE(1, "Buffer underrun, filling up with silence "
436 << (reqBytes - outBytes) << " bytes ");
438 bzero(This->converter_buffer + outBytes, reqBytes - outBytes );
439 outBytes = reqBytes;
442 // fill structure that gets returned to converter
443 ioData->mBuffers[0].mData = (char*)This->converter_buffer;
444 ioData->mBuffers[0].mDataByteSize = outBytes;
446 *ioNumberDataPackets = outBytes / pwlibASBD.mBytesPerPacket;
448 return err;
454 * CoreAudio Player callback function
456 OSStatus PSoundChannelCoreAudio::PlayRenderProc(
457 void* inRefCon,
458 AudioUnitRenderActionFlags* ioActionFlags,
459 const struct AudioTimeStamp* TimeStamp,
460 UInt32 inBusNumber,
461 UInt32 inNumberFrames,
462 struct AudioBufferList* ioData)
464 OSStatus err = noErr;
465 PSoundChannelCoreAudio *This =
466 static_cast<PSoundChannelCoreAudio *>(inRefCon);
468 if( This->state != running_ || This->mCircularBuffer->Empty() ) {
469 // PTRACE(1, __func__ << " terminating");
470 return noErr;
473 //PTRACE(4, __func__ << ", frames " << inNumberFrames);
475 err = AudioConverterFillComplexBuffer(This->converter,
476 PSoundChannelCoreAudio::ComplexBufferFillPlayback,
477 This,
478 &inNumberFrames, // should be packets
479 ioData,
480 NULL /*outPacketDescription*/);
481 checkStatus(err);
484 /* now that cpu intensive work is done, make stereo from mono
485 * assume non-interleaved ==> 1 buffer per channel */
486 UInt32 len = ioData->mBuffers[0].mDataByteSize;
487 if(len > 0 && This->state == running_){
488 unsigned i = 1;
489 while(i < ioData->mNumberBuffers) {
490 memcpy(ioData->mBuffers[i].mData, ioData->mBuffers[0].mData, len);
491 ioData->mBuffers[i].mDataByteSize = len;
492 i++;
496 return err;
502 OSStatus PSoundChannelCoreAudio::RecordProc(
503 void* inRefCon,
504 AudioUnitRenderActionFlags* ioActionFlags,
505 const AudioTimeStamp* inTimeStamp,
506 UInt32 inBusNumber,
507 UInt32 inNumberFrames,
508 AudioBufferList * ioData)
510 //PTRACE(2, __func__ << ", frames " << inNumberFrames );
512 OSStatus err = noErr;
513 PSoundChannelCoreAudio *This =
514 static_cast<PSoundChannelCoreAudio *>(inRefCon);
515 CircularBuffer* inCircBuf = This->mInputCircularBuffer;
516 AudioStreamBasicDescription asbd = This->hwASBD;
518 if(This->state != running_){
519 return noErr;
522 if( This->mRecordInputBufferSize < inNumberFrames * asbd.mFramesPerPacket){
523 PTRACE(1, "Allocated ABL RecordBuffer is too small ");
524 inNumberFrames = This->mRecordInputBufferSize / asbd.mFramesPerPacket;
527 /* fetch the data from the microphone or other input device */
528 AudioBufferList* inputData = This->mInputBufferList;
529 err= AudioUnitRender(This->mAudioUnit,
530 ioActionFlags,
531 inTimeStamp,
532 inBusNumber,
533 inNumberFrames, //# of frames requested
534 inputData);// Audio Buffer List to hold data
535 checkStatus(err);
537 /* in any case reduce to mono by taking only the first buffer */
538 AudioBuffer *audio_buf = &inputData->mBuffers[0];
539 inCircBuf->Fill((char *)audio_buf->mData, audio_buf->mDataByteSize,
540 false, true); // do not wait, overwrite oldest frames
543 * Sample Rate Conversion(SRC)
545 unsigned int frames = inCircBuf->size() / This->hwASBD.mBytesPerFrame;
548 /* given the number of Microphone frames how many 8kHz frames are
549 * to expect, keeping a minimum buffer fill of MIN_INPUT_FILL frames to
550 * have some data handy in case the converter requests more Data */
551 if(frames > MIN_INPUT_FILL){
552 UInt32 pullFrames = int(float(frames-MIN_INPUT_FILL)/This->rateTimes8kHz);
553 UInt32 pullBytes = MIN( This->converter_buffer_size,
554 pullFrames * This->pwlibASBD.mBytesPerFrame);
556 UInt32 pullPackets = pullBytes / This->pwlibASBD.mBytesPerPacket;
558 PTRACE(5, __func__ << " going to pull " << pullPackets << " packets");
560 /* now pull the frames through the converter */
561 AudioBufferList* outputData = This->mOutputBufferList;
562 err = AudioConverterFillComplexBuffer(This->converter,
563 PSoundChannelCoreAudio::ComplexBufferFillRecord,
564 This,
565 &pullPackets,
566 outputData,
567 NULL /*outPacketDescription*/);
568 checkStatus(err);
570 /* put the converted data into the main CircularBuffer for later
571 * fetching by the public Read function */
572 audio_buf = &outputData->mBuffers[0];
573 This->mCircularBuffer->Fill((char*)audio_buf->mData,
574 audio_buf->mDataByteSize,
575 false, true); // do not wait, overwrite oldest frames
578 return err;
581 /**
582 * Callback function called by the converter to fetch more date
584 OSStatus PSoundChannelCoreAudio::ComplexBufferFillRecord(
585 AudioConverterRef inAudioConverter,
586 UInt32 *ioNumberDataPackets,
587 AudioBufferList *ioData,
588 AudioStreamPacketDescription **outDataPacketDesc,
589 void *inUserData)
592 OSStatus err = noErr;
593 PSoundChannelCoreAudio *This =
594 static_cast<PSoundChannelCoreAudio *>(inUserData);
595 CircularBuffer* inCircBuf = This->mInputCircularBuffer;
596 AudioStreamBasicDescription& hwASBD = This->hwASBD;
599 // make sure it's always a multiple of packets
600 UInt32 minPackets = MIN(*ioNumberDataPackets,
601 inCircBuf->size() / hwASBD.mBytesPerPacket );
602 UInt32 ioBytes = minPackets * hwASBD.mBytesPerPacket;
605 //PTRACE(5, __func__ << " " << *ioNumberDataPackets << " requested "
606 // << " fetching " << minPackets << " packets");
608 if(ioBytes > This->converter_buffer_size){
609 PTRACE(1, "converter_buffer too small " << ioBytes << " requested "
610 << " but only " << This->converter_buffer_size << " fit in");
611 ioBytes = This->converter_buffer_size;
614 ioBytes = inCircBuf->Drain((char*)This->converter_buffer, ioBytes, false);
616 if(ioBytes != minPackets * hwASBD.mBytesPerPacket) {
617 // no more a multiple of packet problably !!!
618 PTRACE(1, "Failed to fetch the computed number of packets");
621 ioData->mBuffers[0].mData = This->converter_buffer;
622 ioData->mBuffers[0].mDataByteSize = ioBytes;
624 // assuming non-interleaved or mono
625 *ioNumberDataPackets = ioBytes / hwASBD.mBytesPerPacket;
627 return err;
632 OSStatus PSoundChannelCoreAudio::CallbackSetup(){
633 OSStatus err = noErr;
634 AURenderCallbackStruct callback;
636 callback.inputProcRefCon = this;
637 if (direction == Recorder) {
638 callback.inputProc = RecordProc;
639 /* kAudioOutputUnit stands for both Microphone/Speaker */
640 err = AudioUnitSetProperty(mAudioUnit,
641 kAudioOutputUnitProperty_SetInputCallback,
642 kAudioUnitScope_Global,
644 &callback,
645 sizeof(callback));
648 else {
649 callback.inputProc = PlayRenderProc;
650 err = AudioUnitSetProperty(mAudioUnit,
651 kAudioUnitProperty_SetRenderCallback,
652 kAudioUnitScope_Input,
654 &callback,
655 sizeof(callback));
657 checkStatus(err);
658 return err;
662 /********* Function for configuring & initialization of audio units *********/
665 * Functions to open an AUHAL component and assign it the device indicated
666 * by deviceID. Conigures the unit for match user desired format as close as
667 * possible while not assuming special hardware. (able to change sampling rate)
669 OSStatus PSoundChannelCoreAudio::SetupInputUnit(AudioDeviceID in)
671 OSStatus err = noErr;
673 Component comp;
674 ComponentDescription desc;
676 //There are several different types of Audio Units.
677 //Some audio units serve as Outputs, Mixers, or DSP
678 //units. See AUComponent.h for listing
679 desc.componentType = kAudioUnitType_Output;
681 //Every Component has a subType, which will give a clearer picture
682 //of what this components function will be.
683 desc.componentSubType = kAudioUnitSubType_HALOutput;
685 //all Audio Units in AUComponent.h must use
686 //"kAudioUnitManufacturer_Apple" as the Manufacturer
687 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
688 desc.componentFlags = 0;
689 desc.componentFlagsMask = 0;
691 //Finds a component that meets the desc spec's
692 comp = FindNextComponent(NULL, &desc);
693 if (comp == NULL) return kAudioCodecUnspecifiedError;
695 //gains access to the services provided by the component
696 err = OpenAComponent(comp, &mAudioUnit);
697 checkStatus(err);
699 err = EnableIO();
700 checkStatus(err);
702 err= SetDeviceAsCurrent(in);
703 checkStatus(err);
705 return err;
709 * By default all units are configured for output. If we want to use a
710 * unit for input we must configure it, before assigning the corresponding
711 * device to it. This to make sure that it asks the device driver for the ASBD
712 * of the input direction.
714 OSStatus PSoundChannelCoreAudio::EnableIO()
716 OSStatus err = noErr;
717 UInt32 enableIO;
719 ///////////////
720 //ENABLE IO (INPUT)
721 //You must enable the Audio Unit (AUHAL) for input and disable output
722 //BEFORE setting the AUHAL's current device.
724 //Enable input on the AUHAL
725 enableIO = 1;
726 err = AudioUnitSetProperty(mAudioUnit,
727 kAudioOutputUnitProperty_EnableIO,
728 kAudioUnitScope_Input,
729 1, // input element
730 &enableIO,
731 sizeof(enableIO));
732 checkStatus(err);
734 //disable Output on the AUHAL
735 enableIO = 0;
736 err = AudioUnitSetProperty(mAudioUnit,
737 kAudioOutputUnitProperty_EnableIO,
738 kAudioUnitScope_Output,
739 0, //output element
740 &enableIO,
741 sizeof(enableIO));
742 return err;
747 * Functions to open an AUHAL component and assign it the device indicated
748 * by deviceID. The builtin converter is configured to accept non-interleaved
749 * data.
751 OSStatus PSoundChannelCoreAudio::SetupOutputUnit(AudioDeviceID out){
752 OSStatus err;
754 //An Audio Unit is a OS component
755 //The component description must be setup, then used to
756 //initialize an AudioUnit
757 ComponentDescription desc;
759 desc.componentType = kAudioUnitType_Output;
760 desc.componentSubType = kAudioUnitSubType_HALOutput;
761 //desc.componentSubType = kAudioUnitSubType_DefaultOutput;
762 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
763 desc.componentFlags = 0;
764 desc.componentFlagsMask = 0;
766 //Finds an component that meets the desc spec's
767 Component comp = FindNextComponent(NULL, &desc);
768 if (comp == NULL) return kAudioCodecUnspecifiedError;
770 //gains access to the services provided by the component
771 err = OpenAComponent(comp, &mAudioUnit);
772 checkStatus(err);
774 //enableIO not needed, because output is default
776 err = SetDeviceAsCurrent(out);
777 return err;
781 OSStatus PSoundChannelCoreAudio::SetDeviceAsCurrent(AudioDeviceID id)
783 UInt32 size = sizeof(AudioDeviceID);
784 OSStatus err = noErr;
786 //get the default device if the device id not specified // bogus
787 if(id == kAudioDeviceUnknown)
789 if(direction == Recorder) {
790 err = AudioHardwareGetProperty(
791 kAudioHardwarePropertyDefaultOutputDevice, &size, &id);
792 } else {
793 err = AudioHardwareGetProperty(
794 kAudioHardwarePropertyDefaultInputDevice, &size, &id);
796 checkStatus(err);
799 mDeviceID = id;
801 // Set the Current Device to the AUHAL.
802 // this should be done only after IO has been enabled on the AUHAL.
803 // This means the direction selected, to make sure the ASBD for the proper
804 // direction is requested
805 err = AudioUnitSetProperty(mAudioUnit,
806 kAudioOutputUnitProperty_CurrentDevice,
807 kAudioUnitScope_Global,
809 &mDeviceID,
810 sizeof(mDeviceID));
811 checkStatus(err);
813 return err;
819 * The major task of Open() is to find the matching device ID.
822 BOOL PSoundChannelCoreAudio::Open(const PString & deviceName,
823 Directions dir,
824 unsigned numChannels,
825 unsigned sampleRate,
826 unsigned bitsPerSample)
828 OSStatus err;
830 /* Save whether this is a Player or Recorder */
831 this->direction = dir;
834 * Init the AudioUnit and assign it to the requested AudioDevice
836 if (strcmp(deviceName, CA_DUMMY_DEVICE_NAME) == 0) {
837 /* Dummy device */
838 PTRACE(6, "Dummy device " << direction);
839 mDeviceID = kAudioDeviceUnknown;
840 } else {
842 AudioDeviceID deviceID = GetDeviceID(deviceName, direction == Recorder);
843 if(direction == Player)
844 err = SetupOutputUnit(deviceID);
845 else
846 err = SetupInputUnit(deviceID);
847 checkStatus(err);
852 * Add a listener to print current volume setting in case it is changed
854 err = AudioDeviceAddPropertyListener(mDeviceID, 1,
855 kAudioPropertyWildcardSection, kAudioDevicePropertyVolumeScalar,
856 VolumeChangePropertyListener, (void *)this);
857 checkStatus(err);
860 //os_handle = mDeviceID; // tell PChanne::IsOpen() that the channel is open.
861 os_handle = 8; // tell PChannel::IsOpen() that the channel is open.
862 state = open_;
863 return SetFormat(numChannels, sampleRate, bitsPerSample);
867 * Audio Unit for the Hardware Abstraction Layer(AUHAL) have builtin
868 * converters. It would be nice if we could configure it to spit out/consume
869 * the data in the format the data are passed by Read/Write function calls.
871 * Unfortunately this is not possible for the microphone, because this
872 * converter does not have a buffer inside, so it cannot do any Sample
873 * Rate Conversion(SRC). We would have to set the device nominal sample
874 * rate itself to 8kHz. Unfortunately not all microphones can do that,
875 * so this is not an option. Maybe there will be some change in the future
876 * by Apple, so we leave it here.
878 * For the output we have the problem that we do not know currently how
879 * to configure the channel map so that a mono input channel gets copied
880 * to all output channels, so we still have to do the conversion ourselves
881 * to copy the result onto all output channels.
883 * Still the builtin converters can be used for something useful, such as
884 * converting from interleaved -> non-interleaved and to reduce the number of
885 * bits per sample to save space and time while copying
889 * Configure the builtin AudioConverter to accept non-interleaved data.
890 * Turn off SRC by setting the same sample rate at both ends.
891 * See also general notes above
893 OSStatus PSoundChannelCoreAudio::MatchHALOutputFormat()
895 OSStatus err = noErr;
896 //AudioStreamBasicDescription& asbd = hwASBD;
897 UInt32 size = sizeof (AudioStreamBasicDescription);
899 memset(&hwASBD, 0, size);
902 err = AudioDeviceGetProperty(mDeviceID,
903 0, // channel
904 //true, // isInput
905 false, // isInput
906 kAudioDevicePropertyStreamFormat,
907 &size, &hwASBD);
908 checkStatus(err);
911 //Get the current stream format of the output
912 err = AudioUnitGetProperty (mAudioUnit,
913 kAudioUnitProperty_StreamFormat,
914 kAudioUnitScope_Output,
915 0, // output bus
916 &hwASBD,
917 &size);
918 checkStatus(err);
920 // make sure it is non-interleaved
921 BOOL isInterleaved =
922 !(hwASBD.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
924 hwASBD.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
925 if(isInterleaved){
926 // so its only one buffer containing all data, according to
927 // list.apple.com: You only multiply out by mChannelsPerFrame
928 // if you are doing interleaved.
929 hwASBD.mBytesPerPacket /= hwASBD.mChannelsPerFrame;
930 hwASBD.mBytesPerFrame /= hwASBD.mChannelsPerFrame;
933 //Set the stream format of the output to match the input
934 err = AudioUnitSetProperty (mAudioUnit,
935 kAudioUnitProperty_StreamFormat,
936 kAudioUnitScope_Input,
938 &hwASBD,
939 size);
942 // make sure we really know the current format
943 size = sizeof (AudioStreamBasicDescription);
944 err = AudioUnitGetProperty (mAudioUnit,
945 kAudioUnitProperty_StreamFormat,
946 kAudioUnitScope_Input,
947 0, // input bus
948 &hwASBD,
949 &size);
951 return err;
956 * Configure the builtin AudioConverter to provide data in non-interleaved
957 * format. Turn off SRC by setting the same sample rate at both ends.
958 * See also general notes above
960 OSStatus PSoundChannelCoreAudio::MatchHALInputFormat()
962 OSStatus err = noErr;
963 AudioStreamBasicDescription& asbd = hwASBD;
964 UInt32 size = sizeof (AudioStreamBasicDescription);
966 memset(&asbd, 0, size);
969 err = AudioDeviceGetProperty(mDeviceID,
970 0, // channel
971 true, // isInput
972 kAudioDevicePropertyStreamFormat,
973 &size,
974 &asbd);
975 checkStatus(err);
978 /* This code asks for the supported sample rates of the microphone
979 UInt32 count, numRanges;
980 err = AudioDeviceGetPropertyInfo ( mDeviceID,
981 0, true,
982 kAudioDevicePropertyAvailableNominalSampleRates,
983 &count, NULL );
985 numRanges = count / sizeof(AudioValueRange);
986 AudioValueRange* rangeArray = (AudioValueRange*)malloc ( count );
988 err = AudioDeviceGetProperty ( mDeviceID,
989 0, true,
990 kAudioDevicePropertyAvailableNominalSampleRates,
991 &count, (void*)rangeArray );
992 checkStatus(err);
995 //Get the current stream format of the output
996 err = AudioUnitGetProperty (mAudioUnit,
997 kAudioUnitProperty_StreamFormat,
998 kAudioUnitScope_Input,
999 1, // input bus/
1000 &asbd,
1001 &size);
1004 * make it one-channel, non-interleaved, keeping same sample rate
1006 BOOL isInterleaved =
1007 !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
1009 PTRACE_IF(5, isInterleaved, "channels are interleaved ");
1011 // mFormatID -> assume lpcm !!!
1012 asbd.mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
1013 if(isInterleaved){
1014 // so it's only one buffer containing all channels, according to
1015 //list.apple.com: You only multiple out by mChannelsPerFrame
1016 //if you are doing interleaved.
1017 asbd.mBytesPerPacket /= asbd.mChannelsPerFrame;
1018 asbd.mBytesPerFrame /= asbd.mChannelsPerFrame;
1020 asbd.mChannelsPerFrame = 1;
1022 // Set it to output side of input bus
1023 size = sizeof (AudioStreamBasicDescription);
1024 err = AudioUnitSetProperty (mAudioUnit,
1025 kAudioUnitProperty_StreamFormat,
1026 kAudioUnitScope_Output,
1027 1, // input bus
1028 &asbd,
1029 size);
1030 checkStatus(err);
1032 // make sure we really know the current format
1033 size = sizeof (AudioStreamBasicDescription);
1034 err = AudioUnitGetProperty (mAudioUnit,
1035 kAudioUnitProperty_StreamFormat,
1036 kAudioUnitScope_Output,
1037 1, // input bus
1038 &hwASBD,
1039 &size);
1041 return err;
1046 BOOL PSoundChannelCoreAudio::SetFormat(unsigned numChannels,
1047 unsigned sampleRate,
1048 unsigned bitsPerSample)
1050 // making some assumptions about input format for now
1051 PAssert(sampleRate == 8000 && numChannels == 1 && bitsPerSample == 16,
1052 PUnsupportedFeature);
1054 if(state != open_){
1055 PTRACE(1, "Please select a device first");
1056 return FALSE;
1060 * Setup the pwlibASBD
1062 memset((void *)&pwlibASBD, 0, sizeof(AudioStreamBasicDescription));
1064 /* pwlibASBD->mReserved */
1065 pwlibASBD.mFormatID = kAudioFormatLinearPCM;
1066 pwlibASBD.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
1067 pwlibASBD.mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved;
1068 #if PBYTE_ORDER == PBIG_ENDIAN
1069 pwlibASBD.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
1070 #endif
1071 pwlibASBD.mSampleRate = sampleRate;
1072 pwlibASBD.mChannelsPerFrame = numChannels;
1073 pwlibASBD.mBitsPerChannel = bitsPerSample;
1074 pwlibASBD.mBytesPerFrame = bitsPerSample / 8;
1075 pwlibASBD.mFramesPerPacket = 1;
1076 pwlibASBD.mBytesPerPacket = pwlibASBD.mBytesPerFrame;
1079 if(mDeviceID == kAudioDeviceDummy){
1080 PTRACE(1, "Dummy device");
1081 return TRUE;
1084 OSStatus err;
1085 if(direction == Player)
1086 err = MatchHALOutputFormat();
1087 else
1088 err = MatchHALInputFormat();
1089 checkStatus(err);
1092 * Sample Rate Conversion (SRC)
1093 * Create AudioConverters, input/output buffers, compute conversion rate
1096 PTRACE(3, "ASBD PwLib Format of " << direction << endl << pwlibASBD);
1097 PTRACE(3, "ASBD Hardware Format of " << direction << endl << hwASBD);
1100 // how many samples has the output device compared to pwlib sample rate?
1101 rateTimes8kHz = hwASBD.mSampleRate / pwlibASBD.mSampleRate;
1105 * Create Converter for Sample Rate conversion
1107 if (direction == Player)
1108 err = AudioConverterNew(&pwlibASBD, &hwASBD, &converter);
1109 else
1110 err = AudioConverterNew(&hwASBD, &pwlibASBD, &converter);
1111 checkStatus(err);
1113 UInt32 quality = kAudioConverterQuality_Max;
1114 err = AudioConverterSetProperty(converter,
1115 kAudioConverterSampleRateConverterQuality,
1116 sizeof(UInt32),
1117 &quality);
1118 checkStatus(err);
1120 //if(direction == Recorder){
1121 // trying compute number of requested data more predictably also
1122 // for the first request
1123 UInt32 primeMethod = kConverterPrimeMethod_None;
1124 err = AudioConverterSetProperty(converter,
1125 kAudioConverterPrimeMethod,
1126 sizeof(UInt32),
1127 &primeMethod);
1128 checkStatus(err);
1131 state = setformat_;
1132 return TRUE;
1136 /* gets never called, see sound.h:
1137 * baseChannel->PChannel::GetHandle();
1139 BOOL PSoundChannelCoreAudio::IsOpen() const
1141 //return (os_handle != -1);
1142 return (state != init_ || state != destroy_);
1146 /* gets never called, see sound.h:
1147 * baseChannel->PChannel::GetHandle();
1149 int PSoundChannelCoreAudio::GetHandle() const
1151 PTRACE(1, "GetHandle");
1152 //return os_handle;
1153 return -1;
1156 BOOL PSoundChannelCoreAudio::Abort()
1158 PTRACE(1, "Abort");
1159 PAssert(0, PUnimplementedFunction);
1160 return false;
1167 * SetBuffers is used to create the circular buffer as requested by the caller
1168 * plus all the hidden buffers used for Sample-Rate-Conversion(SRC)
1170 * A device can not be used after calling Open(), SetBuffers() must
1171 * also be called before it can start working.
1173 * size: Size of each buffer
1174 * count: Number of buffers
1177 BOOL PSoundChannelCoreAudio::SetBuffers(PINDEX bufferSize,
1178 PINDEX bufferCount)
1180 OSStatus err = noErr;
1182 if(state != setformat_){
1183 // use GetError
1184 PTRACE(1, "Please specify a format first");
1185 return FALSE;
1188 PTRACE(3, __func__ << direction << " : "
1189 << bufferSize << " BufferSize "<< bufferCount << " BufferCount");
1191 PAssert(bufferSize > 0 && bufferCount > 0 && bufferCount < 65536, \
1192 PInvalidParameter);
1194 this->bufferSizeBytes = bufferSize;
1195 this->bufferCount = bufferCount;
1197 if(mDeviceID == kAudioDeviceDummy){
1198 // abort here
1199 PTRACE(1, "Dummy device");
1200 return TRUE;
1203 mCircularBuffer = new CircularBuffer(bufferSize * bufferCount );
1206 /** Register callback function */
1207 err = CallbackSetup();
1210 /**
1211 * Tune the buffer size of the underlying audio device.
1212 * The aim is to make the device request half of the buffer size on
1213 * each callback.
1215 * Not possible, because buffer size for device input/output is not
1216 * independant of each other. Creates havoc in case SetBuffer is called with
1217 * different buffer size for Player/Recorder Channel
1220 UInt32 targetSizeBytes = bufferSizeBytes / 2;
1221 UInt32 size = sizeof(UInt32);
1222 if (direction == Player) {
1223 err = AudioConverterGetProperty(converter,
1224 kAudioConverterPropertyCalculateOutputBufferSize,
1225 &size, &targetSizeBytes);
1226 } else {
1227 err = AudioConverterGetProperty(converter,
1228 kAudioConverterPropertyCalculateInputBufferSize,
1229 &size, &targetSizeBytes);
1231 checkStatus(err);
1232 if (err) {
1233 return FALSE;
1236 PTRACE(2, __func__ << " AudioDevice buffer size set to "
1237 << targetSizeBytes);
1239 UInt32 targetSizeFrames = targetSizeBytes / hwASBD.mBytesPerFrame;
1240 if (direction == Player) {
1241 err = AudioDeviceSetProperty( mDeviceID,
1242 0, //&ts, timestruct
1243 0, // output channel
1244 true, // isInput
1245 // kAudioDevicePropertyBufferSize,
1246 kAudioDevicePropertyBufferFrameSize,
1247 sizeof(UInt32),
1248 &targetSizeFrames);
1249 } else {
1250 err = AudioDeviceSetProperty( mDeviceID,
1251 0, //&ts, timestruct
1252 1, // input channel
1253 false, // isInput
1254 kAudioDevicePropertyBufferFrameSize,
1255 sizeof(UInt32),
1256 &targetSizeFrames);
1258 checkStatus(err);
1262 /**
1263 * Allocate byte array passed as input to the converter
1265 UInt32 bufferSizeFrames, bufferSizeBytes;
1266 UInt32 propertySize = sizeof(UInt32);
1267 err = AudioDeviceGetProperty( mDeviceID,
1268 0, // output channel,
1269 true, // isInput
1270 kAudioDevicePropertyBufferFrameSize,
1271 &propertySize,
1272 &bufferSizeFrames);
1273 checkStatus(err);
1274 bufferSizeBytes = bufferSizeFrames * hwASBD.mBytesPerFrame;
1275 //UInt32 bufferSizeBytes = targetSizeBytes;
1277 if (direction == Player) {
1278 UInt32 propertySize = sizeof(UInt32);
1279 err = AudioConverterGetProperty(converter,
1280 kAudioConverterPropertyCalculateInputBufferSize,
1281 &propertySize,
1282 &bufferSizeBytes);
1283 checkStatus(err);
1284 converter_buffer_size = bufferSizeBytes;
1285 } else {
1286 // on each turn the device spits out bufferSizeBytes bytes
1287 // the input ringbuffer has at most MIN_INPUT_FILL frames in it
1288 // all other frames were converter during the last callback
1289 converter_buffer_size = bufferSizeBytes +
1290 2 * MIN_INPUT_FILL * hwASBD.mBytesPerFrame;
1292 converter_buffer = (char*)malloc(converter_buffer_size);
1293 if(converter_buffer == NULL)
1294 PTRACE(1, "Failed to allocate converter_buffer");
1295 else
1296 PTRACE(2, "Allocated converter_buffer of size "
1297 << converter_buffer_size );
1300 /** In case of Recording we need a couple of buffers more */
1301 if(direction == Recorder){
1302 SetupAdditionalRecordBuffers();
1306 * AU Setup, allocates necessary buffers...
1308 err = AudioUnitInitialize(mAudioUnit);
1310 //(err);
1312 state = setbuffer_;
1314 return TRUE;
1318 OSStatus PSoundChannelCoreAudio::SetupAdditionalRecordBuffers()
1321 OSStatus err = noErr;
1322 UInt32 bufferSizeFrames, bufferSizeBytes;
1324 /**
1325 * build buffer list to take over the data from the microphone
1327 UInt32 propertySize = sizeof(UInt32);
1328 err = AudioDeviceGetProperty( mDeviceID,
1329 0, // channel, probably all
1330 true, // isInput
1331 //false, // isInput ()
1332 kAudioDevicePropertyBufferFrameSize,
1333 &propertySize,
1334 &bufferSizeFrames);
1335 checkStatus(err);
1336 bufferSizeBytes = bufferSizeFrames * hwASBD.mBytesPerFrame;
1337 bufferSizeBytes += bufferSizeBytes / 10; // +10%
1339 //calculate size of ABL given the last field, assum non-interleaved
1340 UInt32 mChannelsPerFrame = hwASBD.mChannelsPerFrame;
1341 UInt32 propsize = (UInt32) &(((AudioBufferList *)0)->mBuffers[mChannelsPerFrame]);
1343 //malloc buffer lists
1344 mInputBufferList = (AudioBufferList *)malloc(propsize);
1345 mInputBufferList->mNumberBuffers = hwASBD.mChannelsPerFrame;
1347 //pre-malloc buffers for AudioBufferLists
1348 for(UInt32 i =0; i< mInputBufferList->mNumberBuffers ; i++) {
1349 mInputBufferList->mBuffers[i].mNumberChannels = 1;
1350 mInputBufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
1351 mInputBufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
1353 mRecordInputBufferSize = bufferSizeBytes;
1355 /** allocate ringbuffer to cache data before passing them to the converter */
1356 // take only one buffer -> mono, use double buffering
1357 mInputCircularBuffer = new CircularBuffer(bufferSizeBytes * 2);
1360 /**
1361 * Build buffer list that is passed to the Converter to be filled with
1362 * the converted frames.
1364 // given the number of input bytes how many bytes to expect at the output?
1365 bufferSizeBytes += MIN_INPUT_FILL * hwASBD.mBytesPerFrame;
1366 propertySize = sizeof(UInt32);
1367 err = AudioConverterGetProperty(converter,
1368 kAudioConverterPropertyCalculateOutputBufferSize,
1369 &propertySize,
1370 &bufferSizeBytes);
1371 checkStatus(err);
1374 //calculate number of buffers from channels
1375 mChannelsPerFrame = pwlibASBD.mChannelsPerFrame;
1376 propsize = (UInt32) &(((AudioBufferList *)0)->mBuffers[mChannelsPerFrame]);
1378 //malloc buffer lists
1379 mOutputBufferList = (AudioBufferList *)malloc(propsize);
1380 mOutputBufferList->mNumberBuffers = pwlibASBD.mChannelsPerFrame;
1382 //pre-malloc buffers for AudioBufferLists
1383 for(UInt32 i =0; i< mOutputBufferList->mNumberBuffers ; i++) {
1384 mOutputBufferList->mBuffers[i].mNumberChannels = 1;
1385 mOutputBufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
1386 mOutputBufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
1388 mRecordOutputBufferSize = bufferSizeBytes;
1390 return err;
1394 BOOL PSoundChannelCoreAudio::GetBuffers(PINDEX & size,
1395 PINDEX & count)
1397 size = bufferSizeBytes;
1398 count = bufferCount;
1399 return TRUE;
1403 OSStatus PSoundChannelCoreAudio::VolumeChangePropertyListener(AudioDeviceID id,
1404 UInt32 chan, Boolean isInput, AudioDevicePropertyID propID,
1405 void *user_data)
1407 PSoundChannelCoreAudio *This =
1408 static_cast<PSoundChannelCoreAudio*>(user_data);
1409 OSStatus err = noErr;
1410 UInt32 theSize = sizeof(Float32);
1411 Float32 volume;
1414 * Function similar to GetVolume, but we are free to ask the volume
1415 * for the intput/output direction
1418 // not all devices have a master channel
1419 err = AudioDeviceGetProperty(This->mDeviceID, 0, isInput,
1420 kAudioDevicePropertyVolumeScalar,
1421 &theSize, &volume);
1422 if(err != kAudioHardwareNoError) {
1423 // take the value of first channel to be the volume
1424 theSize = sizeof(volume);
1425 err = AudioDeviceGetProperty(This->mDeviceID, 1, isInput,
1426 kAudioDevicePropertyVolumeScalar,
1427 &theSize, &volume);
1430 std::cout << (isInput?"Recorder":"Player")
1431 << " volume updated " << unsigned(100*volume) << std::endl;
1433 return noErr;
1436 #include "maccoreaudio/mute_hack.inl"
1439 * Also check out this to see the difference between streams and channels.
1440 * http://lists.apple.com/archives/coreaudio-api/2001/Nov/msg00155.html
1441 * In short a stream contains several channels, e. g. 2 in case of stereo.
1443 BOOL PSoundChannelCoreAudio::GetVolume(unsigned & volume)
1445 OSStatus err = noErr;
1446 UInt32 theSize;
1447 Float32 theValue;
1448 bool isInput = (direction == Player ? false : true);
1450 if(mDeviceID == kAudioDeviceDummy){
1451 //in the case of a dummy device, we simply return 0 in all cases
1452 PTRACE(1, "Dummy device");
1453 volume = 0;
1454 return TRUE;
1458 theSize = sizeof(theValue);
1459 // not all devices have a master channel
1460 err = AudioDeviceGetProperty(mDeviceID, 0, isInput,
1461 kAudioDevicePropertyVolumeScalar,
1462 &theSize, &theValue);
1463 if(err != kAudioHardwareNoError) {
1464 // take the value of first channel to be the volume
1465 theSize = sizeof(theValue);
1466 err = AudioDeviceGetProperty(mDeviceID, 1, isInput,
1467 kAudioDevicePropertyVolumeScalar,
1468 &theSize, &theValue);
1472 if (err == kAudioHardwareNoError) {
1473 // volume is between 0 and 100?
1474 volume = (unsigned) (theValue * 100);
1475 return TRUE;
1476 } else {
1477 volume = 0;
1478 return FALSE;
1485 * We assume that we are taking always the first stream and that it is stereo,
1486 * which means it maps to the first two channels of the device.
1487 * The Master Channel (0) is unusable because not all devices support it
1489 BOOL PSoundChannelCoreAudio::SetVolume(unsigned volume)
1491 OSStatus err = noErr;
1492 Boolean isWritable;
1493 bool isInput = (direction == Player ? false : true);
1494 bool useMaster = false;
1497 if(mDeviceID == kAudioDeviceDummy) {
1498 PTRACE(1, "Dummy device");
1499 return FALSE;
1502 if(state < setformat_){ // we need to know the stream format
1503 PTRACE(2, __func__ << "AudioStreamBasicDescription not initialized yet");
1504 return FALSE;
1508 // not all devices have a master channel
1509 err = AudioDeviceGetPropertyInfo(mDeviceID, 0, isInput,
1510 kAudioDevicePropertyVolumeScalar,
1511 NULL, &isWritable);
1512 if(err != kAudioHardwareNoError)
1514 // check if we can access the individual channels
1515 err = AudioDeviceGetPropertyInfo(mDeviceID, 1, isInput,
1516 kAudioDevicePropertyVolumeScalar,
1517 NULL, &isWritable);
1518 useMaster = false;
1520 checkStatus(err);
1522 if ((err == kAudioHardwareNoError) && isWritable)
1524 // is the volume between 0 and 100 ?
1525 float theValue = ((float)volume)/100.0;
1526 if(useMaster){
1527 err = AudioDeviceSetProperty(mDeviceID, NULL, 0, isInput,
1528 kAudioDevicePropertyVolumeScalar,
1529 sizeof(float), &theValue);
1530 checkStatus(err);
1531 } else {
1532 // iterate over all channels
1533 for(unsigned ch = 1; ch <= hwASBD.mChannelsPerFrame; ch++)
1535 err = AudioDeviceSetProperty(mDeviceID, NULL, ch, isInput,
1536 kAudioDevicePropertyVolumeScalar,
1537 sizeof(float), &theValue);
1538 checkStatus(err);
1542 /** Not all devices can be muted, so it's no solution and wo drop the
1543 * code alltogether
1545 UInt32 mute = (volume == 0)? 1 * mute * : 0 * unmute * ;
1546 err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
1547 isInput, kAudioDevicePropertyMute, sizeof(UInt32), &mute);
1548 checkStatus(err);
1549 err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
1550 isInput, kAudioDevicePropertySubMute, sizeof(UInt32), &mute);
1551 checkStatus(err);
1558 // if necessary we mute device on the pwlib level by discarding frames.
1559 // this works even when the device does not support muting,
1560 // but we have to make sure that 'this' is either a singleton instance
1561 // or that all threads/instances get the message to be quiet
1563 pthread_mutex_lock(&GetIsMuteMutex());
1564 isMute() = (volume == 0) ? TRUE : FALSE;
1565 pthread_mutex_unlock(&GetIsMuteMutex());
1567 /* No sense to mute this channel, it might opened just as a mixer.
1568 * The playing/recording channel might be another instance of this
1569 * class !!! */
1572 if (!err)
1573 return TRUE;
1574 else
1575 return FALSE;
1579 BOOL PSoundChannelCoreAudio::Write(const void *buf,PINDEX len)
1581 PTRACE(5, "Write called with len " << len);
1583 if(state < setbuffer_){
1584 PTRACE(1, __func__ << " Please initialize device first");
1585 return FALSE;
1588 pthread_mutex_lock(&GetIsMuteMutex());
1589 if(isMute() && state != mute_){
1590 PTRACE(3, __func__ << "muting the " << direction << " device");
1591 state = mute_;
1592 OSStatus err = AudioOutputUnitStop(mAudioUnit);
1593 checkStatus(err);
1594 /* isMute() => state==mute */
1597 if (mDeviceID == kAudioDeviceDummy || isMute() && state == mute_ ) {
1598 lastWriteCount = len;
1600 // safe to assume non-interleaved or mono
1601 UInt32 nr_samples = len / pwlibASBD.mBytesPerFrame;
1602 usleep(UInt32(nr_samples/pwlibASBD.mSampleRate * 1000000)); // 10E-6 [s]
1603 pthread_mutex_unlock(&GetIsMuteMutex());
1604 return TRUE;
1607 // Start the device before putting datA into the buffer
1608 // Otherwise the thread could be locked in case the buffer is full
1609 // and the device is not running and draining the buffer
1610 if(state == setbuffer_ || (state == mute_ && !isMute())){
1611 state = running_;
1612 PTRACE(2, "Starting " << direction << " device.");
1613 OSStatus err = AudioOutputUnitStart(mAudioUnit);
1614 checkStatus(err);
1616 pthread_mutex_unlock(&GetIsMuteMutex());
1618 // Write to circular buffer with locking
1619 lastWriteCount = mCircularBuffer->Fill((const char*)buf, len, true);
1621 return (TRUE);
1625 BOOL PSoundChannelCoreAudio::PlaySound(const PSound & sound,
1626 BOOL wait)
1628 if (!Write((const BYTE *)sound, sound.GetSize()))
1629 return FALSE;
1631 if (wait)
1632 return WaitForPlayCompletion();
1634 return TRUE;
1637 BOOL PSoundChannelCoreAudio::PlayFile(const PFilePath & file,
1638 BOOL wait)
1640 PTRACE(1, __func__ );
1641 PAssert(0, PUnimplementedFunction);
1643 return TRUE;
1646 BOOL PSoundChannelCoreAudio::HasPlayCompleted()
1648 PTRACE(1, __func__ );
1649 PAssert(0, PUnimplementedFunction);
1650 return false;
1653 BOOL PSoundChannelCoreAudio::WaitForPlayCompletion()
1655 PTRACE(1, __func__ );
1656 PAssert(0, PUnimplementedFunction);
1657 return false;
1660 BOOL PSoundChannelCoreAudio::Read(void *buf,
1661 PINDEX len)
1663 PTRACE(5, "Read called with len " << len);
1665 if(state < setbuffer_){
1666 PTRACE(1, __func__ << " Please initialize device first");
1667 return FALSE;
1670 pthread_mutex_lock(&GetIsMuteMutex());
1671 if(isMute() && state != mute_){
1672 PTRACE(2, __func__ << "muting the " << direction << " device");
1673 state = mute_;
1674 OSStatus err = AudioOutputUnitStop(mAudioUnit);
1675 checkStatus(err);
1676 /* isMute() => state==mute */
1679 if (mDeviceID == kAudioDeviceDummy || isMute()) {
1680 lastReadCount = len;
1681 bzero(buf, len);
1683 // we are working with non-interleaved or mono
1684 UInt32 nr_samples = len / pwlibASBD.mBytesPerFrame;
1685 usleep(UInt32(nr_samples/pwlibASBD.mSampleRate * 1000000)); // 10E-6 [s]
1686 pthread_mutex_unlock(&GetIsMuteMutex());
1687 return TRUE;
1690 // Start the device before draining data or the thread might be locked
1691 // on an empty buffer and never wake up, because no device is filling
1692 // with data
1693 if(state == setbuffer_ || (state == mute_ && !isMute())){
1694 state = running_;
1695 PTRACE(2, "Starting " << direction << " device.");
1696 OSStatus err = AudioOutputUnitStart(mAudioUnit);
1697 checkStatus(err);
1699 pthread_mutex_unlock(&GetIsMuteMutex());
1701 lastReadCount = mCircularBuffer->Drain((char*)buf, len, true);
1702 return (TRUE);
1706 BOOL PSoundChannelCoreAudio::RecordSound(PSound & sound)
1708 PTRACE(1, __func__ );
1709 PAssert(0, PUnimplementedFunction);
1710 return false;
1713 BOOL PSoundChannelCoreAudio::RecordFile(const PFilePath & file)
1715 PTRACE(1, __func__ );
1716 PAssert(0, PUnimplementedFunction);
1717 return false;
1720 BOOL PSoundChannelCoreAudio::StartRecording()
1722 if(state != setbuffer_){
1723 PTRACE(1, __func__ << " Initialize the device first");
1724 return FALSE;
1727 pthread_mutex_lock(&GetIsMuteMutex());
1728 if(state == setbuffer_ || (state == mute_ && !isMute()) ){
1729 state = running_;
1730 PTRACE(2,__func__ << "Starting " << direction << " device.");
1731 OSStatus err = AudioOutputUnitStart(mAudioUnit);
1732 checkStatus(err);
1734 pthread_mutex_unlock(&GetIsMuteMutex());
1735 return false;
1738 BOOL PSoundChannelCoreAudio::isRecordBufferFull()
1740 PAssert(direction == Recorder, PInvalidParameter);
1741 if(state != setbuffer_){
1742 PTRACE(1, __func__ << " Initialize the device first");
1743 return FALSE;
1746 return (mCircularBuffer->size() > bufferSizeBytes);
1749 BOOL PSoundChannelCoreAudio::AreAllRecordBuffersFull()
1751 PAssert(direction == Recorder, PInvalidParameter);
1752 if(state != setbuffer_){
1753 PTRACE(1, __func__ << " Initialize the device first");
1754 return FALSE;
1757 return (mCircularBuffer->Full());
1760 BOOL PSoundChannelCoreAudio::WaitForRecordBufferFull()
1762 PTRACE(1, __func__ );
1763 PAssert(0, PUnimplementedFunction);
1764 if (os_handle < 0) {
1765 return FALSE;
1768 return PXSetIOBlock(PXReadBlock, readTimeout);
1771 BOOL PSoundChannelCoreAudio::WaitForAllRecordBuffersFull()
1773 PTRACE(1, __func__ );
1774 PAssert(0, PUnimplementedFunction);
1775 return false;
1779 // End of file