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 "CoreAudioMixMap.h"
23 #include "CoreAudioUnit.h"
24 #include "CoreAudioAEHAL.h"
25 #include "utils/log.h"
28 #include <AudioToolbox/AudioToolbox.h>
31 CCoreAudioMixMap::CCoreAudioMixMap() :
34 m_pMap = (Float32*)calloc(sizeof(AudioChannelLayout), 1);
37 CCoreAudioMixMap::CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout) :
40 Rebuild(inLayout, outLayout);
43 CCoreAudioMixMap::~CCoreAudioMixMap()
49 void CCoreAudioMixMap::Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout)
51 // map[in][out] = mix-level of input_channel[in] into output_channel[out]
56 m_inChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(inLayout);
57 m_outChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(outLayout);
59 // Try to find a 'well-known' matrix
60 const AudioChannelLayout* layouts[] = {&inLayout, &outLayout};
62 AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap,
63 sizeof(layouts), layouts, &propSize);
64 m_pMap = (Float32*)calloc(1,propSize);
66 // Try and get a predefined mixmap
67 OSStatus ret = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap,
68 sizeof(layouts), layouts, &propSize, m_pMap);
71 // If we for some reason don't find a predefined matrix let's build a diagonal matrix,
72 // basically guessing here, but we need to have a mixmap that matches the output and input
73 CLog::Log(LOGDEBUG, "CCoreAudioMixMap::CreateMap: No pre-defined mapping from %d to %d channels, building diagonal matrix.", m_inChannels, m_outChannels);
74 for (UInt32 chan = 0; chan < std::min(m_inChannels, m_outChannels); ++chan)
76 Float32 *vol = m_pMap + (chan * m_outChannels + chan);
77 CLog::Log(LOGDEBUG, "CCoreAudioMixMap::Rebuild %d = %f", chan, *vol);
84 CCoreAudioMixMap *CCoreAudioMixMap::CreateMixMap(CAUOutputDevice *audioUnit, AEAudioFormat &format, AudioChannelLayoutTag layoutTag)
89 AudioStreamBasicDescription fmt;
90 AudioStreamBasicDescription inputFormat;
92 // get the stream input format
93 audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
95 unsigned int channels = format.m_channelLayout.Count();
96 CAEChannelInfo channelLayout = format.m_channelLayout;
98 // Convert XBMC input channel layout format to CoreAudio layout format
99 AudioChannelLayout* pInLayout = (AudioChannelLayout*)malloc(sizeof(AudioChannelLayout) + sizeof(AudioChannelDescription) * channels);
100 pInLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
101 pInLayout->mChannelBitmap = 0;
102 pInLayout->mNumberChannelDescriptions = channels;
103 for (unsigned int chan=0; chan < channels; chan++)
105 AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
106 // Convert from XBMC channel tag to CoreAudio channel tag
107 pDesc->mChannelLabel = g_LabelMap[(unsigned int)channelLayout[chan]];
108 pDesc->mChannelFlags = kAudioChannelFlags_AllOff;
109 pDesc->mCoordinates[0] = 0.0f;
110 pDesc->mCoordinates[1] = 0.0f;
111 pDesc->mCoordinates[2] = 0.0f;
112 if (pDesc->mChannelLabel == kAudioChannelLabel_LFEScreen)
115 // HACK: Fix broken channel layouts coming from some aac sources
116 // that include rear channel but no side channels.
117 // 5.1 streams should include front and side channels.
118 // Rear channels are added by 6.1 and 7.1, so any 5.1
119 // source that claims to have rear channels is wrong.
120 if (inputFormat.mChannelsPerFrame == 6 && hasLFE)
122 // Check for 5.1 configuration (as best we can without getting too silly)
123 for (unsigned int chan=0; chan < inputFormat.mChannelsPerFrame; chan++)
125 AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
126 if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround || pDesc->mChannelLabel == kAudioChannelLabel_RightSurround)
127 break; // Required condition cannot be true
129 if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurroundDirect)
131 // Change [Back Left] to [Side Left]
132 pDesc->mChannelLabel = kAudioChannelLabel_LeftSurround;
133 CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: "
134 "Detected faulty input channel map...fixing(Back Left-->Side Left)");
136 if (pDesc->mChannelLabel == kAudioChannelLabel_RightSurroundDirect)
138 // Change [Back Left] to [Side Left]
139 pDesc->mChannelLabel = kAudioChannelLabel_RightSurround;
140 CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: "
141 "Detected faulty input channel map...fixing(Back Right-->Side Right)");
146 CCoreAudioChannelLayout sourceLayout(*pInLayout);
150 std::string strInLayout;
151 CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: Source Stream Layout: %s",
152 CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)sourceLayout, strInLayout));
154 // Get User-Configured (XBMC) Speaker Configuration
155 AudioChannelLayout guiLayout;
156 guiLayout.mChannelLayoutTag = layoutTag;
157 CCoreAudioChannelLayout userLayout(guiLayout);
158 std::string strUserLayout;
159 CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: User-Configured Speaker Layout: %s",
160 CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)userLayout, strUserLayout));
162 // Get OS-Configured (Audio MIDI Setup) Speaker Configuration (Channel Layout)
163 CCoreAudioChannelLayout deviceLayout;
164 if (!audioUnit->GetPreferredChannelLayout(deviceLayout))
167 // When all channels on the output device are unknown take the gui layout
168 //if(deviceLayout.AllChannelUnknown())
169 // deviceLayout.CopyLayout(guiLayout);
171 std::string strOutLayout;
172 CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: Output Device Layout: %s",
173 CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)deviceLayout, strOutLayout));
176 // Reconcile the OS and GUI layout configurations. Clamp to the minimum number of speakers
177 // For each OS-defined output, see if it exists in the GUI configuration
178 // If it does, add it to the 'union' layout (bitmap?)
179 // User may have configured 5.1 in GUI, but only 2.0 in OS
180 // Resulting layout would be {FL, FR}
181 // User may have configured 2.0 in GUI, and 5.1 in OS
182 // Resulting layout would be {FL, FR}
184 // Correct any configuration incompatibilities
185 //if (CCoreAudioChannelLayout::GetChannelCountForLayout(guiLayout) < CCoreAudioChannelLayout::GetChannelCountForLayout(deviceLayout))
186 // deviceLayout.CopyLayout(guiLayout);
188 // TODO: Skip matrix mixer if input/output are compatible
189 CCoreAudioMixMap *mixMap = new CCoreAudioMixMap();
190 mixMap->Rebuild(*sourceLayout, *(AudioChannelLayout*)deviceLayout);
194 bool CCoreAudioMixMap::SetMixingMatrix(CAUMatrixMixer *mixerUnit,
195 CCoreAudioMixMap *mixMap, AudioStreamBasicDescription *inputFormat,
196 AudioStreamBasicDescription *fmt, int channelOffset)
198 if (!mixerUnit || !inputFormat || !fmt)
201 // Fetch the mixing unit size
203 UInt32 size = sizeof(dims);
204 AudioUnitGetProperty(mixerUnit->GetUnit(),
205 kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
207 if(inputFormat->mChannelsPerFrame + channelOffset > dims[0])
209 CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - input format doesn't fit mixer size %u+%u > %u"
210 , inputFormat->mChannelsPerFrame, channelOffset, dims[0]);
214 if(fmt->mChannelsPerFrame > dims[1])
216 CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't fit mixer size %u > %u"
217 , fmt->mChannelsPerFrame, dims[1]);
221 if(fmt->mChannelsPerFrame < dims[1])
223 CLog::Log(LOGWARNING, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't specify all outputs %u < %u"
224 , fmt->mChannelsPerFrame, dims[1]);
227 // Configure the mixing matrix
228 // The return from kAudioFormatProperty_MatrixMixMap (See Rebuild above)
229 // is a Float32* which is laid out like this:
231 // mapping 2 chan -> 2 chan
234 // or better represented in a tow dimensional array:
239 // mapping 6 chan -> 6 chan:
245 Float32* val = (Float32*)*mixMap;
246 for (UInt32 i = 0; i < inputFormat->mChannelsPerFrame; ++i)
249 std::stringstream layoutStr;
250 for (; j < fmt->mChannelsPerFrame; ++j)
252 Float32 *vol = val + (i * mixMap->m_outChannels + j);
253 layoutStr << *vol << ", ";
254 AudioUnitSetParameter(mixerUnit->GetUnit(),
255 kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, *vol, 0);
257 // zero out additional outputs from this input
258 for (; j < dims[1]; ++j)
260 AudioUnitSetParameter(mixerUnit->GetUnit(),
261 kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, 0.0f, 0);
265 CLog::Log(LOGDEBUG, "CCoreAudioMixMap::SetMixingMatrix channel %d = [%s]", i, layoutStr.str().c_str());
268 CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: "
269 "Mixer Output Format: %d channels, %0.1f kHz, %d bits, %d bytes per frame",
270 (int)fmt->mChannelsPerFrame, fmt->mSampleRate / 1000.0f, (int)fmt->mBitsPerChannel, (int)fmt->mBytesPerFrame);
272 if (!mixerUnit->InitMatrixMixerVolumes())