changed: update version strings for beta4
[xbmc.git] / xbmc / utils / PCMRemap.cpp
blob071f3eeddcfe644569be968b216aa2b253330317
1 /*
2 * Copyright (C) 2005-2010 Team XBMC
3 * http://www.xbmc.org
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.
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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #define __STDC_LIMIT_MACROS
24 #include <cstdlib>
25 #include <string.h>
26 #include <stdio.h>
27 #include <math.h>
29 #include "MathUtils.h"
30 #include "PCMRemap.h"
31 #include "utils/log.h"
32 #include "GUISettings.h"
33 #ifdef _WIN32
34 #include "../win32/PlatformDefs.h"
35 #endif
37 static enum PCMChannels PCMLayoutMap[PCM_MAX_LAYOUT][PCM_MAX_CH + 1] =
39 /* 2.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_INVALID},
40 /* 2.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
41 /* 3.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_INVALID},
42 /* 3.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_INVALID},
43 /* 4.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
44 /* 4.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
45 /* 5.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
46 /* 5.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
47 /* 7.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
48 /* 7.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID}
52 map missing output into channel @ volume level
53 the order of this table is important, mix tables can not depend on channels that have not been defined yet
54 eg, FC can only be mixed into FL, FR as they are the only channels that have been defined
56 #define PCM_MAX_MIX 3
57 static struct PCMMapInfo PCMDownmixTable[PCM_MAX_CH][PCM_MAX_MIX] =
59 /* PCM_FRONT_LEFT */
61 {PCM_INVALID}
63 /* PCM_FRONT_RIGHT */
65 {PCM_INVALID}
67 /* PCM_FRONT_CENTER */
69 {PCM_FRONT_LEFT_OF_CENTER , 1.0},
70 {PCM_FRONT_RIGHT_OF_CENTER, 1.0},
71 {PCM_INVALID}
73 /* PCM_LOW_FREQUENCY */
76 A/52B 7.8 paragraph 2 recomends +10db
77 but due to horrible clipping when normalize
78 is disabled we set this to 1.0
80 {PCM_FRONT_LEFT , 1.0},//3.5},
81 {PCM_FRONT_RIGHT , 1.0},//3.5},
82 {PCM_INVALID}
84 /* PCM_BACK_LEFT */
86 {PCM_FRONT_LEFT , 1.0},
87 {PCM_INVALID}
89 /* PCM_BACK_RIGHT */
91 {PCM_FRONT_RIGHT , 1.0},
92 {PCM_INVALID}
94 /* PCM_FRONT_LEFT_OF_CENTER */
96 {PCM_FRONT_LEFT , 1.0},
97 {PCM_FRONT_CENTER , 1.0, true},
98 {PCM_INVALID}
100 /* PCM_FRONT_RIGHT_OF_CENTER */
102 {PCM_FRONT_RIGHT , 1.0},
103 {PCM_FRONT_CENTER , 1.0, true},
104 {PCM_INVALID}
106 /* PCM_BACK_CENTER */
108 {PCM_BACK_LEFT , 1.0},
109 {PCM_BACK_RIGHT , 1.0},
110 {PCM_INVALID}
112 /* PCM_SIDE_LEFT */
114 {PCM_FRONT_LEFT , 1.0},
115 {PCM_BACK_LEFT , 1.0},
116 {PCM_INVALID}
118 /* PCM_SIDE_RIGHT */
120 {PCM_FRONT_RIGHT , 1.0},
121 {PCM_BACK_RIGHT , 1.0},
122 {PCM_INVALID}
124 /* PCM_TOP_FRONT_LEFT */
126 {PCM_FRONT_LEFT , 1.0},
127 {PCM_INVALID}
129 /* PCM_TOP_FRONT_RIGHT */
131 {PCM_FRONT_RIGHT , 1.0},
132 {PCM_INVALID}
134 /* PCM_TOP_FRONT_CENTER */
136 {PCM_TOP_FRONT_LEFT , 1.0},
137 {PCM_TOP_FRONT_RIGHT , 1.0},
138 {PCM_INVALID}
140 /* PCM_TOP_CENTER */
142 {PCM_TOP_FRONT_LEFT , 1.0},
143 {PCM_TOP_FRONT_RIGHT , 1.0},
144 {PCM_INVALID}
146 /* PCM_TOP_BACK_LEFT */
148 {PCM_BACK_LEFT , 1.0},
149 {PCM_INVALID}
151 /* PCM_TOP_BACK_RIGHT */
153 {PCM_BACK_LEFT , 1.0},
154 {PCM_INVALID}
156 /* PCM_TOP_BACK_CENTER */
158 {PCM_TOP_BACK_LEFT , 1.0},
159 {PCM_TOP_BACK_RIGHT , 1.0},
160 {PCM_INVALID}
164 CPCMRemap::CPCMRemap() :
165 m_inSet (false),
166 m_outSet (false),
167 m_inChannels (0),
168 m_outChannels (0),
169 m_inSampleSize(0)
171 Dispose();
174 CPCMRemap::~CPCMRemap()
176 Dispose();
179 void CPCMRemap::Dispose()
183 /* resolves the channels recursively and returns the new index of tablePtr */
184 struct PCMMapInfo* CPCMRemap::ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr)
186 if (channel == PCM_INVALID) return tablePtr;
188 /* if its a 1 to 1 mapping, return */
189 if (m_useable[channel])
191 tablePtr->channel = channel;
192 tablePtr->level = level;
194 ++tablePtr;
195 tablePtr->channel = PCM_INVALID;
196 return tablePtr;
197 } else
198 if (ifExists)
199 level /= 2;
201 struct PCMMapInfo *info;
202 std::vector<enum PCMChannels>::iterator itt;
204 for(info = PCMDownmixTable[channel]; info->channel != PCM_INVALID; ++info)
206 /* make sure we are not about to recurse into ourself */
207 bool found = false;
208 for(itt = path.begin(); itt != path.end(); ++itt)
209 if (*itt == info->channel)
211 found = true;
212 break;
215 if (found)
216 continue;
218 path.push_back(channel);
219 float l = (info->level * (level / 100)) * 100;
220 tablePtr = ResolveChannel(info->channel, l, info->ifExists, path, tablePtr);
221 path.pop_back();
224 return tablePtr;
228 Builds a lookup table without extra adjustments, useful if we simply
229 want to find out which channels are active.
230 For final adjustments, BuildMap() is used.
232 void CPCMRemap::ResolveChannels()
234 unsigned int in_ch, out_ch;
235 bool hasSide = false;
236 bool hasBack = false;
238 memset(m_useable, 0, sizeof(m_useable));
240 if (!m_outSet)
242 /* Output format is not known yet, assume the full configured map.
243 * Note that m_ignoreLayout-using callers normally ignore the result of
244 * this function when !m_outSet, when it is called only for an advice for
245 * the caller of SetInputFormat about the best possible output map, and
246 * they can still set their output format arbitrarily in their call to
247 * SetOutputFormat. */
248 for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
249 m_useable[*chan] = true;
251 else if (m_ignoreLayout)
253 for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
254 m_useable[m_outMap[out_ch]] = true;
256 else
258 /* figure out what channels we have and can use */
259 for(enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
261 for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
262 if (m_outMap[out_ch] == *chan)
264 m_useable[*chan] = true;
265 break;
270 /* see if our input has side/back channels */
271 for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
272 switch(m_inMap[in_ch])
274 case PCM_SIDE_LEFT:
275 case PCM_SIDE_RIGHT:
276 hasSide = true;
277 break;
279 case PCM_BACK_LEFT:
280 case PCM_BACK_RIGHT:
281 hasBack = true;
282 break;
284 default:;
287 /* if our input has side, and not back channels, and our output doesnt have side channels */
288 if (hasSide && !hasBack && (!m_useable[PCM_SIDE_LEFT] || !m_useable[PCM_SIDE_RIGHT]))
290 CLog::Log(LOGDEBUG, "CPCMRemap: Forcing side channel map to back channels");
291 for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
292 if (m_inMap[in_ch] == PCM_SIDE_LEFT ) m_inMap[in_ch] = PCM_BACK_LEFT;
293 else if (m_inMap[in_ch] == PCM_SIDE_RIGHT) m_inMap[in_ch] = PCM_BACK_RIGHT;
296 /* resolve all the channels */
297 struct PCMMapInfo table[PCM_MAX_CH + 1], *info, *dst;
298 std::vector<enum PCMChannels> path;
300 for (int i = 0; i < PCM_MAX_CH + 1; i++)
302 for (int j = 0; j < PCM_MAX_CH + 1; j++)
303 m_lookupMap[i][j].channel = PCM_INVALID;
306 memset(m_counts, 0, sizeof(m_counts));
307 for(in_ch = 0; in_ch < m_inChannels; ++in_ch) {
309 for (int i = 0; i < PCM_MAX_CH + 1; i++)
310 table[i].channel = PCM_INVALID;
312 ResolveChannel(m_inMap[in_ch], 1.0f, false, path, table);
313 for(info = table; info->channel != PCM_INVALID; ++info)
315 /* find the end of the table */
316 for(dst = m_lookupMap[info->channel]; dst->channel != PCM_INVALID; ++dst);
318 /* append it to the table and set its input offset */
319 dst->channel = m_inMap[in_ch];
320 dst->in_offset = in_ch * 2;
321 dst->level = info->level;
322 m_counts[dst->channel]++;
328 builds a lookup table to convert from the input mapping to the output
329 mapping, this decreases the amount of work per sample to remap it.
331 void CPCMRemap::BuildMap()
333 struct PCMMapInfo *dst;
334 unsigned int out_ch;
336 if (!m_inSet || !m_outSet) return;
338 m_inStride = m_inSampleSize * m_inChannels ;
339 m_outStride = m_inSampleSize * m_outChannels;
341 /* see if we need to normalize the levels */
342 bool dontnormalize = g_guiSettings.GetBool("audiooutput.dontnormalizelevels");
343 CLog::Log(LOGDEBUG, "CPCMRemap: Downmix normalization is %s", (dontnormalize ? "disabled" : "enabled"));
345 ResolveChannels();
347 /* convert the levels into RMS values */
348 float loudest = 0.0;
349 bool hasLoudest = false;
351 for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
353 float scale = 0;
354 int count = 0;
355 for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
357 dst->copy = false;
358 dst->level = dst->level / sqrt((float)m_counts[dst->channel]);
359 scale += dst->level;
360 ++count;
363 /* if there is only 1 channel to mix, and the level is 1.0, then just copy the channel */
364 dst = m_lookupMap[m_outMap[out_ch]];
365 if (count == 1 && dst->level > 0.99 && dst->level < 1.01)
366 dst->copy = true;
368 /* normalize the levels if it is turned on */
369 if (!dontnormalize)
370 for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
372 dst->level /= scale;
373 /* find the loudest output level we have that is not 1-1 */
374 if (dst->level < 1.0 && loudest < dst->level)
376 loudest = dst->level;
377 hasLoudest = true;
382 /* adjust the channels that are too loud */
383 for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
385 CStdString s = "", f;
386 for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
388 if (hasLoudest && dst->copy)
390 dst->level = loudest;
391 dst->copy = false;
394 f.Format("%s(%f%s) ", PCMChannelStr(dst->channel).c_str(), dst->level, dst->copy ? "*" : "");
395 s += f;
397 CLog::Log(LOGDEBUG, "CPCMRemap: %s = %s\n", PCMChannelStr(m_outMap[out_ch]).c_str(), s.c_str());
401 void CPCMRemap::DumpMap(CStdString info, unsigned int channels, enum PCMChannels *channelMap)
403 if (channelMap == NULL)
405 CLog::Log(LOGINFO, "CPCMRemap: %s channel map: NULL", info.c_str());
406 return;
409 CStdString mapping;
410 for(unsigned int i = 0; i < channels; ++i)
411 mapping += ((i == 0) ? "" : ",") + PCMChannelStr(channelMap[i]);
413 CLog::Log(LOGINFO, "CPCMRemap: %s channel map: %s\n", info.c_str(), mapping.c_str());
416 void CPCMRemap::Reset()
418 m_inSet = false;
419 m_outSet = false;
420 Dispose();
423 /* sets the input format, and returns the requested channel layout */
424 enum PCMChannels *CPCMRemap::SetInputFormat(unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize)
426 m_inChannels = channels;
427 m_inSampleSize = sampleSize;
428 m_inSet = channelMap != NULL;
429 if (channelMap)
430 memcpy(m_inMap, channelMap, sizeof(enum PCMChannels) * channels);
432 /* fix me later */
433 assert(sampleSize == 2);
435 /* get the audio layout, and count the channels in it */
436 m_channelLayout = (enum PCMLayout)g_guiSettings.GetInt("audiooutput.channellayout");
437 if (m_channelLayout >= PCM_MAX_LAYOUT) m_channelLayout = PCM_LAYOUT_2_0;
439 CLog::Log(LOGINFO, "CPCMRemap: Configured speaker layout: %s\n", PCMLayoutStr(m_channelLayout).c_str());
442 DumpMap("I", channels, channelMap);
443 BuildMap();
445 /* now remove the empty channels from PCMLayoutMap;
446 * we don't perform upmixing so we want the minimum amount of those */
447 if (channelMap) {
448 if (!m_outSet)
449 ResolveChannels(); /* Do basic channel resolving to find out the empty channels;
450 * If m_outSet == true, this was done already by BuildMap() above */
451 int i = 0;
452 for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
453 if (m_lookupMap[*chan][0].channel != PCM_INVALID) {
454 /* something is mapped here, so add the channel */
455 m_layoutMap[i++] = *chan;
457 m_layoutMap[i] = PCM_INVALID;
458 } else
459 memcpy(m_layoutMap, PCMLayoutMap[m_channelLayout], sizeof(PCMLayoutMap[m_channelLayout]));
461 return m_layoutMap;
464 /* sets the output format supported by the audio renderer */
465 void CPCMRemap::SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout/* = false */)
467 m_outChannels = channels;
468 m_outSet = channelMap != NULL;
469 m_ignoreLayout = ignoreLayout;
470 if (channelMap)
471 memcpy(m_outMap, channelMap, sizeof(enum PCMChannels) * channels);
473 DumpMap("O", channels, channelMap);
474 BuildMap();
477 /* remap the supplied data into out, which must be pre-allocated */
478 void CPCMRemap::Remap(void *data, void *out, unsigned int samples)
480 unsigned int i, ch;
481 uint8_t *insample, *outsample;
482 uint8_t *src, *dst;
484 insample = (uint8_t*)data;
485 outsample = (uint8_t*)out;
488 the output may have channels the input does not have, so zero the data
489 to stop random data being sent to them.
491 memset(out, 0, samples * (m_inSampleSize * m_outChannels));
492 for(i = 0; i < samples; ++i)
494 for(ch = 0; ch < m_outChannels; ++ch)
496 struct PCMMapInfo *info;
497 float value = 0;
499 info = m_lookupMap[m_outMap[ch]];
500 if (info->channel == PCM_INVALID) continue;
502 /* if it is a 1-1 map, we just copy the data to avoid rounding errors */
503 if (info->copy)
505 src = insample + info->in_offset;
506 dst = outsample + ch * m_inSampleSize;
507 *(int16_t*)dst = *(int16_t*)src;
508 continue;
511 for(; info->channel != PCM_INVALID; ++info)
513 src = insample + info->in_offset;
514 value += (float)(*(int16_t*)src) * info->level;
516 dst = outsample + ch * m_inSampleSize;
518 //convert to signed int and clamp to 16 bit
519 int outvalue = MathUtils::round_int(value);
520 if (outvalue > INT16_MAX)
521 outvalue = INT16_MAX;
522 else if (outvalue < INT16_MIN)
523 outvalue = INT16_MIN;
525 *(int16_t*)dst = outvalue;
528 insample += m_inStride;
529 outsample += m_outStride;
533 bool CPCMRemap::CanRemap()
535 return (m_inSet && m_outSet);
538 int CPCMRemap::InBytesToFrames(int bytes)
540 return bytes / m_inSampleSize / m_inChannels;
543 int CPCMRemap::FramesToOutBytes(int frames)
545 return frames * m_inSampleSize * m_outChannels;
548 int CPCMRemap::FramesToInBytes(int frames)
550 return frames * m_inSampleSize * m_inChannels;
553 CStdString CPCMRemap::PCMChannelStr(enum PCMChannels ename)
555 const char* PCMChannelName[] =
557 "FL",
558 "FR",
559 "CE",
560 "LFE",
561 "BL",
562 "BR",
563 "FLOC",
564 "FROC",
565 "BC",
566 "SL",
567 "SR",
568 "TFL",
569 "TFR",
570 "TFC",
571 "TC",
572 "TBL",
573 "TBR",
574 "TBC"
577 int namepos = (int)ename;
578 CStdString namestr;
580 if (namepos < 0 || namepos >= (int)(sizeof(PCMChannelName) / sizeof(const char*)))
581 namestr.Format("UNKNOWN CHANNEL:%i", namepos);
582 else
583 namestr = PCMChannelName[namepos];
585 return namestr;
588 CStdString CPCMRemap::PCMLayoutStr(enum PCMLayout ename)
590 const char* PCMLayoutName[] =
592 "2.0",
593 "2.1",
594 "3.0",
595 "3.1",
596 "4.0",
597 "4.1",
598 "5.0",
599 "5.1",
600 "7.0",
601 "7.1"
604 int namepos = (int)ename;
605 CStdString namestr;
607 if (namepos < 0 || namepos >= (int)(sizeof(PCMLayoutName) / sizeof(const char*)))
608 namestr.Format("UNKNOWN LAYOUT:%i", namepos);
609 else
610 namestr = PCMLayoutName[namepos];
612 return namestr;