2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "cores/AudioEngine/Sinks/AESinkDARWINIOS.h"
22 #include "cores/AudioEngine/Utils/AEUtil.h"
23 #include "cores/AudioEngine/Utils/AERingBuffer.h"
24 #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
25 #include "osx/DarwinUtils.h"
26 #include "utils/log.h"
27 #include "utils/StringUtils.h"
28 #include "threads/Condition.h"
29 #include "windowing/WindowingFactory.h"
32 #include <AudioToolbox/AudioToolbox.h>
34 #define CA_MAX_CHANNELS 8
35 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
36 AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
40 /***************************************************************************************/
41 /***************************************************************************************/
42 #if DO_440HZ_TONE_TEST
43 static void SineWaveGeneratorInitWithFrequency(SineWaveGenerator *ctx, double frequency, double samplerate)
46 // frequency in cycles per second
47 // 2*PI radians per sine wave cycle
48 // sample rate in samples per second
51 // cycles radians seconds radians
52 // ------ * ------- * ------- = -------
53 // second cycle sample sample
54 ctx->currentPhase = 0.0;
55 ctx->phaseIncrement = frequency * 2*M_PI / samplerate;
58 static int16_t SineWaveGeneratorNextSampleInt16(SineWaveGenerator *ctx)
60 int16_t sample = INT16_MAX * sinf(ctx->currentPhase);
62 ctx->currentPhase += ctx->phaseIncrement;
63 // Keep the value between 0 and 2*M_PI
64 while (ctx->currentPhase > 2*M_PI)
65 ctx->currentPhase -= 2*M_PI;
69 static float SineWaveGeneratorNextSampleFloat(SineWaveGenerator *ctx)
71 float sample = MAXFLOAT * sinf(ctx->currentPhase);
73 ctx->currentPhase += ctx->phaseIncrement;
74 // Keep the value between 0 and 2*M_PI
75 while (ctx->currentPhase > 2*M_PI)
76 ctx->currentPhase -= 2*M_PI;
82 /***************************************************************************************/
83 /***************************************************************************************/
90 bool open(AudioStreamBasicDescription outputFormat);
98 unsigned int write(uint8_t *data, unsigned int byte_count);
99 unsigned int chunkSize() { return m_bufferDuration * m_sampleRate; }
100 unsigned int getRealisedSampleRate() { return m_outputFormat.mSampleRate; }
101 static Float64 getCoreAudioRealisedSampleRate();
104 void setCoreAudioBuffersize();
105 bool setCoreAudioInputFormat();
106 void setCoreAudioPreferredSampleRate();
108 bool checkAudioRoute();
109 bool checkSessionProperties();
110 bool activateAudioSession();
111 void deactivateAudioSession();
114 static void sessionPropertyCallback(void *inClientData,
115 AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData);
117 static void sessionInterruptionCallback(void *inClientData, UInt32 inInterruption);
119 static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
120 const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames,
121 AudioBufferList *ioData);
126 AudioUnit m_audioUnit;
127 AudioStreamBasicDescription m_outputFormat;
128 AERingBuffer *m_buffer;
131 Float32 m_outputVolume;
132 Float32 m_outputLatency;
133 Float32 m_bufferDuration;
135 unsigned int m_sampleRate;
136 unsigned int m_frameSize;
137 unsigned int m_frames;
140 bool m_playing_saved;
141 volatile bool m_started;
144 CAAudioUnitSink::CAAudioUnitSink()
145 : m_initialized(false)
149 , m_playing_saved(false)
154 CAAudioUnitSink::~CAAudioUnitSink()
159 bool CAAudioUnitSink::open(AudioStreamBasicDescription outputFormat)
163 m_outputFormat = outputFormat;
164 m_outputLatency = 0.0;
165 m_bufferDuration= 0.0;
166 m_outputVolume = 1.0;
167 m_sampleRate = (unsigned int)outputFormat.mSampleRate;
168 m_frameSize = outputFormat.mChannelsPerFrame * outputFormat.mBitsPerChannel / 8;
170 /* TODO: Reduce the size of this buffer, pre-calculate the size based on how large
171 the buffers are that CA calls us with in the renderCallback - perhaps call
172 the checkSessionProperties() before running this? */
173 m_buffer = new AERingBuffer(16384);
178 bool CAAudioUnitSink::close()
180 deactivateAudioSession();
189 bool CAAudioUnitSink::play(bool mute)
193 if (activateAudioSession())
195 CAAudioUnitSink::mute(mute);
196 m_playing = !AudioOutputUnitStart(m_audioUnit);
203 bool CAAudioUnitSink::mute(bool mute)
210 bool CAAudioUnitSink::pause()
213 m_playing = AudioOutputUnitStop(m_audioUnit);
218 double CAAudioUnitSink::getDelay()
220 double delay = (double)m_buffer->GetReadSize() / m_frameSize;
221 delay /= m_sampleRate;
222 delay += m_bufferDuration + m_outputLatency;
227 double CAAudioUnitSink::cacheSize()
229 return (double)m_buffer->GetMaxSize() / (double)(m_frameSize * m_sampleRate);
232 CCriticalSection mutex;
233 XbmcThreads::ConditionVariable condVar;
235 unsigned int CAAudioUnitSink::write(uint8_t *data, unsigned int frames)
237 if (m_buffer->GetWriteSize() < frames * m_frameSize)
238 { // no space to write - wait for a bit
239 CSingleLock lock(mutex);
243 condVar.wait(lock, 900 * frames / m_sampleRate);
246 unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_frameSize);
248 m_buffer->Write(data, write_frames * m_frameSize);
253 void CAAudioUnitSink::drain()
255 unsigned int bytes = m_buffer->GetReadSize();
258 CSingleLock lock(mutex);
259 condVar.wait(mutex, 900 * bytes / (m_sampleRate * m_frameSize));
260 bytes = m_buffer->GetReadSize();
264 void CAAudioUnitSink::setCoreAudioBuffersize()
266 #if !TARGET_IPHONE_SIMULATOR
267 OSStatus status = noErr;
268 // set the buffer size, this affects the number of samples
269 // that get rendered every time the audio callback is fired.
270 Float32 preferredBufferSize = 512 * m_outputFormat.mChannelsPerFrame / m_outputFormat.mSampleRate;
271 CLog::Log(LOGNOTICE, "%s setting buffer duration to %f", __PRETTY_FUNCTION__, preferredBufferSize);
272 status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
273 sizeof(preferredBufferSize), &preferredBufferSize);
275 CLog::Log(LOGWARNING, "%s preferredBufferSize couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
279 bool CAAudioUnitSink::setCoreAudioInputFormat()
281 // Set the output stream format
282 UInt32 ioDataSize = sizeof(AudioStreamBasicDescription);
283 OSStatus status = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_StreamFormat,
284 kAudioUnitScope_Input, 0, &m_outputFormat, ioDataSize);
287 CLog::Log(LOGERROR, "%s error setting stream format on audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
293 void CAAudioUnitSink::setCoreAudioPreferredSampleRate()
295 Float64 preferredSampleRate = m_outputFormat.mSampleRate;
296 CLog::Log(LOGNOTICE, "%s requesting hw samplerate %f", __PRETTY_FUNCTION__, preferredSampleRate);
297 OSStatus status = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate,
298 sizeof(preferredSampleRate), &preferredSampleRate);
300 CLog::Log(LOGWARNING, "%s preferredSampleRate couldn't be set (error: %d)", __PRETTY_FUNCTION__, (int)status);
303 Float64 CAAudioUnitSink::getCoreAudioRealisedSampleRate()
305 Float64 outputSampleRate = 0.0;
306 UInt32 ioDataSize = sizeof(outputSampleRate);
307 if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
308 &ioDataSize, &outputSampleRate) != noErr)
309 CLog::Log(LOGERROR, "%s: error getting CurrentHardwareSampleRate", __FUNCTION__);
310 return outputSampleRate;
313 bool CAAudioUnitSink::setupAudio()
315 OSStatus status = noErr;
316 if (m_setup && m_audioUnit)
319 // Audio Session Setup
320 UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
321 status = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
322 sizeof(sessionCategory), &sessionCategory);
325 CLog::Log(LOGERROR, "%s error setting sessioncategory (error: %d)", __PRETTY_FUNCTION__, (int)status);
329 AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
330 sessionPropertyCallback, this);
332 AudioSessionAddPropertyListener(kAudioSessionProperty_CurrentHardwareOutputVolume,
333 sessionPropertyCallback, this);
335 if (AudioSessionSetActive(true) != noErr)
339 // Describe a default output unit.
340 AudioComponentDescription description = {};
341 description.componentType = kAudioUnitType_Output;
342 description.componentSubType = kAudioUnitSubType_RemoteIO;
343 description.componentManufacturer = kAudioUnitManufacturer_Apple;
346 AudioComponent component;
347 component = AudioComponentFindNext(NULL, &description);
348 status = AudioComponentInstanceNew(component, &m_audioUnit);
351 CLog::Log(LOGERROR, "%s error creating audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
355 setCoreAudioPreferredSampleRate();
357 // Get the output samplerate for knowing what was setup in reality
358 Float64 realisedSampleRate = getCoreAudioRealisedSampleRate();
359 if (m_outputFormat.mSampleRate != realisedSampleRate)
361 CLog::Log(LOGNOTICE, "%s couldn't set requested samplerate %d, coreaudio will resample to %d instead", __PRETTY_FUNCTION__, (int)m_outputFormat.mSampleRate, (int)realisedSampleRate);
362 // if we don't ca to resample - but instead let activeae resample -
363 // reflect the realised samplerate to the outputformat here
364 // well maybe it is handy in the future - as of writing this
365 // ca was about 6 times faster then activeae ;)
366 //m_outputFormat.mSampleRate = realisedSampleRate;
367 //m_sampleRate = realisedSampleRate;
370 setCoreAudioBuffersize();
371 if (!setCoreAudioInputFormat())
374 // Attach a render callback on the unit
375 AURenderCallbackStruct callbackStruct = {};
376 callbackStruct.inputProc = renderCallback;
377 callbackStruct.inputProcRefCon = this;
378 status = AudioUnitSetProperty(m_audioUnit,
379 kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
380 0, &callbackStruct, sizeof(callbackStruct));
383 CLog::Log(LOGERROR, "%s error setting render callback for audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
387 status = AudioUnitInitialize(m_audioUnit);
390 CLog::Log(LOGERROR, "%s error initializing audioUnit (error: %d)", __PRETTY_FUNCTION__, (int)status);
394 checkSessionProperties();
397 std::string formatString;
398 CLog::Log(LOGNOTICE, "%s setup audio format: %s", __PRETTY_FUNCTION__, StreamDescriptionToString(m_outputFormat, formatString));
403 bool CAAudioUnitSink::checkAudioRoute()
405 // why do we need to know the audio route ?
407 UInt32 propertySize = sizeof(CFStringRef);
408 if (AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route) != noErr)
414 bool CAAudioUnitSink::checkSessionProperties()
419 ioDataSize = sizeof(m_outputVolume);
420 if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputVolume,
421 &ioDataSize, &m_outputVolume) != noErr)
422 CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputVolume", __FUNCTION__);
424 ioDataSize = sizeof(m_outputLatency);
425 if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputLatency,
426 &ioDataSize, &m_outputLatency) != noErr)
427 CLog::Log(LOGERROR, "%s: error getting CurrentHardwareOutputLatency", __FUNCTION__);
429 ioDataSize = sizeof(m_bufferDuration);
430 if (AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
431 &ioDataSize, &m_bufferDuration) != noErr)
432 CLog::Log(LOGERROR, "%s: error getting CurrentHardwareIOBufferDuration", __FUNCTION__);
434 CLog::Log(LOGDEBUG, "%s: volume = %f, latency = %f, buffer = %f", __FUNCTION__, m_outputVolume, m_outputLatency, m_bufferDuration);
438 bool CAAudioUnitSink::activateAudioSession()
444 OSStatus osstat = AudioSessionInitialize(NULL, kCFRunLoopDefaultMode, sessionInterruptionCallback, this);
445 if (osstat == kAudioSessionNoError || osstat == kAudioSessionAlreadyInitialized)
446 m_initialized = true;
449 CLog::Log(LOGERROR, "%s error initializing audio session (error: %d)", __PRETTY_FUNCTION__, (int)osstat);
453 if (checkAudioRoute() && setupAudio())
460 void CAAudioUnitSink::deactivateAudioSession()
465 AudioUnitUninitialize(m_audioUnit);
466 AudioComponentInstanceDispose(m_audioUnit), m_audioUnit = NULL;
467 AudioSessionSetActive(false);
468 AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_AudioRouteChange,
469 sessionPropertyCallback, this);
470 AudioSessionRemovePropertyListenerWithUserData(kAudioSessionProperty_CurrentHardwareOutputVolume,
471 sessionPropertyCallback, this);
478 void CAAudioUnitSink::sessionPropertyCallback(void *inClientData,
479 AudioSessionPropertyID inID, UInt32 inDataSize, const void *inData)
481 CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
483 if (inID == kAudioSessionProperty_AudioRouteChange)
485 if (sink->checkAudioRoute())
486 sink->checkSessionProperties();
488 else if (inID == kAudioSessionProperty_CurrentHardwareOutputVolume)
490 if (inData && inDataSize == 4)
491 sink->m_outputVolume = *(float*)inData;
495 void CAAudioUnitSink::sessionInterruptionCallback(void *inClientData, UInt32 inInterruption)
497 CAAudioUnitSink *sink = (CAAudioUnitSink*)inClientData;
499 if (inInterruption == kAudioSessionBeginInterruption)
501 CLog::Log(LOGDEBUG, "Bgn interuption");
502 sink->m_playing_saved = sink->m_playing;
505 else if (inInterruption == kAudioSessionEndInterruption)
507 CLog::Log(LOGDEBUG, "End interuption");
508 if (sink->m_playing_saved)
510 sink->m_playing_saved = false;
511 sink->play(sink->m_mute);
516 OSStatus CAAudioUnitSink::renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
517 const AudioTimeStamp *inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
519 CAAudioUnitSink *sink = (CAAudioUnitSink*)inRefCon;
521 sink->m_started = true;
523 for (unsigned int i = 0; i < ioData->mNumberBuffers; i++)
525 // buffers come from CA already zero'd, so just copy what is wanted
526 unsigned int wanted = ioData->mBuffers[i].mDataByteSize;
527 unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
528 sink->m_buffer->Read((unsigned char*)ioData->mBuffers[i].mData, bytes);
530 CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
532 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
534 // tell the sink we're good for more data
540 /***************************************************************************************/
541 /***************************************************************************************/
542 static void EnumerateDevices(AEDeviceInfoList &list)
544 CAEDeviceInfo device;
546 device.m_deviceName = "default";
547 device.m_displayName = "Default";
548 device.m_displayNameExtra = "";
549 #if defined(TARGET_DARWIN_IOS_ATV2)
550 device.m_deviceType = AE_DEVTYPE_IEC958;
551 device.m_dataFormats.push_back(AE_FMT_AC3);
552 device.m_dataFormats.push_back(AE_FMT_DTS);
554 // TODO screen changing on ios needs to call
555 // devices changed once this is available in activae
556 if (g_Windowing.GetCurrentScreen() > 0)
558 device.m_deviceType = AE_DEVTYPE_IEC958; //allow passthrough for tvout
559 device.m_dataFormats.push_back(AE_FMT_AC3);
560 device.m_dataFormats.push_back(AE_FMT_DTS);
563 device.m_deviceType = AE_DEVTYPE_PCM;
567 CAEChannelInfo channel_info;
568 for (UInt32 chan = 0; chan < 2; ++chan)
570 if (!device.m_channels.HasChannel(CAChannelMap[chan]))
571 device.m_channels += CAChannelMap[chan];
572 channel_info += CAChannelMap[chan];
575 // there are more supported ( one of those 2 gets resampled
576 // by coreaudio anyway) - but for keeping it save ignore
578 device.m_sampleRates.push_back(44100);
579 device.m_sampleRates.push_back(48000);
581 device.m_dataFormats.push_back(AE_FMT_S16LE);
582 //device.m_dataFormats.push_back(AE_FMT_S24LE3);
583 //device.m_dataFormats.push_back(AE_FMT_S32LE);
584 // AE_FMT_FLOAT is 3% slower on atv2
585 // then S16LE - so leave it out for now
586 //device.m_dataFormats.push_back(AE_FMT_FLOAT);
588 CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
590 list.push_back(device);
593 /***************************************************************************************/
594 /***************************************************************************************/
595 AEDeviceInfoList CAESinkDARWINIOS::m_devices;
597 CAESinkDARWINIOS::CAESinkDARWINIOS()
602 CAESinkDARWINIOS::~CAESinkDARWINIOS()
606 bool CAESinkDARWINIOS::Initialize(AEAudioFormat &format, std::string &device)
609 bool forceRaw = false;
611 std::string devicelower = device;
612 StringUtils::ToLower(devicelower);
613 for (size_t i = 0; i < m_devices.size(); i++)
615 if (devicelower.find(m_devices[i].m_deviceName) != std::string::npos)
617 m_info = m_devices[i];
626 AudioStreamBasicDescription audioFormat = {};
628 // AE_FMT_FLOAT is 3% slower on atv2
629 // then S16LE - so leave it out for now
630 // just leave the code commented in here
631 // as it might come handy at some point maybe ...
632 //if (format.m_dataFormat == AE_FMT_FLOAT)
633 // audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsFloat;
634 //else// this will be selected when AE wants AC3 or DTS or anything other then float
636 audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
637 if (AE_IS_RAW(format.m_dataFormat))
639 format.m_dataFormat = AE_FMT_S16LE;
642 format.m_channelLayout = m_info.m_channels;
643 format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
646 audioFormat.mFormatID = kAudioFormatLinearPCM;
647 switch(format.m_sampleRate)
654 audioFormat.mSampleRate = 44100;
666 audioFormat.mSampleRate = 48000;
670 if (forceRaw)//make sure input and output samplerate match for preventing resampling
671 audioFormat.mSampleRate = CAAudioUnitSink::getCoreAudioRealisedSampleRate();
673 audioFormat.mFramesPerPacket = 1;
674 audioFormat.mChannelsPerFrame= 2;// ios only supports 2 channels
675 audioFormat.mBitsPerChannel = CAEUtil::DataFormatToBits(format.m_dataFormat);
676 audioFormat.mBytesPerFrame = format.m_frameSize;
677 audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame * audioFormat.mFramesPerPacket;
678 audioFormat.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
680 #if DO_440HZ_TONE_TEST
681 SineWaveGeneratorInitWithFrequency(&m_SineWaveGenerator, 440.0, audioFormat.mSampleRate);
684 m_audioSink = new CAAudioUnitSink;
685 m_audioSink->open(audioFormat);
687 format.m_frames = m_audioSink->chunkSize();
688 format.m_frameSamples = format.m_frames * audioFormat.mChannelsPerFrame;
689 // reset to the realised samplerate
690 format.m_sampleRate = m_audioSink->getRealisedSampleRate();
693 m_volume_changed = false;
694 m_audioSink->play(false);
699 void CAESinkDARWINIOS::Deinitialize()
705 bool CAESinkDARWINIOS::IsCompatible(const AEAudioFormat &format, const std::string &device)
707 return ((m_format.m_sampleRate == format.m_sampleRate) &&
708 (m_format.m_dataFormat == format.m_dataFormat) &&
709 (m_format.m_channelLayout == format.m_channelLayout));
712 double CAESinkDARWINIOS::GetDelay()
715 return m_audioSink->getDelay();
719 double CAESinkDARWINIOS::GetCacheTotal()
722 return m_audioSink->cacheSize();
726 unsigned int CAESinkDARWINIOS::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
729 #if DO_440HZ_TONE_TEST
730 if (m_format.m_dataFormat == AE_FMT_FLOAT)
732 float *samples = (float*)data;
733 for (unsigned int j = 0; j < frames ; j++)
735 float sample = SineWaveGeneratorNextSampleFloat(&m_SineWaveGenerator);
743 int16_t *samples = (int16_t*)data;
744 for (unsigned int j = 0; j < frames ; j++)
746 int16_t sample = SineWaveGeneratorNextSampleInt16(&m_SineWaveGenerator);
753 return m_audioSink->write(data, frames);
757 void CAESinkDARWINIOS::Drain()
760 m_audioSink->drain();
763 bool CAESinkDARWINIOS::HasVolume()
768 void CAESinkDARWINIOS::SetVolume(float scale)
770 // CoreAudio uses fixed steps, reverse scale back to percent
771 float gain = CAEUtil::ScaleToGain(scale);
772 m_volume = CAEUtil::GainToPercent(gain);
773 m_volume_changed = true;
776 void CAESinkDARWINIOS::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
779 EnumerateDevices(m_devices);