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