Ticket #2539: audiooutputca.2.cpp

File audiooutputca.2.cpp, 35.0 KB (added by awk@…, 19 years ago)

Updated audiooutputca.cpp to reset devices with AC3 streams

Line 
1/*****************************************************************************
2 * = NAME
3 * audiooutputca.cpp
4 *
5 * = DESCRIPTION
6 * Core Audio glue for Mac OS X.
7 * This plays MythTV audio through the default output device on OS X.
8 *
9 * = REVISION
10 * $Id: audiooutputca.cpp 11165 2006-09-12 10:45:06Z nigel $
11 *
12 * = AUTHORS
13 * Jeremiah Morris
14 *****************************************************************************/
15
16#include <CoreServices/CoreServices.h>
17#include <CoreAudio/CoreAudio.h>
18#include <AudioUnit/AudioUnit.h>
19
20using namespace std;
21
22#include "mythcontext.h"
23#include "audiooutputca.h"
24#include "config.h"
25
26// this holds Core Audio member variables
27class CoreAudio_impl {
28 public:
29 CoreAudio_impl(AudioOutputCA *inSelf);
30
31 // Volume control
32 int GetVolumeChannel(int channel); // Returns 0-100
33 void SetVolumeChannel(int channel, int volume); // range 0-100 for vol
34
35 // You need to implement the following functions
36 bool OpenDevice(void);
37 bool OpenAnalogDevice(void);
38 bool OpenSPDIFDevice(void);
39 void CloseDevice(void);
40 void CloseSPDIF(void);
41
42 int getBufferedOnSoundcard(void);
43
44 void Error(QString msg)
45 {
46 VERBOSE(VB_IMPORTANT, "AudioOutput Error: " + msg);
47 }
48
49 void Warn(QString msg)
50 {
51 VERBOSE(VB_IMPORTANT, "AudioOutput Warning: " + msg);
52 }
53
54 void ReleaseHogMode();
55 bool FindAC3Stream();
56 void ResetAudioDevices();
57
58 AudioUnit mOutputUnit;
59
60 AudioDeviceID mSelectedDevice; /* Keeps DeviceID of the selected device */
61
62 /* CoreAudio SPDIF mode specific */
63 bool mDigitalInUse; /* Digital (SPDIF) output in use */
64 pid_t mHogModePID; /* The keep the pid of our hog status */
65 AudioStreamID mStreamID; /* The StreamID that has a cac3 streamformat */
66 int mStreamIndex; /* The index of mStreamID in an AudioBufferList */
67 AudioStreamBasicDescription mStreamFormat; /* The format we changed the stream to */
68 AudioStreamBasicDescription mStreamFmtRevert; /* The original format of the stream */
69 bool mRevert; /* Wether we need to revert the stream format */
70 bool mChangedMixing;/* Wether we need to set the mixing mode back */
71
72 int mBufferedBytes; /* Data buffered on the soundcard */
73
74 AudioOutputCA *mSelf;
75
76 // callback for delivering audio to output device
77 bool RenderAudio(unsigned char *aubuf, int size,
78 unsigned long long timestamp);
79
80 int AudioStreamChangeFormat( AudioStreamID mStreamID, AudioStreamBasicDescription change_format );
81};
82
83// This callback communicates with Core Audio.
84static OSStatus RenderCallbackAnalog(void *inRefCon,
85 AudioUnitRenderActionFlags *ioActionFlags,
86 const AudioTimeStamp *inTimeStamp,
87 UInt32 inBusNumber,
88 UInt32 inNumberFrames,
89 AudioBufferList *ioData);
90
91static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
92 const AudioTimeStamp * inNow,
93 const void * inInputData,
94 const AudioTimeStamp * inInputTime,
95 AudioBufferList * outOutputData,
96 const AudioTimeStamp * inOutputTime,
97 void * threadGlobals );
98
99/** \class AudioOutputCA
100 * \brief Implements Core Audio (Mac OS X Hardware Abstraction Layer) output.
101 */
102
103AudioOutputCA::AudioOutputCA(
104 QString laudio_main_device, QString laudio_passthru_device,
105 int laudio_bits, int laudio_channels,
106 int laudio_samplerate, AudioOutputSource lsource,
107 bool lset_initial_vol, bool laudio_passthru)
108 : AudioOutputBase(laudio_main_device, laudio_passthru_device,
109 laudio_bits, laudio_channels,
110 laudio_samplerate, lsource,
111 lset_initial_vol, laudio_passthru),
112 mPImpl(new CoreAudio_impl(this))
113{
114 if (laudio_passthru && !mPImpl->FindAC3Stream())
115 {
116 VERBOSE(VB_AUDIO, "AudioOutputCA::AudioOutputCA - Configured for AC3 passthru but could not find an AC3 output stream");
117 Reconfigure(laudio_bits, laudio_channels,
118 laudio_samplerate, false);
119 }
120 else
121 {
122 Reconfigure(laudio_bits, laudio_channels,
123 laudio_samplerate, laudio_passthru);
124 }
125}
126
127AudioOutputCA::~AudioOutputCA()
128{
129 KillAudio();
130
131 delete mPImpl;
132}
133
134bool AudioOutputCA::OpenDevice()
135{
136 bool deviceOpened = mPImpl->OpenDevice();
137
138 if (deviceOpened && internal_vol && set_initial_vol)
139 {
140 QString controlLabel = gContext->GetSetting("MixerControl", "PCM");
141 controlLabel += "MixerVolume";
142 SetCurrentVolume(gContext->GetNumSetting(controlLabel, 80));
143 }
144
145 return deviceOpened;
146}
147
148void AudioOutputCA::CloseDevice()
149{
150 mPImpl->CloseDevice();
151}
152
153
154/* Object-oriented part of callback */
155bool CoreAudio_impl::RenderAudio(unsigned char *aubuf,
156 int size,
157 unsigned long long timestamp)
158{
159 if (mSelf->pauseaudio || mSelf->killaudio)
160 {
161 mSelf->audio_actually_paused = true;
162 return false;
163 }
164
165 /* This callback is called when the sound system requests
166 data. We don't want to block here, because that would
167 just cause dropouts anyway, so we always return whatever
168 data is available. If we haven't received enough, either
169 because we've finished playing or we have a buffer
170 underrun, we play silence to fill the unused space. */
171
172 int written_size = mSelf->GetAudioData(aubuf, size, false);
173 if (written_size && (size > written_size))
174 {
175 // play silence on buffer underrun
176 bzero(aubuf + written_size, size - written_size);
177 }
178
179 /* update audiotime (bufferedBytes is read by getBufferedOnSoundcard) */
180 UInt64 nanos = AudioConvertHostTimeToNanos(
181 timestamp - AudioGetCurrentHostTime());
182 mBufferedBytes = (int)((nanos / 1000000000.0) * // secs
183 (mSelf->effdsp / 100.0) * // samples/sec
184 mSelf->audio_bytes_per_sample); // bytes/sample
185 mSelf->SetAudiotime();
186
187 return (written_size > 0);
188}
189
190void AudioOutputCA::WriteAudio(unsigned char *aubuf, int size)
191{
192 (void)aubuf;
193 (void)size;
194 return; // unneeded and unused in CA
195}
196
197int AudioOutputCA::getSpaceOnSoundcard(void)
198{
199 return 0; // unneeded and unused in CA
200}
201
202int AudioOutputCA::getBufferedOnSoundcard(void)
203{
204 return mPImpl->getBufferedOnSoundcard();
205}
206
207void AudioOutputCA::StartOutputThread(void)
208{
209 return; // no thread for CA
210}
211
212void AudioOutputCA::StopOutputThread(void)
213{
214 return; // no thread for CA
215}
216
217int AudioOutputCA::GetVolumeChannel(int channel)
218{
219 return mPImpl->GetVolumeChannel(channel);
220}
221
222void AudioOutputCA::SetVolumeChannel(int channel, int volume)
223{
224 mPImpl->SetVolumeChannel(channel, volume);
225}
226
227/* This callback provides converted audio data to the default output device. */
228OSStatus RenderCallbackAnalog(void *inRefCon,
229 AudioUnitRenderActionFlags *ioActionFlags,
230 const AudioTimeStamp *inTimeStamp,
231 UInt32 inBusNumber,
232 UInt32 inNumberFrames,
233 AudioBufferList *ioData)
234{
235 (void)inBusNumber;
236 (void)inNumberFrames;
237
238 CoreAudio_impl *inst = (CoreAudio_impl *)inRefCon;
239
240 if (!inst->RenderAudio((unsigned char *)(ioData->mBuffers[0].mData),
241 ioData->mBuffers[0].mDataByteSize,
242 inTimeStamp->mHostTime))
243 {
244 // play silence if RenderAudio returns false
245 bzero(ioData->mBuffers[0].mData, ioData->mBuffers[0].mDataByteSize);
246 *ioActionFlags = kAudioUnitRenderAction_OutputIsSilence;
247 }
248 return noErr;
249}
250
251
252/*****************************************************************************
253 * RenderCallbackSPDIF: callback for SPDIF audio output
254 *****************************************************************************/
255static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice,
256 const AudioTimeStamp * inNow,
257 const void * inInputData,
258 const AudioTimeStamp * inInputTime,
259 AudioBufferList * outOutputData,
260 const AudioTimeStamp * inOutputTime,
261 void * inRefCon )
262{
263 (void)inDevice; // unused
264 (void)inNow; // unused
265 (void)inInputData; // unused
266 (void)inInputTime; // unused
267
268 CoreAudio_impl *inst = (CoreAudio_impl *)inRefCon;
269
270 if (!inst->RenderAudio((unsigned char *)(outOutputData->mBuffers[inst->mStreamIndex].mData),
271 outOutputData->mBuffers[inst->mStreamIndex].mDataByteSize,
272 inOutputTime->mHostTime))
273 {
274 // play silence if RenderAudio returns false
275 bzero(outOutputData->mBuffers[inst->mStreamIndex].mData, outOutputData->mBuffers[inst->mStreamIndex].mDataByteSize);
276 }
277 return noErr;
278}
279
280
281CoreAudio_impl::CoreAudio_impl(AudioOutputCA *inSelf) : mSelf(inSelf)
282{
283 UInt32 i_param_size;
284 AudioDeviceID devid_def = -1;
285 OSStatus err;
286
287 // Create private data
288 mOutputUnit = NULL;
289 mSelectedDevice = 0;
290 mDigitalInUse = false;
291 mRevert = false;
292
293 /* Reset all the devices to a default 'non-hog' (ie mixable) format
294 * if we don't do this we may be unable to find the Default Output device
295 * if we crashed last time leaving it stuck in AC-3 mode */
296 ResetAudioDevices();
297
298 /* Find the ID of the default Device */
299 i_param_size = sizeof( AudioDeviceID );
300 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
301 &i_param_size, &devid_def );
302 if( err != noErr )
303 {
304 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl:CoreAudio_impl - could not get default audio device: %1").arg(err, 0, 16));
305 }
306 mSelectedDevice = devid_def;
307 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::CoreAudio_impl - default device ID = %1").arg(mSelectedDevice, 0, 16));
308}
309
310bool CoreAudio_impl::OpenDevice()
311{
312 OSStatus err = noErr;
313 UInt32 i_param_size = 0;
314
315 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::OpenDevice - Device ID = %1").arg(mSelectedDevice, 0, 16));
316 mStreamIndex = -1;
317
318 i_param_size = sizeof( mHogModePID );
319 err = AudioDeviceGetProperty( mSelectedDevice, 0, FALSE,
320 kAudioDevicePropertyHogMode,
321 &i_param_size, &mHogModePID );
322
323 if( err != noErr )
324 {
325 /* This is not a fatal error. Some drivers simply don't support this property */
326 VERBOSE( VB_AUDIO, QString("CoreAudio_impl::OpenDevice - could not check whether device is hogged: %1").arg(err, 0,16));
327 mHogModePID = -1;
328 }
329
330 if( mHogModePID != -1 && mHogModePID != getpid() )
331 {
332 VERBOSE(VB_IMPORTANT, QString("Selected audio device is exclusively in use by another program - pid = %1").arg(mHogModePID));
333 return false;
334 }
335
336 bool deviceOpened = false;
337 if (mSelf->audio_passthru)
338 {
339 deviceOpened = OpenSPDIFDevice();
340 }
341
342 // If we failed to open the SPDIF device then try openning the analog device instead.
343 if (!deviceOpened)
344 {
345 deviceOpened = OpenAnalogDevice();
346 }
347
348 return deviceOpened;
349}
350
351bool CoreAudio_impl::OpenAnalogDevice()
352{
353 // Get default output device
354 ComponentDescription desc;
355 desc.componentType = kAudioUnitType_Output;
356 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
357 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
358 desc.componentFlags = 0;
359 desc.componentFlagsMask = 0;
360
361 Component comp = FindNextComponent(NULL, &desc);
362 if (comp == NULL)
363 {
364 Error(QString("FindNextComponent failed"));
365 return false;
366 }
367
368 OSStatus err = OpenAComponent(comp, &mOutputUnit);
369 if (err)
370 {
371 Error(QString("OpenAComponent returned %1").arg((long)err));
372 return false;
373 }
374
375 // Attach callback to default output
376 AURenderCallbackStruct input;
377 input.inputProc = RenderCallbackAnalog;
378 input.inputProcRefCon = this;
379
380 err = AudioUnitSetProperty(mOutputUnit,
381 kAudioUnitProperty_SetRenderCallback,
382 kAudioUnitScope_Input,
383 0,
384 &input,
385 sizeof(input));
386 if (err)
387 {
388 Error(QString("AudioUnitSetProperty (callback) returned %1")
389 .arg((long)err));
390 return false;
391 }
392
393 // base class does this after OpenDevice, but we need it now
394 mSelf->audio_bytes_per_sample = mSelf->audio_channels * mSelf->audio_bits / 8;
395
396 // Set up the audio output unit
397 AudioStreamBasicDescription conv_in_desc;
398 bzero(&conv_in_desc, sizeof(AudioStreamBasicDescription));
399 conv_in_desc.mSampleRate = mSelf->audio_samplerate;
400 conv_in_desc.mFormatID = kAudioFormatLinearPCM;
401 conv_in_desc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
402#ifdef WORDS_BIGENDIAN
403 conv_in_desc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
404#endif
405 conv_in_desc.mBytesPerPacket = mSelf->audio_bytes_per_sample;
406 conv_in_desc.mFramesPerPacket = 1;
407 conv_in_desc.mBytesPerFrame = mSelf->audio_bytes_per_sample;
408 conv_in_desc.mChannelsPerFrame = mSelf->audio_channels;
409 conv_in_desc.mBitsPerChannel = mSelf->audio_bits;
410
411 err = AudioUnitSetProperty(mOutputUnit,
412 kAudioUnitProperty_StreamFormat,
413 kAudioUnitScope_Input,
414 0,
415 &conv_in_desc,
416 sizeof(AudioStreamBasicDescription));
417 if (err)
418 {
419 Error(QString("AudioUnitSetProperty returned %1").arg((long)err));
420 return false;
421 }
422
423 // We're all set up - start the audio output unit
424 ComponentResult res = AudioUnitInitialize(mOutputUnit);
425 if (res)
426 {
427 Error(QString("AudioUnitInitialize returned %1").arg((long)res));
428 return false;
429 }
430
431 err = AudioOutputUnitStart(mOutputUnit);
432 if (err)
433 {
434 Error(QString("AudioOutputUnitStart returned %1").arg((long)err));
435 return false;
436 }
437 return true;
438}
439
440bool CoreAudio_impl::OpenSPDIFDevice()
441{
442 OSStatus err = noErr;
443 UInt32 i_param_size = 0, b_mix = 0;
444 Boolean b_writeable = false;
445 AudioStreamID *p_streams = NULL;
446 int i = 0, i_streams = 0;
447
448 /* Start doing the SPDIF setup proces */
449
450 /* Hog the device */
451 i_param_size = sizeof( mHogModePID );
452 mHogModePID = getpid() ;
453
454 err = AudioDeviceSetProperty( mSelectedDevice, 0, 0, FALSE,
455 kAudioDevicePropertyHogMode, i_param_size, &mHogModePID );
456
457 if( err != noErr )
458 {
459 VERBOSE( VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - failed to set hogmode: %1").arg(err, 0, 16));
460 return false;
461 }
462
463 /* Set mixable to false if we are allowed to */
464 err = AudioDeviceGetPropertyInfo( mSelectedDevice, 0, FALSE, kAudioDevicePropertySupportsMixing,
465 &i_param_size, &b_writeable );
466
467 err = AudioDeviceGetProperty( mSelectedDevice, 0, FALSE, kAudioDevicePropertySupportsMixing,
468 &i_param_size, &b_mix );
469
470 if( !err && b_writeable )
471 {
472 b_mix = 0;
473 err = AudioDeviceSetProperty( mSelectedDevice, 0, 0, FALSE,
474 kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
475 mChangedMixing = true;
476 }
477
478 if( err != noErr )
479 {
480 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - failed to set mixmode: %1").arg(err));
481 ReleaseHogMode();
482 return false;
483 }
484
485 /* Get a list of all the streams on this device */
486 err = AudioDeviceGetPropertyInfo( mSelectedDevice, 0, FALSE,
487 kAudioDevicePropertyStreams,
488 &i_param_size, NULL );
489 if( err != noErr )
490 {
491 VERBOSE( VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not get number of streams: %1").arg(err));
492 ReleaseHogMode();
493 return false;
494 }
495
496 i_streams = i_param_size / sizeof( AudioStreamID );
497 p_streams = (AudioStreamID *)malloc( i_param_size );
498 if( p_streams == NULL )
499 {
500 VERBOSE(VB_IMPORTANT, "CoreAudio_impl::OpenSPDIF - out of memory" );
501 ReleaseHogMode();
502 return false;
503 }
504
505 err = AudioDeviceGetProperty( mSelectedDevice, 0, FALSE,
506 kAudioDevicePropertyStreams,
507 &i_param_size, p_streams );
508
509 if( err != noErr )
510 {
511 VERBOSE( VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not get number of streams: %1").arg(err));
512 if( p_streams ) free( p_streams );
513 ReleaseHogMode();
514 return false;
515 }
516
517 for( i = 0; i < i_streams && mStreamIndex < 0 ; i++ )
518 {
519 /* Find a stream with a cac3 stream */
520 AudioStreamBasicDescription *p_format_list = NULL;
521 int i_formats = 0, j = 0;
522
523 /* Retrieve all the stream formats supported by each output stream */
524 err = AudioStreamGetPropertyInfo( p_streams[i], 0,
525 kAudioStreamPropertyPhysicalFormats,
526 &i_param_size, NULL );
527 if( err != noErr )
528 {
529 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not get number of streamformats: %1").arg(err));
530 continue;
531 }
532
533 i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
534 p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
535 if( p_format_list == NULL )
536 {
537 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not malloc the memory" ));
538 continue;
539 }
540
541 err = AudioStreamGetProperty( p_streams[i], 0,
542 kAudioStreamPropertyPhysicalFormats,
543 &i_param_size, p_format_list );
544 if( err != noErr )
545 {
546 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not get the list of streamformats: %1").arg(err));
547 if( p_format_list) free( p_format_list);
548 continue;
549 }
550
551 /* Check if one of the supported formats is a digital format */
552 for( j = 0; j < i_formats; j++ )
553 {
554 if( p_format_list[j].mFormatID == 'IAC3' ||
555 p_format_list[j].mFormatID == kAudioFormat60958AC3 )
556 {
557 VERBOSE(VB_AUDIO, "CoreAudio_impl::OpenSPDIF - found digital format");
558 mDigitalInUse = true;
559 break;
560 }
561 }
562
563 if (!mDigitalInUse)
564 {
565 ReleaseHogMode();
566 return false; // No AC3 format streams - try and fallback to analog
567 }
568
569 if( mDigitalInUse )
570 {
571 /* if this stream supports a digital (cac3) format, then go set it. */
572 int i_requested_rate_format = -1;
573 int i_current_rate_format = -1;
574 int i_backup_rate_format = -1;
575
576 mStreamID = p_streams[i];
577 mStreamIndex = i;
578
579 if( mRevert == false )
580 {
581 /* Retrieve the original format of this stream first if not done so already */
582 i_param_size = sizeof( mStreamFmtRevert );
583 err = AudioStreamGetProperty( mStreamID, 0,
584 kAudioStreamPropertyPhysicalFormat,
585 &i_param_size,
586 &mStreamFmtRevert );
587 if( err != noErr )
588 {
589 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - could not retrieve the original streamformat: %1").arg(err));
590 continue;
591 }
592 mRevert = true;
593 }
594
595 for( j = 0; j < i_formats; j++ )
596 {
597 if( p_format_list[j].mFormatID == 'IAC3' ||
598 p_format_list[j].mFormatID == kAudioFormat60958AC3 )
599 {
600 if( p_format_list[j].mSampleRate == mSelf->audio_samplerate )
601 {
602 i_requested_rate_format = j;
603 break;
604 }
605 else if( p_format_list[j].mSampleRate == mStreamFmtRevert.mSampleRate )
606 {
607 i_current_rate_format = j;
608 }
609 else
610 {
611 if( i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate )
612 i_backup_rate_format = j;
613 }
614 }
615
616 }
617
618 if( i_requested_rate_format >= 0 ) /* We prefer to output at the samplerate of the original audio */
619 mStreamFormat = p_format_list[i_requested_rate_format];
620 else if( i_current_rate_format >= 0 ) /* If not possible, we will try to use the current samplerate of the device */
621 mStreamFormat = p_format_list[i_current_rate_format];
622 else mStreamFormat = p_format_list[i_backup_rate_format]; /* And if we have to, any digital format will be just fine (highest rate possible) */
623 }
624 if( p_format_list ) free( p_format_list );
625 }
626 if( p_streams ) free( p_streams );
627
628 if( !AudioStreamChangeFormat( mStreamID, mStreamFormat ) )
629 {
630 ReleaseHogMode();
631 return false;
632 }
633
634 /* Add IOProc callback */
635 err = AudioDeviceAddIOProc( mSelectedDevice,
636 (AudioDeviceIOProc)RenderCallbackSPDIF,
637 (void *)this );
638 if( err != noErr )
639 {
640 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - AudioDeviceAddIOProc failed: %1").arg(err));
641 ReleaseHogMode();
642 return false;
643 }
644
645 /* Start device */
646 err = AudioDeviceStart( mSelectedDevice, (AudioDeviceIOProc)RenderCallbackSPDIF );
647 if( err != noErr )
648 {
649 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - AudioDeviceStart failed: %1").arg(err));
650
651 err = AudioDeviceRemoveIOProc( mSelectedDevice,
652 (AudioDeviceIOProc)RenderCallbackSPDIF );
653 if( err != noErr )
654 {
655 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::OpenSPDIF - AudioDeviceRemoveIOProc failed: %1").arg(err));
656 }
657 ReleaseHogMode();
658 return false;
659 }
660
661 return true;
662}
663
664void CoreAudio_impl::CloseDevice()
665{
666 if (mOutputUnit)
667 {
668 AudioOutputUnitStop(mOutputUnit);
669 AudioUnitUninitialize(mOutputUnit);
670 AudioUnitReset(mOutputUnit,
671 kAudioUnitScope_Input, NULL);
672 CloseComponent(mOutputUnit);
673 mOutputUnit = NULL;
674 }
675
676 if (mDigitalInUse)
677 {
678 // Close SPDIF
679 CloseSPDIF();
680 }
681 if (mHogModePID == getpid())
682 {
683 ReleaseHogMode();
684 }
685}
686
687void CoreAudio_impl::CloseSPDIF()
688{
689 OSStatus err;
690 UInt32 i_param_size;
691
692 /* Stop device */
693 err = AudioDeviceStop( mSelectedDevice,
694 (AudioDeviceIOProc)RenderCallbackSPDIF );
695 if( err != noErr )
696 {
697 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::CloseSPDIF - AudioDeviceStop failed: %1").arg(err, 0, 16));
698 }
699
700 /* Remove IOProc callback */
701 err = AudioDeviceRemoveIOProc( mSelectedDevice,
702 (AudioDeviceIOProc)RenderCallbackSPDIF );
703 if( err != noErr )
704 {
705 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::CloseSPDIF - AudioDeviceRemoveIOProc failed: %1").arg(err, 0, 16));
706 }
707
708 if( mRevert )
709 {
710 AudioStreamChangeFormat( mStreamID, mStreamFmtRevert );
711 }
712
713 if( mChangedMixing && mStreamFmtRevert.mFormatID != kAudioFormat60958AC3 )
714 {
715 int b_mix;
716 Boolean b_writeable;
717 /* Revert mixable to true if we are allowed to */
718 err = AudioDeviceGetPropertyInfo( mSelectedDevice, 0, FALSE, kAudioDevicePropertySupportsMixing,
719 &i_param_size, &b_writeable );
720
721 err = AudioDeviceGetProperty( mSelectedDevice, 0, FALSE, kAudioDevicePropertySupportsMixing,
722 &i_param_size, &b_mix );
723
724 if( !err && b_writeable )
725 {
726 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::CloseSPDIF - mixable is: %1").arg(b_mix));
727 b_mix = 1;
728 err = AudioDeviceSetProperty( mSelectedDevice, 0, 0, FALSE,
729 kAudioDevicePropertySupportsMixing, i_param_size, &b_mix );
730 }
731
732 if( err != noErr )
733 {
734 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::CloseSPDIF - failed to set mixmode: %d").arg(err, 0, 16));
735 }
736 }
737}
738
739int CoreAudio_impl::getBufferedOnSoundcard(void)
740{
741 return mBufferedBytes;
742}
743
744
745int CoreAudio_impl::GetVolumeChannel(int channel)
746{
747 // FIXME: this only returns global volume
748 (void)channel;
749 Float32 volume;
750 if (!AudioUnitGetParameter(mOutputUnit,
751 kHALOutputParam_Volume,
752 kAudioUnitScope_Global,
753 0,
754 &volume))
755 {
756 return (int)lroundf(volume * 100.0);
757 }
758 return 0; // error case
759}
760
761void CoreAudio_impl::SetVolumeChannel(int channel, int volume)
762{
763 // FIXME: this only sets global volume
764 (void)channel;
765 AudioUnitSetParameter(mOutputUnit,
766 kHALOutputParam_Volume,
767 kAudioUnitScope_Global,
768 0,
769 (volume * 0.01),
770 0);
771}
772
773/*****************************************************************************
774 * AudioStreamChangeFormat: Change mStreamID to change_format
775 *****************************************************************************/
776int CoreAudio_impl::AudioStreamChangeFormat( AudioStreamID mStreamID, AudioStreamBasicDescription change_format )
777{
778 OSStatus err = noErr;
779
780 VERBOSE(VB_IMPORTANT, QString("AudioStreamChangeFormat changing format for stream %1").arg(mStreamID));
781 /* change the format */
782 err = AudioStreamSetProperty( mStreamID, 0, 0,
783 kAudioStreamPropertyPhysicalFormat,
784 sizeof( AudioStreamBasicDescription ),
785 &change_format );
786 if( err != noErr )
787 {
788 VERBOSE( VB_IMPORTANT, QString("AudioStreamChangeFormat could not set the stream format: %1").arg(err, 0, 16));
789 return false;
790 }
791
792 return true;
793}
794
795void CoreAudio_impl::ReleaseHogMode()
796{
797 OSStatus err;
798 UInt32 i_param_size;
799
800 mHogModePID = -1;
801 i_param_size = sizeof( mHogModePID );
802 err = AudioDeviceSetProperty( mSelectedDevice, 0, 0, FALSE,
803 kAudioDevicePropertyHogMode, i_param_size, &mHogModePID );
804 if (err != noErr)
805 {
806 VERBOSE(VB_AUDIO, QString("CoreAudio_impl::ReleaseHogMode - release failed %1").arg(err, 0, 16));
807 }
808
809}
810
811bool CoreAudio_impl::FindAC3Stream()
812{
813 OSStatus err;
814 UInt32 i_param_size;
815 AudioStreamID *p_streams = NULL;
816 int i = 0, i_streams = 0;
817 bool foundAC3Stream = false;
818
819 /* Get a list of all the streams on this device */
820 err = AudioDeviceGetPropertyInfo( mSelectedDevice, 0, FALSE,
821 kAudioDevicePropertyStreams,
822 &i_param_size, NULL );
823 if( err != noErr )
824 {
825 VERBOSE( VB_IMPORTANT, QString("CoreAudio_impl::FindAC3Stream - could not get number of streams: %1").arg(err));
826 ReleaseHogMode();
827 return false;
828 }
829
830 i_streams = i_param_size / sizeof( AudioStreamID );
831 p_streams = (AudioStreamID *)malloc( i_param_size );
832 if( p_streams == NULL )
833 {
834 VERBOSE(VB_IMPORTANT, "CoreAudio_impl::FindAC3Stream - out of memory" );
835 ReleaseHogMode();
836 return false;
837 }
838
839 err = AudioDeviceGetProperty( mSelectedDevice, 0, FALSE,
840 kAudioDevicePropertyStreams,
841 &i_param_size, p_streams );
842
843 if( err != noErr )
844 {
845 VERBOSE( VB_IMPORTANT, QString("CoreAudio_impl::FindAC3Stream - could not get number of streams: %1").arg(err));
846 if( p_streams ) free( p_streams );
847 ReleaseHogMode();
848 return false;
849 }
850
851 for( i = 0; i < i_streams && !foundAC3Stream ; i++ )
852 {
853 /* Find a stream with a cac3 stream */
854 AudioStreamBasicDescription *p_format_list = NULL;
855 int i_formats = 0, j = 0;
856
857 /* Retrieve all the stream formats supported by each output stream */
858 err = AudioStreamGetPropertyInfo( p_streams[i], 0,
859 kAudioStreamPropertyPhysicalFormats,
860 &i_param_size, NULL );
861 if( err != noErr )
862 {
863 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::FindAC3Stream - could not get number of streamformats: %1").arg(err));
864 continue;
865 }
866
867 i_formats = i_param_size / sizeof( AudioStreamBasicDescription );
868 p_format_list = (AudioStreamBasicDescription *)malloc( i_param_size );
869 if( p_format_list == NULL )
870 {
871 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::FindAC3Stream - could not malloc the memory" ));
872 continue;
873 }
874
875 err = AudioStreamGetProperty( p_streams[i], 0,
876 kAudioStreamPropertyPhysicalFormats,
877 &i_param_size, p_format_list );
878 if( err != noErr )
879 {
880 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::FindAC3Stream - could not get the list of streamformats: %1").arg(err));
881 if( p_format_list) free( p_format_list);
882 continue;
883 }
884
885 /* Check if one of the supported formats is a digital format */
886 for( j = 0; j < i_formats; j++ )
887 {
888 if( p_format_list[j].mFormatID == 'IAC3' ||
889 p_format_list[j].mFormatID == kAudioFormat60958AC3 )
890 {
891 VERBOSE(VB_AUDIO, "CoreAudio_impl::FindAC3Stream - found digital format");
892 foundAC3Stream = true;
893 break;
894 }
895 }
896
897 if (p_format_list)
898 free(p_format_list);
899 }
900
901 if (p_streams)
902 free(p_streams);
903
904 return foundAC3Stream;
905}
906
907// Reset any devices with an AC3 stream back to a Linear PCM so that they can become a default
908// output device
909void CoreAudio_impl::ResetAudioDevices()
910{
911 UInt32 i_param_size;
912 OSStatus err;
913 AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &i_param_size, NULL);
914 AudioDeviceID *p_device_list = (AudioDeviceID*) malloc(i_param_size);
915 UInt32 numDevices = i_param_size / sizeof(AudioDeviceID);
916 AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &i_param_size, p_device_list);
917 UInt32 i=0;
918 for (i=0; i < numDevices; i++)
919 {
920 err = AudioDeviceGetPropertyInfo(p_device_list[i], 0, false, kAudioDevicePropertyStreams, &i_param_size, NULL);
921 if (err != noErr)
922 {
923 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::ResetAudioDevices - could not get number of streams: %1 %2").arg(err).arg(err, 0, 16));
924 continue;
925 }
926 AudioStreamID *p_stream_list = (AudioStreamID*)malloc(i_param_size);
927 UInt32 numStreams = i_param_size/sizeof(AudioStreamID);
928 AudioDeviceGetProperty(p_device_list[i], 0, false, kAudioDevicePropertyStreams, &i_param_size, p_stream_list);
929 UInt32 j=0;
930 for (j=0; j < numStreams; j++)
931 {
932 // Find the streams current physical format
933 AudioStreamBasicDescription currentFormat;
934 i_param_size = sizeof(currentFormat);
935 AudioStreamGetProperty(p_stream_list[j], 0, kAudioStreamPropertyPhysicalFormat, &i_param_size, &currentFormat);
936
937 // If it's currently AC-3/SPDIF then reset it to some mixable format
938 if ((currentFormat.mFormatID == 'IAC3') || (currentFormat.mFormatID == kAudioFormat60958AC3))
939 {
940 err = AudioStreamGetPropertyInfo(p_stream_list[j], 0, kAudioStreamPropertyAvailablePhysicalFormats, &i_param_size, NULL);
941 if (err != noErr)
942 {
943 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::ResetAudioDevices - could not get number of physical formats: %1 %2").arg(err).arg(err, 0, 16));
944 continue;
945 }
946 AudioStreamBasicDescription *p_format_list = (AudioStreamBasicDescription*)malloc(i_param_size);
947 err = AudioStreamGetProperty(p_stream_list[j], 0, kAudioStreamPropertyAvailablePhysicalFormats, &i_param_size, p_format_list);
948 if (err != noErr)
949 {
950 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::ResetAudioDevices - could not get list of physical formats: %1 %2").arg(err).arg(err, 0, 16));
951 continue;
952 }
953 UInt32 numFormats = i_param_size / sizeof(AudioStreamBasicDescription);
954 UInt32 formatIndex = 0;
955 bool streamReset = false;
956 for (formatIndex=0; (formatIndex < numFormats) && !streamReset; formatIndex++ )
957 {
958 if (p_format_list[formatIndex].mFormatID == kAudioFormatLinearPCM)
959 {
960 err = AudioStreamSetProperty(p_stream_list[j], NULL, 0, kAudioStreamPropertyPhysicalFormat, sizeof(AudioStreamBasicDescription), &(p_format_list[formatIndex]));
961 if (err != noErr)
962 {
963 VERBOSE(VB_IMPORTANT, QString("CoreAudio_impl::ResetAudioDevices - could not set physical format: %1 %2").arg(err).arg(err, 0, 16));
964 continue;
965 }
966 else
967 {
968 streamReset = true;
969 sleep(1); // For the change to take effect
970 }
971 }
972 }
973 free(p_format_list);
974 }
975 }
976 free(p_stream_list);
977 }
978 free(p_device_list);
979}
980