2 * Copyright (C) 2011-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 "CoreAudioUnit.h"
23 #include "CoreAudioAEHAL.h"
24 #include "cores/AudioEngine/Utils/AEUtil.h"
25 #include "utils/log.h"
27 #include <AudioToolbox/AUGraph.h>
29 CCoreAudioUnit::CCoreAudioUnit() :
34 m_Initialized (false ),
36 m_busNumber (INVALID_BUS )
40 CCoreAudioUnit::~CCoreAudioUnit()
45 bool CCoreAudioUnit::Open(AUGraph audioGraph, AudioComponentDescription desc)
52 m_Initialized = false;
54 ret = AUGraphAddNode(audioGraph, &desc, &m_audioNode);
57 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
58 "Error add m_outputNode. Error = %s", GetError(ret).c_str());
62 ret = AUGraphNodeInfo(audioGraph, m_audioNode, 0, &m_audioUnit);
65 CLog::Log(LOGERROR, "CCoreAudioGraph::Open: "
66 "Error getting m_outputNode. Error = %s", GetError(ret).c_str());
70 m_audioGraph = audioGraph;
76 bool CCoreAudioUnit::Open(AUGraph audioGraph, OSType type, OSType subType, OSType manufacturer)
78 AudioComponentDescription desc = {0};
79 desc.componentType = type;
80 desc.componentSubType = subType;
81 desc.componentManufacturer = manufacturer;
83 return Open(audioGraph, desc);
86 void CCoreAudioUnit::Close()
88 if (!m_Initialized && !m_audioUnit)
96 if (m_busNumber != INVALID_BUS)
98 OSStatus ret = AUGraphDisconnectNodeInput(m_audioGraph, m_audioNode, m_busNumber);
99 if (ret && ret != kAUGraphErr_NodeNotFound)
101 CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
102 "Unable to disconnect AudioUnit. Error = %s", GetError(ret).c_str());
105 ret = AUGraphRemoveNode(m_audioGraph, m_audioNode);
108 CLog::Log(LOGERROR, "CCoreAudioUnit::Close: "
109 "Unable to remove AudioUnit. Error = %s", GetError(ret).c_str());
113 AUGraphUpdate(m_audioGraph, NULL);
115 m_Initialized = false;
121 bool CCoreAudioUnit::GetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
123 if (!m_audioUnit || !pDesc)
126 UInt32 size = sizeof(AudioStreamBasicDescription);
127 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
128 kAudioUnitProperty_StreamFormat, scope, bus, pDesc, &size);
131 CLog::Log(LOGERROR, "CCoreAudioUnit::GetFormat: "
132 "Unable to get AudioUnit format. Bus : %d Scope : %d : Error = %s",
133 (int)bus, (int)scope, GetError(ret).c_str());
139 bool CCoreAudioUnit::SetFormat(AudioStreamBasicDescription* pDesc, AudioUnitScope scope, AudioUnitElement bus)
141 if (!m_audioUnit || !pDesc)
144 OSStatus ret = AudioUnitSetProperty(m_audioUnit,
145 kAudioUnitProperty_StreamFormat, scope, bus, pDesc, sizeof(AudioStreamBasicDescription));
148 CLog::Log(LOGERROR, "CCoreAudioUnit::SetFormat: "
149 "Unable to set AudioUnit format. Bus : %d Scope : %d : Error = %s",
150 (int)bus, (int)scope, GetError(ret).c_str());
156 bool CCoreAudioUnit::SetMaxFramesPerSlice(UInt32 maxFrames)
161 OSStatus ret = AudioUnitSetProperty(m_audioUnit,
162 kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(UInt32));
165 CLog::Log(LOGERROR, "CCoreAudioUnit::SetMaxFramesPerSlice: "
166 "Unable to set AudioUnit max frames per slice. Error = %s", GetError(ret).c_str());
172 bool CCoreAudioUnit::GetSupportedChannelLayouts(AudioChannelLayoutList* pLayouts)
174 if (!m_audioUnit || !pLayouts)
178 Boolean writable = false;
179 OSStatus ret = AudioUnitGetPropertyInfo(m_audioUnit,
180 kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Input, 0, &propSize, &writable);
183 CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
184 "Unable to retrieve supported channel layout property info. Error = %s", GetError(ret).c_str());
187 UInt32 layoutCount = propSize / sizeof(AudioChannelLayoutTag);
188 AudioChannelLayoutTag* pSuppLayouts = new AudioChannelLayoutTag[layoutCount];
189 ret = AudioUnitGetProperty(m_audioUnit,
190 kAudioUnitProperty_SupportedChannelLayoutTags, kAudioUnitScope_Output, 0, pSuppLayouts, &propSize);
193 CLog::Log(LOGERROR, "CCoreAudioUnit::GetSupportedChannelLayouts: "
194 "Unable to retrieve supported channel layouts. Error = %s", GetError(ret).c_str());
197 for (UInt32 layout = 0; layout < layoutCount; layout++)
198 pLayouts->push_back(pSuppLayouts[layout]);
199 delete[] pSuppLayouts;
203 bool CCoreAudioUnit::SetInputSource(ICoreAudioSource* pSource)
207 return SetRenderProc();
209 return RemoveRenderProc();
212 bool CCoreAudioUnit::SetRenderProc()
214 if (!m_audioUnit || m_renderProc)
217 AURenderCallbackStruct callbackInfo;
218 callbackInfo.inputProc = RenderCallback; // Function to be called each time the AudioUnit needs data
219 callbackInfo.inputProcRefCon = this; // Pointer to be returned in the callback proc
220 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
221 kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
224 CLog::Log(LOGERROR, "CCoreAudioUnit::SetRenderProc: "
225 "Unable to set AudioUnit render callback. Error = %s", GetError(ret).c_str());
229 m_renderProc = RenderCallback;
234 bool CCoreAudioUnit::RemoveRenderProc()
236 if (!m_audioUnit || !m_renderProc)
239 AudioUnitInitialize(m_audioUnit);
241 AURenderCallbackStruct callbackInfo;
242 callbackInfo.inputProc = nil;
243 callbackInfo.inputProcRefCon = nil;
244 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioUnitProperty_SetRenderCallback,
245 kAudioUnitScope_Input, 0, &callbackInfo, sizeof(AURenderCallbackStruct));
248 CLog::Log(LOGERROR, "CCoreAudioUnit::RemoveRenderProc: "
249 "Unable to remove AudioUnit render callback. Error = %s", GetError(ret).c_str());
259 OSStatus CCoreAudioUnit::RenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
260 const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
262 OSStatus ret = noErr;
263 CCoreAudioUnit *audioUnit = (CCoreAudioUnit*)inRefCon;
265 if (audioUnit->m_pSource)
267 ret = audioUnit->m_pSource->Render(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
271 ioData->mBuffers[0].mDataByteSize = 0;
273 *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
280 void CCoreAudioUnit::GetFormatDesc(AEAudioFormat format,
281 AudioStreamBasicDescription *streamDesc, AudioStreamBasicDescription *coreaudioDesc)
283 unsigned int bps = CAEUtil::DataFormatToBits(format.m_dataFormat);
285 // Set the input stream format for the AudioUnit
286 // We use the default DefaultOuput AudioUnit, so we only can set the input stream format.
287 // The autput format is automaticaly set to the input format.
288 streamDesc->mFormatID = kAudioFormatLinearPCM; // Data encoding format
289 streamDesc->mFormatFlags = kLinearPCMFormatFlagIsPacked;
290 switch (format.m_dataFormat)
294 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
295 streamDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
302 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
303 streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
306 streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
309 streamDesc->mFormatFlags |= kAudioFormatFlagIsBigEndian;
310 streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
313 streamDesc->mFormatFlags |= kAudioFormatFlagsNativeEndian;
314 streamDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
317 streamDesc->mChannelsPerFrame = format.m_channelLayout.Count(); // Number of interleaved audiochannels
318 streamDesc->mSampleRate = (Float64)format.m_sampleRate; // the sample rate of the audio stream
319 streamDesc->mBitsPerChannel = bps; // Number of bits per sample, per channel
320 streamDesc->mBytesPerFrame = (bps>>3) * format.m_channelLayout.Count(); // Size of a frame == 1 sample per channel
321 streamDesc->mFramesPerPacket = 1; // The smallest amount of indivisible data. Always 1 for uncompressed audio
322 streamDesc->mBytesPerPacket = streamDesc->mBytesPerFrame * streamDesc->mFramesPerPacket;
323 streamDesc->mReserved = 0;
325 // Audio units use noninterleaved 32-bit floating point
326 // linear PCM data for input and output, ...except in the
327 // case of an audio unit that is a data format converter,
328 // which converts to or from this format.
329 coreaudioDesc->mFormatID = kAudioFormatLinearPCM;
330 coreaudioDesc->mFormatFlags = kAudioFormatFlagsNativeEndian |
331 kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
332 switch (format.m_dataFormat)
335 coreaudioDesc->mFormatFlags |= kAudioFormatFlagIsFloat;
337 coreaudioDesc->mFormatFlags |= kAudioFormatFlagsAudioUnitCanonical;
340 coreaudioDesc->mBitsPerChannel = bps; //sizeof(Float32)<<3;
341 coreaudioDesc->mSampleRate = (Float64)format.m_sampleRate;;
342 coreaudioDesc->mFramesPerPacket = 1;
343 coreaudioDesc->mChannelsPerFrame = streamDesc->mChannelsPerFrame;
344 coreaudioDesc->mBytesPerFrame = (bps>>3); //sizeof(Float32);
345 coreaudioDesc->mBytesPerPacket = (bps>>3); //sizeof(Float32);
348 float CCoreAudioUnit::GetLatency()
354 UInt32 size = sizeof(latency);
356 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
357 kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &latency, &size);
361 CLog::Log(LOGERROR, "CCoreAudioUnit::GetLatency: "
362 "Unable to set AudioUnit latency. Error = %s", GetError(ret).c_str());
369 bool CCoreAudioUnit::Stop()
374 AudioOutputUnitStop(m_audioUnit);
379 bool CCoreAudioUnit::Start()
384 AudioOutputUnitStart(m_audioUnit);
389 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
391 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
392 CAUOutputDevice::CAUOutputDevice() :
397 CAUOutputDevice::~CAUOutputDevice()
401 bool CAUOutputDevice::SetCurrentDevice(AudioDeviceID deviceId)
406 OSStatus ret = AudioUnitSetProperty(m_audioUnit, kAudioOutputUnitProperty_CurrentDevice,
407 kAudioUnitScope_Global, kOutputBus, &deviceId, sizeof(AudioDeviceID));
410 CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentDevice: "
411 "Unable to set current device. Error = %s", GetError(ret).c_str());
415 m_DeviceId = deviceId;
420 bool CAUOutputDevice::GetChannelMap(CoreAudioChannelList* pChannelMap)
426 Boolean writable = false;
427 AudioUnitGetPropertyInfo(m_audioUnit,
428 kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, &size, &writable);
430 UInt32 channels = size/sizeof(SInt32);
431 SInt32* pMap = new SInt32[channels];
432 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
433 kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, &size);
435 CLog::Log(LOGERROR, "CCoreAudioUnit::GetInputChannelMap: "
436 "Unable to retrieve AudioUnit input channel map. Error = %s", GetError(ret).c_str());
438 for (UInt32 i = 0; i < channels; i++)
439 pChannelMap->push_back(pMap[i]);
444 bool CAUOutputDevice::SetChannelMap(CoreAudioChannelList* pChannelMap)
446 // The number of array elements must match the
447 // number of output channels provided by the device
448 if (!m_audioUnit || !pChannelMap)
451 UInt32 channels = pChannelMap->size();
452 UInt32 size = sizeof(SInt32) * channels;
453 SInt32* pMap = new SInt32[channels];
454 for (UInt32 i = 0; i < channels; i++)
455 pMap[i] = (*pChannelMap)[i];
457 OSStatus ret = AudioUnitSetProperty(m_audioUnit,
458 kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Input, 0, pMap, size);
460 CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
461 "Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
466 Float32 CAUOutputDevice::GetCurrentVolume()
471 Float32 volPct = 0.0f;
472 OSStatus ret = AudioUnitGetParameter(m_audioUnit,
473 kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &volPct);
476 CLog::Log(LOGERROR, "CCoreAudioUnit::GetCurrentVolume: "
477 "Unable to get AudioUnit volume. Error = %s", GetError(ret).c_str());
483 bool CAUOutputDevice::SetCurrentVolume(Float32 vol)
488 OSStatus ret = AudioUnitSetParameter(m_audioUnit, kHALOutputParam_Volume,
489 kAudioUnitScope_Global, 0, vol, 0);
492 CLog::Log(LOGERROR, "CCoreAudioUnit::SetCurrentVolume: "
493 "Unable to set AudioUnit volume. Error = %s", GetError(ret).c_str());
499 UInt32 CAUOutputDevice::GetBufferFrameSize()
504 UInt32 bufferSize = 0;
505 UInt32 size = sizeof(UInt32);
507 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
508 kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Input, 0, &bufferSize, &size);
511 CLog::Log(LOGERROR, "CCoreAudioUnit::GetBufferFrameSize: "
512 "Unable to get current device's buffer size. Error = %s", GetError(ret).c_str());
518 bool CAUOutputDevice::EnableInputOuput()
524 UInt32 size=sizeof(UInt32);
525 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
526 kAudioOutputUnitProperty_HasIO,kAudioUnitScope_Input, 1, &hasio, &size);
532 ret = AudioUnitSetProperty(m_audioUnit,
533 kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &enable, sizeof(enable));
536 CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: "
537 "Unable to enable input on bus 1. Error = %s", GetError(ret).c_str());
542 ret = AudioUnitSetProperty(m_audioUnit,
543 kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &enable, sizeof(enable));
546 CLog::Log(LOGERROR, "CAUOutputDevice::EnableInputOuput:: "
547 "Unable to disable output on bus 0. Error = %s", GetError(ret).c_str());
555 bool CAUOutputDevice::GetPreferredChannelLayout(CCoreAudioChannelLayout& layout)
560 AudioObjectPropertyAddress propertyAddress;
561 propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
562 propertyAddress.mElement = 0;
563 propertyAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
564 if (!AudioObjectHasProperty(m_DeviceId, &propertyAddress))
567 UInt32 propertySize = 0;
568 OSStatus ret = AudioObjectGetPropertyDataSize(m_DeviceId, &propertyAddress, 0, NULL, &propertySize);
570 CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
571 "Unable to retrieve preferred channel layout size. Error = %s", GetError(ret).c_str());
573 void *pBuf = malloc(propertySize);
574 ret = AudioObjectGetPropertyData(m_DeviceId, &propertyAddress, 0, NULL, &propertySize, pBuf);
576 CLog::Log(LOGERROR, "CAUOutputDevice::GetPreferredChannelLayout: "
577 "Unable to retrieve preferred channel layout. Error = %s", GetError(ret).c_str());
580 // Copy the result into the caller's instance
581 layout.CopyLayout(*((AudioChannelLayout*)pBuf));
584 return (ret == noErr);
587 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
589 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
590 CAUMatrixMixer::CAUMatrixMixer()
594 CAUMatrixMixer::~CAUMatrixMixer()
598 bool CAUMatrixMixer::InitMatrixMixerVolumes()
600 // Fetch thechannel configuration
602 UInt32 size = sizeof(dims);
603 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
604 kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
607 CLog::Log(LOGERROR, "CAUMatrixMixer::Initialize:: "
608 "Get matrix dimesion. Error = %s", GetError(ret).c_str());
612 // Initialize global, input, and output levels
613 if (!SetGlobalVolume(1.0f))
615 for (UInt32 i = 0; i < dims[0]; i++)
616 if (!SetInputVolume(i, 1.0f))
618 for (UInt32 i = 0; i < dims[1]; i++)
619 if (!SetOutputVolume(i, 1.0f))
625 UInt32 CAUMatrixMixer::GetInputBusCount()
631 UInt32 size = sizeof(busCount);
632 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
633 kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, &size);
636 CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputBusCount: "
637 "Unable to get input bus count. Error = %s", GetError(ret).c_str());
643 bool CAUMatrixMixer::SetInputBusFormat(UInt32 busCount, AudioStreamBasicDescription *pFormat)
649 for (UInt32 i = 0; i < busCount; i++)
651 AudioUnitSetParameter(m_audioUnit, kMatrixMixerParam_Enable, kAudioUnitScope_Input, i, enable, 0);
652 if (!SetFormat(pFormat, kAudioUnitScope_Input, i))
659 bool CAUMatrixMixer::SetInputBusCount(UInt32 busCount)
664 OSStatus ret = AudioUnitSetProperty(m_audioUnit,
665 kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(UInt32));
668 CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputBusCount: "
669 "Unable to set input bus count. Error = %s", GetError(ret).c_str());
675 UInt32 CAUMatrixMixer::GetOutputBusCount()
681 UInt32 size = sizeof(busCount);
682 OSStatus ret = AudioUnitGetProperty(m_audioUnit,
683 kAudioUnitProperty_ElementCount, kAudioUnitScope_Output, 0, &busCount, &size);
686 CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputBusCount: "
687 "Unable to get output bus count. Error = %s", GetError(ret).c_str());
693 bool CAUMatrixMixer::SetOutputBusCount(UInt32 busCount)
698 OSStatus ret = AudioUnitSetProperty(m_audioUnit,
699 kAudioUnitProperty_BusCount, kAudioUnitScope_Output, 0, &busCount, sizeof(UInt32));
702 CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputBusCount: "
703 "Unable to set output bus count. Error = %s", GetError(ret).c_str());
709 Float32 CAUMatrixMixer::GetGlobalVolume()
715 OSStatus ret = AudioUnitGetParameter(m_audioUnit,
716 kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, &vol);
719 CLog::Log(LOGERROR, "CAUMatrixMixer::GetGlobalVolume: "
720 "Unable to get global volume. Error = %s", GetError(ret).c_str());
726 bool CAUMatrixMixer::SetGlobalVolume(Float32 vol)
731 OSStatus ret = AudioUnitSetParameter(m_audioUnit,
732 kMatrixMixerParam_Volume, kAudioUnitScope_Global, 0xFFFFFFFF, vol, 0);
735 CLog::Log(LOGERROR, "CAUMatrixMixer::SetGlobalVolume: "
736 "Unable to set global volume. Error = %s", GetError(ret).c_str());
742 Float32 CAUMatrixMixer::GetInputVolume(UInt32 element)
748 OSStatus ret = AudioUnitGetParameter(m_audioUnit,
749 kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, &vol);
752 CLog::Log(LOGERROR, "CAUMatrixMixer::GetInputVolume: "
753 "Unable to get input volume. Error = %s", GetError(ret).c_str());
759 bool CAUMatrixMixer::SetInputVolume(UInt32 element, Float32 vol)
764 OSStatus ret = AudioUnitSetParameter(m_audioUnit,
765 kMatrixMixerParam_Volume, kAudioUnitScope_Input, element, vol, 0);
768 CLog::Log(LOGERROR, "CAUMatrixMixer::SetInputVolume: "
769 "Unable to set input volume. Error = %s", GetError(ret).c_str());
775 Float32 CAUMatrixMixer::GetOutputVolume(UInt32 element)
781 OSStatus ret = AudioUnitGetParameter(m_audioUnit,
782 kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, &vol);
785 CLog::Log(LOGERROR, "CAUMatrixMixer::GetOutputVolume: "
786 "Unable to get output volume. Error = %s", GetError(ret).c_str());
792 bool CAUMatrixMixer::SetOutputVolume(UInt32 element, Float32 vol)
797 OSStatus ret = AudioUnitSetParameter(m_audioUnit,
798 kMatrixMixerParam_Volume, kAudioUnitScope_Output, element, vol, 0);
801 CLog::Log(LOGERROR, "CAUMatrixMixer::SetOutputVolume: "
802 "Unable to set output volume. Error = %s", GetError(ret).c_str());