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