Ticket #2539: audiooutputca.2.cpp

File audiooutputca.2.cpp, 35.0 KB (added by awk@…, 14 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