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 |
|
---|
20 | using namespace std;
|
---|
21 |
|
---|
22 | #include "mythcontext.h"
|
---|
23 | #include "audiooutputca.h"
|
---|
24 | #include "config.h"
|
---|
25 |
|
---|
26 | // this holds Core Audio member variables
|
---|
27 | class 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.
|
---|
83 | static OSStatus RenderCallbackAnalog(void *inRefCon,
|
---|
84 | AudioUnitRenderActionFlags *ioActionFlags,
|
---|
85 | const AudioTimeStamp *inTimeStamp,
|
---|
86 | UInt32 inBusNumber,
|
---|
87 | UInt32 inNumberFrames,
|
---|
88 | AudioBufferList *ioData);
|
---|
89 |
|
---|
90 | static 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 |
|
---|
98 | AudioOutputCA::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 |
|
---|
122 | AudioOutputCA::~AudioOutputCA()
|
---|
123 | {
|
---|
124 | KillAudio();
|
---|
125 |
|
---|
126 | delete mPImpl;
|
---|
127 | }
|
---|
128 |
|
---|
129 | bool 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 |
|
---|
143 | void AudioOutputCA::CloseDevice()
|
---|
144 | {
|
---|
145 | mPImpl->CloseDevice();
|
---|
146 | }
|
---|
147 |
|
---|
148 |
|
---|
149 | /* Object-oriented part of callback */
|
---|
150 | bool 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 |
|
---|
185 | void AudioOutputCA::WriteAudio(unsigned char *aubuf, int size)
|
---|
186 | {
|
---|
187 | (void)aubuf;
|
---|
188 | (void)size;
|
---|
189 | return; // unneeded and unused in CA
|
---|
190 | }
|
---|
191 |
|
---|
192 | int AudioOutputCA::getSpaceOnSoundcard(void)
|
---|
193 | {
|
---|
194 | return 0; // unneeded and unused in CA
|
---|
195 | }
|
---|
196 |
|
---|
197 | int AudioOutputCA::getBufferedOnSoundcard(void)
|
---|
198 | {
|
---|
199 | return mPImpl->getBufferedOnSoundcard();
|
---|
200 | }
|
---|
201 |
|
---|
202 | void AudioOutputCA::StartOutputThread(void)
|
---|
203 | {
|
---|
204 | return; // no thread for CA
|
---|
205 | }
|
---|
206 |
|
---|
207 | void AudioOutputCA::StopOutputThread(void)
|
---|
208 | {
|
---|
209 | return; // no thread for CA
|
---|
210 | }
|
---|
211 |
|
---|
212 | int AudioOutputCA::GetVolumeChannel(int channel)
|
---|
213 | {
|
---|
214 | return mPImpl->GetVolumeChannel(channel);
|
---|
215 | }
|
---|
216 |
|
---|
217 | void 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. */
|
---|
223 | OSStatus 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 | *****************************************************************************/
|
---|
250 | static 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 |
|
---|
276 | CoreAudio_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 |
|
---|
299 | bool 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 |
|
---|
340 | bool 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 |
|
---|
429 | bool 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 |
|
---|
653 | void 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 |
|
---|
676 | void 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 |
|
---|
728 | int CoreAudio_impl::getBufferedOnSoundcard(void)
|
---|
729 | {
|
---|
730 | return mBufferedBytes;
|
---|
731 | }
|
---|
732 |
|
---|
733 |
|
---|
734 | int 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 |
|
---|
750 | void 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 | *****************************************************************************/
|
---|
765 | int 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 |
|
---|
784 | void 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 |
|
---|
800 | bool 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 | }
|
---|