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