1 /**
2 * coreaudio.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * CoreAudio sound channel driver for Mac OS X.
6 *
7 * Yet Another Telephony Engine - a fully featured software PBX and IVR
8 * Copyright (C) 2004-2014 Null Team
9 *
10 * This software is distributed under multiple licenses;
11 * see the COPYING file in the main directory for licensing
12 * information for this specific distribution.
13 *
14 * This use of this software may be subject to additional restrictions.
15 * See the LEGAL file in the main directory for details.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22
23 #include <yatephone.h>
24
25 #include <AudioUnit/AudioUnit.h>
26 #include <AudioToolbox/AudioToolbox.h>
27
28 #define FRAME_SIZE 320
29 #define DEFAULT_SAMPLE_RATE 8000
30
31 using namespace TelEngine;
32
33 namespace { //anonymous
34
35 class CoreAudioSource : public ThreadedSource
36 {
37 public:
38 CoreAudioSource(unsigned int rate = DEFAULT_SAMPLE_RATE);
39 ~CoreAudioSource();
40 // inherited methods
41 bool init();
42 virtual void run();
43 virtual void cleanup();
44 virtual bool control(NamedList& params);
45
46 // append to the internal buffer data read from input source
47 void sendData(AudioBufferList *buf);
48 // provide data to the AudioConverter taken from the internal buffer
49 DataBlock getData(UInt32 pkts);
50
51 // helper function for allocating buffers
52 AudioBufferList* allocateAudioBufferList(UInt32 numChannels, UInt32 size);
53 // helper function for freeing buffers
54 void destroyAudioBufferList(AudioBufferList* list);
55 // helper function for obtaining an AudioConverter
56 OSStatus buildConverter(AudioStreamBasicDescription inFormat, AudioConverterRef* ac);
57
58 // obtain the output format of the AudioUnit
outFormat() const59 inline AudioStreamBasicDescription outFormat() const
60 { return m_outDevFormat; }
61 // obtain the output sample rate
rate() const62 inline unsigned int rate() const
63 { return m_rate; }
64
65 private:
66 // callback for obtaining data from input source
67 static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
68 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
69 // default input AudioUnit
70 AudioUnit fAudioUnit;
71 // input buffer
72 AudioBufferList* m_inAudioBuffer;
73 // sample rate converter
74 AudioConverterRef m_audioConvert;
75 // input device id
76 AudioDeviceID fInputDevID;
77 // output format of the AudioUnit
78 AudioStreamBasicDescription m_outDevFormat;
79 // the desired format for yate, passed to the sample rate converter
80 AudioStreamBasicDescription m_convertToFormat;
81 // total amount of bytes sent
82 unsigned int m_total;
83 // check if the AudioDevice supports setting the volume
84 bool m_volSettable;
85 // number of channels for the device
86 unsigned int m_channels;
87 // internal buffer
88 DataBlock m_data;
89 // output sample rate
90 unsigned int m_rate;
91 };
92
93 class CoreAudioConsumer : public DataConsumer, public Mutex
94 {
95 public:
96 CoreAudioConsumer(unsigned int rate = DEFAULT_SAMPLE_RATE);
97 ~CoreAudioConsumer();
98 bool init();
99 // inherited methods
100 virtual unsigned long Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags);
101 virtual bool control(NamedList& params);
102 virtual void getData(AudioBufferList* buf);
103 // obtain the input sample rate
rate() const104 inline unsigned int rate() const
105 { return m_rate; }
106
107 private:
108 // callback through which the AudioUnit requires data to play
109 static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
110 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
111 // the AudioUnit
112 AudioUnit fAudioUnit;
113 // total amount of data written to the output
114 unsigned int m_total;
115 // check if the AudioDevice supports setting the volume
116 bool m_volSettable;
117 // number of channels for the device
118 unsigned int m_channels;
119 // AudioDevice ID
120 AudioDeviceID fOutputDevID;
121 // internal buffer
122 DataBlock m_data;
123 // input sample rate
124 unsigned int m_rate;
125 };
126
127 class CoreAudioChan : public CallEndpoint
128 {
129 public:
130 CoreAudioChan(const String& dev, unsigned int rate = DEFAULT_SAMPLE_RATE);
131 ~CoreAudioChan();
132 bool init();
133 virtual void disconnected(bool final, const char *reason);
134 void answer();
setTarget(const char * target=0)135 inline void setTarget(const char* target = 0)
136 { m_target = target; }
getTarget() const137 inline const String& getTarget() const
138 { return m_target; }
rate() const139 inline unsigned int rate() const
140 { return m_rate; }
141 private:
142 String m_dev;
143 String m_target;
144 unsigned int m_rate;
145 };
146
147 class CoreAudioHandler;
148
149 class CoreAudioPlugin : public Plugin
150 {
151 public:
152 CoreAudioPlugin();
153 virtual void initialize();
154 virtual bool isBusy() const;
155 private:
156 CoreAudioHandler* m_handler;
157 };
158
159 static CoreAudioChan* s_audioChan = 0;
160
161 INIT_PLUGIN(CoreAudioPlugin);
162
163 class CoreAudioHandler : public MessageHandler
164 {
165 public:
CoreAudioHandler(const char * name)166 CoreAudioHandler(const char *name)
167 : MessageHandler(name,100,__plugin.name())
168 { }
169 virtual bool received(Message &msg);
170 };
171
172 class StatusHandler : public MessageHandler
173 {
174 public:
StatusHandler()175 StatusHandler()
176 : MessageHandler("engine.status",100,__plugin.name())
177 { }
178 virtual bool received(Message &msg);
179 };
180
181 class DropHandler : public MessageHandler
182 {
183 public:
DropHandler()184 DropHandler()
185 : MessageHandler("call.drop",100,__plugin.name())
186 { }
187 virtual bool received(Message &msg);
188 };
189
190 class MasqHandler : public MessageHandler
191 {
192 public:
MasqHandler(int prio)193 MasqHandler(int prio)
194 : MessageHandler("chan.masquerade",prio,__plugin.name())
195 { }
196 virtual bool received(Message &msg);
197 };
198
199 class AttachHandler : public MessageHandler
200 {
201 public:
AttachHandler()202 AttachHandler()
203 : MessageHandler("chan.attach",100,__plugin.name())
204 { }
205 virtual bool received(Message &msg);
206 };
207
208 // test if a device permits setting the volume
checkVolumeSettable(AudioDeviceID devId,UInt32 inChannel,Boolean isInput)209 static bool checkVolumeSettable(AudioDeviceID devId, UInt32 inChannel,Boolean isInput)
210 {
211 Boolean isWritable = false;
212
213 AudioObjectPropertyScope volumeScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
214 AudioObjectPropertyAddress volumeAddress = {kAudioDevicePropertyVolumeScalar,volumeScope,inChannel};
215
216 Boolean hasProperty = AudioObjectHasProperty(devId,&volumeAddress);
217 if (!hasProperty) {
218 DDebug(DebugAll, "CoreAudio - %s AudioUnit does not have 'kAudioDevicePropertyVolumeScalar' property on channel %u",
219 (isInput ? "Input" : "Output"),(unsigned int)inChannel);
220 return false;
221 }
222
223 OSStatus err = AudioObjectIsPropertySettable(devId,&volumeAddress,&isWritable);
224 if (err != noErr) {
225 DDebug(DebugAll, "CoreAudio - %s AudioUnit Failed to get if volume property is settable on channel=%u, err=%4.4s, %ld",(isInput ? "Input" : "Output"),
226 (unsigned int)inChannel,(char*)&err,(long int)err);
227 return false;
228 }
229 return isWritable;
230 }
231
232 // callback for the sample rate converter
convertCallback(AudioConverterRef inAudioConverter,UInt32 * ioNumberDataPackets,AudioBufferList * ioData,AudioStreamPacketDescription ** outDataPacketDescription,void * inUserData)233 OSStatus convertCallback(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData,
234 AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
235 {
236 CoreAudioSource* src = static_cast<CoreAudioSource*> (inUserData);
237 if (!src)
238 return 1;
239 // try to get data with the required length
240 DataBlock data = src->getData(*ioNumberDataPackets);
241 if (data.length() > 0)
242 XDebug(DebugInfo,"CoreAudio::convertCallBack() packetsReq=%d pktsAvailable=%d", (int)*ioNumberDataPackets,data.length()/2);
243
244 // signal that we have no data to convert and return
245 if (data.length() == 0) {
246 *ioNumberDataPackets = 0;
247 return 1;
248 }
249 // determine how much we can read into the converter's input buffer
250 UInt32 maxPackets = data.length() / src->outFormat().mBytesPerFrame;
251 if (*ioNumberDataPackets > maxPackets)
252 *ioNumberDataPackets = maxPackets;
253 else
254 maxPackets = *ioNumberDataPackets;
255
256 // fill the converters input buffer
257 ioData->mBuffers[0].mData = data.data();
258 ioData->mBuffers[0].mDataByteSize = maxPackets * src->outFormat().mBytesPerFrame;
259 ioData->mBuffers[0].mNumberChannels = 1;
260 data.cut(-(maxPackets * src->outFormat().mBytesPerFrame));
261 return noErr;
262 }
263
CoreAudioSource(unsigned int rate)264 CoreAudioSource::CoreAudioSource(unsigned int rate)
265 : m_inAudioBuffer(0), m_audioConvert(NULL), fInputDevID(0),
266 m_total(0), m_volSettable(false), m_channels(0), m_rate(rate)
267 {
268 Debug(DebugAll,"CoreAudioSource::CoreAudioSource() [%p]",this);
269 if (m_rate != DEFAULT_SAMPLE_RATE)
270 m_format << "/" << m_rate;
271 }
272
~CoreAudioSource()273 CoreAudioSource::~CoreAudioSource()
274 {
275 Debug(DebugAll,"CoreAudioSource::~CoreAudioSource() [%p] total=%u",this,m_total);
276 OSStatus err = AudioOutputUnitStop(fAudioUnit);
277 if(err != noErr)
278 Debug(DebugInfo,"CoreAudioSource::~CoreAudioSource() [%p] - Failed to stop AU",this);
279 err = AudioUnitUninitialize(fAudioUnit);
280 if(err != noErr)
281 Debug(DebugInfo,"CoreAudioSource::~CoreAudioSource() [%p] - Failed to uninitialize AU",this);
282 destroyAudioBufferList(m_inAudioBuffer);
283 }
284
init()285 bool CoreAudioSource::init()
286 {
287 OSStatus err = noErr;
288 UInt32 param;
289
290 // open the AudioOutputUnit, provide description
291 #ifdef MAC_OS_X_VERSION_10_6
292 AudioComponent component;
293 AudioComponentDescription description;
294 #else
295 Component component;
296 ComponentDescription description;
297 #endif
298
299 description.componentType = kAudioUnitType_Output;
300 description.componentSubType = kAudioUnitSubType_HALOutput;
301 description.componentManufacturer = kAudioUnitManufacturer_Apple;
302 description.componentFlags = 0;
303 description.componentFlagsMask = 0;
304
305 #ifdef MAC_OS_X_VERSION_10_6
306 if((component = AudioComponentFindNext(NULL,&description)))
307 err = AudioComponentInstanceNew(component,&fAudioUnit);
308 #else
309 if((component = FindNextComponent(NULL,&description)))
310 err = OpenAComponent(component,&fAudioUnit);
311 #endif
312
313 if(err != noErr) {
314 fAudioUnit = NULL;
315 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to open component error==%4.4s, %ld",this,(char*)&err,(long int)err);
316 return false;
317 }
318
319 // configure AudioOutputUnit for input, enable input on the AUHAL
320 param = 1;
321 err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Input,1,¶m,sizeof(UInt32));
322 if (err == noErr) {
323 // disable output on the AUHAL
324 param = 0;
325 err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output,0,¶m,sizeof(UInt32));
326 }
327 else {
328 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to configure AudioUnit for input error==%4.4s, %ld",this,(char*)&err,(long int)err);
329 return false;
330 }
331
332 // select the default input device
333 param = sizeof(AudioDeviceID);
334
335 AudioObjectPropertyAddress devAddress = {kAudioHardwarePropertyDefaultInputDevice,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster};
336 err = AudioObjectGetPropertyData(kAudioObjectSystemObject,&devAddress,0,NULL,¶m,&fInputDevID);
337 if(err != noErr) {
338 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get input device error==%4.4s, %ld",this,(char*)&err,(long int)err);
339 return false;
340 }
341
342 // set the current device to the AudioUnit
343 err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&fInputDevID,sizeof(AudioDeviceID));
344 if (err != noErr) {
345 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to set AU input device=%4.4s, %ld",this,(char*)&err,(long int)err);
346 return false;
347 }
348
349 // setup render callback
350 AURenderCallbackStruct callback;
351 callback.inputProc = CoreAudioSource::inputCallback;
352 callback.inputProcRefCon = this;
353 err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_SetInputCallback,kAudioUnitScope_Global,0,&callback,sizeof(AURenderCallbackStruct));
354 if (err != noErr) {
355 Debug(DebugInfo,"CoreAudioSource::init() [%p] - could not set callback error==%4.4s, %ld",this,(char*)&err,(long int)err);
356 return false;
357 }
358
359 // get hardware device format
360 param = sizeof(AudioStreamBasicDescription);
361 AudioStreamBasicDescription devFormat;
362 err = AudioUnitGetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,1,&devFormat,¶m);
363 if(err != noErr) {
364 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get input device AudioStreamBasicDescription error==%4.4s, %ld",this,(char*)&err,(long int)err);
365 return false;
366 }
367
368 DDebug(DebugInfo,"CoreAudioSource::init() [%p] - hardware device input format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
369 "bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
370 this,(unsigned int)devFormat.mChannelsPerFrame,devFormat.mSampleRate,(unsigned int)devFormat.mBitsPerChannel,
371 (unsigned int)devFormat.mBytesPerFrame,(unsigned int)devFormat.mFramesPerPacket,(unsigned int)devFormat.mBytesPerPacket,
372 (unsigned int)devFormat.mFormatFlags);
373
374 m_outDevFormat.mChannelsPerFrame = 1;
375 m_outDevFormat.mSampleRate = devFormat.mSampleRate;
376 m_outDevFormat.mFormatID = kAudioFormatLinearPCM;
377 m_outDevFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
378 m_outDevFormat.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
379 #if __BIG_ENDIAN__
380 m_outDevFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
381 #endif
382 m_outDevFormat.mBytesPerFrame = sizeof(int16_t) * devFormat.mChannelsPerFrame;
383 m_outDevFormat.mBitsPerChannel = m_outDevFormat.mBytesPerFrame * 8;
384 m_outDevFormat.mFramesPerPacket = 1;
385 m_outDevFormat.mBytesPerPacket = m_outDevFormat.mBytesPerFrame;
386
387 // remembering the number of channels for the device
388 m_channels = devFormat.mChannelsPerFrame;
389
390 // set the AudioUnit output data format
391 err = AudioUnitSetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,1,&m_outDevFormat,sizeof(AudioStreamBasicDescription));
392 if(err != noErr) {
393 Debug(DebugInfo, "CoreAudioSource::init() [%p] - failed to set output data format error==%4.4s, %ld",this,(char*)&err,(long int)err);
394 return false;
395 }
396
397 DDebug(DebugInfo,"CoreAudioSource::init() [%p] - AudioUnit output format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
398 "bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
399 this,(unsigned int)m_outDevFormat.mChannelsPerFrame,m_outDevFormat.mSampleRate,(unsigned int)m_outDevFormat.mBitsPerChannel,
400 (unsigned int)m_outDevFormat.mBytesPerFrame,(unsigned int)m_outDevFormat.mFramesPerPacket,(unsigned int)m_outDevFormat.mBytesPerPacket,
401 (unsigned int)m_outDevFormat.mFormatFlags);
402
403 // obtain a sample rate converter
404 err = buildConverter(m_outDevFormat,&m_audioConvert);
405 if (err != noErr) {
406 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get sample rate converter error==%4.4s, %ld",this,(char*)&err,(long int)err);
407 return false;
408 }
409
410 // get the number of frames in the IO buffer
411 UInt32 audioSamples;
412 param = sizeof(UInt32);
413 err = AudioUnitGetProperty(fAudioUnit,kAudioDevicePropertyBufferFrameSize,kAudioUnitScope_Global,0,&audioSamples,¶m);
414 if(err != noErr) {
415 Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get audio sample size error==%4.4s, %ld",this,(char*)&err,(long int)err);
416 return false;
417 }
418
419 // Initialize the AU
420 err = AudioUnitInitialize(fAudioUnit);
421 if(err != noErr) {
422 Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to initialize AU error==%4.4s, %ld",this,(char*)&err,(long int)err);
423 return false;
424 }
425
426 // allocate AudioBufferList
427 m_inAudioBuffer = allocateAudioBufferList(m_outDevFormat.mChannelsPerFrame,audioSamples * m_outDevFormat.mBytesPerFrame);
428 if(m_inAudioBuffer == NULL) {
429 Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to allocate audio buffers",this);
430 return false;
431 }
432
433 // Start pulling for audio data
434 err = AudioOutputUnitStart(fAudioUnit);
435 if(err != noErr) {
436 Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to start the AudioUnit error==%4.4s, %ld",this,(char*)&err,(long int)err);
437 return false;
438 }
439 else
440 Debug(DebugInfo,"CoreAudioSource::init() [%p] - AudioUnit started",this);
441
442 // check if the device lets us set the volume
443 m_volSettable = false;
444 for (unsigned int i = 0; i <= m_channels; i++)
445 m_volSettable = checkVolumeSettable(fInputDevID,i,true) || m_volSettable;
446 Debug(DebugAll,"CoreAudioSource::init() [%p] - volume %s settable",this,(m_volSettable ? "is" : "isn't"));
447
448 return start("CoreAudioSource");
449 }
450
buildConverter(AudioStreamBasicDescription inputFormat,AudioConverterRef * ac)451 OSStatus CoreAudioSource::buildConverter(AudioStreamBasicDescription inputFormat, AudioConverterRef* ac)
452 {
453 m_convertToFormat.mChannelsPerFrame = 1;
454 m_convertToFormat.mSampleRate = rate();
455 m_convertToFormat.mFormatID = kAudioFormatLinearPCM;
456 m_convertToFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
457 m_convertToFormat.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
458 #if __BIG_ENDIAN__
459 m_convertToFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
460 #endif
461 m_convertToFormat.mBitsPerChannel = sizeof(int16_t) * 8;
462 m_convertToFormat.mBytesPerFrame = m_convertToFormat.mBitsPerChannel / 8;
463 m_convertToFormat.mFramesPerPacket = 1;
464 m_convertToFormat.mBytesPerPacket = m_convertToFormat.mBytesPerFrame;
465
466 DDebug(DebugInfo,"CoreAudioSource::buildConverter() [%p] - AudioConverter output format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
467 "bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
468 this,(unsigned int)m_convertToFormat.mChannelsPerFrame,m_convertToFormat.mSampleRate,(unsigned int)m_convertToFormat.mBitsPerChannel,
469 (unsigned int)m_convertToFormat.mBytesPerFrame,(unsigned int)m_convertToFormat.mFramesPerPacket,(unsigned int)m_convertToFormat.mBytesPerPacket,
470 (unsigned int)m_convertToFormat.mFormatFlags);
471
472 OSStatus err = noErr;
473 err = AudioConverterNew(&inputFormat,&m_convertToFormat,ac);
474 if (err != noErr) {
475 Debug(DebugInfo,"CoreAudioSource::buildConverter() [%p] failed to get converter error==%4.4s, %ld",this,(char*)&err,(long int)err);
476 return err;
477 }
478
479 // set channel map
480 SInt32 channelMap[] = { 0 };
481 err = AudioConverterSetProperty(*ac, kAudioConverterChannelMap, sizeof(SInt32), channelMap);
482 // set converter complexity
483 UInt32 size = sizeof(kAudioConverterSampleRateConverterComplexity_Mastering);
484 UInt32 prop = kAudioConverterSampleRateConverterComplexity_Mastering;
485 err = AudioConverterSetProperty(*ac,kAudioConverterSampleRateConverterComplexity,size,&prop);
486 if (err != noErr)
487 Debug(DebugInfo,"CoreAudioSource::buildConverter() [%p] failed to set converter complexity error==%4.4s, %ld",this,(char*)&err,(long int)err);
488 return noErr;
489 }
490
allocateAudioBufferList(UInt32 numChannels,UInt32 size)491 AudioBufferList* CoreAudioSource::allocateAudioBufferList(UInt32 numChannels, UInt32 size)
492 {
493 AudioBufferList* list;
494 DDebug(DebugAll,"CoreAudioSource::allocateAudioBufferList(channels= %d,size=%d) [%p]",(int)numChannels,(int)size,this);
495 list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
496 if(list == NULL)
497 return NULL;
498
499 list->mNumberBuffers = numChannels;
500 for(UInt32 i = 0; i < numChannels; ++i) {
501 list->mBuffers[i].mNumberChannels = 1;
502 list->mBuffers[i].mDataByteSize = size;
503 list->mBuffers[i].mData = malloc(size);
504 if(list->mBuffers[i].mData == NULL) {
505 destroyAudioBufferList(list);
506 return NULL;
507 }
508 }
509 return list;
510 }
511
destroyAudioBufferList(AudioBufferList * list)512 void CoreAudioSource::destroyAudioBufferList(AudioBufferList* list)
513 {
514 DDebug(DebugAll,"CoreAudioSource::destroyAudioBufferList(list=%p) [%p]",list,this);
515 if(list) {
516 for(UInt32 i = 0; i < list->mNumberBuffers; i++) {
517 if(list->mBuffers[i].mData)
518 free(list->mBuffers[i].mData);
519 }
520 free(list);
521 }
522 }
523
sendData(AudioBufferList * buf)524 void CoreAudioSource::sendData(AudioBufferList* buf)
525 {
526 // append to internal buffer data we receive from input
527 if (!buf)
528 return;
529 lock();
530 for (unsigned int i = 0; i < m_outDevFormat.mChannelsPerFrame; i++)
531 m_data.append(buf->mBuffers[i].mData,buf->mBuffers[i].mDataByteSize);
532 XDebug(DebugAll,"CoreAudioSource::sendData(buffer=%p,buffer_length=%d), internal buffer length=%d [%p]",buf,(int)buf->mBuffers[0].mDataByteSize,m_data.length(),this);
533 unlock();
534 }
535
getData(UInt32 pkts)536 DataBlock CoreAudioSource::getData(UInt32 pkts)
537 {
538 // return to the converter a data block with the required size or the maximum available
539 DataBlock data;
540 lock();
541 if (pkts * m_outDevFormat.mBytesPerFrame > m_data.length())
542 pkts = m_data.length() / m_outDevFormat.mBytesPerFrame;
543 data.assign(m_data.data(),pkts * m_outDevFormat.mBytesPerFrame);
544 m_data.cut(-pkts * m_outDevFormat.mBytesPerFrame );
545 unlock();
546 return data;
547 }
548
inputCallback(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)549 OSStatus CoreAudioSource::inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
550 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
551 {
552 CoreAudioSource* source = (CoreAudioSource*) inRefCon;
553 OSStatus err = noErr;
554 // Render into audio buffer
555 err = AudioUnitRender(source->fAudioUnit,ioActionFlags,inTimeStamp,inBusNumber,inNumberFrames,source->m_inAudioBuffer);
556 if(err)
557 Debug(DebugInfo,"CoreAudioSource::inputCallback() [%p] AudioUnitRender() failed with error=%4.4s, %ld",source,(char*)&err,(long int)err);
558
559 source->sendData(source->m_inAudioBuffer);
560 return err;
561 }
562
run()563 void CoreAudioSource::run()
564 {
565 DataBlock frame;
566 AudioBufferList fillBufList;
567 fillBufList.mNumberBuffers = 1;
568 fillBufList.mBuffers[0].mNumberChannels = 1;
569 fillBufList.mBuffers[0].mData = new char[FRAME_SIZE];
570
571 do {
572 if (!looping())
573 break;
574 if (frame.length() < FRAME_SIZE) {
575 // try to get more data from input and convert it to the desired sample rate
576 UInt32 outBuffSize = FRAME_SIZE / m_convertToFormat.mBytesPerPacket;
577 //AudioStreamPacketDescription* pktDesc = NULL;
578 fillBufList.mBuffers[0].mDataByteSize = FRAME_SIZE;
579 OSStatus err = AudioConverterFillComplexBuffer(m_audioConvert,convertCallback,this,&outBuffSize,&fillBufList,NULL/*pktDesc*/);
580 if (err != noErr && err != 1)
581 Debug(DebugInfo,"CoreAudioSource::run() - AudioConvertFillComplexBuffer() failed with error=%4.4s, %ld", (char*)&err, (long int)err);
582 if (outBuffSize == 0) {
583 Thread::idle();
584 continue;
585 }
586 frame.append(fillBufList.mBuffers[0].mData,outBuffSize * m_convertToFormat.mBytesPerPacket);
587 }
588
589 if (frame.length() >= FRAME_SIZE) {
590 // we have enough data to send forward
591 DataBlock data(frame.data(),FRAME_SIZE,false);
592 Forward(data);
593 data.clear(false);
594 frame.cut(-FRAME_SIZE);
595 m_total += FRAME_SIZE;
596 }
597 }
598 while (true);
599 delete [] (char*)fillBufList.mBuffers[0].mData;
600 Debug(DebugAll,"CoreAudioSource [%p] end of data",this);
601 }
602
cleanup()603 void CoreAudioSource::cleanup()
604 {
605 Debug(DebugAll,"CoreAudioSource [%p] cleanup, total=%u",this,m_total);
606 AudioConverterDispose(m_audioConvert);
607 ThreadedSource::cleanup();
608 }
609
control(NamedList & params)610 bool CoreAudioSource::control(NamedList& params)
611 {
612 DDebug(DebugAll,"CoreAudioSource::control() [%p]",this);
613 if (!m_volSettable)
614 return TelEngine::controlReturn(¶ms,false);
615 int vol = params.getIntValue("in_volume",-1);
616 if (vol == -1) {
617 Debug(DebugAll,"CoreAudioSource::control() [%p] - invalid value to set for volume",this);
618 return TelEngine::controlReturn(¶ms,false);
619 }
620 Float32 volValue = vol / 100.0;
621
622 bool setVolStatus = false;
623 bool getVolStatus = false;
624 int setVolValue = 0;
625 for (unsigned int i = 0; i <= m_channels; i++) {
626 AudioObjectPropertyAddress volumeAddress = {kAudioDevicePropertyVolumeScalar,kAudioDevicePropertyScopeInput,i};
627 OSStatus err = AudioObjectSetPropertyData(fInputDevID,&volumeAddress,0,NULL,sizeof(Float32),&volValue);
628 if (err != noErr)
629 DDebug(DebugAll,"CoreAudioSource::control() [%p] - set volume failed with error=%4.4s, %ld on channel %u",this,(char*)&err,(long int)err,i);
630 setVolStatus = (err == noErr) || setVolStatus;
631
632 // get the actual set volume value
633 Float32 setVolumePerChannel = 0;
634 UInt32 size = sizeof(setVolumePerChannel);
635 err = AudioObjectGetPropertyData(fInputDevID,&volumeAddress,0,NULL,&size,&setVolumePerChannel);
636 if (err != noErr)
637 DDebug(DebugAll,"CoreAudioSource::control() [%p] - get volume failed with error=%4.4s, %ld on channel %u",this,(char*)&err,(long int)err,i);
638 else {
639 if (setVolValue / 100.0 < setVolumePerChannel)
640 setVolValue = setVolumePerChannel * 100;
641 }
642 getVolStatus = (err == noErr) || getVolStatus;
643 }
644 if (getVolStatus)
645 params.setParam("in_volume",String(setVolValue));
646 if (!setVolStatus)
647 Debug(DebugAll,"CoreAudioSource::control() [%p] - set volume failed on all channels",this);
648
649 if(params.getParam("out_volume"))
650 return TelEngine::controlReturn(¶ms,false);
651 return TelEngine::controlReturn(¶ms,setVolStatus);
652 }
653
654
CoreAudioConsumer(unsigned int rate)655 CoreAudioConsumer::CoreAudioConsumer(unsigned int rate)
656 : Mutex(false,"CoreAudioConsumer"),
657 m_total(0), m_volSettable(false), m_channels(0), fOutputDevID(0), m_rate(rate)
658 {
659 Debug(DebugAll,"CoreAudioConsumer::CoreAudioConsumer() [%p]",this);
660 if (m_rate != DEFAULT_SAMPLE_RATE)
661 m_format << "/" << m_rate;
662 }
663
~CoreAudioConsumer()664 CoreAudioConsumer::~CoreAudioConsumer()
665 {
666 Debug(DebugAll,"CoreAudioConsumer::~CoreAudioConsumer() [%p] total=%u",this,m_total);
667 OSStatus err = AudioOutputUnitStop(fAudioUnit);
668 if(err != noErr)
669 Debug(DebugInfo,"CoreAudioConsumer::~CoreAudioConsumer() [%p] - Failed to stop output AudioUnit error=%4.4s, %ld",this,(char*)&err,(long int)err);
670 err = AudioUnitUninitialize(fAudioUnit);
671 if(err != noErr)
672 Debug(DebugInfo,"CoreAudioConsumer::~CoreAudioConsumer() [%p] - Failed to uninitialize the AudioUnit error=%4.4s, %ld",this,(char*)&err,(long int)err);
673 }
674
init()675 bool CoreAudioConsumer::init()
676 {
677 OSStatus err = noErr;
678
679 // open the AudioOutputUnit, provide description
680 #ifdef MAC_OS_X_VERSION_10_6
681 AudioComponent component;
682 AudioComponentDescription description;
683 #else
684 Component component;
685 ComponentDescription description;
686 #endif
687
688 description.componentType = kAudioUnitType_Output;
689 description.componentSubType = kAudioUnitSubType_DefaultOutput;
690 description.componentManufacturer = kAudioUnitManufacturer_Apple;
691 description.componentFlags = 0;
692 description.componentFlagsMask = 0;
693
694 #ifdef MAC_OS_X_VERSION_10_6
695 if((component = AudioComponentFindNext(NULL,&description)))
696 err = AudioComponentInstanceNew(component,&fAudioUnit);
697 #else
698 if((component = FindNextComponent(NULL,&description)))
699 err = OpenAComponent(component,&fAudioUnit);
700 #endif
701
702 if(err != noErr) {
703 Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - failed to open component error==%4.4s, %ld",this,(char*)&err,(long int)err);
704 fAudioUnit = NULL;
705 return false;
706 }
707
708 // set up the callback to generate output to the output unit
709 AURenderCallbackStruct callback;
710 callback.inputProc = CoreAudioConsumer::outputCallback;
711 callback.inputProcRefCon = this;
712 err = AudioUnitSetProperty (fAudioUnit,kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Input,0,&callback,sizeof(callback));
713 if (err != noErr)
714 Debug(DebugInfo,"CoreAudioConsumer::init() [%p]- callback could not be set error=%4.4s, %ld",this,(char*)&err,(long int)err);
715
716 // provide the input format of the date we're supplying
717 AudioStreamBasicDescription inputFormat;
718 inputFormat.mSampleRate = rate();
719 inputFormat.mFormatID = kAudioFormatLinearPCM;
720 inputFormat.mBitsPerChannel = sizeof(int16_t) * 8; // = 16
721 inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8;
722 inputFormat.mFramesPerPacket = 1;
723 inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame;
724 inputFormat.mChannelsPerFrame = 1;
725 inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
726 inputFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
727
728 err = AudioUnitSetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,&inputFormat,sizeof(AudioStreamBasicDescription));
729 if (err != noErr) {
730 Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - set input format failed error==%4.4s, %ld",this,(char*)&err,(long int)err);
731 return false;
732 }
733 DDebug(DebugInfo,"CoreAudioConsumer::init() [%p] - intput format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
734 "bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
735 this,(unsigned int)inputFormat.mChannelsPerFrame,inputFormat.mSampleRate,(unsigned int)inputFormat.mBitsPerChannel,
736 (unsigned int)inputFormat.mBytesPerFrame,(unsigned int)inputFormat.mFramesPerPacket,(unsigned int)inputFormat.mBytesPerPacket,
737 (unsigned int)inputFormat.mFormatFlags);
738
739 // initialize the AudioUnit
740 err = AudioUnitInitialize(fAudioUnit);
741 if (err != noErr) {
742 Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - AudioUnitInitialize failed error=%4.4s, %ld",this,(char*)&err,(long int)err);
743 return false;
744 }
745
746 // start the AudioUnit
747 err = AudioOutputUnitStart(fAudioUnit);
748 if (err != noErr) {
749 Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - AudioUnitStart failed error=%4.4s, %ld",this,(char*)&err,(long int)err);
750 return false;
751 }
752
753 // get the id of the default output device
754 UInt32 param = sizeof(AudioDeviceID);
755 param = sizeof(AudioDeviceID);
756 AudioObjectPropertyAddress devAddress = {kAudioHardwarePropertyDefaultOutputDevice,kAudioObjectPropertyScopeGlobal,kAudioObjectPropertyElementMaster};
757 err = AudioObjectGetPropertyData(kAudioObjectSystemObject,&devAddress,0,NULL,¶m,&fOutputDevID);
758 if(err != noErr)
759 Debug(DebugMild,"CoreAudioConsumer::init() [%p] - Failed to get the device id of the output device error==%4.4s, %ld",this,(char*)&err,(long int)err);
760
761 // get hardware device format
762 param = sizeof(AudioStreamBasicDescription);
763 AudioStreamBasicDescription devFormat;
764 err = AudioUnitGetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,0,&devFormat,¶m);
765 if(err != noErr) {
766 Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - failed to get input device AudioStreamBasicDescription error==%4.4s, %ld",this,(char*)&err,(long int)err);
767 // we didn't get the hardware format, but it's a safe bet that we have at least 1 channel
768 m_channels = 1;
769 }
770 else {
771 m_channels = devFormat.mChannelsPerFrame;
772
773 DDebug(DebugInfo,"CoreAudioConsumer::init() [%p] - hardware device input format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
774 "bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
775 this,(unsigned int)devFormat.mChannelsPerFrame,devFormat.mSampleRate,(unsigned int)devFormat.mBitsPerChannel,
776 (unsigned int)devFormat.mBytesPerFrame,(unsigned int)devFormat.mFramesPerPacket,(unsigned int)devFormat.mBytesPerPacket,
777 (unsigned int)devFormat.mFormatFlags);
778 }
779
780 m_volSettable = false;
781 for (unsigned int i = 0; i <= m_channels; i++)
782 m_volSettable = checkVolumeSettable(fOutputDevID,i,false) || m_volSettable;
783 Debug(DebugAll,"CoreAudioConsumer::init() - volume %s settable",(m_volSettable ? "is" : "isn't"));
784 return true;
785 }
786
getData(AudioBufferList * buf)787 void CoreAudioConsumer::getData(AudioBufferList* buf)
788 {
789 if (!buf)
790 return;
791 // put the data into the output buffer
792 UInt32 len = buf->mBuffers[0].mDataByteSize; // there should be only one buffer;
793 lock();
794 if (m_data.length() == 0) {
795 ::memset(buf->mBuffers[0].mData,0,len);
796 unlock();
797 return;
798 }
799 if (len > m_data.length())
800 len = m_data.length();
801 if (len > 0) {
802 ::memcpy(buf->mBuffers[0].mData,m_data.data(),len);
803 m_data.cut(-len);
804 }
805 unlock();
806 }
807
outputCallback(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)808 OSStatus CoreAudioConsumer::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
809 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
810 {
811 CoreAudioConsumer* dst = static_cast<CoreAudioConsumer*>(inRefCon);
812 if (!dst)
813 return 1;
814 XDebug(DebugAll,"CoreAudioConsumer::outputCallback() [%p] inNumberFrames=%d buffersCount=%d buffersize=%d",dst,(unsigned int)inNumberFrames,
815 (unsigned int)ioData->mNumberBuffers,(unsigned int)ioData->mBuffers[0].mDataByteSize);
816 dst->getData(ioData);
817 return noErr;
818 }
819
Consume(const DataBlock & data,unsigned long tStamp,unsigned long flags)820 unsigned long CoreAudioConsumer::Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags)
821 {
822 // append to the internal buffer received data
823 if (data.null())
824 return 0;
825 lock();
826 m_total += data.length();
827 m_data.append(data);
828 unlock();
829 return invalidStamp();
830 }
831
control(NamedList & params)832 bool CoreAudioConsumer::control(NamedList& params)
833 {
834 DDebug(DebugAll,"CoreAudioConsumer::control() [%p]",this);
835 if (!m_volSettable)
836 return TelEngine::controlReturn(¶ms,false);
837 int vol = params.getIntValue("out_volume",-1);
838 if (vol == -1) {
839 Debug(DebugAll,"CoreAudioConsumer::control() [%p] invalid value to set for volume",this);
840 return TelEngine::controlReturn(¶ms,false);
841 }
842 Float32 volValue = vol / 100.0;
843
844 bool setVolStatus = false;
845 bool getVolStatus = false;
846 int setVolValue = 0;
847 for (unsigned int i = 0; i <= m_channels; i++) {
848 // set the volume for the output on every channel
849 AudioObjectPropertyAddress volumeAddress = {kAudioDevicePropertyVolumeScalar,kAudioDevicePropertyScopeOutput,i};
850 OSStatus err = AudioObjectSetPropertyData(fOutputDevID,&volumeAddress,0,NULL,sizeof(Float32),&volValue);
851 if (err != noErr)
852 DDebug(DebugAll,"CoreAudioConsumer::control() [%p] - set volume failed with error=%4.4s, %ld on channel %u",this,(char*)&err,(long int)err,i);
853 setVolStatus = (err == noErr) || setVolStatus;
854
855 // get the actual set volume value
856 Float32 setVolumePerChannel = 0;
857 UInt32 size = sizeof(setVolumePerChannel);
858 err = AudioObjectGetPropertyData(fOutputDevID,&volumeAddress,0,NULL,&size,&setVolumePerChannel);
859 if (err != noErr)
860 DDebug(DebugAll,"CoreAudioComsumer::control() [%p] - get volume failed with error=%4.4s, %ld on channel %u",this,(char*)&err,(long int)err,i);
861 else {
862 if (setVolValue / 100.0 < setVolumePerChannel)
863 setVolValue = setVolumePerChannel * 100;
864 }
865 getVolStatus = (err == noErr) || getVolStatus;
866 }
867 if (getVolStatus)
868 params.setParam("out_volume",String(setVolValue));
869 if (!setVolStatus)
870 Debug(DebugAll,"CoreAudioConsumer::control() [%p] - set volume failed on all channels",this);
871
872 return TelEngine::controlReturn(¶ms,setVolStatus);
873 }
874
CoreAudioChan(const String & dev,unsigned int rate)875 CoreAudioChan::CoreAudioChan(const String& dev, unsigned int rate)
876 : CallEndpoint("coreaudio"),
877 m_dev(dev), m_rate(rate)
878 {
879 Debug(DebugAll,"CoreAudioChan::CoreAudioChan ('%s') [%p]",dev.c_str(),this);
880 s_audioChan = this;
881 }
882
~CoreAudioChan()883 CoreAudioChan::~CoreAudioChan()
884 {
885 Debug(DebugAll,"CoreAudioChan::~CoreAudioChan() [%p]",this);
886 setTarget();
887 setSource();
888 setConsumer();
889 s_audioChan = 0;
890 }
891
init()892 bool CoreAudioChan::init()
893 {
894 CoreAudioSource* source = new CoreAudioSource(rate());
895 if (!source->init()) {
896 source->deref();
897 return false;
898 }
899 setSource(source);
900 source->deref();
901 CoreAudioConsumer* cons = new CoreAudioConsumer(rate());
902 if (!cons->init()) {
903 cons->deref();
904 setSource();
905 return false;
906 }
907 setConsumer(cons);
908 cons->deref();
909 return true;
910 }
911
disconnected(bool final,const char * reason)912 void CoreAudioChan::disconnected(bool final, const char *reason)
913 {
914 Debug(DebugInfo,"CoreAudioChan::disconnected() '%s' [%p]",reason,this);
915 setTarget();
916 }
917
answer()918 void CoreAudioChan::answer()
919 {
920 Message* m = new Message("call.answered");
921 m->addParam("module","coreaudio");
922 String tmp("coreaudio/");
923 tmp += m_dev;
924 m->addParam("id",tmp);
925 if (m_target)
926 m->addParam("targetid",m_target);
927 Engine::enqueue(m);
928 }
929
received(Message & msg)930 bool CoreAudioHandler::received(Message &msg)
931 {
932 Debug(DebugInfo,"CoreAudio received call.execute");
933 String dest(msg.getValue("callto"));
934 if (dest.null())
935 return false;
936 static const Regexp r("^coreaudio/\\(.*\\)$");
937 if (!dest.matches(r))
938 return false;
939 if (s_audioChan) {
940 msg.setParam("error","busy");
941 return false;
942 }
943 CoreAudioChan *chan = new CoreAudioChan(dest.matchString(1).c_str(),msg.getIntValue("rate",DEFAULT_SAMPLE_RATE));
944 if (!chan->init()) {
945 chan->destruct();
946 return false;
947 }
948 CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
949 Debug(DebugInfo,"We are routing to device '%s'",dest.matchString(1).c_str());
950 if (ch && chan->connect(ch,msg.getValue("reason"))) {
951 chan->setTarget(msg.getValue("id"));
952 msg.setParam("peerid",dest);
953 msg.setParam("targetid",dest);
954 chan->answer();
955 chan->deref();
956 }
957 else {
958 const char *direct = msg.getValue("direct");
959 if (direct) {
960 Message m("call.execute");
961 m.addParam("module","audiocore");
962 m.addParam("cdrtrack",String::boolText(false));
963 m.addParam("id",dest);
964 m.addParam("caller",dest);
965 m.addParam("callto",direct);
966 m.userData(chan);
967 if (Engine::dispatch(m)) {
968 chan->setTarget(m.getValue("targetid"));
969 msg.addParam("targetid",chan->getTarget());
970 chan->deref();
971 return true;
972 }
973 Debug(DebugInfo,"CoreAudio outgoing call not accepted!");
974 chan->destruct();
975 return false;
976 }
977 const char *targ = msg.getValue("target");
978 if (!targ) {
979 Debug(DebugWarn,"CoreAudio outgoing call with no target!");
980 chan->destruct();
981 return false;
982 }
983 Message m("call.route");
984 m.addParam("module","audiocore");
985 m.addParam("cdrtrack",String::boolText(false));
986 m.addParam("id",dest);
987 m.addParam("caller",dest);
988 m.addParam("called",targ);
989 if (Engine::dispatch(m)) {
990 m = "call.execute";
991 m.addParam("callto",m.retValue());
992 m.retValue() = 0;
993 m.userData(chan);
994 if (Engine::dispatch(m)) {
995 chan->setTarget(m.getValue("targetid"));
996 msg.addParam("targetid",chan->getTarget());
997 chan->deref();
998 return true;
999 }
1000 Debug(DebugInfo,"CoreAudio outgoing call not accepted!");
1001 }
1002 else
1003 Debug(DebugWarn,"CoreAudio outgoing call but no route!");
1004 chan->destruct();
1005 return false;
1006 }
1007
1008 return true;
1009 }
1010
received(Message & msg)1011 bool StatusHandler::received(Message &msg)
1012 {
1013 const String* sel = msg.getParam("module");
1014 if (sel && (*sel != "coreaudio"))
1015 return false;
1016 msg.retValue() << "name=coreaudio,type=misc;chan=" << (s_audioChan != 0 ) << "\r\n";
1017 return false;
1018 }
1019
received(Message & msg)1020 bool MasqHandler::received(Message &msg)
1021 {
1022 String id(msg.getValue("id"));
1023 if (msg.getParam("message") && id.startsWith("coreaudio/")) {
1024 msg = msg.getValue("message");
1025 msg.clearParam("message");
1026 if (s_audioChan) {
1027 msg.addParam("targetid",s_audioChan->getTarget());
1028 msg.userData(s_audioChan);
1029 }
1030 }
1031 return false;
1032 }
1033
received(Message & msg)1034 bool DropHandler::received(Message &msg)
1035 {
1036 String id(msg.getValue("id"));
1037 if (id.null() || id.startsWith("coreaudio/")) {
1038 if (s_audioChan) {
1039 Debug("CoreAudio",DebugInfo,"ping call");
1040 s_audioChan->disconnect();
1041 }
1042 return !id.null();
1043 }
1044 return false;
1045 }
1046
received(Message & msg)1047 bool AttachHandler::received(Message& msg)
1048 {
1049 int more = 2;
1050 String src(msg.getValue("source"));
1051 if (src.null())
1052 more--;
1053 else {
1054 if (!src.startSkip("coreaudio/",false))
1055 src = "";
1056 }
1057
1058 String cons(msg.getValue("consumer"));
1059 if (cons.null())
1060 more--;
1061 else {
1062 if (!cons.startSkip("coreaudio/",false))
1063 cons = "";
1064 }
1065
1066 if (src.null() && cons.null())
1067 return false;
1068 if (src && cons && (src != cons)) {
1069 Debug(DebugWarn,"CoreAudio asked to attach source '%s' and consumer '%s'",src.c_str(),cons.c_str());
1070 return false;
1071 }
1072
1073 RefPointer<DataEndpoint> dd = static_cast<DataEndpoint*>(msg.userObject(YATOM("DataEndpoint")));
1074 if (!dd) {
1075 CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userObject(YATOM("CallEndpoint")));
1076 if (ch) {
1077 DataEndpoint::commonMutex().lock();
1078 dd = ch->setEndpoint();
1079 DataEndpoint::commonMutex().unlock();
1080 if (!dd)
1081 return false;
1082 }
1083 }
1084 if (!dd) {
1085 Debug(DebugWarn,"CoreAudio attach request with no control or data channel!");
1086 return false;
1087 }
1088
1089 if (src) {
1090 CoreAudioSource* s = new CoreAudioSource(msg.getIntValue("rate",DEFAULT_SAMPLE_RATE));
1091 if (s->init())
1092 dd->setSource(s);
1093 s->deref();
1094 }
1095
1096 if (cons) {
1097 CoreAudioConsumer* c = new CoreAudioConsumer(msg.getIntValue("rate",DEFAULT_SAMPLE_RATE));
1098 if (c->init())
1099 dd->setConsumer(c);
1100 c->deref();
1101 }
1102
1103 // Stop dispatching if we handled all requested
1104 return !more;
1105 }
1106
CoreAudioPlugin()1107 CoreAudioPlugin::CoreAudioPlugin()
1108 : Plugin("coreaudio"),
1109 m_handler(0)
1110 {
1111 Output("Loaded module CoreAudio");
1112 }
1113
initialize()1114 void CoreAudioPlugin::initialize()
1115 {
1116 Output("Initializing module CoreAudio");
1117 if (!m_handler) {
1118 m_handler = new CoreAudioHandler("call.execute");
1119 Engine::install(m_handler);
1120 Engine::install(new MasqHandler(10));
1121 Engine::install(new DropHandler());
1122 Engine::install(new StatusHandler());
1123 Engine::install(new AttachHandler());
1124 }
1125 }
1126
isBusy() const1127 bool CoreAudioPlugin::isBusy() const
1128 {
1129 return (s_audioChan != 0);
1130 }
1131
1132 }; // anonymous namespace
1133
1134 /* vi: set ts=8 sw=4 sts=4 noet: */
1135