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