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 #if !defined(MAC_OS_VERSION_12_0)
30 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
31 #endif
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(unsigned char *aubuf, int size)
399 {
400  (void)aubuf;
401  (void)size;
402 }
403 
405 {
406  return m_bufferedBytes;
407 }
408 
412 std::chrono::milliseconds AudioOutputCA::GetAudiotime(void)
413 {
414  std::chrono::milliseconds audbuf_timecode = GetBaseAudBufTimeCode();
415 
416  if (audbuf_timecode == 0ms)
417  return 0ms;
418 
419  int totalbuffer = audioready() + GetBufferedOnSoundcard();
420 
421  return audbuf_timecode - millisecondsFromFloat(totalbuffer * 100000.0 /
424 }
425 
426 /* This callback provides converted audio data to the default output device. */
427 OSStatus RenderCallbackAnalog(void *inRefCon,
428  AudioUnitRenderActionFlags *ioActionFlags,
429  const AudioTimeStamp *inTimeStamp,
430  UInt32 inBusNumber,
431  UInt32 inNumberFrames,
432  AudioBufferList *ioData)
433 {
434  (void)inBusNumber;
435  (void)inNumberFrames;
436 
437  AudioOutputCA *inst = (static_cast<CoreAudioData *>(inRefCon))->mCA;
438 
439  if (!inst->RenderAudio((unsigned char *)(ioData->mBuffers[0].mData),
440  ioData->mBuffers[0].mDataByteSize,
441  inTimeStamp->mHostTime))
442  {
443  // play silence if RenderAudio returns false
444  memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
445  *ioActionFlags = kAudioUnitRenderAction_OutputIsSilence;
446  }
447  return noErr;
448 }
449 
450 int AudioOutputCA::GetVolumeChannel(int channel) const
451 {
452  // FIXME: this only returns global volume
453  (void)channel;
454  Float32 volume;
455 
456  if (!AudioUnitGetParameter(d->mOutputUnit,
457  kHALOutputParam_Volume,
458  kAudioUnitScope_Global, 0, &volume))
459  return (int)lroundf(volume * 100.0F);
460 
461  return 0; // error case
462 }
463 
464 void AudioOutputCA::SetVolumeChannel(int channel, int volume)
465 {
466  // FIXME: this only sets global volume
467  (void)channel;
468  AudioUnitSetParameter(d->mOutputUnit, kHALOutputParam_Volume,
469  kAudioUnitScope_Global, 0, (volume * 0.01F), 0);
470 }
471 
472 // IOProc style callback for SPDIF audio output
473 static OSStatus RenderCallbackSPDIF(AudioDeviceID inDevice,
474  const AudioTimeStamp *inNow,
475  const void *inInputData,
476  const AudioTimeStamp *inInputTime,
477  AudioBufferList *outOutputData,
478  const AudioTimeStamp *inOutputTime,
479  void *inRefCon)
480 {
481  auto *d = static_cast<CoreAudioData *>(inRefCon);
482  AudioOutputCA *a = d->mCA;
483  int index = d->mStreamIndex;
484 
485  (void)inDevice; // unused
486  (void)inNow; // unused
487  (void)inInputData; // unused
488  (void)inInputTime; // unused
489 
490  /*
491  * HACK: No idea why this would be the case, but after the second run, we get
492  * incorrect value
493  */
494  if (d->mBytesPerPacket > 0 &&
495  outOutputData->mBuffers[index].mDataByteSize > d->mBytesPerPacket)
496  {
497  outOutputData->mBuffers[index].mDataByteSize = d->mBytesPerPacket;
498  }
499  if (!a->RenderAudio((unsigned char *)(outOutputData->mBuffers[index].mData),
500  outOutputData->mBuffers[index].mDataByteSize,
501  inOutputTime->mHostTime))
502  {
503  // play silence if RenderAudio returns false
504  memset(outOutputData->mBuffers[index].mData, 0,
505  outOutputData->mBuffers[index].mDataByteSize);
506  }
507  return noErr;
508 }
509 
511 {
512  // Reset all the devices to a default 'non-hog' and mixable format.
513  // If we don't do this we may be unable to find the Default Output device.
514  // (e.g. if we crashed last time leaving it stuck in AC-3 mode)
516 
518 }
519 
520 CoreAudioData::CoreAudioData(AudioOutputCA *parent, AudioDeviceID deviceID) :
521  mCA(parent)
522 {
524  mDeviceID = deviceID;
525 }
526 
527 CoreAudioData::CoreAudioData(AudioOutputCA *parent, QString deviceName) :
528  mCA(parent)
529 {
531  mDeviceID = GetDeviceWithName(deviceName);
532  if (!mDeviceID)
533  {
534  // Didn't find specified device, use default one
536  if (deviceName != "Default Output Device")
537  {
538  Warn(QString("CoreAudioData: \"%1\" not found, using default device %2.")
539  .arg(deviceName).arg(mDeviceID));
540  }
541  }
542  Debug(QString("CoreAudioData: device number is %1")
543  .arg(mDeviceID));
544 }
545 
546 AudioDeviceID CoreAudioData::GetDeviceWithName(const QString &deviceName)
547 {
548  UInt32 size = 0;
549  AudioDeviceID deviceID = 0;
550  AudioObjectPropertyAddress pa
551  {
552  kAudioHardwarePropertyDevices,
553  kAudioObjectPropertyScopeGlobal,
555  };
556 
557  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
558  0, nullptr, &size);
559  if (err)
560  {
561  Warn(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
562  "Error [%1]")
563  .arg(err));
564  return deviceID;
565  }
566 
567  UInt32 deviceCount = size / sizeof(AudioDeviceID);
568  std::vector<AudioDeviceID> devices = {};
569  devices.resize(deviceCount);
570 
571  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
572  0, nullptr, &size, devices.data());
573  if (err)
574  {
575  Warn(QString("GetDeviceWithName: Unable to retrieve the list of available devices. "
576  "Error [%1]")
577  .arg(err));
578  }
579  else
580  {
581  for (const auto & dev : devices)
582  {
583  CoreAudioData device(nullptr, dev);
584  if (device.GetTotalOutputChannels() == 0)
585  continue;
586  QString *name = device.GetName();
587  if (name && *name == deviceName)
588  {
589  Debug(QString("GetDeviceWithName: Found: %1").arg(*name));
590  deviceID = dev;
591  delete name;
592  }
593  if (deviceID)
594  break;
595  }
596  }
597  return deviceID;
598 }
599 
601 {
602  UInt32 paramSize;
603  AudioDeviceID deviceId = 0;
604  AudioObjectPropertyAddress pa
605  {
606  kAudioHardwarePropertyDefaultOutputDevice,
607  kAudioObjectPropertyScopeGlobal,
609  };
610 
611  // Find the ID of the default Device
612  paramSize = sizeof(deviceId);
613  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
614  0, nullptr, &paramSize, &deviceId);
615  if (err == noErr)
616  Debug(QString("GetDefaultOutputDevice: default device ID = %1").arg(deviceId));
617  else
618  {
619  Warn(QString("GetDefaultOutputDevice: could not get default audio device: [%1]")
620  .arg(OSS_STATUS(err)));
621  deviceId = 0;
622  }
623  return deviceId;
624 }
625 
627 {
628  if (!mDeviceID)
629  return 0;
630  UInt32 channels = 0;
631  UInt32 size = 0;
632  AudioObjectPropertyAddress pa
633  {
634  kAudioDevicePropertyStreamConfiguration,
635  kAudioDevicePropertyScopeOutput, // Scope needs to be set to output to find output streams
637  };
638 
639  OSStatus err = AudioObjectGetPropertyDataSize(mDeviceID, &pa,
640  0, nullptr, &size);
641  if (err)
642  {
643  Warn(QString("GetTotalOutputChannels: Unable to get "
644  "size of device output channels - id: %1 Error = [%2]")
645  .arg(mDeviceID)
646  .arg(err));
647  return 0;
648  }
649 
650  auto *pList = (AudioBufferList *)malloc(size);
651  err = AudioObjectGetPropertyData(mDeviceID, &pa,
652  0, nullptr, &size, pList);
653  if (!err)
654  {
655  for (UInt32 buffer = 0; buffer < pList->mNumberBuffers; buffer++)
656  channels += pList->mBuffers[buffer].mNumberChannels;
657  }
658  else
659  {
660  Warn(QString("GetTotalOutputChannels: Unable to get "
661  "total device output channels - id: %1 Error = [%2]")
662  .arg(mDeviceID)
663  .arg(err));
664  }
665  Debug(QString("GetTotalOutputChannels: Found %1 channels in %2 buffers")
666  .arg(channels).arg(pList->mNumberBuffers));
667  free(pList);
668  return channels;
669 }
670 
672 {
673  if (!mDeviceID)
674  return nullptr;
675 
676  AudioObjectPropertyAddress pa
677  {
678  kAudioObjectPropertyName,
679  kAudioObjectPropertyScopeGlobal,
681  };
682 
683  CFStringRef name;
684  UInt32 propertySize = sizeof(CFStringRef);
685  OSStatus err = AudioObjectGetPropertyData(mDeviceID, &pa,
686  0, nullptr, &propertySize, &name);
687  if (err)
688  {
689  Error(QString("AudioObjectGetPropertyData for kAudioObjectPropertyName error: [%1]")
690  .arg(err));
691  return nullptr;
692  }
693  char *cname = new char[CFStringGetLength(name) + 1];
694  CFStringGetCString(name, cname, CFStringGetLength(name) + 1, kCFStringEncodingUTF8);
695  auto *qname = new QString(cname);
696  delete[] cname;
697  return qname;
698 }
699 
701 {
702  UInt32 val = 0;
703  UInt32 size = sizeof(val);
704  AudioObjectPropertyAddress pa
705  {
706  kAudioHardwarePropertyHogModeIsAllowed,
707  kAudioObjectPropertyScopeGlobal,
709  };
710 
711  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr, &size, &val);
712  if (err)
713  {
714  Warn(QString("GetAutoHogMode: Unable to get auto 'hog' mode. Error = [%1]")
715  .arg(err));
716  return false;
717  }
718  return (val == 1);
719 }
720 
722 {
723  UInt32 val = enable ? 1 : 0;
724  AudioObjectPropertyAddress pa
725  {
726  kAudioHardwarePropertyHogModeIsAllowed,
727  kAudioObjectPropertyScopeGlobal,
729  };
730 
731  OSStatus err = AudioObjectSetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr,
732  sizeof(val), &val);
733  if (err)
734  {
735  Warn(QString("SetAutoHogMode: Unable to set auto 'hog' mode. Error = [%1]")
736  .arg(err));
737  }
738 }
739 
741 {
742  pid_t PID;
743  UInt32 PIDsize = sizeof(PID);
744  AudioObjectPropertyAddress pa
745  {
746  kAudioDevicePropertyHogMode,
747  kAudioObjectPropertyScopeGlobal,
749  };
750 
751  OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, nullptr,
752  &PIDsize, &PID);
753  if (err != noErr)
754  {
755  // This is not a fatal error.
756  // Some drivers simply don't support this property
757  Debug(QString("GetHogStatus: unable to check: [%1]")
758  .arg(err));
759  return -1;
760  }
761  return PID;
762 }
763 
765 {
766  AudioObjectPropertyAddress pa
767  {
768  kAudioDevicePropertyHogMode,
769  kAudioObjectPropertyScopeGlobal,
771  };
772 
773  // According to Jeff Moore (Core Audio, Apple), Setting kAudioDevicePropertyHogMode
774  // is a toggle and the only way to tell if you do get hog mode is to compare
775  // the returned pid against getpid, if the match, you have hog mode, if not you don't.
776  if (!mDeviceID)
777  return false;
778 
779  if (hog)
780  {
781  if (mHog == -1) // Not already set
782  {
783  Debug(QString("SetHogStatus: Setting 'hog' status on device %1")
784  .arg(mDeviceID));
785  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
786  sizeof(mHog), &mHog);
787  if (err || mHog != getpid())
788  {
789  Warn(QString("SetHogStatus: Unable to set 'hog' status. Error = [%1]")
790  .arg(OSS_STATUS(err)));
791  return false;
792  }
793  Debug(QString("SetHogStatus: Successfully set 'hog' status on device %1")
794  .arg(mDeviceID));
795  }
796  }
797  else
798  {
799  if (mHog > -1) // Currently Set
800  {
801  Debug(QString("SetHogStatus: Releasing 'hog' status on device %1")
802  .arg(mDeviceID));
803  pid_t hogPid = -1;
804  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
805  sizeof(hogPid), &hogPid);
806  if (err || hogPid == getpid())
807  {
808  Warn(QString("SetHogStatus: Unable to release 'hog' status. Error = [%1]")
809  .arg(OSS_STATUS(err)));
810  return false;
811  }
812  mHog = hogPid; // Reset internal state
813  }
814  }
815  return true;
816 }
817 
819 {
820  if (!mDeviceID)
821  return false;
822  int restore = -1;
823  if (mMixerRestore == -1) // This is our first change to this setting. Store the original setting for restore
824  restore = (GetMixingSupport() ? 1 : 0);
825  UInt32 mixEnable = mix ? 1 : 0;
826  Debug(QString("SetMixingSupport: %1abling mixing for device %2")
827  .arg(mix ? "En" : "Dis")
828  .arg(mDeviceID));
829 
830  AudioObjectPropertyAddress pa
831  {
832  kAudioDevicePropertySupportsMixing,
833  kAudioObjectPropertyScopeGlobal,
835  };
836  OSStatus err = AudioObjectSetPropertyData(mDeviceID, &pa, 0, nullptr,
837  sizeof(mixEnable), &mixEnable);
838  if (err)
839  {
840  Warn(QString("SetMixingSupport: Unable to set MixingSupport to %1. Error = [%2]")
841  .arg(mix ? "'On'" : "'Off'")
842  .arg(OSS_STATUS(err)));
843  return false;
844  }
845  if (mMixerRestore == -1)
846  mMixerRestore = restore;
847  return true;
848 }
849 
851 {
852  if (!mDeviceID)
853  return false;
854  UInt32 val = 0;
855  UInt32 size = sizeof(val);
856  AudioObjectPropertyAddress pa
857  {
858  kAudioDevicePropertySupportsMixing,
859  kAudioObjectPropertyScopeGlobal,
861  };
862  OSStatus err = AudioObjectGetPropertyData(mDeviceID, &pa, 0, nullptr,
863  &size, &val);
864  if (err)
865  return false;
866  return (val > 0);
867 }
868 
873 {
874  OSStatus err;
875  UInt32 listSize;
876  AudioStreamIDVec vec {};
877 
878  AudioObjectPropertyAddress pa
879  {
880  kAudioDevicePropertyStreams,
881  kAudioObjectPropertyScopeGlobal,
883  };
884 
885  err = AudioObjectGetPropertyDataSize(d, &pa,
886  0, nullptr, &listSize);
887  if (err != noErr)
888  {
889  Error(QString("StreamsList: could not get list size: [%1]")
890  .arg(OSS_STATUS(err)));
891  return {};
892  }
893  try
894  {
895  // Bugfix: vec.reserve will not change size of vector since contents are updated directly via memory copy
896  // In general all std::vector arrays have been changed from reserve to resize.
897  vec.resize(listSize / sizeof(AudioStreamID));
898  }
899  catch (...)
900  {
901  Error("StreamsList(): out of memory?");
902  return {};
903  }
904 
905  err = AudioObjectGetPropertyData(d, &pa,
906  0, nullptr, &listSize, vec.data());
907  if (err != noErr)
908  {
909  Error(QString("StreamsList: could not get list: [%1]")
910  .arg(OSS_STATUS(err)));
911  return {};
912  }
913 
914  return vec;
915 }
916 
918 {
919  OSStatus err;
921  UInt32 listSize;
922 
923  AudioObjectPropertyAddress pa
924  {
925  // Bugfix: kAudioStreamPropertyPhysicalFormats, is meant to only give array of AudioStreamBasicDescription
926  kAudioStreamPropertyAvailablePhysicalFormats, // gives array of AudioStreamRangedDescription
927  kAudioObjectPropertyScopeGlobal,
929  };
930 
931  // Retrieve all the stream formats supported by this output stream
932  err = AudioObjectGetPropertyDataSize(s, &pa, 0, nullptr, &listSize);
933  if (err != noErr)
934  {
935  Warn(QString("FormatsList(): couldn't get list size: [%1]")
936  .arg(OSS_STATUS(err)));
937  return {};
938  }
939 
940  try
941  {
942  vec.resize(listSize / sizeof(AudioStreamRangedDescription));
943  }
944  catch (...)
945  {
946  Error("FormatsList(): out of memory?");
947  return {};
948  }
949 
950  err = AudioObjectGetPropertyData(s, &pa, 0, nullptr, &listSize, vec.data());
951  if (err != noErr)
952  {
953  Warn(QString("FormatsList: couldn't get list: [%1]")
954  .arg(OSS_STATUS(err)));
955  return {};
956  }
957 
958  return vec;
959 }
960 
961 static UInt32 sNumberCommonSampleRates = 15;
962 static Float64 sCommonSampleRates[] = {
963  8000.0, 11025.0, 12000.0,
964  16000.0, 22050.0, 24000.0,
965  32000.0, 44100.0, 48000.0,
966  64000.0, 88200.0, 96000.0,
967  128000.0, 176400.0, 192000.0 };
968 
969 static bool IsRateCommon(Float64 inRate)
970 {
971  bool theAnswer = false;
972  for(UInt32 i = 0; !theAnswer && (i < sNumberCommonSampleRates); i++)
973  {
974  theAnswer = inRate == sCommonSampleRates[i];
975  }
976  return theAnswer;
977 }
978 
980 {
981  OSStatus err;
982  AudioValueRangeVec ranges;
983  RatesVec finalvec;
984  UInt32 listSize;
985 
986  AudioObjectPropertyAddress pa
987  {
988  kAudioDevicePropertyAvailableNominalSampleRates,
989  kAudioObjectPropertyScopeGlobal,
991  };
992 
993  // retrieve size of rate list
994  err = AudioObjectGetPropertyDataSize(d, &pa, 0, nullptr, &listSize);
995  if (err != noErr)
996  {
997  Warn(QString("RatesList(): couldn't get data rate list size: [%1]")
998  .arg(err));
999  return {};
1000  }
1001 
1002  try
1003  {
1004  ranges.resize(listSize / sizeof(AudioValueRange));
1005  finalvec.reserve(listSize / sizeof(AudioValueRange));
1006  }
1007  catch (...)
1008  {
1009  Error("RatesList(): out of memory?");
1010  return {};
1011  }
1012 
1013  err = AudioObjectGetPropertyData(d, &pa, 0, nullptr, &listSize, ranges.data());
1014  if (err != noErr)
1015  {
1016  Warn(QString("RatesList(): couldn't get list: [%1]")
1017  .arg(err));
1018  return {};
1019  }
1020 
1021  // iterate through the ranges and add the minimum, maximum, and common rates in between
1022  UInt32 theFirstIndex;
1023  UInt32 theLastIndex = 0;
1024  for(auto range : ranges)
1025  {
1026  theFirstIndex = theLastIndex;
1027  // find the index of the first common rate greater than or equal to the minimum
1028  while((theFirstIndex < sNumberCommonSampleRates) && (sCommonSampleRates[theFirstIndex] < range.mMinimum))
1029  theFirstIndex++;
1030 
1031  if (theFirstIndex >= sNumberCommonSampleRates)
1032  break;
1033 
1034  theLastIndex = theFirstIndex;
1035  // find the index of the first common rate greater than or equal to the maximum
1036  while((theLastIndex < sNumberCommonSampleRates) && (sCommonSampleRates[theLastIndex] < range.mMaximum))
1037  {
1038  finalvec.push_back(sCommonSampleRates[theLastIndex]);
1039  theLastIndex++;
1040  }
1041  if (IsRateCommon(range.mMinimum))
1042  finalvec.push_back(range.mMinimum);
1043  else if (IsRateCommon(range.mMaximum))
1044  finalvec.push_back(range.mMaximum);
1045  }
1046 
1047  return finalvec;
1048 }
1049 
1050 bool CoreAudioData::ChannelsList(AudioDeviceID /*d*/, bool passthru, ChannelsArr& chans)
1051 {
1052  AudioStreamIDVec streams;
1054  bool founddigital = false;
1055 
1056  chans.fill(false);
1057 
1058  streams = StreamsList(mDeviceID);
1059  if (streams.empty())
1060  return false;
1061 
1062  if (passthru)
1063  {
1064  for (auto stream : streams)
1065  {
1066  formats = FormatsList(stream);
1067  if (formats.empty())
1068  continue;
1069 
1070  // Find a stream with a cac3 stream
1071  for (auto format : formats)
1072  {
1073  Debug(QString("ChannelsList: (passthru) found format: %1")
1074  .arg(StreamDescriptionToString(format.mFormat)));
1075  // Add supported number of channels
1076  if (format.mFormat.mChannelsPerFrame <= CHANNELS_MAX)
1077  chans[format.mFormat.mChannelsPerFrame-1] = true;
1078 
1079  if (format.mFormat.mFormatID == 'IAC3' ||
1080  format.mFormat.mFormatID == kAudioFormat60958AC3)
1081  {
1082  // By default AC3 has 6 (5.1) channels but CoreAudio seems to set mChannelsPerFrame to 2
1083  // and considers it a "2 channel Encoded Digital Audio"
1084  chans[6-1] = true;
1085  // chans[format.mFormat.mChannelsPerFrame-1] = true;
1086  founddigital = true;
1087  }
1088  }
1089  }
1090  }
1091 
1092  if (!founddigital)
1093  {
1094  for (auto stream : streams)
1095  {
1096  formats = FormatsList(stream);
1097  if (formats.empty())
1098  continue;
1099  for (auto format : formats)
1100  {
1101  Debug(QString("ChannelsList: (!founddigital) found format: %1")
1102  .arg(StreamDescriptionToString(format.mFormat)));
1103  if (format.mFormat.mChannelsPerFrame <= CHANNELS_MAX)
1104  chans[format.mFormat.mChannelsPerFrame-1] = true;
1105  }
1106  }
1107  }
1108  return true;
1109 }
1110 
1112 {
1113  AudioComponentDescription desc;
1114  AudioStreamBasicDescription DeviceFormat;
1115  AudioChannelLayout *layout;
1116  AudioChannelLayout new_layout;
1117  AudioDeviceID defaultDevice = GetDefaultOutputDevice();
1118  AudioObjectPropertyAddress pa
1119  {
1120  kAudioHardwarePropertyDevices,
1121  kAudioObjectPropertyScopeGlobal,
1123  };
1124 
1125  Debug("OpenAnalog: Entering");
1126 
1127  desc.componentType = kAudioUnitType_Output;
1128  if (defaultDevice == mDeviceID)
1129  {
1130  desc.componentSubType = kAudioUnitSubType_DefaultOutput;
1131  }
1132  else
1133  {
1134  desc.componentSubType = kAudioUnitSubType_HALOutput;
1135  }
1136  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
1137  desc.componentFlags = 0;
1138  desc.componentFlagsMask = 0;
1139  mDigitalInUse = false;
1140 
1141  AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
1142  if (comp == nullptr)
1143  {
1144  Error("OpenAnalog: AudioComponentFindNext failed");
1145  return false;
1146  }
1147 
1148  OSErr err = AudioComponentInstanceNew(comp, &mOutputUnit);
1149  if (err)
1150  {
1151  Error(QString("OpenAnalog: AudioComponentInstanceNew returned %1")
1152  .arg(err));
1153  return false;
1154  }
1155 
1156  // Check if we have IO
1157  UInt32 hasIO = 0;
1158  UInt32 size_hasIO = sizeof(hasIO);
1159  err = AudioUnitGetProperty(mOutputUnit,
1160  kAudioOutputUnitProperty_HasIO,
1161  kAudioUnitScope_Output,
1162  0,
1163  &hasIO, &size_hasIO);
1164  Debug(QString("OpenAnalog: HasIO (output) = %1").arg(hasIO));
1165  if (!hasIO)
1166  {
1167  UInt32 enableIO = 1;
1168  err = AudioUnitSetProperty(mOutputUnit,
1169  kAudioOutputUnitProperty_EnableIO,
1170  kAudioUnitScope_Global,
1171  0,
1172  &enableIO, sizeof(enableIO));
1173  if (err)
1174  {
1175  Warn(QString("OpenAnalog: failed enabling IO: %1")
1176  .arg(err));
1177  }
1178  hasIO = 0;
1179  err = AudioUnitGetProperty(mOutputUnit,
1180  kAudioOutputUnitProperty_HasIO,
1181  kAudioUnitScope_Output,
1182  0,
1183  &hasIO, &size_hasIO);
1184  Debug(QString("HasIO = %1").arg(hasIO));
1185  }
1186 
1187  /*
1188  * We shouldn't have to do this distinction, however for some unknown reasons
1189  * assigning device to AudioUnit fail when switching from SPDIF mode
1190  */
1191  if (defaultDevice != mDeviceID)
1192  {
1193  err = AudioUnitSetProperty(mOutputUnit,
1194  kAudioOutputUnitProperty_CurrentDevice,
1195  kAudioUnitScope_Global,
1196  0,
1197  &mDeviceID, sizeof(mDeviceID));
1198  if (err)
1199  {
1200  Error(QString("OpenAnalog: Unable to set current device to %1. Error = %2")
1201  .arg(mDeviceID)
1202  .arg(err));
1203  return -1;
1204  }
1205  }
1206  /* Get the current format */
1207  UInt32 param_size = sizeof(AudioStreamBasicDescription);
1208 
1209  err = AudioUnitGetProperty(mOutputUnit,
1210  kAudioUnitProperty_StreamFormat,
1211  kAudioUnitScope_Input,
1212  0,
1213  &DeviceFormat,
1214  &param_size );
1215  if (err)
1216  {
1217  Warn(QString("OpenAnalog: Unable to retrieve current stream format: [%1]")
1218  .arg(err));
1219  }
1220  else
1221  {
1222  Debug(QString("OpenAnalog: current format is: %1")
1223  .arg(StreamDescriptionToString(DeviceFormat)));
1224  }
1225  /* Get the channel layout of the device side of the unit */
1226  pa.mSelector = kAudioDevicePropertyPreferredChannelLayout;
1227  err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1228  0, nullptr, &param_size);
1229 
1230  if(!err)
1231  {
1232  layout = (AudioChannelLayout *) malloc(param_size);
1233 
1234  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1235  0, nullptr, &param_size, layout);
1236 
1237  /* We need to "fill out" the ChannelLayout, because there are multiple ways that it can be set */
1238  if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap)
1239  {
1240  /* bitmap defined channellayout */
1241  err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForBitmap,
1242  sizeof(UInt32), &layout->mChannelBitmap,
1243  &param_size,
1244  layout);
1245  if (err)
1246  Warn("OpenAnalog: Can't retrieve current channel layout");
1247  }
1248  else if(layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions )
1249  {
1250  /* layouttags defined channellayout */
1251  err = AudioFormatGetProperty(kAudioFormatProperty_ChannelLayoutForTag,
1252  sizeof(AudioChannelLayoutTag),
1253  &layout->mChannelLayoutTag,
1254  &param_size,
1255  layout);
1256  if (err)
1257  Warn("OpenAnalog: Can't retrieve current channel layout");
1258  }
1259 
1260  Debug(QString("OpenAnalog: Layout of AUHAL has %1 channels")
1261  .arg(layout->mNumberChannelDescriptions));
1262 
1263  int channels_found = 0;
1264  for(UInt32 i = 0; i < layout->mNumberChannelDescriptions; i++)
1265  {
1266  Debug(QString("OpenAnalog: this is channel: %1")
1267  .arg(layout->mChannelDescriptions[i].mChannelLabel));
1268 
1269  switch( layout->mChannelDescriptions[i].mChannelLabel)
1270  {
1271  case kAudioChannelLabel_Left:
1272  case kAudioChannelLabel_Right:
1273  case kAudioChannelLabel_Center:
1274  case kAudioChannelLabel_LFEScreen:
1275  case kAudioChannelLabel_LeftSurround:
1276  case kAudioChannelLabel_RightSurround:
1277  case kAudioChannelLabel_RearSurroundLeft:
1278  case kAudioChannelLabel_RearSurroundRight:
1279  case kAudioChannelLabel_CenterSurround:
1280  channels_found++;
1281  break;
1282  default:
1283  Debug(QString("unrecognized channel form provided by driver: %1")
1284  .arg(layout->mChannelDescriptions[i].mChannelLabel));
1285  }
1286  }
1287  if(channels_found == 0)
1288  {
1289  Warn("Audio device is not configured. "
1290  "You should configure your speaker layout with "
1291  "the \"Audio Midi Setup\" utility in /Applications/"
1292  "Utilities.");
1293  }
1294  free(layout);
1295  }
1296  else
1297  {
1298  Warn("this driver does not support kAudioDevicePropertyPreferredChannelLayout.");
1299  }
1300 
1301  memset (&new_layout, 0, sizeof(new_layout));
1302  switch(mCA->m_channels)
1303  {
1304  case 1:
1305  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
1306  break;
1307  case 2:
1308  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
1309  break;
1310  case 6:
1311  // 3F2-LFE L R C LFE LS RS
1312  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
1313  break;
1314  case 8:
1315  // We need
1316  // 3F4-LFE L R C LFE Rls Rrs LS RS
1317  // but doesn't exist, so we'll swap channels later
1318  new_layout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_7_1_A; // L R C LFE Ls Rs Lc Rc
1319  break;
1320  }
1321  /* Set new_layout as the layout */
1322  err = AudioUnitSetProperty(mOutputUnit,
1323  kAudioUnitProperty_AudioChannelLayout,
1324  kAudioUnitScope_Input,
1325  0,
1326  &new_layout, sizeof(new_layout));
1327  if (err)
1328  {
1329  Warn(QString("OpenAnalog: couldn't set channels layout [%1]")
1330  .arg(err));
1331  }
1332 
1333  if(new_layout.mNumberChannelDescriptions > 0)
1334  free(new_layout.mChannelDescriptions);
1335 
1336  // Set up the audio output unit
1337  int formatFlags;
1338  switch (mCA->m_outputFormat)
1339  {
1340  case FORMAT_S16:
1341  formatFlags = kLinearPCMFormatFlagIsSignedInteger;
1342  break;
1343  case FORMAT_FLT:
1344  formatFlags = kLinearPCMFormatFlagIsFloat;
1345  break;
1346  default:
1347  formatFlags = kLinearPCMFormatFlagIsSignedInteger;
1348  break;
1349  }
1350 
1351  AudioStreamBasicDescription conv_in_desc;
1352  memset(&conv_in_desc, 0, sizeof(AudioStreamBasicDescription));
1353  conv_in_desc.mSampleRate = mCA->m_sampleRate;
1354  conv_in_desc.mFormatID = kAudioFormatLinearPCM;
1355  conv_in_desc.mFormatFlags = formatFlags |
1356  kAudioFormatFlagsNativeEndian |
1357  kLinearPCMFormatFlagIsPacked;
1358  conv_in_desc.mBytesPerPacket = mCA->m_outputBytesPerFrame;
1359  // This seems inefficient, does it hurt if we increase this?
1360  conv_in_desc.mFramesPerPacket = 1;
1361  conv_in_desc.mBytesPerFrame = mCA->m_outputBytesPerFrame;
1362  conv_in_desc.mChannelsPerFrame = mCA->m_channels;
1363  conv_in_desc.mBitsPerChannel =
1365 
1366  /* Set AudioUnit input format */
1367  err = AudioUnitSetProperty(mOutputUnit,
1368  kAudioUnitProperty_StreamFormat,
1369  kAudioUnitScope_Input,
1370  0,
1371  &conv_in_desc,
1372  sizeof(AudioStreamBasicDescription));
1373  if (err)
1374  {
1375  Error(QString("OpenAnalog: AudioUnitSetProperty returned [%1]")
1376  .arg(err));
1377  return false;
1378  }
1379  Debug(QString("OpenAnalog: set format as %1")
1380  .arg(StreamDescriptionToString(conv_in_desc)));
1381  /* Retrieve actual format */
1382  err = AudioUnitGetProperty(mOutputUnit,
1383  kAudioUnitProperty_StreamFormat,
1384  kAudioUnitScope_Input,
1385  0,
1386  &DeviceFormat,
1387  &param_size);
1388 
1389  Debug(QString("OpenAnalog: the actual set AU format is %1")
1390  .arg(StreamDescriptionToString(DeviceFormat)));
1391 
1392  // Attach callback to default output
1393  AURenderCallbackStruct input;
1394  input.inputProc = RenderCallbackAnalog;
1395  input.inputProcRefCon = this;
1396 
1397  err = AudioUnitSetProperty(mOutputUnit,
1398  kAudioUnitProperty_SetRenderCallback,
1399  kAudioUnitScope_Input,
1400  0, &input, sizeof(input));
1401  if (err)
1402  {
1403  Error(QString("OpenAnalog: AudioUnitSetProperty (callback) returned [%1]")
1404  .arg(err));
1405  return false;
1406  }
1407  mIoProc = true;
1408 
1409  // We're all set up - start the audio output unit
1410  ComponentResult res = AudioUnitInitialize(mOutputUnit);
1411  if (res)
1412  {
1413  Error(QString("OpenAnalog: AudioUnitInitialize error: [%1]")
1414  .arg(res));
1415  return false;
1416  }
1417  mInitialized = true;
1418 
1419  err = AudioOutputUnitStart(mOutputUnit);
1420  if (err)
1421  {
1422  Error(QString("OpenAnalog: AudioOutputUnitStart error: [%1]")
1423  .arg(err));
1424  return false;
1425  }
1426  mStarted = true;
1427  return true;
1428 }
1429 
1431 {
1432  OSStatus err;
1433 
1434  Debug(QString("CloseAnalog: Entering: %1")
1435  .arg((long)mOutputUnit));
1436  if (mOutputUnit)
1437  {
1438  if (mStarted)
1439  {
1440  err = AudioOutputUnitStop(mOutputUnit);
1441  Debug(QString("CloseAnalog: AudioOutputUnitStop %1")
1442  .arg(err));
1443  }
1444  if (mInitialized)
1445  {
1446  err = AudioUnitUninitialize(mOutputUnit);
1447  Debug(QString("CloseAnalog: AudioUnitUninitialize %1")
1448  .arg(err));
1449  }
1450  err = AudioComponentInstanceDispose(mOutputUnit);
1451  Debug(QString("CloseAnalog: CloseComponent %1")
1452  .arg(err));
1453  mOutputUnit = nullptr;
1454  }
1455  mIoProc = false;
1456  mInitialized = false;
1457  mStarted = false;
1458  mWasDigital = false;
1459 }
1460 
1462 {
1463  OSStatus err;
1464  AudioStreamIDVec streams;
1465  AudioStreamBasicDescription outputFormat {};
1466 
1467  Debug("OpenSPDIF: Entering");
1468 
1469  streams = StreamsList(mDeviceID);
1470  if (streams.empty())
1471  {
1472  Warn("OpenSPDIF: Couldn't retrieve list of streams");
1473  return false;
1474  }
1475 
1476  for (size_t i = 0; i < streams.size(); ++i)
1477  {
1479  if (formats.empty())
1480  continue;
1481 
1482  // Find a stream with a cac3 stream
1483  for (auto format : formats)
1484  {
1485  Debug(QString("OpenSPDIF: Considering Physical Format: %1")
1486  .arg(StreamDescriptionToString(format.mFormat)));
1487  if ((format.mFormat.mFormatID == 'IAC3' ||
1488  format.mFormat.mFormatID == kAudioFormat60958AC3) &&
1489  format.mFormat.mSampleRate == mCA->m_sampleRate)
1490  {
1491  Debug("OpenSPDIF: Found digital format");
1492  mStreamIndex = i;
1493  mStreamID = streams[i];
1494  outputFormat = format.mFormat;
1495  break;
1496  }
1497  }
1498  if (outputFormat.mFormatID)
1499  break;
1500  }
1501 
1502  if (!outputFormat.mFormatID)
1503  {
1504  Error(QString("OpenSPDIF: Couldn't find suitable output"));
1505  return false;
1506  }
1507 
1508  if (!mRevertFormat)
1509  {
1510  AudioObjectPropertyAddress pa
1511  {
1512  kAudioStreamPropertyPhysicalFormat,
1513  kAudioObjectPropertyScopeGlobal,
1515  };
1516 
1517  // Retrieve the original format of this stream first
1518  // if not done so already
1519  UInt32 paramSize = sizeof(mFormatOrig);
1520  err = AudioObjectGetPropertyData(mStreamID, &pa, 0, nullptr,
1521  &paramSize, &mFormatOrig);
1522  if (err != noErr)
1523  {
1524  Warn(QString("OpenSPDIF - could not retrieve the original streamformat: [%1]")
1525  .arg(OSS_STATUS(err)));
1526  }
1527  else
1528  {
1529  mRevertFormat = true;
1530  }
1531  }
1532 
1533  mDigitalInUse = true;
1534 
1535  SetAutoHogMode(false);
1536  bool autoHog = GetAutoHogMode();
1537  if (!autoHog)
1538  {
1539  // Hog the device
1540  SetHogStatus(true);
1541  // Set mixable to false if we are allowed to
1542  SetMixingSupport(false);
1543  }
1544 
1545  mFormatNew = outputFormat;
1547  {
1548  return false;
1549  }
1550  mBytesPerPacket = mFormatNew.mBytesPerPacket;
1551 
1552  // Add IOProc callback
1553  err = AudioDeviceCreateIOProcID(mDeviceID,
1554  (AudioDeviceIOProc)RenderCallbackSPDIF,
1555  (void *)this, &mIoProcID);
1556  if (err != noErr)
1557  {
1558  Error(QString("OpenSPDIF: AudioDeviceCreateIOProcID failed: [%1]")
1559  .arg(OSS_STATUS(err)));
1560  return false;
1561  }
1562  mIoProc = true;
1563 
1564  // Start device
1565  err = AudioDeviceStart(mDeviceID, mIoProcID);
1566  if (err != noErr)
1567  {
1568  Error(QString("OpenSPDIF: AudioDeviceStart failed: [%1]")
1569  .arg(OSS_STATUS(err)));
1570  return false;
1571  }
1572  mStarted = true;
1573  return true;
1574 }
1575 
1577 {
1578  OSStatus err;
1579 
1580  Debug(QString("CloseSPDIF: Entering [%1]").arg(mDigitalInUse));;
1581  if (!mDigitalInUse)
1582  return;
1583 
1584  // Stop device
1585  if (mStarted)
1586  {
1587  err = AudioDeviceStop(mDeviceID, mIoProcID);
1588  if (err != noErr)
1589  Error(QString("CloseSPDIF: AudioDeviceStop failed: [%1]")
1590  .arg(OSS_STATUS(err)));
1591  mStarted = false;
1592  }
1593 
1594  // Remove IOProc callback
1595  if (mIoProc)
1596  {
1597  err = AudioDeviceDestroyIOProcID(mDeviceID, mIoProcID);
1598  if (err != noErr)
1599  Error(QString("CloseSPDIF: AudioDeviceDestroyIOProcID failed: [%1]")
1600  .arg(OSS_STATUS(err)));
1601  mIoProc = false;
1602  }
1603 
1604  if (mRevertFormat)
1605  {
1607  mRevertFormat = false;
1608  }
1609 
1610  SetHogStatus(false);
1611  if (mMixerRestore > -1) // We changed the mixer status
1613  AudioHardwareUnload();
1614  mMixerRestore = -1;
1615  mBytesPerPacket = -1;
1616  mStreamIndex = -1;
1617  mWasDigital = true;
1618 }
1619 
1621  AudioStreamBasicDescription format)
1622 {
1623  Debug(QString("AudioStreamChangeFormat: %1 -> %2")
1624  .arg(s)
1625  .arg(StreamDescriptionToString(format)));
1626 
1627  AudioObjectPropertyAddress pa
1628  {
1629  kAudioStreamPropertyPhysicalFormat,
1630  kAudioObjectPropertyScopeGlobal,
1632  };
1633  OSStatus err = AudioObjectSetPropertyData(s, &pa, 0, nullptr,
1634  sizeof(format), &format);
1635  if (err != noErr)
1636  {
1637  Error(QString("AudioStreamChangeFormat couldn't set stream format: [%1]")
1638  .arg(OSS_STATUS(err)));
1639  return false;
1640  }
1641  return true;
1642 }
1643 
1645 {
1646  AudioStreamIDVec streams;
1647 
1648 
1649  // Get a list of all the streams on this device
1650  streams = StreamsList(mDeviceID);
1651  if (streams.empty())
1652  return false;
1653 
1654  for (auto stream : streams)
1655  {
1657  if (formats.empty())
1658  continue;
1659 
1660  // Find a stream with a cac3 stream
1661  for (auto format : formats) {
1662  if (format.mFormat.mFormatID == 'IAC3' ||
1663  format.mFormat.mFormatID == kAudioFormat60958AC3)
1664  {
1665  Debug("FindAC3Stream: found digital format");
1666  return true;
1667  }
1668  }
1669  }
1670 
1671  return false;
1672 }
1673 
1679 {
1680  UInt32 size;
1681  AudioObjectPropertyAddress pa
1682  {
1683  kAudioHardwarePropertyDevices,
1684  kAudioObjectPropertyScopeGlobal,
1686  };
1687 
1688  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1689  0, nullptr, &size);
1690  if (err)
1691  {
1692  Warn(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
1693  "Error [%1]")
1694  .arg(err));
1695  return;
1696  }
1697 
1698  std::vector<AudioDeviceID> devices = {};
1699  devices.resize(size / sizeof(AudioDeviceID));
1700  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1701  0, nullptr, &size, devices.data());
1702  if (err)
1703  {
1704  Warn(QString("GetPropertyData: Unable to retrieve the list of available devices. "
1705  "Error [%1]")
1706  .arg(err));
1707  return;
1708  }
1709 
1710  for (const auto & dev : devices)
1711  {
1712  AudioStreamIDVec streams;
1713 
1714  streams = StreamsList(dev);
1715  if (streams.empty())
1716  continue;
1717  for (auto stream : streams)
1718  ResetStream(stream);
1719  }
1720 }
1721 
1722 void CoreAudioData::ResetStream(AudioStreamID s)
1723 {
1724  AudioStreamBasicDescription currentFormat;
1725  OSStatus err;
1726  UInt32 paramSize;
1727  AudioObjectPropertyAddress pa
1728  {
1729  kAudioStreamPropertyPhysicalFormat,
1730  kAudioObjectPropertyScopeGlobal,
1732  };
1733 
1734 
1735  // Find the streams current physical format
1736  paramSize = sizeof(currentFormat);
1737  AudioObjectGetPropertyData(s, &pa, 0, nullptr,
1738  &paramSize, &currentFormat);
1739 
1740  // If it's currently AC-3/SPDIF then reset it to some mixable format
1741  if (currentFormat.mFormatID == 'IAC3' ||
1742  currentFormat.mFormatID == kAudioFormat60958AC3)
1743  {
1745 
1746 
1747  if (formats.empty())
1748  return;
1749 
1750  for (auto format : formats) {
1751  if (format.mFormat.mFormatID == kAudioFormatLinearPCM)
1752  {
1753  Debug(QString("ResetStream: Resetting stream %1 to %2").arg(s).arg(StreamDescriptionToString(format.mFormat)));
1754  err = AudioObjectSetPropertyData(s, &pa, 0, nullptr,
1755  sizeof(format), &(format.mFormat));
1756  if (err != noErr)
1757  {
1758  Warn(QString("ResetStream: could not set physical format: [%1]")
1759  .arg(OSS_STATUS(err)));
1760  continue;
1761  }
1762 
1763  sleep(1); // For the change to take effect
1764  }
1765  }
1766  }
1767 }
1768 
1769 QMap<QString, QString> *AudioOutputCA::GetDevices(const char */*type*/)
1770 {
1771  auto *devs = new QMap<QString, QString>();
1772 
1773  // Obtain a list of all available audio devices
1774  UInt32 size = 0;
1775 
1776  AudioObjectPropertyAddress pa
1777  {
1778  kAudioHardwarePropertyDevices,
1779  kAudioObjectPropertyScopeGlobal,
1781  };
1782 
1783  OSStatus err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa,
1784  0, nullptr, &size);
1785  if (err)
1786  {
1787  VBAUDIO(QString("GetPropertyDataSize: Unable to retrieve the property sizes. "
1788  "Error [%1]")
1789  .arg(err));
1790  return devs;
1791  }
1792 
1793  UInt32 deviceCount = size / sizeof(AudioDeviceID);
1794  std::vector<AudioDeviceID> devices = {};
1795  devices.resize(deviceCount);
1796  err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa,
1797  0, nullptr, &size, devices.data());
1798  if (err)
1799  {
1800  VBAUDIO(QString("AudioOutputCA::GetDevices: Unable to retrieve the list of "
1801  "available devices. Error [%1]")
1802  .arg(err));
1803  }
1804  else
1805  {
1806  VBAUDIO(QString("GetDevices: Number of devices: %1").arg(deviceCount));
1807 
1808  for (const auto & dev : devices)
1809  {
1810  CoreAudioData device(nullptr, dev);
1811  if (device.GetTotalOutputChannels() == 0)
1812  continue;
1813  QString *name = device.GetName();
1814  if (name)
1815  {
1816  devs->insert(*name, QString());
1817  delete name;
1818  }
1819  }
1820  }
1821  return devs;
1822 }
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:1722
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:740
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:818
IsRateCommon
static bool IsRateCommon(Float64 inRate)
Definition: audiooutputca.cpp:969
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:404
CoreAudioData::AudioStreamChangeFormat
static int AudioStreamChangeFormat(AudioStreamID s, AudioStreamBasicDescription format)
Definition: audiooutputca.cpp:1620
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:600
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:868
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:412
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:1644
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:1769
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:850
CoreAudioData::CloseAnalog
void CloseAnalog()
Definition: audiooutputca.cpp:1430
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
CoreAudioData::GetDeviceWithName
static AudioDeviceID GetDeviceWithName(const QString &deviceName)
Definition: audiooutputca.cpp:546
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)
Definition: audiooutputca.cpp:473
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:474
CoreAudioData::StreamsList
static AudioStreamIDVec StreamsList(AudioDeviceID d)
Get a list of all the streams on this device.
Definition: audiooutputca.cpp:872
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:979
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:626
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:1678
sNumberCommonSampleRates
static UInt32 sNumberCommonSampleRates
Definition: audiooutputca.cpp:961
CoreAudioData::SetAutoHogMode
static void SetAutoHogMode(bool enable)
Definition: audiooutputca.cpp:721
kAudioObjectPropertyElementMain
#define kAudioObjectPropertyElementMain
Definition: audiooutputca.cpp:30
AudioOutputBase::m_pauseAudio
bool m_pauseAudio
Definition: audiooutputbase.h:199
sCommonSampleRates
static Float64 sCommonSampleRates[]
Definition: audiooutputca.cpp:962
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:79
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
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:910
AudioOutputBase::m_outputFormat
AudioFormat m_outputFormat
Definition: audiooutputbase.h:179
CoreAudioData::SetHogStatus
bool SetHogStatus(bool hog)
Definition: audiooutputca.cpp:764
CoreAudioData::CoreAudioData
CoreAudioData(AudioOutputCA *parent)
Definition: audiooutputca.cpp:510
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:1016
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:174
AudioOutputBase::m_setInitialVol
bool m_setInitialVol
Definition: audiooutputbase.h:203
RenderCallbackAnalog
static OSStatus RenderCallbackAnalog(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
Definition: audiooutputca.cpp:427
CoreAudioData::OpenSPDIF
bool OpenSPDIF()
Definition: audiooutputca.cpp:1461
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:1111
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:125
CoreAudioData::mFormatOrig
AudioStreamBasicDescription mFormatOrig
Definition: audiooutputca.cpp:160
CoreAudioData::FormatsList
static AudioStreamRangedVec FormatsList(AudioStreamID s)
Definition: audiooutputca.cpp:917
_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:1050
CoreAudioData::GetAutoHogMode
static bool GetAutoHogMode()
Definition: audiooutputca.cpp:700
audiooutputca.h
AudioOutputCA::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputca.cpp:464
CoreAudioData::GetName
QString * GetName()
Definition: audiooutputca.cpp:671
AudioValueRangeVec
std::vector< AudioValueRange > AudioValueRangeVec
Definition: audiooutputca.cpp:40
AudioOutputCA::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputca.cpp:450
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:1576
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:896
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