MythTV  master
audiooutputca.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * = NAME
3  * audiooutputca.cpp
4  *
5  * = DESCRIPTION
6  * Core Audio glue for Mac OS X.
7  *
8  * = REVISION
9  * $Id$
10  *
11  * = AUTHORS
12  * Jeremiah Morris, Andrew Kimpton, Nigel Pearson, Jean-Yves Avenard
13  *****************************************************************************/
14 
15 #include <array>
16 #include <vector>
17 
18 #include <CoreServices/CoreServices.h>
19 #include <CoreAudio/CoreAudio.h>
20 #include <AudioUnit/AudioUnit.h>
21 #include <AudioToolbox/AudioFormat.h>
22 
23 #include "mythcorecontext.h"
24 #include "audiooutputca.h"
25 #include "config.h"
26 #include "SoundTouch.h"
27 
28 #define LOC QString("CoreAudio: ")
29 
30 #define CHANNELS_MIN 1
31 #define CHANNELS_MAX 8
32 
33 using AudioStreamIDVec = std::vector<AudioStreamID>;
34 using AudioStreamRangedVec = std::vector<AudioStreamRangedDescription>;
35 using AudioValueRangeVec = std::vector<AudioValueRange>;
36 using RatesVec = std::vector<int>;
37 using ChannelsArr = std::array<bool,CHANNELS_MAX>;
38 
39 #define OSS_STATUS(x) UInt32ToFourCC((UInt32*)&(x))
40 char* UInt32ToFourCC(const UInt32* pVal)
41 {
42  UInt32 inVal = *pVal;
43  char* pIn = (char*)&inVal;
44  static char fourCC[5];
45  fourCC[4] = 0;
46  fourCC[3] = pIn[0];
47  fourCC[2] = pIn[1];
48  fourCC[1] = pIn[2];
49  fourCC[0] = pIn[3];
50  return fourCC;
51 }
52 
53 QString StreamDescriptionToString(AudioStreamBasicDescription desc)
54 {
55  UInt32 formatId = desc.mFormatID;
56  char* fourCC = UInt32ToFourCC(&formatId);
57  QString str;
58 
59  switch (desc.mFormatID)
60  {
61  case kAudioFormatLinearPCM:
62  str = QString("[%1] %2%3 Channel %4-bit %5 %6 (%7Hz)")
63  .arg(fourCC)
64  .arg((desc.mFormatFlags & kAudioFormatFlagIsNonMixable) ? "" : "Mixable ")
65  .arg(desc.mChannelsPerFrame)
66  .arg(desc.mBitsPerChannel)
67  .arg((desc.mFormatFlags & kAudioFormatFlagIsFloat) ? "Floating Point" : "Signed Integer")
68  .arg((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
69  .arg((UInt32)desc.mSampleRate);
70  break;
71  case kAudioFormatAC3:
72  str = QString("[%1] AC-3/DTS (%2Hz)")
73  .arg(fourCC)
74  .arg((UInt32)desc.mSampleRate);
75  break;
76  case kAudioFormat60958AC3:
77  str = QString("[%1] AC-3/DTS for S/PDIF %2 (%3Hz)")
78  .arg(fourCC)
79  .arg((desc.mFormatFlags & kAudioFormatFlagIsBigEndian) ? "BE" : "LE")
80  .arg((UInt32)desc.mSampleRate);
81  break;
82  default:
83  str = QString("[%1]").arg(fourCC);
84  break;
85  }
86  return str;
87 }
88 
94 public:
95  explicit CoreAudioData(AudioOutputCA *parent);
96  CoreAudioData(AudioOutputCA *parent, AudioDeviceID deviceID);
97  CoreAudioData(AudioOutputCA *parent, QString deviceName);
98 
99  static AudioDeviceID GetDefaultOutputDevice();
101  QString *GetName();
102  static AudioDeviceID GetDeviceWithName(const QString& deviceName);
103 
104  bool OpenDevice();
105  int OpenAnalog();
106  void CloseAnalog();
107  bool OpenSPDIF ();
108  void CloseSPDIF();
109 
110  static void SetAutoHogMode(bool enable);
111  static bool GetAutoHogMode();
112  static pid_t GetHogStatus();
113  bool SetHogStatus(bool hog);
114  bool SetMixingSupport(bool mix);
115  bool GetMixingSupport();
116 
117  bool FindAC3Stream();
118  static void ResetAudioDevices();
119  static void ResetStream(AudioStreamID s);
120  static RatesVec RatesList(AudioDeviceID d);
121  bool ChannelsList(AudioDeviceID d, bool passthru, ChannelsArr& chans);
122 
123  static AudioStreamIDVec StreamsList(AudioDeviceID d);
124  static AudioStreamRangedVec FormatsList(AudioStreamID s);
125 
126  static int AudioStreamChangeFormat(AudioStreamID s,
127  AudioStreamBasicDescription format);
128 
129  // TODO: Convert these to macros!
130  static void Debug(const QString& msg)
131  { LOG(VB_AUDIO, LOG_INFO, "CoreAudioData::" + msg); }
132 
133  static void Error(const QString& msg)
134  { LOG(VB_GENERAL, LOG_ERR, "CoreAudioData Error:" + msg); }
135 
136  static void Warn (const QString& msg)
137  { LOG(VB_GENERAL, LOG_WARNING, "CoreAudioData Warning:" + msg); }
138 
139  AudioOutputCA *mCA {nullptr}; // We could subclass, but this ends up tidier
140 
141  // Analog output specific
142  AudioUnit mOutputUnit {nullptr};
143 
144  // SPDIF mode specific
145  bool mDigitalInUse {false}; // Is the digital (SPDIF) output in use?
146  pid_t mHog {-1};
147  int mMixerRestore {-1};
148  AudioDeviceID mDeviceID {0};
149  AudioStreamID mStreamID {}; // StreamID that has a cac3 streamformat
150  int mStreamIndex {-1}; // Index of mStreamID in an AudioBufferList
151  UInt32 mBytesPerPacket {UINT32_MAX};
152  AudioStreamBasicDescription mFormatOrig {}; // The original format the stream
153  AudioStreamBasicDescription mFormatNew {}; // The format we changed the stream to
154  bool mRevertFormat {false}; // Do we need to revert the stream format?
155  bool mIoProc {false};
156  bool mInitialized {false};
157  bool mStarted {false};
158  bool mWasDigital {false};
159  AudioDeviceIOProcID mIoProcID {};
160 };
161 
162 // These callbacks communicate with Core Audio.
163 static OSStatus RenderCallbackAnalog(void *inRefCon,
164  AudioUnitRenderActionFlags *ioActionFlags,
165  const AudioTimeStamp *inTimeStamp,
166  UInt32 inBusNumber,
167  UInt32 inNumberFrames,
168  AudioBufferList *ioData);
169 static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
170  const AudioTimeStamp *inNow,
171  const void *inInputData,
172  const AudioTimeStamp *inInputTime,
173  AudioBufferList *outOutputData,
174  const AudioTimeStamp *inOutputTime,
175  void *inRefCon);
176 
182 AudioOutputBase(settings)
183 {
184  m_mainDevice.remove(0, 10);
185  VBAUDIO(QString("AudioOutputCA::AudioOutputCA searching %1").arg(m_mainDevice));
186  d = new CoreAudioData(this, m_mainDevice);
187 
188  InitSettings(settings);
189  if (settings.m_init)
190  Reconfigure(settings);
191 }
192 
194 {
195  KillAudio();
196 
197  delete d;
198 }
199 
201 {
202  auto *settings = new AudioOutputSettings();
203 
204  // Seek hardware sample rate available
206 
207  if (rates.empty())
208  {
209  // Error retrieving rates, assume 48kHz
210  settings->AddSupportedRate(48000);
211  }
212  else
213  {
214  while (int rate = settings->GetNextRate())
215  {
216  for (auto entry : rates)
217  {
218  if (entry != rate)
219  continue;
220  settings->AddSupportedRate(entry);
221  }
222  }
223  }
224 
225  // Supported format: 16 bits audio or float
226  settings->AddSupportedFormat(FORMAT_S16);
227  settings->AddSupportedFormat(FORMAT_FLT);
228 
229  ChannelsArr channels {};
230  if (!d->ChannelsList(d->mDeviceID, digital, channels))
231  {
232  // Error retrieving list of supported channels, assume stereo only
233  settings->AddSupportedChannels(2);
234  }
235  else
236  {
237  for (int i = CHANNELS_MIN; i <= CHANNELS_MAX; i++)
238  {
239  if (channels[i])
240  {
241  Debug(QString("Support %1 channels").arg(i));
242  // In case 8 channels are supported but not 6, fake 6
243  if (i == 8 && !channels[6])
244  settings->AddSupportedChannels(6);
245  settings->AddSupportedChannels(i);
246  }
247  }
248  }
249 
250  if (d->FindAC3Stream())
251  {
252  settings->setPassthrough(1); // yes passthrough
253  }
254  return settings;
255 }
256 
258 {
259  bool deviceOpened = false;
260 
261  if (d->mWasDigital)
262  {
263  }
264  Debug("OpenDevice: Entering");
265  if (m_passthru || m_enc)
266  {
267  Debug("OpenDevice() Trying Digital.");
268  if (!(deviceOpened = d->OpenSPDIF()))
269  d->CloseSPDIF();
270  }
271 
272  if (!deviceOpened)
273  {
274  Debug("OpenDevice() Trying Analog.");
275  int result = -1;
276  //for (int i=0; result < 0 && i < 10; i++)
277  {
278  result = d->OpenAnalog();
279  Debug(QString("OpenDevice: OpenAnalog = %1").arg(result));
280  if (result < 0)
281  {
282  d->CloseAnalog();
283  usleep((1000 * 1000) - 1); // Argument to usleep must be less than 1 million
284  }
285  }
286  deviceOpened = (result > 0);
287  }
288 
289  if (!deviceOpened)
290  {
291  Error("Couldn't open any audio device!");
292  d->CloseAnalog();
293  return false;
294  }
295 
297  {
298  QString controlLabel = gCoreContext->GetSetting("MixerControl", "PCM");
299  controlLabel += "MixerVolume";
300  SetCurrentVolume(gCoreContext->GetNumSetting(controlLabel, 80));
301  }
302 
303  return true;
304 }
305 
307 {
308  VBAUDIO(QString("CloseDevice [%1]: Entering")
309  .arg(d->mDigitalInUse ? "SPDIF" : "Analog"));
310  if (d->mDigitalInUse)
311  d->CloseSPDIF();
312  else
313  d->CloseAnalog();
314 }
315 
316 template <class AudioDataType>
317 static inline void _ReorderSmpteToCA(AudioDataType *buf, uint frames)
318 {
319  AudioDataType tmpLS;
320  AudioDataType tmpRS;
321  AudioDataType tmpRLs;
322  AudioDataType tmpRRs;
323  AudioDataType *buf2;
324  for (uint i = 0; i < frames; i++)
325  {
326  buf = buf2 = buf + 4;
327  tmpRLs = *buf++;
328  tmpRRs = *buf++;
329  tmpLS = *buf++;
330  tmpRS = *buf++;
331 
332  *buf2++ = tmpLS;
333  *buf2++ = tmpRS;
334  *buf2++ = tmpRLs;
335  *buf2++ = tmpRRs;
336  }
337 }
338 
339 static inline void ReorderSmpteToCA(void *buf, uint frames, AudioFormat format)
340 {
341  switch(AudioOutputSettings::FormatToBits(format))
342  {
343  case 8: _ReorderSmpteToCA((uchar *)buf, frames); break;
344  case 16: _ReorderSmpteToCA((short *)buf, frames); break;
345  default: _ReorderSmpteToCA((int *)buf, frames); break;
346  }
347 }
348 
350 bool AudioOutputCA::RenderAudio(unsigned char *aubuf,
351  int size, unsigned long long timestamp)
352 {
353  if (m_pauseAudio || m_killAudio)
354  {
355  m_actuallyPaused = true;
356  return false;
357  }
358 
359  /* This callback is called when the sound system requests
360  data. We don't want to block here, because that would
361  just cause dropouts anyway, so we always return whatever
362  data is available. If we haven't received enough, either
363  because we've finished playing or we have a buffer
364  underrun, we play silence to fill the unused space. */
365 
366  int written_size = GetAudioData(aubuf, size, false);
367  if (written_size && (size > written_size))
368  {
369  // play silence on buffer underrun
370  memset(aubuf + written_size, 0, size - written_size);
371  }
372 
373  //Audio received is in SMPTE channel order, reorder to CA unless passthru
374  if (!m_passthru && m_channels == 8)
375  {
377  }
378 
379  /* update audiotime (m_bufferedBytes is read by GetBufferedOnSoundcard) */
380  UInt64 nanos = AudioConvertHostTimeToNanos(timestamp -
381  AudioGetCurrentHostTime());
382  m_bufferedBytes = (int)((nanos / 1000000000.0) * // secs
383  (m_effDsp / 100.0) * // frames/sec
384  m_outputBytesPerFrame); // bytes/frame
385 
386  return (written_size > 0);
387 }
388 
389 // unneeded and unused in CA
390 void AudioOutputCA::WriteAudio(unsigned char *aubuf, int size)
391 {
392  (void)aubuf;
393  (void)size;
394 }
395 
397 {
398  return m_bufferedBytes;
399 }
400 
405 {
406  int audbuf_timecode = GetBaseAudBufTimeCode();
407 
408  if (!audbuf_timecode)
409  return 0;
410 
411  int totalbuffer = audioready() + GetBufferedOnSoundcard();
412 
413  return audbuf_timecode - (int)(totalbuffer * 100000.0 /
416 }
417 
418 /* This callback provides converted audio data to the default output device. */
419 OSStatus RenderCallbackAnalog(void *inRefCon,
420  AudioUnitRenderActionFlags *ioActionFlags,
421  const AudioTimeStamp *inTimeStamp,
422  UInt32 inBusNumber,
423  UInt32 inNumberFrames,
424  AudioBufferList *ioData)
425 {
426  (void)inBusNumber;
427  (void)inNumberFrames;
428 
429  AudioOutputCA *inst = (static_cast<CoreAudioData *>(inRefCon))->mCA;
430 
431  if (!inst->RenderAudio((unsigned char *)(ioData->mBuffers[0].mData),
432  ioData->mBuffers[0].mDataByteSize,
433  inTimeStamp->mHostTime))
434  {
435  // play silence if RenderAudio returns false
436  memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
437  *ioActionFlags = kAudioUnitRenderAction_OutputIsSilence;
438  }
439  return noErr;
440 }
441 
443 {
444  // FIXME: this only returns global volume
445  (void)channel;
446  Float32 volume;
447 
448  if (!AudioUnitGetParameter(d->mOutputUnit,
449  kHALOutputParam_Volume,
450  kAudioUnitScope_Global, 0, &volume))
451  return (int)lroundf(volume * 100.0F);
452 
453  return 0; // error case
454 }
455 
457 {
458  // FIXME: this only sets global volume
459  (void)channel;
460  AudioUnitSetParameter(d->mOutputUnit, kHALOutputParam_Volume,
461  kAudioUnitScope_Global, 0, (volume * 0.01F), 0);
462 }
463 
464 // IOProc style callback for SPDIF audio output
465 static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
466  const AudioTimeStamp *inNow,
467  const void *inInputData,
468  const AudioTimeStamp *inInputTime,
469  AudioBufferList *outOutputData,
470  const AudioTimeStamp *inOutputTime,
471  void *inRefCon)
472 {
473  auto *d = static_cast<CoreAudioData *>(inRefCon);
474  AudioOutputCA *a = d->mCA;
475  int index = d->mStreamIndex;
476 
477  (void)inDevice; // unused
478  (void)inNow; // unused
479  (void)inInputData; // unused
480  (void)inInputTime; // unused
481 
482  /*
483  * HACK: No idea why this would be the case, but after the second run, we get
484  * incorrect value
485  */
486  if (d->mBytesPerPacket > 0 &&
487  outOutputData->mBuffers[index].mDataByteSize > d->mBytesPerPacket)
488  {
489  outOutputData->mBuffers[index].mDataByteSize = d->mBytesPerPacket;
490  }
491  if (!a->RenderAudio((unsigned char *)(outOutputData->mBuffers[index].mData),
492  outOutputData->mBuffers[index].mDataByteSize,
493  inOutputTime->mHostTime))
494  {
495  // play silence if RenderAudio returns false
496  memset(outOutputData->mBuffers[index].mData, 0,
497  outOutputData->mBuffers[index].mDataByteSize);
498  }
499  return noErr;
500 }
501 
503 {
504  // Reset all the devices to a default 'non-hog' and mixable format.
505  // If we don't do this we may be unable to find the Default Output device.
506  // (e.g. if we crashed last time leaving it stuck in AC-3 mode)
508 
510 }
511 
512 CoreAudioData::CoreAudioData(AudioOutputCA *parent, AudioDeviceID deviceID) :
513  mCA(parent)
514 {
516  mDeviceID = deviceID;
517 }
518 
519 CoreAudioData::CoreAudioData(AudioOutputCA *parent, QString deviceName) :
520  mCA(parent)
521 {
523  mDeviceID = GetDeviceWithName(deviceName);
524  if (!mDeviceID)
525  {
526  // Didn't find specified device, use default one
528  if (deviceName != "Default Output Device")
529  {
530  Warn(QString("CoreAudioData: \"%1\" not found, using default device %2.")
531  .arg(deviceName).arg(mDeviceID));
532  }
533  }
534  Debug(QString("CoreAudioData: device number is %1")
535  .arg(mDeviceID));
536 }
537 
538 AudioDeviceID CoreAudioData::GetDeviceWithName(const QString &deviceName)
539 {
540  UInt32 size = 0;
541  AudioDeviceID deviceID = 0;
542  AudioObjectPropertyAddress pa
543  {
544  kAudioHardwarePropertyDevices,
545  kAudioObjectPropertyScopeGlobal,
546  kAudioObjectPropertyElementMaster
547  };
548 
549  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
550  0, nullptr, &size);
551  if (err)
552  {
553  Warn(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
554  "Error [%1]")
555  .arg(err));
556  return deviceID;
557  }
558 
559  UInt32 deviceCount = size / sizeof(AudioDeviceID);
560  auto* pDevices = new AudioDeviceID[deviceCount];
561 
562  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
563  0, nullptr, &size, pDevices);
564  if (err)
565  {
566  Warn(QString("GetDeviceWithName: Unable to retrieve the list of available devices. "
567  "Error [%1]")
568  .arg(err));
569  }
570  else
571  {
572  for (UInt32 dev = 0; dev < deviceCount; dev++)
573  {
574  CoreAudioData device(nullptr, pDevices[dev]);
575  if (device.GetTotalOutputChannels() == 0)
576  continue;
577  QString *name = device.GetName();
578  if (name && name == deviceName)
579  {
580  Debug(QString("GetDeviceWithName: Found: %1").arg(*name));
581  deviceID = pDevices[dev];
582  delete name;
583  }
584  if (deviceID)
585  break;
586  }
587  }
588  delete[] pDevices;
589  return deviceID;
590 }
591 
593 {
594  UInt32 paramSize;
595  AudioDeviceID deviceId = 0;
596  AudioObjectPropertyAddress pa
597  {
598  kAudioHardwarePropertyDefaultOutputDevice,
599  kAudioObjectPropertyScopeGlobal,
600  kAudioObjectPropertyElementMaster
601  };
602 
603  // Find the ID of the default Device
604  paramSize = sizeof(deviceId);
605  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
606  0, nullptr, &paramSize, &deviceId);
607  if (err == noErr)
608  Debug(QString("GetDefaultOutputDevice: default device ID = %1").arg(deviceId));
609  else
610  {
611  Warn(QString("GetDefaultOutputDevice: could not get default audio device: [%1]")
612  .arg(OSS_STATUS(err)));
613  deviceId = 0;
614  }
615  return deviceId;
616 }
617 
619 {
620  if (!mDeviceID)
621  return 0;
622  UInt32 channels = 0;
623  UInt32 size = 0;
624  AudioObjectPropertyAddress pa
625  {
626  kAudioDevicePropertyStreamConfiguration,
627  kAudioObjectPropertyScopeGlobal,
628  kAudioObjectPropertyElementMaster
629  };
630 
631  OSStatus err = AudioObjectGetPropertyDataSize(mDeviceID, &pa,
632  0, nullptr, &size);
633  if (err)
634  {
635  Warn(QString("GetTotalOutputChannels: Unable to get "
636  "size of device output channels - id: %1 Error = [%2]")
637  .arg(mDeviceID)
638  .arg(err));
639  return 0;
640  }
641 
642  auto *pList = (AudioBufferList *)malloc(size);
643  err = AudioObjectGetPropertyData(mDeviceID, &pa,
644  0, nullptr, &size, pList);
645  if (!err)
646  {
647  for (UInt32 buffer = 0; buffer < pList->mNumberBuffers; buffer++)
648  channels += pList->mBuffers[buffer].mNumberChannels;
649  }
650  else
651  {
652  Warn(QString("GetTotalOutputChannels: Unable to get "
653  "total device output channels - id: %1 Error = [%2]")
654  .arg(mDeviceID)
655  .arg(err));
656  }
657  Debug(QString("GetTotalOutputChannels: Found %1 channels in %2 buffers")
658  .arg(channels).arg(pList->mNumberBuffers));
659  free(pList);
660  return channels;
661 }
662 
664 {
665  if (!mDeviceID)
666  return nullptr;
667 
668  AudioObjectPropertyAddress pa
669  {
670  kAudioObjectPropertyName,
671  kAudioObjectPropertyScopeGlobal,
672  kAudioObjectPropertyElementMaster
673  };
674 
675  CFStringRef name;
676  UInt32 propertySize = sizeof(CFStringRef);
677  OSStatus err = AudioObjectGetPropertyData(mDeviceID, &pa,
678  0, nullptr, &propertySize, &name);
679  if (err)
680  {
681  Error(QString("AudioObjectGetPropertyData for kAudioObjectPropertyName error: [%1]")
682  .arg(err));
683  return nullptr;
684  }
685  char *cname = new char[CFStringGetLength(name) + 1];
686  CFStringGetCString(name, cname, CFStringGetLength(name) + 1, kCFStringEncodingUTF8);
687  auto *qname = new QString(cname);
688  delete[] cname;
689  return qname;
690 }
691 
693 {
694  UInt32 val = 0;
695  UInt32 size = sizeof(val);
696  AudioObjectPropertyAddress pa
697  {
698  kAudioHardwarePropertyHogModeIsAllowed,
699  kAudioObjectPropertyScopeGlobal,
700  kAudioObjectPropertyElementMaster
701  };
702 
703  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr, &size, &val);
704  if (err)
705  {
706  Warn(QString("GetAutoHogMode: Unable to get auto 'hog' mode. Error = [%1]")
707  .arg(err));
708  return false;
709  }
710  return (val == 1);
711 }
712 
714 {
715  UInt32 val = enable ? 1 : 0;
716  AudioObjectPropertyAddress pa
717  {
718  kAudioHardwarePropertyHogModeIsAllowed,
719  kAudioObjectPropertyScopeGlobal,
720  kAudioObjectPropertyElementMaster
721  };
722 
723  OSStatus err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr,
724  sizeof(val), &val);
725  if (err)
726  {
727  Warn(QString("SetAutoHogMode: Unable to set auto 'hog' mode. Error = [%1]")
728  .arg(err));
729  }
730 }
731 
733 {
734  pid_t PID;
735  UInt32 PIDsize = sizeof(PID);
736  AudioObjectPropertyAddress pa
737  {
738  kAudioDevicePropertyHogMode,
739  kAudioObjectPropertyScopeGlobal,
740  kAudioObjectPropertyElementMaster
741  };
742 
743  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr,
744  &PIDsize, &PID);
745  if (err != noErr)
746  {
747  // This is not a fatal error.
748  // Some drivers simply don't support this property
749  Debug(QString("GetHogStatus: unable to check: [%1]")
750  .arg(err));
751  return -1;
752  }
753  return PID;
754 }
755 
757 {
758  AudioObjectPropertyAddress pa
759  {
760  kAudioDevicePropertyHogMode,
761  kAudioObjectPropertyScopeGlobal,
762  kAudioObjectPropertyElementMaster
763  };
764 
765  // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
766  // is a toggle and the only way to tell if you do get hog mode is to compare
767  // the returned pid against getpid, if the match, you have hog mode, if not you don't.
768  if (!mDeviceID)
769  return false;
770 
771  if (hog)
772  {
773  if (mHog == -1) // Not already set
774  {
775  Debug(QString("SetHogStatus: Setting 'hog' status on device %1")
776  .arg(mDeviceID));
777  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
778  sizeof(mHog), &mHog);
779  if (err || mHog != getpid())
780  {
781  Warn(QString("SetHogStatus: Unable to set 'hog' status. Error = [%1]")
782  .arg(OSS_STATUS(err)));
783  return false;
784  }
785  Debug(QString("SetHogStatus: Successfully set 'hog' status on device %1")
786  .arg(mDeviceID));
787  }
788  }
789  else
790  {
791  if (mHog > -1) // Currently Set
792  {
793  Debug(QString("SetHogStatus: Releasing 'hog' status on device %1")
794  .arg(mDeviceID));
795  pid_t hogPid = -1;
796  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
797  sizeof(hogPid), &hogPid);
798  if (err || hogPid == getpid())
799  {
800  Warn(QString("SetHogStatus: Unable to release 'hog' status. Error = [%1]")
801  .arg(OSS_STATUS(err)));
802  return false;
803  }
804  mHog = hogPid; // Reset internal state
805  }
806  }
807  return true;
808 }
809 
811 {
812  if (!mDeviceID)
813  return false;
814  int restore = -1;
815  if (mMixerRestore == -1) // This is our first change to this setting. Store the original setting for restore
816  restore = (GetMixingSupport() ? 1 : 0);
817  UInt32 mixEnable = mix ? 1 : 0;
818  Debug(QString("SetMixingSupport: %1abling mixing for device %2")
819  .arg(mix ? "En" : "Dis")
820  .arg(mDeviceID));
821 
822  AudioObjectPropertyAddress pa
823  {
824  kAudioDevicePropertySupportsMixing,
825  kAudioObjectPropertyScopeGlobal,
826  kAudioObjectPropertyElementMaster
827  };
828  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
829  sizeof(mixEnable), &mixEnable);
830  if (err)
831  {
832  Warn(QString("SetMixingSupport: Unable to set MixingSupport to %1. Error = [%2]")
833  .arg(mix ? "'On'" : "'Off'")
834  .arg(OSS_STATUS(err)));
835  return false;
836  }
837  if (mMixerRestore == -1)
838  mMixerRestore = restore;
839  return true;
840 }
841 
843 {
844  if (!mDeviceID)
845  return false;
846  UInt32 val = 0;
847  UInt32 size = sizeof(val);
848  AudioObjectPropertyAddress pa
849  {
850  kAudioDevicePropertySupportsMixing,
851  kAudioObjectPropertyScopeGlobal,
852  kAudioObjectPropertyElementMaster
853  };
854  OSStatus err = AudioObjectGetPropertyData(mDeviceID, &pa, 0, nullptr,
855  &size, &val);
856  if (err)
857  return false;
858  return (val > 0);
859 }
860 
865 {
866  OSStatus err;
867  UInt32 listSize;
868  AudioStreamIDVec vec {};
869 
870  AudioObjectPropertyAddress pa
871  {
872  kAudioDevicePropertyStreams,
873  kAudioObjectPropertyScopeGlobal,
874  kAudioObjectPropertyElementMaster
875  };
876 
877  err = AudioObjectGetPropertyDataSize(d, &pa,
878  0, nullptr, &listSize);
879  if (err != noErr)
880  {
881  Error(QString("StreamsList: could not get list size: [%1]")
882  .arg(OSS_STATUS(err)));
883  return {};
884  }
885 
886  try
887  {
888  vec.reserve(listSize / sizeof(AudioStreamID));
889  }
890  catch (...)
891  {
892  Error("StreamsList(): out of memory?");
893  return {};
894  }
895 
896  err = AudioObjectGetPropertyData(d, &pa,
897  0, nullptr, &listSize, vec.data());
898  if (err != noErr)
899  {
900  Error(QString("StreamsList: could not get list: [%1]")
901  .arg(OSS_STATUS(err)));
902  return {};
903  }
904 
905  return vec;
906 }
907 
909 {
910  OSStatus err;
912  UInt32 listSize;
913 
914  AudioObjectPropertyAddress pa
915  {
916  kAudioStreamPropertyPhysicalFormats,
917  kAudioObjectPropertyScopeGlobal,
918  kAudioObjectPropertyElementMaster
919  };
920 
921  // Retrieve all the stream formats supported by this output stream
922  err = AudioObjectGetPropertyDataSize(s, &pa, 0, nullptr, &listSize);
923  if (err != noErr)
924  {
925  Warn(QString("FormatsList(): couldn't get list size: [%1]")
926  .arg(OSS_STATUS(err)));
927  return {};
928  }
929 
930  try
931  {
932  vec.reserve(listSize / sizeof(AudioStreamRangedDescription));
933  }
934  catch (...)
935  {
936  Error("FormatsList(): out of memory?");
937  return {};
938  }
939 
940  err = AudioObjectGetPropertyData(s, &pa, 0, nullptr, &listSize, vec.data());
941  if (err != noErr)
942  {
943  Warn(QString("FormatsList: couldn't get list: [%1]")
944  .arg(OSS_STATUS(err)));
945  return {};
946  }
947 
948  return vec;
949 }
950 
951 static UInt32 sNumberCommonSampleRates = 15;
952 static Float64 sCommonSampleRates[] = {
953  8000.0, 11025.0, 12000.0,
954  16000.0, 22050.0, 24000.0,
955  32000.0, 44100.0, 48000.0,
956  64000.0, 88200.0, 96000.0,
957  128000.0, 176400.0, 192000.0 };
958 
959 static bool IsRateCommon(Float64 inRate)
960 {
961  bool theAnswer = false;
962  for(UInt32 i = 0; !theAnswer && (i < sNumberCommonSampleRates); i++)
963  {
964  theAnswer = inRate == sCommonSampleRates[i];
965  }
966  return theAnswer;
967 }
968 
970 {
971  OSStatus err;
972  AudioValueRangeVec ranges;
973  RatesVec finalvec;
974  UInt32 listSize;
975 
976  AudioObjectPropertyAddress pa
977  {
978  kAudioDevicePropertyAvailableNominalSampleRates,
979  kAudioObjectPropertyScopeGlobal,
980  kAudioObjectPropertyElementMaster
981  };
982 
983  // retrieve size of rate list
984  err = AudioObjectGetPropertyDataSize(d, &pa, 0, nullptr, &listSize);
985  if (err != noErr)
986  {
987  Warn(QString("RatesList(): couldn't get data rate list size: [%1]")
988  .arg(err));
989  return {};
990  }
991 
992  try
993  {
994  ranges.reserve(listSize / sizeof(AudioValueRange));
995  finalvec.reserve(listSize / sizeof(AudioValueRange));
996  }
997  catch (...)
998  {
999  Error("RatesList(): out of memory?");
1000  return {};
1001  }
1002 
1003  err = AudioObjectGetPropertyData(d, &pa, 0, nullptr, &listSize, ranges.data());
1004  if (err != noErr)
1005  {
1006  Warn(QString("RatesList(): couldn't get list: [%1]")
1007  .arg(err));
1008  return {};
1009  }
1010 
1011  // iterate through the ranges and add the minimum, maximum, and common rates in between
1012  UInt32 theFirstIndex;
1013  UInt32 theLastIndex = 0;
1014  for(auto range : ranges)
1015  {
1016  theFirstIndex = theLastIndex;
1017  // find the index of the first common rate greater than or equal to the minimum
1018  while((theFirstIndex < sNumberCommonSampleRates) && (sCommonSampleRates[theFirstIndex] < range.mMinimum))
1019  theFirstIndex++;
1020 
1021  if (theFirstIndex >= sNumberCommonSampleRates)
1022  break;
1023 
1024  theLastIndex = theFirstIndex;
1025  // find the index of the first common rate greater than or equal to the maximum
1026  while((theLastIndex < sNumberCommonSampleRates) && (sCommonSampleRates[theLastIndex] < range.mMaximum))
1027  {
1028  finalvec.push_back(sCommonSampleRates[theLastIndex]);
1029  theLastIndex++;
1030  }
1031  if (IsRateCommon(range.mMinimum))
1032  finalvec.push_back(range.mMinimum);
1033  else if (IsRateCommon(range.mMaximum))
1034  finalvec.push_back(range.mMaximum);
1035  }
1036 
1037  return finalvec;
1038 }
1039 
1040 bool CoreAudioData::ChannelsList(AudioDeviceID /*d*/, bool passthru, ChannelsArr& chans)
1041 {
1042  AudioStreamIDVec streams;
1044  bool founddigital = false;
1045 
1046  chans.fill(false);
1047 
1048  streams = StreamsList(mDeviceID);
1049  if (streams.empty())
1050  return false;
1051 
1052  if (passthru)
1053  {
1054  for (auto stream : streams)
1055  {
1056  formats = FormatsList(stream);
1057  if (formats.empty())
1058  continue;
1059 
1060  // Find a stream with a cac3 stream
1061  for (auto format : formats)
1062  {
1063  if (format.mFormat.mFormatID == 'IAC3' ||
1064  format.mFormat.mFormatID == kAudioFormat60958AC3)
1065  {
1066  chans[format.mFormat.mChannelsPerFrame] = true;
1067  founddigital = true;
1068  }
1069  }
1070  }
1071  }
1072 
1073  if (!founddigital)
1074  {
1075  for (auto stream : streams)
1076  {
1077  formats = FormatsList(stream);
1078  if (formats.empty())
1079  continue;
1080  for (auto format : formats)
1081  if (format.mFormat.mChannelsPerFrame <= CHANNELS_MAX)
1082  chans[format.mFormat.mChannelsPerFrame] = true;
1083  }
1084  }
1085  return true;
1086 }
1087 
1089 {
1090  AudioComponentDescription desc;
1091  AudioStreamBasicDescription DeviceFormat;
1092  AudioChannelLayout *layout;
1093  AudioChannelLayout new_layout;
1094  AudioDeviceID defaultDevice = GetDefaultOutputDevice();
1095  AudioObjectPropertyAddress pa
1096  {
1097  kAudioHardwarePropertyDevices,
1098  kAudioObjectPropertyScopeGlobal,
1099  kAudioObjectPropertyElementMaster
1100  };
1101 
1102  Debug("OpenAnalog: Entering");
1103 
1104  desc.componentType = kAudioUnitType_Output;
1105  if (defaultDevice == mDeviceID)
1106  {
1107  desc.componentSubType = kAudioUnitSubType_DefaultOutput;
1108  }
1109  else
1110  {
1111  desc.componentSubType = kAudioUnitSubType_HALOutput;
1112  }
1113  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1114  desc.componentFlags = 0;
1115  desc.componentFlagsMask = 0;
1116  mDigitalInUse = false;
1117 
1118  AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
1119  if (comp == nullptr)
1120  {
1121  Error("OpenAnalog: AudioComponentFindNext failed");
1122  return false;
1123  }
1124 
1125  OSErr err = AudioComponentInstanceNew(comp, &mOutputUnit);
1126  if (err)
1127  {
1128  Error(QString("OpenAnalog: AudioComponentInstanceNew returned %1")
1129  .arg(err));
1130  return false;
1131  }
1132 
1133  // Check if we have IO
1134  UInt32 hasIO = 0;
1135  UInt32 size_hasIO = sizeof(hasIO);
1136  err = AudioUnitGetProperty(mOutputUnit,
1137  kAudioOutputUnitProperty_HasIO,
1138  kAudioUnitScope_Output,
1139  0,
1140  &hasIO, &size_hasIO);
1141  Debug(QString("OpenAnalog: HasIO (output) = %1").arg(hasIO));
1142  if (!hasIO)
1143  {
1144  UInt32 enableIO = 1;
1145  err = AudioUnitSetProperty(mOutputUnit,
1146  kAudioOutputUnitProperty_EnableIO,
1147  kAudioUnitScope_Global,
1148  0,
1149  &enableIO, sizeof(enableIO));
1150  if (err)
1151  {
1152  Warn(QString("OpenAnalog: failed enabling IO: %1")
1153  .arg(err));
1154  }
1155  hasIO = 0;
1156  err = AudioUnitGetProperty(mOutputUnit,
1157  kAudioOutputUnitProperty_HasIO,
1158  kAudioUnitScope_Output,
1159  0,
1160  &hasIO, &size_hasIO);
1161  Debug(QString("HasIO = %1").arg(hasIO));
1162  }
1163 
1164  /*
1165  * We shouldn't have to do this distinction, however for some unknown reasons
1166  * assigning device to AudioUnit fail when switching from SPDIF mode
1167  */
1168  if (defaultDevice != mDeviceID)
1169  {
1170  err = AudioUnitSetProperty(mOutputUnit,
1171  kAudioOutputUnitProperty_CurrentDevice,
1172  kAudioUnitScope_Global,
1173  0,
1174  &mDeviceID, sizeof(mDeviceID));
1175  if (err)
1176  {
1177  Error(QString("OpenAnalog: Unable to set current device to %1. Error = %2")
1178  .arg(mDeviceID)
1179  .arg(err));
1180  return -1;
1181  }
1182  }
1183  /* Get the current format */
1184  UInt32 param_size = sizeof(AudioStreamBasicDescription);
1185 
1186  err = AudioUnitGetProperty(mOutputUnit,
1187  kAudioUnitProperty_StreamFormat,
1188  kAudioUnitScope_Input,
1189  0,
1190  &DeviceFormat,
1191  &param_size );
1192  if (err)
1193  {
1194  Warn(QString("OpenAnalog: Unable to retrieve current stream format: [%1]")
1195  .arg(err));
1196  }
1197  else
1198  {
1199  Debug(QString("OpenAnalog: current format is: %1")
1200  .arg(StreamDescriptionToString(DeviceFormat)));
1201  }
1202  /* Get the channel layout of the device side of the unit */
1203  pa.mSelector = kAudioDevicePropertyPreferredChannelLayout;
1204  err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1205  0, nullptr, &param_size);
1206 
1207  if(!err)
1208  {
1209  layout = (AudioChannelLayout *) malloc(param_size);
1210 
1211  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1212  0, nullptr, &param_size, layout);
1213 
1214  /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
1215  if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
1216  {
1217  /* bitmap defined channellayout */
1218  err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
1219  sizeof(UInt32), &layout->mChannelBitmap,
1220  &param_size,
1221  layout);
1222  if (err)
1223  Warn("OpenAnalog: Can't retrieve current channel layout");
1224  }
1225  else if(layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
1226  {
1227  /* layouttags defined channellayout */
1228  err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
1229  sizeof(AudioChannelLayoutTag),
1230  &layout->mChannelLayoutTag,
1231  &param_size,
1232  layout);
1233  if (err)
1234  Warn("OpenAnalog: Can't retrieve current channel layout");
1235  }
1236 
1237  Debug(QString("OpenAnalog: Layout of AUHAL has %1 channels")
1238  .arg(layout->mNumberChannelDescriptions));
1239 
1240  int channels_found = 0;
1241  for(UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++)
1242  {
1243  Debug(QString("OpenAnalog: this is channel: %1")
1244  .arg(layout->mChannelDescriptions[i].mChannelLabel));
1245 
1246  switch( layout->mChannelDescriptions[i].mChannelLabel)
1247  {
1248  case kAudioChannelLabel_Left:
1249  case kAudioChannelLabel_Right:
1250  case kAudioChannelLabel_Center:
1251  case kAudioChannelLabel_LFEScreen:
1252  case kAudioChannelLabel_LeftSurround:
1253  case kAudioChannelLabel_RightSurround:
1254  case kAudioChannelLabel_RearSurroundLeft:
1255  case kAudioChannelLabel_RearSurroundRight:
1256  case kAudioChannelLabel_CenterSurround:
1257  channels_found++;
1258  break;
1259  default:
1260  Debug(QString("unrecognized channel form provided by driver: %1")
1261  .arg(layout->mChannelDescriptions[i].mChannelLabel));
1262  }
1263  }
1264  if(channels_found == 0)
1265  {
1266  Warn("Audio device is not configured. "
1267  "You should configure your speaker layout with "
1268  "the \"Audio Midi Setup\" utility in /Applications/"
1269  "Utilities.");
1270  }
1271  free(layout);
1272  }
1273  else
1274  {
1275  Warn("this driver does not support kAudioDevicePropertyPreferredChannelLayout.");
1276  }
1277 
1278  memset (&new_layout, 0, sizeof(new_layout));
1279  switch(mCA->m_channels)
1280  {
1281  case 1:
1282  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
1283  break;
1284  case 2:
1285  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
1286  break;
1287  case 6:
1288  // 3F2-LFE L R C LFE LS RS
1289  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
1290  break;
1291  case 8:
1292  // We need
1293  // 3F4-LFE L R C LFE Rls Rrs LS RS
1294  // but doesn't exist, so we'll swap channels later
1295  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
1296  break;
1297  }
1298  /* Set new_layout as the layout */
1299  err = AudioUnitSetProperty(mOutputUnit,
1300  kAudioUnitProperty_AudioChannelLayout,
1301  kAudioUnitScope_Input,
1302  0,
1303  &new_layout, sizeof(new_layout));
1304  if (err)
1305  {
1306  Warn(QString("OpenAnalog: couldn't set channels layout [%1]")
1307  .arg(err));
1308  }
1309 
1310  if(new_layout.mNumberChannelDescriptions > 0)
1311  free(new_layout.mChannelDescriptions);
1312 
1313  // Set up the audio output unit
1314  int formatFlags;
1315  switch (mCA->m_outputFormat)
1316  {
1317  case FORMAT_S16:
1318  formatFlags = kLinearPCMFormatFlagIsSignedInteger;
1319  break;
1320  case FORMAT_FLT:
1321  formatFlags = kLinearPCMFormatFlagIsFloat;
1322  break;
1323  default:
1324  formatFlags = kLinearPCMFormatFlagIsSignedInteger;
1325  break;
1326  }
1327 
1328  AudioStreamBasicDescription conv_in_desc;
1329  memset(&conv_in_desc, 0, sizeof(AudioStreamBasicDescription));
1330  conv_in_desc.mSampleRate = mCA->m_sampleRate;
1331  conv_in_desc.mFormatID = kAudioFormatLinearPCM;
1332  conv_in_desc.mFormatFlags = formatFlags |
1333  kAudioFormatFlagsNativeEndian |
1334  kLinearPCMFormatFlagIsPacked;
1335  conv_in_desc.mBytesPerPacket = mCA->m_outputBytesPerFrame;
1336  // This seems inefficient, does it hurt if we increase this?
1337  conv_in_desc.mFramesPerPacket = 1;
1338  conv_in_desc.mBytesPerFrame = mCA->m_outputBytesPerFrame;
1339  conv_in_desc.mChannelsPerFrame = mCA->m_channels;
1340  conv_in_desc.mBitsPerChannel =
1342 
1343  /* Set AudioUnit input format */
1344  err = AudioUnitSetProperty(mOutputUnit,
1345  kAudioUnitProperty_StreamFormat,
1346  kAudioUnitScope_Input,
1347  0,
1348  &conv_in_desc,
1349  sizeof(AudioStreamBasicDescription));
1350  if (err)
1351  {
1352  Error(QString("OpenAnalog: AudioUnitSetProperty returned [%1]")
1353  .arg(err));
1354  return false;
1355  }
1356  Debug(QString("OpenAnalog: set format as %1")
1357  .arg(StreamDescriptionToString(conv_in_desc)));
1358  /* Retrieve actual format */
1359  err = AudioUnitGetProperty(mOutputUnit,
1360  kAudioUnitProperty_StreamFormat,
1361  kAudioUnitScope_Input,
1362  0,
1363  &DeviceFormat,
1364  &param_size);
1365 
1366  Debug(QString("OpenAnalog: the actual set AU format is %1")
1367  .arg(StreamDescriptionToString(DeviceFormat)));
1368 
1369  // Attach callback to default output
1370  AURenderCallbackStruct input;
1371  input.inputProc = RenderCallbackAnalog;
1372  input.inputProcRefCon = this;
1373 
1374  err = AudioUnitSetProperty(mOutputUnit,
1375  kAudioUnitProperty_SetRenderCallback,
1376  kAudioUnitScope_Input,
1377  0, &input, sizeof(input));
1378  if (err)
1379  {
1380  Error(QString("OpenAnalog: AudioUnitSetProperty (callback) returned [%1]")
1381  .arg(err));
1382  return false;
1383  }
1384  mIoProc = true;
1385 
1386  // We're all set up - start the audio output unit
1387  ComponentResult res = AudioUnitInitialize(mOutputUnit);
1388  if (res)
1389  {
1390  Error(QString("OpenAnalog: AudioUnitInitialize error: [%1]")
1391  .arg(res));
1392  return false;
1393  }
1394  mInitialized = true;
1395 
1396  err = AudioOutputUnitStart(mOutputUnit);
1397  if (err)
1398  {
1399  Error(QString("OpenAnalog: AudioOutputUnitStart error: [%1]")
1400  .arg(err));
1401  return false;
1402  }
1403  mStarted = true;
1404  return true;
1405 }
1406 
1408 {
1409  OSStatus err;
1410 
1411  Debug(QString("CloseAnalog: Entering: %1")
1412  .arg((long)mOutputUnit));
1413  if (mOutputUnit)
1414  {
1415  if (mStarted)
1416  {
1417  err = AudioOutputUnitStop(mOutputUnit);
1418  Debug(QString("CloseAnalog: AudioOutputUnitStop %1")
1419  .arg(err));
1420  }
1421  if (mInitialized)
1422  {
1423  err = AudioUnitUninitialize(mOutputUnit);
1424  Debug(QString("CloseAnalog: AudioUnitUninitialize %1")
1425  .arg(err));
1426  }
1427  err = AudioComponentInstanceDispose(mOutputUnit);
1428  Debug(QString("CloseAnalog: CloseComponent %1")
1429  .arg(err));
1430  mOutputUnit = nullptr;
1431  }
1432  mIoProc = false;
1433  mInitialized = false;
1434  mStarted = false;
1435  mWasDigital = false;
1436 }
1437 
1439 {
1440  OSStatus err;
1441  AudioStreamIDVec streams;
1442  AudioStreamBasicDescription outputFormat {};
1443 
1444  Debug("OpenSPDIF: Entering");
1445 
1446  streams = StreamsList(mDeviceID);
1447  if (streams.empty())
1448  {
1449  Warn("OpenSPDIF: Couldn't retrieve list of streams");
1450  return false;
1451  }
1452 
1453  for (size_t i = 0; i < streams.size(); ++i)
1454  {
1456  if (formats.empty())
1457  continue;
1458 
1459  // Find a stream with a cac3 stream
1460  for (auto format : formats)
1461  {
1462  Debug(QString("OpenSPDIF: Considering Physical Format: %1")
1463  .arg(StreamDescriptionToString(format.mFormat)));
1464  if ((format.mFormat.mFormatID == 'IAC3' ||
1465  format.mFormat.mFormatID == kAudioFormat60958AC3) &&
1466  format.mFormat.mSampleRate == mCA->m_sampleRate)
1467  {
1468  Debug("OpenSPDIF: Found digital format");
1469  mStreamIndex = i;
1470  mStreamID = streams[i];
1471  outputFormat = format.mFormat;
1472  break;
1473  }
1474  }
1475  if (outputFormat.mFormatID)
1476  break;
1477  }
1478 
1479  if (!outputFormat.mFormatID)
1480  {
1481  Error(QString("OpenSPDIF: Couldn't find suitable output"));
1482  return false;
1483  }
1484 
1485  if (!mRevertFormat)
1486  {
1487  AudioObjectPropertyAddress pa
1488  {
1489  kAudioStreamPropertyPhysicalFormat,
1490  kAudioObjectPropertyScopeGlobal,
1491  kAudioObjectPropertyElementMaster
1492  };
1493 
1494  // Retrieve the original format of this stream first
1495  // if not done so already
1496  UInt32 paramSize = sizeof(mFormatOrig);
1497  err = AudioObjectGetPropertyData(mStreamID, &pa, 0, nullptr,
1498  &paramSize, &mFormatOrig);
1499  if (err != noErr)
1500  {
1501  Warn(QString("OpenSPDIF - could not retrieve the original streamformat: [%1]")
1502  .arg(OSS_STATUS(err)));
1503  }
1504  else
1505  {
1506  mRevertFormat = true;
1507  }
1508  }
1509 
1510  mDigitalInUse = true;
1511 
1512  SetAutoHogMode(false);
1513  bool autoHog = GetAutoHogMode();
1514  if (!autoHog)
1515  {
1516  // Hog the device
1517  SetHogStatus(true);
1518  // Set mixable to false if we are allowed to
1519  SetMixingSupport(false);
1520  }
1521 
1522  mFormatNew = outputFormat;
1524  {
1525  return false;
1526  }
1527  mBytesPerPacket = mFormatNew.mBytesPerPacket;
1528 
1529  // Add IOProc callback
1530  err = AudioDeviceCreateIOProcID(mDeviceID,
1531  (AudioDeviceIOProc)RenderCallbackSPDIF,
1532  (void *)this, &mIoProcID);
1533  if (err != noErr)
1534  {
1535  Error(QString("OpenSPDIF: AudioDeviceCreateIOProcID failed: [%1]")
1536  .arg(OSS_STATUS(err)));
1537  return false;
1538  }
1539  mIoProc = true;
1540 
1541  // Start device
1542  err = AudioDeviceStart(mDeviceID, mIoProcID);
1543  if (err != noErr)
1544  {
1545  Error(QString("OpenSPDIF: AudioDeviceStart failed: [%1]")
1546  .arg(OSS_STATUS(err)));
1547  return false;
1548  }
1549  mStarted = true;
1550  return true;
1551 }
1552 
1554 {
1555  OSStatus err;
1556 
1557  Debug(QString("CloseSPDIF: Entering [%1]").arg(mDigitalInUse));;
1558  if (!mDigitalInUse)
1559  return;
1560 
1561  // Stop device
1562  if (mStarted)
1563  {
1564  err = AudioDeviceStop(mDeviceID, mIoProcID);
1565  if (err != noErr)
1566  Error(QString("CloseSPDIF: AudioDeviceStop failed: [%1]")
1567  .arg(OSS_STATUS(err)));
1568  mStarted = false;
1569  }
1570 
1571  // Remove IOProc callback
1572  if (mIoProc)
1573  {
1574  err = AudioDeviceDestroyIOProcID(mDeviceID, mIoProcID);
1575  if (err != noErr)
1576  Error(QString("CloseSPDIF: AudioDeviceDestroyIOProcID failed: [%1]")
1577  .arg(OSS_STATUS(err)));
1578  mIoProc = false;
1579  }
1580 
1581  if (mRevertFormat)
1582  {
1584  mRevertFormat = false;
1585  }
1586 
1587  SetHogStatus(false);
1588  if (mMixerRestore > -1) // We changed the mixer status
1590  AudioHardwareUnload();
1591  mMixerRestore = -1;
1592  mBytesPerPacket = -1;
1593  mStreamIndex = -1;
1594  mWasDigital = true;
1595 }
1596 
1598  AudioStreamBasicDescription format)
1599 {
1600  Debug(QString("AudioStreamChangeFormat: %1 -> %2")
1601  .arg(s)
1602  .arg(StreamDescriptionToString(format)));
1603 
1604  AudioObjectPropertyAddress pa
1605  {
1606  kAudioStreamPropertyPhysicalFormat,
1607  kAudioObjectPropertyScopeGlobal,
1608  kAudioObjectPropertyElementMaster
1609  };
1610  OSStatus err = AudioObjectSetPropertyData(s, &pa, 0, nullptr,
1611  sizeof(format), &format);
1612  if (err != noErr)
1613  {
1614  Error(QString("AudioStreamChangeFormat couldn't set stream format: [%1]")
1615  .arg(OSS_STATUS(err)));
1616  return false;
1617  }
1618  return true;
1619 }
1620 
1622 {
1623  AudioStreamIDVec streams;
1624 
1625 
1626  // Get a list of all the streams on this device
1627  streams = StreamsList(mDeviceID);
1628  if (streams.empty())
1629  return false;
1630 
1631  for (auto stream : streams)
1632  {
1634  if (formats.empty())
1635  continue;
1636 
1637  // Find a stream with a cac3 stream
1638  for (auto format : formats) {
1639  if (format.mFormat.mFormatID == 'IAC3' ||
1640  format.mFormat.mFormatID == kAudioFormat60958AC3)
1641  {
1642  Debug("FindAC3Stream: found digital format");
1643  return true;
1644  }
1645  }
1646  }
1647 
1648  return false;
1649 }
1650 
1656 {
1657  UInt32 size;
1658  AudioObjectPropertyAddress pa
1659  {
1660  kAudioHardwarePropertyDevices,
1661  kAudioObjectPropertyScopeGlobal,
1662  kAudioObjectPropertyElementMaster
1663  };
1664 
1665  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1666  0, nullptr, &size);
1667  if (err)
1668  {
1669  Warn(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
1670  "Error [%1]")
1671  .arg(err));
1672  return;
1673  }
1674 
1675  std::vector<AudioDeviceID> devices = {};
1676  devices.resize(size / sizeof(AudioDeviceID));
1677  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1678  0, nullptr, &size, devices.data());
1679  if (err)
1680  {
1681  Warn(QString("GetPropertyData: Unable to retrieve the list of available devices. "
1682  "Error [%1]")
1683  .arg(err));
1684  return;
1685  }
1686 
1687  for (const auto & dev : devices)
1688  {
1689  AudioStreamIDVec streams;
1690 
1691  streams = StreamsList(dev);
1692  if (streams.empty())
1693  continue;
1694  for (auto stream : streams)
1695  ResetStream(stream);
1696  }
1697 }
1698 
1699 void CoreAudioData::ResetStream(AudioStreamID s)
1700 {
1701  AudioStreamBasicDescription currentFormat;
1702  OSStatus err;
1703  UInt32 paramSize;
1704  AudioObjectPropertyAddress pa
1705  {
1706  kAudioStreamPropertyPhysicalFormat,
1707  kAudioObjectPropertyScopeGlobal,
1708  kAudioObjectPropertyElementMaster
1709  };
1710 
1711 
1712  // Find the streams current physical format
1713  paramSize = sizeof(currentFormat);
1714  AudioObjectGetPropertyData(s, &pa, 0, nullptr,
1715  &paramSize, &currentFormat);
1716 
1717  // If it's currently AC-3/SPDIF then reset it to some mixable format
1718  if (currentFormat.mFormatID == 'IAC3' ||
1719  currentFormat.mFormatID == kAudioFormat60958AC3)
1720  {
1722  bool streamReset = false;
1723 
1724 
1725  if (formats.empty())
1726  return;
1727 
1728  for (auto format : formats) {
1729  if (format.mFormat.mFormatID == kAudioFormatLinearPCM)
1730  {
1731  err = AudioObjectSetPropertyData(s, &pa, 0, nullptr,
1732  sizeof(format), &(format.mFormat));
1733  if (err != noErr)
1734  {
1735  Warn(QString("ResetStream: could not set physical format: [%1]")
1736  .arg(OSS_STATUS(err)));
1737  continue;
1738  }
1739 
1740  streamReset = true;
1741  sleep(1); // For the change to take effect
1742  }
1743  }
1744  }
1745 }
1746 
1747 QMap<QString, QString> *AudioOutputCA::GetDevices(const char */*type*/)
1748 {
1749  auto *devs = new QMap<QString, QString>();
1750 
1751  // Obtain a list of all available audio devices
1752  UInt32 size = 0;
1753 
1754  AudioObjectPropertyAddress pa
1755  {
1756  kAudioHardwarePropertyDevices,
1757  kAudioObjectPropertyScopeGlobal,
1758  kAudioObjectPropertyElementMaster
1759  };
1760 
1761  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1762  0, nullptr, &size);
1763  if (err)
1764  {
1765  VBAUDIO(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
1766  "Error [%1]")
1767  .arg(err));
1768  return devs;
1769  }
1770 
1771  UInt32 deviceCount = size / sizeof(AudioDeviceID);
1772  auto* pDevices = new AudioDeviceID[deviceCount];
1773  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1774  0, nullptr, &size, pDevices);
1775  if (err)
1776  {
1777  VBAUDIO(QString("AudioOutputCA::GetDevices: Unable to retrieve the list of "
1778  "available devices. Error [%1]")
1779  .arg(err));
1780  }
1781  else
1782  {
1783  VBAUDIO(QString("GetDevices: Number of devices: %1").arg(deviceCount));
1784 
1785  for (UInt32 dev = 0; dev < deviceCount; dev++)
1786  {
1787  CoreAudioData device(nullptr, pDevices[dev]);
1788  if (device.GetTotalOutputChannels() == 0)
1789  continue;
1790  QString *name = device.GetName();
1791  if (name)
1792  {
1793  devs->insert(*name, QString());
1794  delete name;
1795  }
1796  }
1797  }
1798  return devs;
1799 }
AudioOutputBase::m_enc
bool m_enc
Definition: audiooutputbase.h:187
VolumeBase::SetCurrentVolume
virtual void SetCurrentVolume(int value)
Definition: volumebase.cpp:123
OSS_STATUS
#define OSS_STATUS(x)
Definition: audiooutputca.cpp:39
AudioOutputCA::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputca.cpp:257
channel
QDomElement channel
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:498
AudioOutputCA::m_bufferedBytes
int m_bufferedBytes
Definition: audiooutputca.h:58
CoreAudioData::ResetStream
static void ResetStream(AudioStreamID s)
Definition: audiooutputca.cpp:1699
VBAUDIO
#define VBAUDIO(str)
Definition: audiooutputbase.h:19
FORMAT_S16
@ FORMAT_S16
Definition: audiooutputsettings.h:28
CHANNELS_MIN
#define CHANNELS_MIN
Definition: audiooutputca.cpp:30
CoreAudioData::GetHogStatus
static pid_t GetHogStatus()
Definition: audiooutputca.cpp:732
CoreAudioData::mStarted
bool mStarted
Definition: audiooutputca.cpp:157
CoreAudioData::mMixerRestore
int mMixerRestore
Definition: audiooutputca.cpp:147
RatesVec
std::vector< int > RatesVec
Definition: audiooutputca.cpp:36
CoreAudioData::SetMixingSupport
bool SetMixingSupport(bool mix)
Definition: audiooutputca.cpp:810
IsRateCommon
static bool IsRateCommon(Float64 inRate)
Definition: audiooutputca.cpp:959
StreamDescriptionToString
QString StreamDescriptionToString(AudioStreamBasicDescription desc)
Definition: audiooutputca.cpp:53
AudioOutputCA::GetBufferedOnSoundcard
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
Definition: audiooutputca.cpp:396
CoreAudioData::AudioStreamChangeFormat
static int AudioStreamChangeFormat(AudioStreamID s, AudioStreamBasicDescription format)
Definition: audiooutputca.cpp:1597
AudioOutputCA::RenderAudio
bool RenderAudio(unsigned char *aubuf, int size, unsigned long long timestamp)
Object-oriented part of callback.
Definition: audiooutputca.cpp:350
CoreAudioData::GetDefaultOutputDevice
static AudioDeviceID GetDefaultOutputDevice()
Definition: audiooutputca.cpp:592
AudioStreamIDVec
std::vector< AudioStreamID > AudioStreamIDVec
Definition: audiooutputca.cpp:33
ReorderSmpteToCA
static void ReorderSmpteToCA(void *buf, uint frames, AudioFormat format)
Definition: audiooutputca.cpp:339
d
static const uint16_t * d
Definition: iso6937tables.cpp:1025
AudioOutputCA::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool digital) override
Definition: audiooutputca.cpp:200
VolumeBase::m_internalVol
bool m_internalVol
Definition: volumebase.h:41
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:856
AudioOutputSettings::FormatToBits
static int FormatToBits(AudioFormat format)
Definition: audiooutputsettings.cpp:159
CoreAudioData::OpenDevice
bool OpenDevice()
arg
arg(title).arg(filename).arg(doDelete))
CoreAudioData
This holds Core Audio member variables and low-level audio IO methods The name is now a misnomer,...
Definition: audiooutputca.cpp:93
AudioOutputCA::d
CoreAudioData * d
Definition: audiooutputca.h:55
CoreAudioData::mIoProcID
AudioDeviceIOProcID mIoProcID
Definition: audiooutputca.cpp:159
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
AudioOutputCA::GetAudiotime
int64_t GetAudiotime(void) override
Reimplement the base class's version of GetAudiotime() so that we don't use gettimeofday or Qt mutexe...
Definition: audiooutputca.cpp:404
CoreAudioData::mStreamIndex
int mStreamIndex
Definition: audiooutputca.cpp:150
CoreAudioData::Debug
static void Debug(const QString &msg)
Definition: audiooutputca.cpp:130
AudioOutputBase::m_sampleRate
int m_sampleRate
Definition: audiooutputbase.h:177
CoreAudioData::FindAC3Stream
bool FindAC3Stream()
Definition: audiooutputca.cpp:1621
AudioOutputBase::m_killAudio
bool m_killAudio
Definition: audiooutputbase.h:194
CoreAudioData::mDigitalInUse
bool mDigitalInUse
Definition: audiooutputca.cpp:145
CoreAudioData::mStreamID
AudioStreamID mStreamID
Definition: audiooutputca.cpp:149
AudioOutputCA
Implements Core Audio (Mac OS X Hardware Abstraction Layer) output.
Definition: audiooutputca.h:14
AudioOutputCA::GetDevices
static QMap< QString, QString > * GetDevices(const char *type=nullptr)
Definition: audiooutputca.cpp:1747
AudioSettings
Definition: audiosettings.h:29
AudioOutputCA::~AudioOutputCA
virtual ~AudioOutputCA()
Definition: audiooutputca.cpp:193
AudioOutputBase::m_mainDevice
QString m_mainDevice
Definition: audiooutputbase.h:182
sleep
unsigned sleep(unsigned int x)
Definition: compat.h:159
AudioOutputBase::m_stretchFactor
float m_stretchFactor
Definition: audiooutputbase.h:190
CoreAudioData::GetMixingSupport
bool GetMixingSupport()
Definition: audiooutputca.cpp:842
CoreAudioData::CloseAnalog
void CloseAnalog()
Definition: audiooutputca.cpp:1407
AudioOutputBase
Definition: audiooutputbase.h:50
AudioSettings::m_init
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:84
CoreAudioData::GetDeviceWithName
static AudioDeviceID GetDeviceWithName(const QString &deviceName)
Definition: audiooutputca.cpp:538
CoreAudioData::mBytesPerPacket
UInt32 mBytesPerPacket
Definition: audiooutputca.cpp:151
RenderCallbackSPDIF
static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const void *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inRefCon)
Definition: audiooutputca.cpp:465
formats
const std::array< const std::string, 8 > formats
Definition: vbilut.cpp:189
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:32
AudioOutputBase::Reconfigure
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
Definition: audiooutputbase.cpp:464
CoreAudioData::StreamsList
static AudioStreamIDVec StreamsList(AudioDeviceID d)
Get a list of all the streams on this device.
Definition: audiooutputca.cpp:864
CHANNELS_MAX
#define CHANNELS_MAX
Definition: audiooutputca.cpp:31
AudioOutputBase::GetAudioData
int GetAudioData(uchar *buffer, int buf_size, bool full_buffer, volatile uint *local_raud=nullptr)
Copy frames from the audiobuffer into the buffer provided.
Definition: audiooutputbase.cpp:1728
AudioOutputCA::CloseDevice
void CloseDevice(void) override
Definition: audiooutputca.cpp:306
CoreAudioData::RatesList
static RatesVec RatesList(AudioDeviceID d)
Definition: audiooutputca.cpp:969
CoreAudioData::mHog
pid_t mHog
Definition: audiooutputca.cpp:146
AudioOutputCA::CoreAudioData
friend class CoreAudioData
Definition: audiooutputca.h:56
AudioStreamRangedVec
std::vector< AudioStreamRangedDescription > AudioStreamRangedVec
Definition: audiooutputca.cpp:34
CoreAudioData::mRevertFormat
bool mRevertFormat
Definition: audiooutputca.cpp:154
CoreAudioData::GetTotalOutputChannels
int GetTotalOutputChannels()
Definition: audiooutputca.cpp:618
CoreAudioData::ResetAudioDevices
static void ResetAudioDevices()
Reset any devices with an AC3 stream back to a Linear PCM so that they can become a default output de...
Definition: audiooutputca.cpp:1655
sNumberCommonSampleRates
static UInt32 sNumberCommonSampleRates
Definition: audiooutputca.cpp:951
CoreAudioData::SetAutoHogMode
static void SetAutoHogMode(bool enable)
Definition: audiooutputca.cpp:713
AudioOutputBase::m_pauseAudio
bool m_pauseAudio
Definition: audiooutputbase.h:196
sCommonSampleRates
static Float64 sCommonSampleRates[]
Definition: audiooutputca.cpp:952
CoreAudioData::Warn
static void Warn(const QString &msg)
Definition: audiooutputca.cpp:136
uint
unsigned int uint
Definition: compat.h:140
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
AudioOutputCA::WriteAudio
void WriteAudio(unsigned char *aubuf, int size) override
Definition: audiooutputca.cpp:390
AudioOutputBase::m_passthru
bool m_passthru
Definition: audiooutputbase.h:186
AudioOutputCA::Debug
static void Debug(const QString &msg)
Definition: audiooutputca.h:33
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:930
AudioOutputBase::m_outputFormat
AudioFormat m_outputFormat
Definition: audiooutputbase.h:176
CoreAudioData::SetHogStatus
bool SetHogStatus(bool hog)
Definition: audiooutputca.cpp:756
CoreAudioData::CoreAudioData
CoreAudioData(AudioOutputCA *parent)
Definition: audiooutputca.cpp:502
CoreAudioData::mOutputUnit
AudioUnit mOutputUnit
Definition: audiooutputca.cpp:142
AudioOutputBase::audioready
int audioready() const
Get the scaled number of bytes in the audiobuffer, i.e.
Definition: audiooutputbase.cpp:1003
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:171
AudioOutputBase::m_setInitialVol
bool m_setInitialVol
Definition: audiooutputbase.h:200
RenderCallbackAnalog
static OSStatus RenderCallbackAnalog(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
Definition: audiooutputca.cpp:419
CoreAudioData::OpenSPDIF
bool OpenSPDIF()
Definition: audiooutputca.cpp:1438
AudioOutputBase::m_outputBytesPerFrame
int m_outputBytesPerFrame
Definition: audiooutputbase.h:174
mythcorecontext.h
CoreAudioData::mIoProc
bool mIoProc
Definition: audiooutputca.cpp:155
AudioOutputBase::m_actuallyPaused
bool m_actuallyPaused
Definition: audiooutputbase.h:197
CoreAudioData::OpenAnalog
int OpenAnalog()
Definition: audiooutputca.cpp:1088
CoreAudioData::mFormatNew
AudioStreamBasicDescription mFormatNew
Definition: audiooutputca.cpp:153
CoreAudioData::Error
static void Error(const QString &msg)
Definition: audiooutputca.cpp:133
CoreAudioData::mCA
AudioOutputCA * mCA
Definition: audiooutputca.cpp:139
AudioOutputBase::GetBaseAudBufTimeCode
int GetBaseAudBufTimeCode() const
Definition: audiooutputbase.h:165
AudioOutputSettings
Definition: audiooutputsettings.h:53
CoreAudioData::mDeviceID
AudioDeviceID mDeviceID
Definition: audiooutputca.cpp:148
AudioOutputCA::AudioOutputCA
AudioOutputCA(const AudioSettings &settings)
Definition: audiooutputca.cpp:181
AudioOutputBase::m_effDsp
int m_effDsp
Definition: audiooutputbase.h:178
AudioOutputBase::InitSettings
void InitSettings(const AudioSettings &settings)
Definition: audiooutputbase.cpp:116
CoreAudioData::mFormatOrig
AudioStreamBasicDescription mFormatOrig
Definition: audiooutputca.cpp:152
CoreAudioData::FormatsList
static AudioStreamRangedVec FormatsList(AudioStreamID s)
Definition: audiooutputca.cpp:908
_ReorderSmpteToCA
static void _ReorderSmpteToCA(AudioDataType *buf, uint frames)
Definition: audiooutputca.cpp:317
CoreAudioData::mWasDigital
bool mWasDigital
Definition: audiooutputca.cpp:158
CoreAudioData::ChannelsList
bool ChannelsList(AudioDeviceID d, bool passthru, ChannelsArr &chans)
Definition: audiooutputca.cpp:1040
CoreAudioData::GetAutoHogMode
static bool GetAutoHogMode()
Definition: audiooutputca.cpp:692
audiooutputca.h
AudioOutputCA::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputca.cpp:456
CoreAudioData::GetName
QString * GetName()
Definition: audiooutputca.cpp:663
AudioValueRangeVec
std::vector< AudioValueRange > AudioValueRangeVec
Definition: audiooutputca.cpp:35
AudioOutputCA::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputca.cpp:442
AudioFormat
AudioFormat
Definition: audiooutputsettings.h:25
ChannelsArr
std::array< bool, CHANNELS_MAX > ChannelsArr
Definition: audiooutputca.cpp:37
CoreAudioData::mInitialized
bool mInitialized
Definition: audiooutputca.cpp:156
CoreAudioData::CloseSPDIF
void CloseSPDIF()
Definition: audiooutputca.cpp:1553
UInt32ToFourCC
char * UInt32ToFourCC(const UInt32 *pVal)
Definition: audiooutputca.cpp:40
AudioOutputCA::Error
static void Error(const QString &msg)
Definition: audiooutputca.h:36
MThread::usleep
static void usleep(unsigned long time)
Definition: mthread.cpp:346
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:916