2 * Copyright (C) 2005-2014 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/AEFactory.h"
22 #include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h"
23 #include "cores/AudioEngine/Utils/AEUtil.h"
24 #include "cores/AudioEngine/Utils/AERingBuffer.h"
25 #include "cores/AudioEngine/Sinks/osx/CoreAudioHelpers.h"
26 #include "cores/AudioEngine/Sinks/osx/CoreAudioHardware.h"
27 #include "osx/DarwinUtils.h"
28 #include "utils/log.h"
29 #include "utils/StringUtils.h"
30 #include "threads/Condition.h"
31 #include "threads/CriticalSection.h"
35 #define CA_MAX_CHANNELS 8
36 static enum AEChannel CAChannelMap[CA_MAX_CHANNELS + 1] = {
37 AE_CH_FL , AE_CH_FR , AE_CH_BL , AE_CH_BR , AE_CH_FC , AE_CH_LFE , AE_CH_SL , AE_CH_SR ,
41 static bool HasSampleRate(const AESampleRateList &list, const unsigned int samplerate)
43 for (size_t i = 0; i < list.size(); ++i)
45 if (list[i] == samplerate)
51 static bool HasDataFormat(const AEDataFormatList &list, const enum AEDataFormat format)
53 for (size_t i = 0; i < list.size(); ++i)
55 if (list[i] == format)
61 typedef std::vector< std::pair<AudioDeviceID, CAEDeviceInfo> > CADeviceList;
63 static void EnumerateDevices(CADeviceList &list)
67 std::string defaultDeviceName;
68 CCoreAudioHardware::GetOutputDeviceName(defaultDeviceName);
70 CoreAudioDeviceList deviceIDList;
71 CCoreAudioHardware::GetOutputDevices(&deviceIDList);
72 while (!deviceIDList.empty())
74 AudioDeviceID deviceID = deviceIDList.front();
75 CCoreAudioDevice caDevice(deviceID);
77 device.m_channels.Reset();
78 device.m_dataFormats.clear();
79 device.m_sampleRates.clear();
81 device.m_deviceType = AE_DEVTYPE_PCM;
82 device.m_deviceName = caDevice.GetName();
83 device.m_displayName = device.m_deviceName;
84 device.m_displayNameExtra = "";
86 // flag indicating that passthroughformats where added throughout the stream enumeration
87 bool hasPassthroughFormats = false;
88 // the maximum number of channels found in the streams
89 UInt32 numMaxChannels = 0;
90 // the terminal type as reported by ca
91 UInt32 caTerminalType = 0;
93 bool isDigital = caDevice.IsDigital(caTerminalType);
96 CLog::Log(LOGDEBUG, "EnumerateDevices:Device(%s)" , device.m_deviceName.c_str());
97 AudioStreamIdList streams;
98 if (caDevice.GetStreams(&streams))
100 for (AudioStreamIdList::iterator j = streams.begin(); j != streams.end(); ++j)
102 StreamFormatList streams;
103 if (CCoreAudioStream::GetAvailablePhysicalFormats(*j, &streams))
105 for (StreamFormatList::iterator i = streams.begin(); i != streams.end(); ++i)
107 AudioStreamBasicDescription desc = i->mFormat;
108 std::string formatString;
109 CLog::Log(LOGDEBUG, "EnumerateDevices:Format(%s)" ,
110 StreamDescriptionToString(desc, formatString));
112 // add stream format info
113 switch (desc.mFormatID)
115 case kAudioFormatAC3:
116 case kAudioFormat60958AC3:
117 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
118 device.m_dataFormats.push_back(AE_FMT_AC3);
119 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
120 device.m_dataFormats.push_back(AE_FMT_DTS);
121 hasPassthroughFormats = true;
122 isDigital = true;// sanity - those are always digital devices!
125 AEDataFormat format = AE_FMT_INVALID;
126 switch(desc.mBitsPerChannel)
129 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
130 format = AE_FMT_S16BE;
133 // if it is no digital stream per definition
134 // check if the device name suggests that it is digital
135 // (some hackintonshs are not so smart in announcing correct
139 std::string devNameLower = device.m_deviceName;
140 StringUtils::ToLower(devNameLower);
141 isDigital = devNameLower.find("digital") != std::string::npos;
144 /* Passthrough is possible with a 2ch digital output */
145 if (desc.mChannelsPerFrame == 2 && isDigital)
147 if (desc.mSampleRate == 48000)
149 if (!HasDataFormat(device.m_dataFormats, AE_FMT_AC3))
150 device.m_dataFormats.push_back(AE_FMT_AC3);
151 if (!HasDataFormat(device.m_dataFormats, AE_FMT_DTS))
152 device.m_dataFormats.push_back(AE_FMT_DTS);
153 hasPassthroughFormats = true;
155 else if (desc.mSampleRate == 192000)
157 if (!HasDataFormat(device.m_dataFormats, AE_FMT_EAC3))
158 device.m_dataFormats.push_back(AE_FMT_EAC3);
159 hasPassthroughFormats = true;
162 format = AE_FMT_S16LE;
166 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
167 format = AE_FMT_S24BE3;
169 format = AE_FMT_S24LE3;
172 if (desc.mFormatFlags & kAudioFormatFlagIsFloat)
173 format = AE_FMT_FLOAT;
176 if (desc.mFormatFlags & kAudioFormatFlagIsBigEndian)
177 format = AE_FMT_S32BE;
179 format = AE_FMT_S32LE;
184 if (numMaxChannels < desc.mChannelsPerFrame)
185 numMaxChannels = desc.mChannelsPerFrame;
187 if (format != AE_FMT_INVALID && !HasDataFormat(device.m_dataFormats, format))
188 device.m_dataFormats.push_back(format);
193 CAEChannelInfo channel_info;
194 for (UInt32 chan = 0; chan < CA_MAX_CHANNELS && chan < desc.mChannelsPerFrame; ++chan)
196 if (!device.m_channels.HasChannel(CAChannelMap[chan]))
197 device.m_channels += CAChannelMap[chan];
198 channel_info += CAChannelMap[chan];
201 // add sample rate info
202 if (!HasSampleRate(device.m_sampleRates, desc.mSampleRate))
203 device.m_sampleRates.push_back(desc.mSampleRate);
210 // flag indicating that the device name "sounds" like HDMI
211 bool hasHdmiName = device.m_deviceName.find("HDMI") != std::string::npos;
212 // flag indicating that the device name "sounds" like DisplayPort
213 bool hasDisplayPortName = device.m_deviceName.find("DisplayPort") != std::string::npos;
215 // decide the type of the device based on the discovered information
217 // device defaults to PCM (see start of the while loop)
218 // it can be HDMI, DisplayPort or Optical
219 // for all of those types it needs to support
220 // passthroughformats and needs to be a digital port
221 if (hasPassthroughFormats && isDigital)
223 // if the max number of channels was more then 2
224 // this can be HDMI or DisplayPort or Thunderbolt
225 if (numMaxChannels > 2)
227 // either the devicename suggests its HDMI
228 // or CA reported the terminalType as HDMI
229 if (hasHdmiName || caTerminalType == kIOAudioDeviceTransportTypeHdmi)
230 device.m_deviceType = AE_DEVTYPE_HDMI;
232 // either the devicename suggests its DisplayPort
233 // or CA reported the terminalType as DisplayPort or Thunderbolt
234 if (hasDisplayPortName || caTerminalType == kIOAudioDeviceTransportTypeDisplayPort || caTerminalType == kIOAudioDeviceTransportTypeThunderbolt)
235 device.m_deviceType = AE_DEVTYPE_DP;
237 else// treat all other digital passthrough devices as optical
238 device.m_deviceType = AE_DEVTYPE_IEC958;
241 // devicename based overwrites from former code - maybe FIXME at some point when we
242 // are sure that the upper detection does its job in all[tm] use cases
244 device.m_deviceType = AE_DEVTYPE_HDMI;
245 if (hasDisplayPortName)
246 device.m_deviceType = AE_DEVTYPE_DP;
249 list.push_back(std::make_pair(deviceID, device));
250 //in the first place of the list add the default device
251 //with name "default" - if this is selected
252 //we will output to whatever osx claims to be default
253 //(allows transition from headphones to speaker and stuff
255 if(defaultDeviceName == device.m_deviceName)
257 device.m_deviceName = "default";
258 device.m_displayName = "Default";
259 list.insert(list.begin(), std::make_pair(deviceID, device));
262 deviceIDList.pop_front();
266 /* static, threadsafe access to the device list */
267 static CADeviceList s_devices;
268 static CCriticalSection s_devicesLock;
270 static void EnumerateDevices()
272 CADeviceList devices;
273 EnumerateDevices(devices);
275 CSingleLock lock(s_devicesLock);
280 static CADeviceList GetDevices()
284 CSingleLock lock(s_devicesLock);
290 OSStatus deviceChangedCB(AudioObjectID inObjectID,
291 UInt32 inNumberAddresses,
292 const AudioObjectPropertyAddress inAddresses[],
295 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - reenumerating");
296 CAEFactory::DeviceChange();
297 CLog::Log(LOGDEBUG, "CoreAudio: audiodevicelist changed - done");
301 void RegisterDeviceChangedCB(bool bRegister, void *ref)
303 OSStatus ret = noErr;
304 const AudioObjectPropertyAddress inAdr =
306 kAudioHardwarePropertyDevices,
307 kAudioObjectPropertyScopeGlobal,
308 kAudioObjectPropertyElementMaster
312 ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
314 ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &inAdr, deviceChangedCB, ref);
317 CLog::Log(LOGERROR, "CCoreAudioAE::Deinitialize - error %s a listener callback for device changes!", bRegister?"attaching":"removing");
321 ////////////////////////////////////////////////////////////////////////////////////////////
322 CAESinkDARWINOSX::CAESinkDARWINOSX()
323 : m_latentFrames(0), m_outputBitstream(false), m_outputBuffer(NULL), m_buffer(NULL)
325 // By default, kAudioHardwarePropertyRunLoop points at the process's main thread on SnowLeopard,
326 // If your process lacks such a run loop, you can set kAudioHardwarePropertyRunLoop to NULL which
327 // tells the HAL to run it's own thread for notifications (which was the default prior to SnowLeopard).
328 // So tell the HAL to use its own thread for similar behavior under all supported versions of OSX.
329 CFRunLoopRef theRunLoop = NULL;
330 AudioObjectPropertyAddress theAddress = {
331 kAudioHardwarePropertyRunLoop,
332 kAudioObjectPropertyScopeGlobal,
333 kAudioObjectPropertyElementMaster
335 OSStatus theError = AudioObjectSetPropertyData(kAudioObjectSystemObject,
336 &theAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
337 if (theError != noErr)
339 CLog::Log(LOGERROR, "CCoreAudioAE::constructor: kAudioHardwarePropertyRunLoop error.");
341 RegisterDeviceChangedCB(true, this);
345 CAESinkDARWINOSX::~CAESinkDARWINOSX()
347 RegisterDeviceChangedCB(false, this);
350 float ScoreStream(const AudioStreamBasicDescription &desc, const AEAudioFormat &format)
353 if (format.m_dataFormat == AE_FMT_AC3 ||
354 format.m_dataFormat == AE_FMT_DTS)
356 if (desc.mFormatID == kAudioFormat60958AC3 ||
357 desc.mFormatID == 'IAC3' ||
358 desc.mFormatID == kAudioFormatAC3)
360 if (desc.mSampleRate == format.m_sampleRate &&
361 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
362 desc.mChannelsPerFrame == format.m_channelLayout.Count())
369 if (format.m_dataFormat == AE_FMT_AC3 ||
370 format.m_dataFormat == AE_FMT_DTS ||
371 format.m_dataFormat == AE_FMT_EAC3)
372 { // we should be able to bistreaming in PCM if the samplerate, bitdepth and channels match
373 if (desc.mSampleRate == format.m_sampleRate &&
374 desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat) &&
375 desc.mChannelsPerFrame == format.m_channelLayout.Count() &&
376 desc.mFormatID == kAudioFormatLinearPCM)
382 { // non-passthrough, whatever works is fine
383 if (desc.mFormatID == kAudioFormatLinearPCM)
385 if (desc.mSampleRate == format.m_sampleRate)
387 else if (desc.mSampleRate > format.m_sampleRate)
389 if (desc.mChannelsPerFrame == format.m_channelLayout.Count())
391 else if (desc.mChannelsPerFrame > format.m_channelLayout.Count())
393 if (format.m_dataFormat == AE_FMT_FLOAT)
394 { // for float, prefer the highest bitdepth we have
395 if (desc.mBitsPerChannel >= 16)
396 score += (desc.mBitsPerChannel / 8);
400 if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
402 else if (desc.mBitsPerChannel == CAEUtil::DataFormatToBits(format.m_dataFormat))
410 bool CAESinkDARWINOSX::Initialize(AEAudioFormat &format, std::string &device)
412 AudioDeviceID deviceID = 0;
413 CADeviceList devices = GetDevices();
414 if (StringUtils::EqualsNoCase(device, "default"))
416 CCoreAudioHardware::GetOutputDeviceName(device);
417 CLog::Log(LOGNOTICE, "%s: Opening default device %s", __PRETTY_FUNCTION__, device.c_str());
420 for (size_t i = 0; i < devices.size(); i++)
422 if (device.find(devices[i].second.m_deviceName) != std::string::npos)
424 m_info = devices[i].second;
425 deviceID = devices[i].first;
431 CLog::Log(LOGERROR, "%s: Unable to find device %s", __FUNCTION__, device.c_str());
435 m_device.Open(deviceID);
437 // Fetch a list of the streams defined by the output device
438 AudioStreamIdList streams;
439 m_device.GetStreams(&streams);
441 CLog::Log(LOGDEBUG, "%s: Finding stream for format %s", __FUNCTION__, CAEUtil::DataFormatToStr(format.m_dataFormat));
443 bool passthrough = false;
444 UInt32 outputIndex = 0;
445 float outputScore = 0;
446 AudioStreamBasicDescription outputFormat = {0};
447 AudioStreamID outputStream = 0;
449 /* The theory is to score based on
450 1. Matching passthrough characteristics (i.e. passthrough flag)
451 2. Matching sample rate.
452 3. Matching bits per channel (or higher).
453 4. Matching number of channels (or higher).
456 for (AudioStreamIdList::const_iterator i = streams.begin(); i != streams.end(); ++i)
458 // Probe physical formats
459 StreamFormatList formats;
460 CCoreAudioStream::GetAvailablePhysicalFormats(*i, &formats);
461 for (StreamFormatList::const_iterator j = formats.begin(); j != formats.end(); ++j)
463 const AudioStreamBasicDescription &desc = j->mFormat;
465 float score = ScoreStream(desc, format);
467 std::string formatString;
468 CLog::Log(LOGDEBUG, "%s: Physical Format: %s rated %f", __FUNCTION__, StreamDescriptionToString(desc, formatString), score);
470 if (score > outputScore)
472 passthrough = score > 1000;
482 if (!outputFormat.mFormatID)
484 CLog::Log(LOGERROR, "%s, Unable to find suitable stream", __FUNCTION__);
488 /* Update our AE format */
489 format.m_sampleRate = outputFormat.mSampleRate;
490 if (outputFormat.mChannelsPerFrame != format.m_channelLayout.Count())
491 { /* update the channel count. We assume that they're layed out as given in CAChannelMap.
492 if they're not, this is plain wrong */
493 format.m_channelLayout.Reset();
494 for (unsigned int i = 0; i < outputFormat.mChannelsPerFrame; i++)
495 format.m_channelLayout += CAChannelMap[i];
498 m_outputBitstream = passthrough && outputFormat.mFormatID == kAudioFormatLinearPCM;
500 std::string formatString;
501 CLog::Log(LOGDEBUG, "%s: Selected stream[%u] - id: 0x%04X, Physical Format: %s %s", __FUNCTION__, outputIndex, outputStream, StreamDescriptionToString(outputFormat, formatString), m_outputBitstream ? "bitstreamed passthrough" : "");
503 SetHogMode(passthrough);
505 // Configure the output stream object
506 m_outputStream.Open(outputStream);
508 AudioStreamBasicDescription virtualFormat, previousPhysicalFormat;
509 m_outputStream.GetVirtualFormat(&virtualFormat);
510 m_outputStream.GetPhysicalFormat(&previousPhysicalFormat);
511 CLog::Log(LOGDEBUG, "%s: Previous Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
512 CLog::Log(LOGDEBUG, "%s: Previous Physical Format: %s", __FUNCTION__, StreamDescriptionToString(previousPhysicalFormat, formatString));
514 m_outputStream.SetPhysicalFormat(&outputFormat); // Set the active format (the old one will be reverted when we close)
515 m_outputStream.GetVirtualFormat(&virtualFormat);
516 CLog::Log(LOGDEBUG, "%s: New Virtual Format: %s", __FUNCTION__, StreamDescriptionToString(virtualFormat, formatString));
517 CLog::Log(LOGDEBUG, "%s: New Physical Format: %s", __FUNCTION__, StreamDescriptionToString(outputFormat, formatString));
519 m_latentFrames = m_device.GetNumLatencyFrames();
520 m_latentFrames += m_outputStream.GetNumLatencyFrames();
522 /* TODO: Should we use the virtual format to determine our data format? */
523 format.m_frameSize = format.m_channelLayout.Count() * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
524 format.m_frames = m_device.GetBufferSize();
525 format.m_frameSamples = format.m_frames * format.m_channelLayout.Count();
527 if (m_outputBitstream)
529 m_outputBuffer = new int16_t[format.m_frameSamples];
530 /* TODO: Do we need this? */
531 m_device.SetNominalSampleRate(format.m_sampleRate);
534 unsigned int num_buffers = 4;
535 m_buffer = new AERingBuffer(num_buffers * format.m_frames * format.m_frameSize);
536 CLog::Log(LOGDEBUG, "%s: using buffer size: %u (%f ms)", __FUNCTION__, m_buffer->GetMaxSize(), (float)m_buffer->GetMaxSize() / (format.m_sampleRate * format.m_frameSize));
540 format.m_dataFormat = AE_FMT_S16NE;
542 format.m_dataFormat = AE_FMT_FLOAT;
544 // Register for data request callbacks from the driver and start
545 m_device.AddIOProc(renderCallback, this);
550 void CAESinkDARWINOSX::SetHogMode(bool on)
552 // TODO: Auto hogging sets this for us. Figure out how/when to turn it off or use it
553 // It appears that leaving this set will aslo restore the previous stream format when the
554 // Application exits. If auto hogging is set and we try to set hog mode, we will deadlock
555 // From the SDK docs: "If the AudioDevice is in a non-mixable mode, the HAL will automatically take hog mode on behalf of the first process to start an IOProc."
557 // Lock down the device. This MUST be done PRIOR to switching to a non-mixable format, if it is done at all
558 // If it is attempted after the format change, there is a high likelihood of a deadlock
559 // We may need to do this sooner to enable mix-disable (i.e. before setting the stream format)
562 // Auto-Hog does not always un-hog the device when changing back to a mixable mode.
563 // Handle this on our own until it is fixed.
564 CCoreAudioHardware::SetAutoHogMode(false);
565 bool autoHog = CCoreAudioHardware::GetAutoHogMode();
566 CLog::Log(LOGDEBUG, " CoreAudioRenderer::InitializeEncoded: "
567 "Auto 'hog' mode is set to '%s'.", autoHog ? "On" : "Off");
571 m_device.SetHogStatus(on);
572 m_device.SetMixingSupport(!on);
575 void CAESinkDARWINOSX::Deinitialize()
578 m_device.RemoveIOProc();
580 m_outputStream.Close();
587 m_outputBitstream = false;
589 delete[] m_outputBuffer;
590 m_outputBuffer = NULL;
595 bool CAESinkDARWINOSX::IsCompatible(const AEAudioFormat &format, const std::string &device)
597 return ((m_format.m_sampleRate == format.m_sampleRate) &&
598 (m_format.m_dataFormat == format.m_dataFormat) &&
599 (m_format.m_channelLayout == format.m_channelLayout));
602 double CAESinkDARWINOSX::GetDelay()
606 // Calculate the duration of the data in the cache
607 double delay = (double)m_buffer->GetReadSize() / (double)m_format.m_frameSize;
608 delay += (double)m_latentFrames;
609 delay /= (double)m_format.m_sampleRate;
615 double CAESinkDARWINOSX::GetCacheTotal()
617 return (double)m_buffer->GetMaxSize() / (double)(m_format.m_frameSize * m_format.m_sampleRate);
620 CCriticalSection mutex;
621 XbmcThreads::ConditionVariable condVar;
623 unsigned int CAESinkDARWINOSX::AddPackets(uint8_t *data, unsigned int frames, bool hasAudio, bool blocking)
625 if (m_buffer->GetWriteSize() < frames * m_format.m_frameSize)
626 { // no space to write - wait for a bit
627 CSingleLock lock(mutex);
628 unsigned int timeout = 900 * frames / m_format.m_sampleRate;
632 // we are using a timer here for beeing sure for timeouts
633 // condvar can be woken spuriously as signaled
634 XbmcThreads::EndTime timer(timeout);
635 condVar.wait(mutex, timeout);
636 if (!m_started && timer.IsTimePast())
640 unsigned int write_frames = std::min(frames, m_buffer->GetWriteSize() / m_format.m_frameSize);
642 m_buffer->Write(data, write_frames * m_format.m_frameSize);
647 void CAESinkDARWINOSX::Drain()
649 int bytes = m_buffer->GetReadSize();
650 int totalBytes = bytes;
651 int maxNumTimeouts = 3;
652 unsigned int timeout = 900 * bytes / (m_format.m_sampleRate * m_format.m_frameSize);
653 while (bytes && maxNumTimeouts > 0)
655 CSingleLock lock(mutex);
656 XbmcThreads::EndTime timer(timeout);
657 condVar.wait(mutex, timeout);
659 bytes = m_buffer->GetReadSize();
660 // if we timeout and don't
661 // consum bytes - decrease maxNumTimeouts
662 if (timer.IsTimePast() && bytes == totalBytes)
668 void CAESinkDARWINOSX::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
672 for (CADeviceList::const_iterator i = s_devices.begin(); i != s_devices.end(); ++i)
673 list.push_back(i->second);
676 OSStatus CAESinkDARWINOSX::renderCallback(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData)
678 CAESinkDARWINOSX *sink = (CAESinkDARWINOSX*)inClientData;
680 sink->m_started = true;
681 for (unsigned int i = 0; i < outOutputData->mNumberBuffers; i++)
683 if (sink->m_outputBitstream)
685 /* HACK for bitstreaming AC3/DTS via PCM.
686 We reverse the float->S16LE conversion done in the stream or device */
687 static const float mul = 1.0f / (INT16_MAX + 1);
689 unsigned int wanted = std::min(outOutputData->mBuffers[i].mDataByteSize / sizeof(float), (size_t)sink->m_format.m_frameSamples) * sizeof(int16_t);
690 if (wanted <= sink->m_buffer->GetReadSize())
692 sink->m_buffer->Read((unsigned char *)sink->m_outputBuffer, wanted);
693 int16_t *src = sink->m_outputBuffer;
694 float *dest = (float*)outOutputData->mBuffers[i].mData;
695 for (unsigned int i = 0; i < wanted / 2; i++)
696 *dest++ = *src++ * mul;
701 /* buffers appear to come from CA already zero'd, so just copy what is wanted */
702 unsigned int wanted = outOutputData->mBuffers[i].mDataByteSize;
703 unsigned int bytes = std::min(sink->m_buffer->GetReadSize(), wanted);
704 sink->m_buffer->Read((unsigned char*)outOutputData->mBuffers[i].mData, bytes);
706 CLog::Log(LOGERROR, "%s: %sFLOW (%i vs %i) bytes", __FUNCTION__, bytes > wanted ? "OVER" : "UNDER", bytes, wanted);
709 // tell the sink we're good for more data