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
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 *******************/
41 // should also produce output when ptracing is disabled
42 #define checkStatus( err ) \
44 OSStatus error = static_cast<OSStatus>(err);\
45 PTRACE(1, "CoreAudio Error " << __func__ << " " \
46 << error << "(" << (char*)&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";
66 ostream
& operator<<(ostream
&os
, PSoundChannel::Directions
&dir
)
68 if(dir
== PSoundChannel::Player
)
70 else if(dir
== PSoundChannel::Recorder
)
73 os
<< " Unknown direction ";
77 ostream
& operator<<(ostream
&os
, AudioValueRange range
)
79 os
<< range
.mMinimum
<< " " << range
.mMaximum
;
84 ostream
& operator<<(ostream
&os
, PSoundChannelCoreAudio::State
&state
)
87 case PSoundChannelCoreAudio::init_
:
90 case PSoundChannelCoreAudio::open_
:
93 case PSoundChannelCoreAudio::setformat_
:
96 case PSoundChannelCoreAudio::setbuffer_
:
99 case PSoundChannelCoreAudio::running_
:
102 case PSoundChannelCoreAudio::destroy_
:
106 os
<< " Unknown state ";
113 #define checkStatus( err ) \
115 OSStatus error = static_cast<OSStatus>(err);\
116 cout << "CoreAudio Error " << __func__ << " " \
117 << error << "(" << (char*)&err << ")" << endl; \
122 /***** PSound implementation *****/
124 PSound::PSound(unsigned channels
,
126 unsigned bitsPerSample
,
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
);
144 BOOL
PSound::Load(const PFilePath
& filename
)
146 PAssert(0, PUnimplementedFunction
);
150 BOOL
PSound::Save(const PFilePath
& filename
)
152 PAssert(0, PUnimplementedFunction
);
158 PAssert(0, PUnimplementedFunction
);
162 void PSound::SetFormat(unsigned numChannels
,
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
,
175 PAssert(0, PUnimplementedFunction
);
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
)
197 PSoundChannelCoreAudio::PSoundChannelCoreAudio(const PString
& device
,
199 unsigned numChannels
,
201 unsigned bitsPerSample
)
202 : state(init_
), mCircularBuffer(NULL
), converter_buffer(NULL
),
203 mInputCircularBuffer(NULL
), mInputBufferList(NULL
), mOutputBufferList(NULL
)
206 Open(device
, dir
, numChannels
, sampleRate
, bitsPerSample
);
211 PSoundChannelCoreAudio::~PSoundChannelCoreAudio()
213 OSStatus err
= noErr
;
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 */
223 err
= AudioOutputUnitStop(mAudioUnit
);
225 PTRACE(1, direction
<< " AudioUnit stopped" );
226 usleep(1000*20); // ensure that all callbacks terminated
230 /* check for all buffers unconditionally */
231 err
= AudioUnitUninitialize(mAudioUnit
);
235 err
= AudioConverterDispose(converter
);
239 err
= CloseComponent(mAudioUnit
);
241 err
= AudioDeviceRemovePropertyListener(mDeviceID
, 1,
242 kAudioPropertyWildcardSection
, kAudioDevicePropertyVolumeScalar
,
243 VolumeChangePropertyListener
);
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.
278 unsigned PSoundChannelCoreAudio::GetChannels() const
280 if(state
>= setformat_
)
281 return pwlibASBD
.mChannelsPerFrame
;
286 unsigned PSoundChannelCoreAudio::GetSampleRate() const
288 if(state
>= setformat_
)
289 return (unsigned)(pwlibASBD
.mSampleRate
);
294 unsigned PSoundChannelCoreAudio::GetSampleSize() const
296 if(state
>= setformat_
)
297 return (pwlibASBD
.mBitsPerChannel
);
304 * Functions for retrieving AudioDevice list
306 #include "maccoreaudio/maccoreaudio_devices.inl"
309 PString
PSoundChannelCoreAudio::GetDefaultDevice(Directions dir
)
311 OSStatus err
= noErr
;
315 theSize
= sizeof(AudioDeviceID
);
318 err
= AudioHardwareGetProperty(
319 kAudioHardwarePropertyDefaultOutputDevice
,
323 err
= AudioHardwareGetProperty(
324 kAudioHardwarePropertyDefaultInputDevice
,
329 return CADeviceName(theID
);
331 return CA_DUMMY_DEVICE_NAME
;
335 PStringList
PSoundChannelCoreAudio::GetDeviceNames(Directions dir
)
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
) {
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
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
,
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
);
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
;
454 * CoreAudio Player callback function
456 OSStatus
PSoundChannelCoreAudio::PlayRenderProc(
458 AudioUnitRenderActionFlags
* ioActionFlags
,
459 const struct AudioTimeStamp
* TimeStamp
,
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");
473 //PTRACE(4, __func__ << ", frames " << inNumberFrames);
475 err
= AudioConverterFillComplexBuffer(This
->converter
,
476 PSoundChannelCoreAudio::ComplexBufferFillPlayback
,
478 &inNumberFrames
, // should be packets
480 NULL
/*outPacketDescription*/);
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_
){
489 while(i
< ioData
->mNumberBuffers
) {
490 memcpy(ioData
->mBuffers
[i
].mData
, ioData
->mBuffers
[0].mData
, len
);
491 ioData
->mBuffers
[i
].mDataByteSize
= len
;
502 OSStatus
PSoundChannelCoreAudio::RecordProc(
504 AudioUnitRenderActionFlags
* ioActionFlags
,
505 const AudioTimeStamp
* inTimeStamp
,
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_
){
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
,
533 inNumberFrames
, //# of frames requested
534 inputData
);// Audio Buffer List to hold data
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
,
567 NULL
/*outPacketDescription*/);
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
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
,
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
;
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
,
649 callback
.inputProc
= PlayRenderProc
;
650 err
= AudioUnitSetProperty(mAudioUnit
,
651 kAudioUnitProperty_SetRenderCallback
,
652 kAudioUnitScope_Input
,
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
;
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
);
702 err
= SetDeviceAsCurrent(in
);
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
;
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
726 err
= AudioUnitSetProperty(mAudioUnit
,
727 kAudioOutputUnitProperty_EnableIO
,
728 kAudioUnitScope_Input
,
734 //disable Output on the AUHAL
736 err
= AudioUnitSetProperty(mAudioUnit
,
737 kAudioOutputUnitProperty_EnableIO
,
738 kAudioUnitScope_Output
,
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
751 OSStatus
PSoundChannelCoreAudio::SetupOutputUnit(AudioDeviceID out
){
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
);
774 //enableIO not needed, because output is default
776 err
= SetDeviceAsCurrent(out
);
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
);
793 err
= AudioHardwareGetProperty(
794 kAudioHardwarePropertyDefaultInputDevice
, &size
, &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
,
819 * The major task of Open() is to find the matching device ID.
822 BOOL
PSoundChannelCoreAudio::Open(const PString
& deviceName
,
824 unsigned numChannels
,
826 unsigned bitsPerSample
)
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) {
838 PTRACE(6, "Dummy device " << direction
);
839 mDeviceID
= kAudioDeviceUnknown
;
842 AudioDeviceID deviceID
= GetDeviceID(deviceName
, direction
== Recorder
);
843 if(direction
== Player
)
844 err
= SetupOutputUnit(deviceID
);
846 err
= SetupInputUnit(deviceID
);
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);
860 //os_handle = mDeviceID; // tell PChanne::IsOpen() that the channel is open.
861 os_handle
= 8; // tell PChannel::IsOpen() that the channel is 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,
906 kAudioDevicePropertyStreamFormat,
911 //Get the current stream format of the output
912 err
= AudioUnitGetProperty (mAudioUnit
,
913 kAudioUnitProperty_StreamFormat
,
914 kAudioUnitScope_Output
,
920 // make sure it is non-interleaved
922 !(hwASBD
.mFormatFlags
& kAudioFormatFlagIsNonInterleaved
);
924 hwASBD
.mFormatFlags
|= kAudioFormatFlagIsNonInterleaved
;
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
,
942 // make sure we really know the current format
943 size
= sizeof (AudioStreamBasicDescription
);
944 err
= AudioUnitGetProperty (mAudioUnit
,
945 kAudioUnitProperty_StreamFormat
,
946 kAudioUnitScope_Input
,
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,
972 kAudioDevicePropertyStreamFormat,
978 /* This code asks for the supported sample rates of the microphone
979 UInt32 count, numRanges;
980 err = AudioDeviceGetPropertyInfo ( mDeviceID,
982 kAudioDevicePropertyAvailableNominalSampleRates,
985 numRanges = count / sizeof(AudioValueRange);
986 AudioValueRange* rangeArray = (AudioValueRange*)malloc ( count );
988 err = AudioDeviceGetProperty ( mDeviceID,
990 kAudioDevicePropertyAvailableNominalSampleRates,
991 &count, (void*)rangeArray );
995 //Get the current stream format of the output
996 err
= AudioUnitGetProperty (mAudioUnit
,
997 kAudioUnitProperty_StreamFormat
,
998 kAudioUnitScope_Input
,
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
;
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
,
1032 // make sure we really know the current format
1033 size
= sizeof (AudioStreamBasicDescription
);
1034 err
= AudioUnitGetProperty (mAudioUnit
,
1035 kAudioUnitProperty_StreamFormat
,
1036 kAudioUnitScope_Output
,
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
);
1055 PTRACE(1, "Please select a device first");
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
;
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");
1085 if(direction
== Player
)
1086 err
= MatchHALOutputFormat();
1088 err
= MatchHALInputFormat();
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
);
1110 err
= AudioConverterNew(&hwASBD
, &pwlibASBD
, &converter
);
1113 UInt32 quality
= kAudioConverterQuality_Max
;
1114 err
= AudioConverterSetProperty(converter
,
1115 kAudioConverterSampleRateConverterQuality
,
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
,
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");
1156 BOOL
PSoundChannelCoreAudio::Abort()
1159 PAssert(0, PUnimplementedFunction
);
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
,
1180 OSStatus err
= noErr
;
1182 if(state
!= setformat_
){
1184 PTRACE(1, "Please specify a format first");
1188 PTRACE(3, __func__
<< direction
<< " : "
1189 << bufferSize
<< " BufferSize "<< bufferCount
<< " BufferCount");
1191 PAssert(bufferSize
> 0 && bufferCount
> 0 && bufferCount
< 65536, \
1194 this->bufferSizeBytes
= bufferSize
;
1195 this->bufferCount
= bufferCount
;
1197 if(mDeviceID
== kAudioDeviceDummy
){
1199 PTRACE(1, "Dummy device");
1203 mCircularBuffer
= new CircularBuffer(bufferSize
* bufferCount
);
1206 /** Register callback function */
1207 err
= CallbackSetup();
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
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);
1227 err = AudioConverterGetProperty(converter,
1228 kAudioConverterPropertyCalculateInputBufferSize,
1229 &size, &targetSizeBytes);
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
1245 // kAudioDevicePropertyBufferSize,
1246 kAudioDevicePropertyBufferFrameSize,
1250 err = AudioDeviceSetProperty( mDeviceID,
1251 0, //&ts, timestruct
1254 kAudioDevicePropertyBufferFrameSize,
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,
1270 kAudioDevicePropertyBufferFrameSize
,
1274 bufferSizeBytes
= bufferSizeFrames
* hwASBD
.mBytesPerFrame
;
1275 //UInt32 bufferSizeBytes = targetSizeBytes;
1277 if (direction
== Player
) {
1278 UInt32 propertySize
= sizeof(UInt32
);
1279 err
= AudioConverterGetProperty(converter
,
1280 kAudioConverterPropertyCalculateInputBufferSize
,
1284 converter_buffer_size
= bufferSizeBytes
;
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");
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
);
1318 OSStatus
PSoundChannelCoreAudio::SetupAdditionalRecordBuffers()
1321 OSStatus err
= noErr
;
1322 UInt32 bufferSizeFrames
, bufferSizeBytes
;
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
1331 //false, // isInput ()
1332 kAudioDevicePropertyBufferFrameSize
,
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);
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
,
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
;
1394 BOOL
PSoundChannelCoreAudio::GetBuffers(PINDEX
& size
,
1397 size
= bufferSizeBytes
;
1398 count
= bufferCount
;
1403 OSStatus
PSoundChannelCoreAudio::VolumeChangePropertyListener(AudioDeviceID id
,
1404 UInt32 chan
, Boolean isInput
, AudioDevicePropertyID propID
,
1407 PSoundChannelCoreAudio
*This
=
1408 static_cast<PSoundChannelCoreAudio
*>(user_data
);
1409 OSStatus err
= noErr
;
1410 UInt32 theSize
= sizeof(Float32
);
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
,
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
,
1430 std::cout
<< (isInput
?"Recorder":"Player")
1431 << " volume updated " << unsigned(100*volume
) << std::endl
;
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
;
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");
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);
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
;
1493 bool isInput
= (direction
== Player
? false : true);
1494 bool useMaster
= false;
1497 if(mDeviceID
== kAudioDeviceDummy
) {
1498 PTRACE(1, "Dummy device");
1502 if(state
< setformat_
){ // we need to know the stream format
1503 PTRACE(2, __func__
<< "AudioStreamBasicDescription not initialized yet");
1508 // not all devices have a master channel
1509 err
= AudioDeviceGetPropertyInfo(mDeviceID
, 0, isInput
,
1510 kAudioDevicePropertyVolumeScalar
,
1512 if(err
!= kAudioHardwareNoError
)
1514 // check if we can access the individual channels
1515 err
= AudioDeviceGetPropertyInfo(mDeviceID
, 1, isInput
,
1516 kAudioDevicePropertyVolumeScalar
,
1522 if ((err
== kAudioHardwareNoError
) && isWritable
)
1524 // is the volume between 0 and 100 ?
1525 float theValue
= ((float)volume
)/100.0;
1527 err
= AudioDeviceSetProperty(mDeviceID
, NULL
, 0, isInput
,
1528 kAudioDevicePropertyVolumeScalar
,
1529 sizeof(float), &theValue
);
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
);
1542 /** Not all devices can be muted, so it's no solution and wo drop the
1545 UInt32 mute = (volume == 0)? 1 * mute * : 0 * unmute * ;
1546 err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
1547 isInput, kAudioDevicePropertyMute, sizeof(UInt32), &mute);
1549 err = AudioDeviceSetProperty(mDeviceID, NULL, useMaster?0:1,
1550 isInput, kAudioDevicePropertySubMute, sizeof(UInt32), &mute);
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
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");
1588 pthread_mutex_lock(&GetIsMuteMutex());
1589 if(isMute() && state
!= mute_
){
1590 PTRACE(3, __func__
<< "muting the " << direction
<< " device");
1592 OSStatus err
= AudioOutputUnitStop(mAudioUnit
);
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());
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())){
1612 PTRACE(2, "Starting " << direction
<< " device.");
1613 OSStatus err
= AudioOutputUnitStart(mAudioUnit
);
1616 pthread_mutex_unlock(&GetIsMuteMutex());
1618 // Write to circular buffer with locking
1619 lastWriteCount
= mCircularBuffer
->Fill((const char*)buf
, len
, true);
1625 BOOL
PSoundChannelCoreAudio::PlaySound(const PSound
& sound
,
1628 if (!Write((const BYTE
*)sound
, sound
.GetSize()))
1632 return WaitForPlayCompletion();
1637 BOOL
PSoundChannelCoreAudio::PlayFile(const PFilePath
& file
,
1640 PTRACE(1, __func__
);
1641 PAssert(0, PUnimplementedFunction
);
1646 BOOL
PSoundChannelCoreAudio::HasPlayCompleted()
1648 PTRACE(1, __func__
);
1649 PAssert(0, PUnimplementedFunction
);
1653 BOOL
PSoundChannelCoreAudio::WaitForPlayCompletion()
1655 PTRACE(1, __func__
);
1656 PAssert(0, PUnimplementedFunction
);
1660 BOOL
PSoundChannelCoreAudio::Read(void *buf
,
1663 PTRACE(5, "Read called with len " << len
);
1665 if(state
< setbuffer_
){
1666 PTRACE(1, __func__
<< " Please initialize device first");
1670 pthread_mutex_lock(&GetIsMuteMutex());
1671 if(isMute() && state
!= mute_
){
1672 PTRACE(2, __func__
<< "muting the " << direction
<< " device");
1674 OSStatus err
= AudioOutputUnitStop(mAudioUnit
);
1676 /* isMute() => state==mute */
1679 if (mDeviceID
== kAudioDeviceDummy
|| isMute()) {
1680 lastReadCount
= 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());
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
1693 if(state
== setbuffer_
|| (state
== mute_
&& !isMute())){
1695 PTRACE(2, "Starting " << direction
<< " device.");
1696 OSStatus err
= AudioOutputUnitStart(mAudioUnit
);
1699 pthread_mutex_unlock(&GetIsMuteMutex());
1701 lastReadCount
= mCircularBuffer
->Drain((char*)buf
, len
, true);
1706 BOOL
PSoundChannelCoreAudio::RecordSound(PSound
& sound
)
1708 PTRACE(1, __func__
);
1709 PAssert(0, PUnimplementedFunction
);
1713 BOOL
PSoundChannelCoreAudio::RecordFile(const PFilePath
& file
)
1715 PTRACE(1, __func__
);
1716 PAssert(0, PUnimplementedFunction
);
1720 BOOL
PSoundChannelCoreAudio::StartRecording()
1722 if(state
!= setbuffer_
){
1723 PTRACE(1, __func__
<< " Initialize the device first");
1727 pthread_mutex_lock(&GetIsMuteMutex());
1728 if(state
== setbuffer_
|| (state
== mute_
&& !isMute()) ){
1730 PTRACE(2,__func__
<< "Starting " << direction
<< " device.");
1731 OSStatus err
= AudioOutputUnitStart(mAudioUnit
);
1734 pthread_mutex_unlock(&GetIsMuteMutex());
1738 BOOL
PSoundChannelCoreAudio::isRecordBufferFull()
1740 PAssert(direction
== Recorder
, PInvalidParameter
);
1741 if(state
!= setbuffer_
){
1742 PTRACE(1, __func__
<< " Initialize the device first");
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");
1757 return (mCircularBuffer
->Full());
1760 BOOL
PSoundChannelCoreAudio::WaitForRecordBufferFull()
1762 PTRACE(1, __func__
);
1763 PAssert(0, PUnimplementedFunction
);
1764 if (os_handle
< 0) {
1768 return PXSetIOBlock(PXReadBlock
, readTimeout
);
1771 BOOL
PSoundChannelCoreAudio::WaitForAllRecordBuffersFull()
1773 PTRACE(1, __func__
);
1774 PAssert(0, PUnimplementedFunction
);