2 * Copyright (C) 2010-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/>.
24 #include "cores/AudioEngine/AEFactory.h"
25 #include "cores/AudioEngine/Utils/AEUtil.h"
26 #include "utils/log.h"
27 #include "settings/GUISettings.h"
31 CAERemap::CAERemap() : m_inChannels(0), m_outChannels(0)
33 memset(m_mixInfo, 0, sizeof(m_mixInfo));
40 bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize/* = false */, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
42 if (!input.Count() || !output.Count())
45 /* build the downmix matrix */
46 memset(m_mixInfo, 0, sizeof(m_mixInfo));
49 /* figure which channels we have */
50 for (unsigned int o = 0; o < output.Count(); ++o)
51 m_mixInfo[output[o]].in_dst = true;
53 /* flag invalid channels for forced downmix */
54 if (stdChLayout != AE_CH_LAYOUT_INVALID)
56 CAEChannelInfo layout = stdChLayout;
57 for (unsigned int o = 0; o < output.Count(); ++o)
58 if (!layout.HasChannel(output[o]))
59 m_mixInfo[output[o]].in_dst = false;
62 m_outChannels = output.Count();
64 /* lookup the channels that exist in the output */
65 for (unsigned int i = 0; i < input.Count(); ++i)
67 AEMixInfo *info = &m_mixInfo[input[i]];
68 AEMixLevel *lvl = &info->srcIndex[info->srcCount++];
74 for (unsigned int o = 0; o < output.Count(); ++o)
76 if (input[i] == output[o])
88 /* the final stage does not need any down/upmix */
92 /* downmix from the specified channel to the specified list of channels */
93 #define RM(from, ...) \
94 static AEChannel downmix_##from[] = {__VA_ARGS__, AE_CH_NULL}; \
95 ResolveMix(from, CAEChannelInfo(downmix_##from));
98 the order of this is important as we can not mix channels
99 into ones that have already been resolved... eg
104 TBR will get resolved to BR, and then TBC will get
105 resolved to TBL, but since TBR has been resolved it will
106 never make it to the output. The order has to be reversed
107 as the TBC center depends on the TBR downmix... eg
112 if for any reason out of order mapping becomes required
113 looping this list should resolve the channels.
115 RM(AE_CH_TBC , AE_CH_TBL, AE_CH_TBR);
116 RM(AE_CH_TBR , AE_CH_BR);
117 RM(AE_CH_TBL , AE_CH_BL);
118 RM(AE_CH_TC , AE_CH_TFL, AE_CH_TFR);
119 RM(AE_CH_TFC , AE_CH_TFL, AE_CH_TFR);
120 RM(AE_CH_TFR , AE_CH_FR);
121 RM(AE_CH_TFL , AE_CH_FL);
122 RM(AE_CH_SR , AE_CH_BR, AE_CH_FR);
123 RM(AE_CH_SL , AE_CH_BL, AE_CH_FL);
124 RM(AE_CH_BC , AE_CH_BL, AE_CH_BR);
125 RM(AE_CH_FROC, AE_CH_FR, AE_CH_FC);
126 RM(AE_CH_FLOC, AE_CH_FL, AE_CH_FC);
127 RM(AE_CH_BL , AE_CH_FL);
128 RM(AE_CH_BR , AE_CH_FR);
129 RM(AE_CH_LFE , AE_CH_FL, AE_CH_FR);
130 RM(AE_CH_FL , AE_CH_FC);
131 RM(AE_CH_FR , AE_CH_FC);
132 RM(AE_CH_BROC, AE_CH_BR, AE_CH_BC);
133 RM(AE_CH_BLOC, AE_CH_BL, AE_CH_BC);
135 /* since everything eventually mixes down to FC we need special handling for it */
136 if (m_mixInfo[AE_CH_FC].in_src)
138 /* if there is no output FC channel, try to mix it the best possible way */
139 if (!m_mixInfo[AE_CH_FC].in_dst)
141 /* if we have TFC & FL & FR */
142 if (m_mixInfo[AE_CH_TFC].in_dst && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
144 RM(AE_CH_FC, AE_CH_TFC, AE_CH_FL, AE_CH_FR);
147 else if (m_mixInfo[AE_CH_TFC].in_dst)
149 RM(AE_CH_FC, AE_CH_TFC);
151 /* if we have FLOC & FROC */
152 else if (m_mixInfo[AE_CH_FLOC].in_dst && m_mixInfo[AE_CH_FROC].in_dst)
154 RM(AE_CH_FC, AE_CH_FLOC, AE_CH_FROC);
156 /* if we have TC & FL & FR */
157 else if (m_mixInfo[AE_CH_TC].in_dst && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
159 RM(AE_CH_FC, AE_CH_TC, AE_CH_FL, AE_CH_FR);
161 /* if we have FL & FR */
162 else if (m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
164 RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
166 /* if we have TFL & TFR */
167 else if (m_mixInfo[AE_CH_TFL].in_dst && m_mixInfo[AE_CH_TFR].in_dst)
169 RM(AE_CH_FC, AE_CH_TFL, AE_CH_TFR);
171 /* we dont have enough speakers to emulate FC */
177 /* if there is only one channel in the source and it is the FC and we have FL & FR, upmix to dual mono */
178 if (m_inChannels == 1 && m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
180 RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
187 if (g_guiSettings.GetBool("audiooutput.stereoupmix"))
188 BuildUpmixMatrix(input, output);
190 /* normalize the values */
196 //FIXME: guisetting is reversed, change the setting name after frodo
197 normalize = !g_guiSettings.GetBool("audiooutput.normalizelevels");
198 CLog::Log(LOGDEBUG, "AERemap: Downmix normalization is %s", (normalize ? "enabled" : "disabled"));
204 for (unsigned int o = 0; o < output.Count(); ++o)
206 AEMixInfo *info = &m_mixInfo[output[o]];
208 for (int i = 0; i < info->srcCount; ++i)
209 sum += info->srcIndex[i].level;
215 float scale = 1.0f / max;
216 for (unsigned int o = 0; o < output.Count(); ++o)
218 AEMixInfo *info = &m_mixInfo[output[o]];
219 for (int i = 0; i < info->srcCount; ++i)
220 info->srcIndex[i].level *= scale;
225 /* dump the matrix */
226 CLog::Log(LOGINFO, "==[Downmix Matrix]==");
227 for (unsigned int o = 0; o < output.Count(); ++o)
229 AEMixInfo *info = &m_mixInfo[output[o]];
230 if (info->srcCount == 0)
234 s << CAEChannelInfo::GetChName(output[o]) << " =";
235 for (int i = 0; i < info->srcCount; ++i)
236 s << " " << CAEChannelInfo::GetChName(input[info->srcIndex[i].index]) << "(" << info->srcIndex[i].level << ")";
238 CLog::Log(LOGINFO, "%s", s.str().c_str());
240 CLog::Log(LOGINFO, "====================\n");
246 void CAERemap::ResolveMix(const AEChannel from, CAEChannelInfo to)
248 AEMixInfo *fromInfo = &m_mixInfo[from];
249 if (fromInfo->in_dst || !fromInfo->in_src)
252 for (unsigned int i = 0; i < to.Count(); ++i)
254 AEMixInfo *toInfo = &m_mixInfo[to[i]];
255 toInfo->in_src = true;
256 for (int o = 0; o < fromInfo->srcCount; ++o)
258 AEMixLevel *fromLvl = &fromInfo->srcIndex[o];
259 AEMixLevel *toLvl = NULL;
261 /* if its already in the output, then we need to combine the levels */
262 for (int l = 0; l < toInfo->srcCount; ++l)
263 if (toInfo->srcIndex[l].index == fromLvl->index)
265 toLvl = &toInfo->srcIndex[l];
266 toLvl->level = (fromLvl->level + toLvl->level) / sqrt((float)to.Count() + 1.0f);
273 toLvl = &toInfo->srcIndex[toInfo->srcCount++];
274 toLvl->index = fromLvl->index;
275 toLvl->level = fromLvl->level / sqrt((float)to.Count());
279 fromInfo->srcCount = 0;
280 fromInfo->in_src = false;
283 /* This method has unrolled loop for higher performance */
284 void CAERemap::Remap(float * const in, float * const out, const unsigned int frames) const
286 const unsigned int frameBlocks = frames & ~0x3;
288 for (int o = 0; o < m_outChannels; ++o)
290 const AEMixInfo *info = &m_mixInfo[m_output[o]];
294 unsigned int odx = 0;
295 for(; f < frameBlocks; f += 4)
297 out[odx + o] = 0.0f, odx += m_outChannels;
298 out[odx + o] = 0.0f, odx += m_outChannels;
299 out[odx + o] = 0.0f, odx += m_outChannels;
300 out[odx + o] = 0.0f, odx += m_outChannels;
303 switch (frames & 0x3)
305 case 3: out[odx + o] = 0.0f, odx += m_outChannels;
306 case 2: out[odx + o] = 0.0f, odx += m_outChannels;
307 case 1: out[odx + o] = 0.0f;
312 /* if there is only 1 source, just copy it so we dont break DPL */
313 if (info->srcCount == 1)
316 unsigned int idx = 0;
317 unsigned int odx = 0;
318 unsigned int srcIndex = info->srcIndex[0].index;
319 /* the compiler has a better chance of optimizing this if it is done in parallel */
320 for (; f < frameBlocks; f += 4)
322 out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
323 out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
324 out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
325 out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
328 switch (frames & 0x3)
330 case 3: out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
331 case 2: out[odx + o] = in[idx + srcIndex], idx += m_inChannels, odx += m_outChannels;
332 case 1: out[odx + o] = in[idx + srcIndex];
337 for (unsigned int f = 0; f < frames; ++f)
339 float *outOffset = out + (f * m_outChannels) + o;
340 float *inOffset = in + (f * m_inChannels);
343 int blocks = info->srcCount & ~0x3;
345 /* the compiler has a better chance of optimizing this if it is done in parallel */
347 float f1 = 0.0, f2 = 0.0, f3 = 0.0, f4 = 0.0;
348 for (; i < blocks; i += 4)
350 f1 += inOffset[info->srcIndex[i].index] * info->srcIndex[i].level;
351 f2 += inOffset[info->srcIndex[i+1].index] * info->srcIndex[i+1].level;
352 f3 += inOffset[info->srcIndex[i+2].index] * info->srcIndex[i+2].level;
353 f4 += inOffset[info->srcIndex[i+3].index] * info->srcIndex[i+3].level;
356 /* unrolled loop for higher performance */
357 switch (info->srcCount & 0x3)
359 case 3: f3 += inOffset[info->srcIndex[i+2].index] * info->srcIndex[i+2].level;
360 case 2: f2 += inOffset[info->srcIndex[i+1].index] * info->srcIndex[i+1].level;
361 case 1: f1 += inOffset[info->srcIndex[i].index] * info->srcIndex[i].level;
364 *outOffset += (f1+f2+f3+f4);
370 inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output)
372 #define UM(from, to) \
373 if (!m_mixInfo[to].in_src && m_mixInfo[to].in_dst) \
375 AEMixInfo *toInfo = &m_mixInfo[to ]; \
376 AEMixInfo *fromInfo = &m_mixInfo[from]; \
377 toInfo->srcIndex[toInfo->srcCount].level = 1.0f; \
378 toInfo->srcIndex[toInfo->srcCount].index = fromInfo->srcIndex[0].index; \
379 toInfo ->srcCount++; \
380 fromInfo->cpyCount++; \
383 if (m_mixInfo[AE_CH_FL].in_src)
385 UM(AE_CH_FL, AE_CH_BL );
386 UM(AE_CH_FL, AE_CH_SL );
387 UM(AE_CH_FL, AE_CH_FC );
388 UM(AE_CH_FL, AE_CH_LFE);
391 if (m_mixInfo[AE_CH_FR].in_src)
393 UM(AE_CH_FR, AE_CH_BR );
394 UM(AE_CH_FR, AE_CH_SR );
395 UM(AE_CH_FR, AE_CH_FC );
396 UM(AE_CH_FR, AE_CH_LFE);
399 /* fix the levels of anything we added */
400 for (unsigned int i = 0; i < output.Count(); ++i)
402 AEMixInfo *outputInfo = &m_mixInfo[output[i]];
403 if (!outputInfo->in_src && outputInfo->srcCount > 0)
404 for (int src = 0; src < outputInfo->srcCount; ++src)
406 AEChannel srcChannel = input[outputInfo->srcIndex[src].index];
407 AEMixInfo *inputInfo = &m_mixInfo[srcChannel];
408 outputInfo->srcIndex[src].level /= sqrt((float)(inputInfo->cpyCount + 1));
412 /* fix the source levels also */
413 for (unsigned int i = 0; i < input.Count(); ++i)
415 AEMixInfo *inputInfo = &m_mixInfo[input[i]];
416 if (!inputInfo->in_dst || inputInfo->cpyCount == 0)
418 inputInfo->srcIndex[0].level /= sqrt((float)(inputInfo->cpyCount + 1));