Ticket #2539: audiooutputca.cpp

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

Revised Implementation of AudioOutputCA

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