Ticket #2539: audiooutputca.cpp

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