[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / AudioEngine / Utils / AERemap.cpp
1 /*
2  *      Copyright (C) 2010-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20 #include <math.h>
21 #include <sstream>
22
23 #include "AERemap.h"
24 #include "cores/AudioEngine/AEFactory.h"
25 #include "cores/AudioEngine/Utils/AEUtil.h"
26 #include "utils/log.h"
27 #include "settings/GUISettings.h"
28
29 using namespace std;
30
31 CAERemap::CAERemap() : m_inChannels(0), m_outChannels(0) 
32 {
33   memset(m_mixInfo, 0, sizeof(m_mixInfo));
34 }
35
36 CAERemap::~CAERemap()
37 {
38 }
39
40 bool CAERemap::Initialize(CAEChannelInfo input, CAEChannelInfo output, bool finalStage, bool forceNormalize/* = false */, enum AEStdChLayout stdChLayout/* = AE_CH_LAYOUT_INVALID */)
41 {
42   if (!input.Count() || !output.Count())
43     return false;
44
45   /* build the downmix matrix */
46   memset(m_mixInfo, 0, sizeof(m_mixInfo));
47   m_output = output;
48
49   /* figure which channels we have */
50   for (unsigned int o = 0; o < output.Count(); ++o)
51     m_mixInfo[output[o]].in_dst = true;
52
53   /* flag invalid channels for forced downmix */
54   if (stdChLayout != AE_CH_LAYOUT_INVALID)
55   {
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;
60   }
61
62   m_outChannels = output.Count();
63
64   /* lookup the channels that exist in the output */
65   for (unsigned int i = 0; i < input.Count(); ++i)
66   {
67     AEMixInfo  *info = &m_mixInfo[input[i]];
68     AEMixLevel *lvl  = &info->srcIndex[info->srcCount++];
69     info->in_src = true;
70     lvl->index   = i;
71     lvl->level   = 1.0f;
72     if (!info->in_dst)
73     {
74       for (unsigned int o = 0; o < output.Count(); ++o)
75       {
76         if (input[i] == output[o])
77         {
78           info->outIndex = o;
79           break;
80         }
81       }
82     }
83
84     m_inChannels = i;
85   }
86   ++m_inChannels;
87
88   /* the final stage does not need any down/upmix */
89   if (finalStage)
90     return true;
91
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));
96
97   /*
98     the order of this is important as we can not mix channels
99     into ones that have already been resolved... eg
100
101     TBR -> BR
102     TBC -> TBL & TBR
103
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
108
109     TBC -> TBL & TBR
110     TBR -> BR
111
112     if for any reason out of order mapping becomes required
113     looping this list should resolve the channels.
114   */
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);
134
135   /* since everything eventually mixes down to FC we need special handling for it */
136   if (m_mixInfo[AE_CH_FC].in_src)
137   {
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)
140     {
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)
143       {
144         RM(AE_CH_FC, AE_CH_TFC, AE_CH_FL, AE_CH_FR);
145       }
146       /* if we have TFC */
147       else if (m_mixInfo[AE_CH_TFC].in_dst)
148       {
149         RM(AE_CH_FC, AE_CH_TFC);
150       }
151       /* if we have FLOC & FROC */
152       else if (m_mixInfo[AE_CH_FLOC].in_dst && m_mixInfo[AE_CH_FROC].in_dst)
153       {
154         RM(AE_CH_FC, AE_CH_FLOC, AE_CH_FROC);
155       }
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)
158       {
159         RM(AE_CH_FC, AE_CH_TC, AE_CH_FL, AE_CH_FR);
160       }
161       /* if we have FL & FR */
162       else if (m_mixInfo[AE_CH_FL].in_dst && m_mixInfo[AE_CH_FR].in_dst)
163       {
164         RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
165       }
166       /* if we have TFL & TFR */
167       else if (m_mixInfo[AE_CH_TFL].in_dst && m_mixInfo[AE_CH_TFR].in_dst)
168       {
169         RM(AE_CH_FC, AE_CH_TFL, AE_CH_TFR);
170       }
171       /* we dont have enough speakers to emulate FC */
172       else
173         return false;
174     }
175     else
176     {
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)
179       {
180         RM(AE_CH_FC, AE_CH_FL, AE_CH_FR);
181       }
182     }
183   }
184
185   #undef RM
186
187   if (g_guiSettings.GetBool("audiooutput.stereoupmix"))
188     BuildUpmixMatrix(input, output);
189
190   /* normalize the values */
191   bool normalize;
192   if (forceNormalize)
193     normalize = true;
194   else
195   {
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"));
199   }
200
201   if (normalize)
202   {
203     float max = 0;
204     for (unsigned int o = 0; o < output.Count(); ++o)
205     {
206       AEMixInfo *info = &m_mixInfo[output[o]];
207       float sum = 0;
208       for (int i = 0; i < info->srcCount; ++i)
209         sum += info->srcIndex[i].level;
210
211       if (sum > max)
212         max = sum;
213     }
214
215     float scale = 1.0f / max;
216     for (unsigned int o = 0; o < output.Count(); ++o)
217     {
218       AEMixInfo *info = &m_mixInfo[output[o]];
219       for (int i = 0; i < info->srcCount; ++i)
220         info->srcIndex[i].level *= scale;
221     }
222   }
223
224 #if 0
225   /* dump the matrix */
226   CLog::Log(LOGINFO, "==[Downmix Matrix]==");
227   for (unsigned int o = 0; o < output.Count(); ++o)
228   {
229     AEMixInfo *info = &m_mixInfo[output[o]];
230     if (info->srcCount == 0)
231       continue;
232
233     std::stringstream s;
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 << ")";
237
238     CLog::Log(LOGINFO, "%s", s.str().c_str());
239   }
240   CLog::Log(LOGINFO, "====================\n");
241 #endif
242
243   return true;
244 }
245
246 void CAERemap::ResolveMix(const AEChannel from, CAEChannelInfo to)
247 {
248   AEMixInfo *fromInfo = &m_mixInfo[from];
249   if (fromInfo->in_dst || !fromInfo->in_src)
250     return;
251
252   for (unsigned int i = 0; i < to.Count(); ++i)
253   {
254     AEMixInfo *toInfo = &m_mixInfo[to[i]];
255     toInfo->in_src = true;
256     for (int o = 0; o < fromInfo->srcCount; ++o)
257     {
258       AEMixLevel *fromLvl = &fromInfo->srcIndex[o];
259       AEMixLevel *toLvl   = NULL;
260
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)
264          {
265            toLvl = &toInfo->srcIndex[l];
266            toLvl->level = (fromLvl->level + toLvl->level) / sqrt((float)to.Count() + 1.0f);
267            break;
268          }
269
270       if (toLvl)
271         continue;
272
273       toLvl = &toInfo->srcIndex[toInfo->srcCount++];
274       toLvl->index = fromLvl->index;
275       toLvl->level = fromLvl->level / sqrt((float)to.Count());
276     }
277   }
278
279   fromInfo->srcCount = 0;
280   fromInfo->in_src   = false;
281 }
282
283 /* This method has unrolled loop for higher performance */
284 void CAERemap::Remap(float * const in, float * const out, const unsigned int frames) const
285 {
286   const unsigned int frameBlocks = frames & ~0x3;
287
288   for (int o = 0; o < m_outChannels; ++o)
289   {
290     const AEMixInfo *info = &m_mixInfo[m_output[o]];
291     if (!info->in_dst)
292     {
293       unsigned int f = 0;
294       unsigned int odx = 0;
295       for(; f < frameBlocks; f += 4)
296       {
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;
301       }
302
303       switch (frames & 0x3)
304       {
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;
308       }
309       continue;
310     }
311
312     /* if there is only 1 source, just copy it so we dont break DPL */
313     if (info->srcCount == 1)
314     {
315       unsigned int f = 0;
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)
321       {
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;
326       }
327
328       switch (frames & 0x3)
329       {
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];
333       }
334     }
335     else
336     {
337       for (unsigned int f = 0; f < frames; ++f)
338       {
339         float *outOffset = out + (f * m_outChannels) + o;
340         float *inOffset  = in  + (f * m_inChannels);
341         *outOffset = 0.0f;
342
343         int blocks = info->srcCount & ~0x3;
344
345         /* the compiler has a better chance of optimizing this if it is done in parallel */
346         int i = 0;
347         float f1 = 0.0, f2 = 0.0, f3 = 0.0, f4 = 0.0;
348         for (; i < blocks; i += 4)
349         {
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;
354         }
355
356         /* unrolled loop for higher performance */
357         switch (info->srcCount & 0x3)
358         {
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;
362         }
363
364         *outOffset += (f1+f2+f3+f4);
365       }
366     }
367   }
368 }
369
370 inline void CAERemap::BuildUpmixMatrix(const CAEChannelInfo& input, const CAEChannelInfo& output)
371 {
372   #define UM(from, to) \
373     if (!m_mixInfo[to].in_src && m_mixInfo[to].in_dst) \
374     { \
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++; \
381     }
382
383   if (m_mixInfo[AE_CH_FL].in_src)
384   {
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);
389   }
390
391   if (m_mixInfo[AE_CH_FR].in_src)
392   {
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);
397   }
398
399   /* fix the levels of anything we added */
400   for (unsigned int i = 0; i < output.Count(); ++i)
401   {
402     AEMixInfo *outputInfo = &m_mixInfo[output[i]];
403     if (!outputInfo->in_src && outputInfo->srcCount > 0)
404       for (int src = 0; src < outputInfo->srcCount; ++src)
405       {
406         AEChannel srcChannel = input[outputInfo->srcIndex[src].index];
407         AEMixInfo *inputInfo = &m_mixInfo[srcChannel];
408         outputInfo->srcIndex[src].level /= sqrt((float)(inputInfo->cpyCount + 1));
409       }
410   }
411
412   /* fix the source levels also */
413   for (unsigned int i = 0; i < input.Count(); ++i)
414   {
415     AEMixInfo *inputInfo = &m_mixInfo[input[i]];
416     if (!inputInfo->in_dst || inputInfo->cpyCount == 0)
417       continue;
418     inputInfo->srcIndex[0].level /= sqrt((float)(inputInfo->cpyCount + 1));
419   }
420 }