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 |
|
---|
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 | 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.
|
---|
84 | static OSStatus RenderCallbackAnalog(void *inRefCon,
|
---|
85 | AudioUnitRenderActionFlags *ioActionFlags,
|
---|
86 | const AudioTimeStamp *inTimeStamp,
|
---|
87 | UInt32 inBusNumber,
|
---|
88 | UInt32 inNumberFrames,
|
---|
89 | AudioBufferList *ioData);
|
---|
90 |
|
---|
91 | static 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 |
|
---|
103 | AudioOutputCA::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 |
|
---|
127 | AudioOutputCA::~AudioOutputCA()
|
---|
128 | {
|
---|
129 | KillAudio();
|
---|
130 |
|
---|
131 | delete mPImpl;
|
---|
132 | }
|
---|
133 |
|
---|
134 | bool 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 |
|
---|
148 | void AudioOutputCA::CloseDevice()
|
---|
149 | {
|
---|
150 | mPImpl->CloseDevice();
|
---|
151 | }
|
---|
152 |
|
---|
153 |
|
---|
154 | /* Object-oriented part of callback */
|
---|
155 | bool 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 |
|
---|
190 | void AudioOutputCA::WriteAudio(unsigned char *aubuf, int size)
|
---|
191 | {
|
---|
192 | (void)aubuf;
|
---|
193 | (void)size;
|
---|
194 | return; // unneeded and unused in CA
|
---|
195 | }
|
---|
196 |
|
---|
197 | int AudioOutputCA::getSpaceOnSoundcard(void)
|
---|
198 | {
|
---|
199 | return 0; // unneeded and unused in CA
|
---|
200 | }
|
---|
201 |
|
---|
202 | int AudioOutputCA::getBufferedOnSoundcard(void)
|
---|
203 | {
|
---|
204 | return mPImpl->getBufferedOnSoundcard();
|
---|
205 | }
|
---|
206 |
|
---|
207 | void AudioOutputCA::StartOutputThread(void)
|
---|
208 | {
|
---|
209 | return; // no thread for CA
|
---|
210 | }
|
---|
211 |
|
---|
212 | void AudioOutputCA::StopOutputThread(void)
|
---|
213 | {
|
---|
214 | return; // no thread for CA
|
---|
215 | }
|
---|
216 |
|
---|
217 | int AudioOutputCA::GetVolumeChannel(int channel)
|
---|
218 | {
|
---|
219 | return mPImpl->GetVolumeChannel(channel);
|
---|
220 | }
|
---|
221 |
|
---|
222 | void 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. */
|
---|
228 | OSStatus 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 | *****************************************************************************/
|
---|
255 | static 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 |
|
---|
281 | CoreAudio_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 |
|
---|
310 | bool 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 |
|
---|
351 | bool 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 |
|
---|
440 | bool 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 |
|
---|
664 | void 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 |
|
---|
687 | void 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 |
|
---|
739 | int CoreAudio_impl::getBufferedOnSoundcard(void)
|
---|
740 | {
|
---|
741 | return mBufferedBytes;
|
---|
742 | }
|
---|
743 |
|
---|
744 |
|
---|
745 | int 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 |
|
---|
761 | void 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 | *****************************************************************************/
|
---|
776 | int 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 |
|
---|
795 | void 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 |
|
---|
811 | bool 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
|
---|
909 | void 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, ¤tFormat);
|
---|
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 |
|
---|