2 * Copyright (C) 2005-2010 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, 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
29 #include "MathUtils.h"
31 #include "utils/log.h"
32 #include "GUISettings.h"
34 #include "../win32/PlatformDefs.h"
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
57 static struct PCMMapInfo PCMDownmixTable
[PCM_MAX_CH
][PCM_MAX_MIX
] =
67 /* PCM_FRONT_CENTER */
69 {PCM_FRONT_LEFT_OF_CENTER
, 1.0},
70 {PCM_FRONT_RIGHT_OF_CENTER
, 1.0},
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},
86 {PCM_FRONT_LEFT
, 1.0},
91 {PCM_FRONT_RIGHT
, 1.0},
94 /* PCM_FRONT_LEFT_OF_CENTER */
96 {PCM_FRONT_LEFT
, 1.0},
97 {PCM_FRONT_CENTER
, 1.0, true},
100 /* PCM_FRONT_RIGHT_OF_CENTER */
102 {PCM_FRONT_RIGHT
, 1.0},
103 {PCM_FRONT_CENTER
, 1.0, true},
106 /* PCM_BACK_CENTER */
108 {PCM_BACK_LEFT
, 1.0},
109 {PCM_BACK_RIGHT
, 1.0},
114 {PCM_FRONT_LEFT
, 1.0},
115 {PCM_BACK_LEFT
, 1.0},
120 {PCM_FRONT_RIGHT
, 1.0},
121 {PCM_BACK_RIGHT
, 1.0},
124 /* PCM_TOP_FRONT_LEFT */
126 {PCM_FRONT_LEFT
, 1.0},
129 /* PCM_TOP_FRONT_RIGHT */
131 {PCM_FRONT_RIGHT
, 1.0},
134 /* PCM_TOP_FRONT_CENTER */
136 {PCM_TOP_FRONT_LEFT
, 1.0},
137 {PCM_TOP_FRONT_RIGHT
, 1.0},
142 {PCM_TOP_FRONT_LEFT
, 1.0},
143 {PCM_TOP_FRONT_RIGHT
, 1.0},
146 /* PCM_TOP_BACK_LEFT */
148 {PCM_BACK_LEFT
, 1.0},
151 /* PCM_TOP_BACK_RIGHT */
153 {PCM_BACK_LEFT
, 1.0},
156 /* PCM_TOP_BACK_CENTER */
158 {PCM_TOP_BACK_LEFT
, 1.0},
159 {PCM_TOP_BACK_RIGHT
, 1.0},
164 CPCMRemap::CPCMRemap() :
174 CPCMRemap::~CPCMRemap()
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
;
195 tablePtr
->channel
= PCM_INVALID
;
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 */
208 for(itt
= path
.begin(); itt
!= path
.end(); ++itt
)
209 if (*itt
== info
->channel
)
218 path
.push_back(channel
);
219 float l
= (info
->level
* (level
/ 100)) * 100;
220 tablePtr
= ResolveChannel(info
->channel
, l
, info
->ifExists
, path
, 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
));
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;
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;
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
])
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
;
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"));
347 /* convert the levels into RMS values */
349 bool hasLoudest
= false;
351 for(out_ch
= 0; out_ch
< m_outChannels
; ++out_ch
)
355 for(dst
= m_lookupMap
[m_outMap
[out_ch
]]; dst
->channel
!= PCM_INVALID
; ++dst
)
358 dst
->level
= dst
->level
/ sqrt((float)m_counts
[dst
->channel
]);
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)
368 /* normalize the levels if it is turned on */
370 for(dst
= m_lookupMap
[m_outMap
[out_ch
]]; dst
->channel
!= PCM_INVALID
; ++dst
)
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
;
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
;
394 f
.Format("%s(%f%s) ", PCMChannelStr(dst
->channel
).c_str(), dst
->level
, dst
->copy
? "*" : "");
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());
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()
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
;
430 memcpy(m_inMap
, channelMap
, sizeof(enum PCMChannels
) * channels
);
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
);
445 /* now remove the empty channels from PCMLayoutMap;
446 * we don't perform upmixing so we want the minimum amount of those */
449 ResolveChannels(); /* Do basic channel resolving to find out the empty channels;
450 * If m_outSet == true, this was done already by BuildMap() above */
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
;
459 memcpy(m_layoutMap
, PCMLayoutMap
[m_channelLayout
], sizeof(PCMLayoutMap
[m_channelLayout
]));
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
;
471 memcpy(m_outMap
, channelMap
, sizeof(enum PCMChannels
) * channels
);
473 DumpMap("O", channels
, channelMap
);
477 /* remap the supplied data into out, which must be pre-allocated */
478 void CPCMRemap::Remap(void *data
, void *out
, unsigned int samples
)
481 uint8_t *insample
, *outsample
;
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
;
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 */
505 src
= insample
+ info
->in_offset
;
506 dst
= outsample
+ ch
* m_inSampleSize
;
507 *(int16_t*)dst
= *(int16_t*)src
;
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
[] =
577 int namepos
= (int)ename
;
580 if (namepos
< 0 || namepos
>= (int)(sizeof(PCMChannelName
) / sizeof(const char*)))
581 namestr
.Format("UNKNOWN CHANNEL:%i", namepos
);
583 namestr
= PCMChannelName
[namepos
];
588 CStdString
CPCMRemap::PCMLayoutStr(enum PCMLayout ename
)
590 const char* PCMLayoutName
[] =
604 int namepos
= (int)ename
;
607 if (namepos
< 0 || namepos
>= (int)(sizeof(PCMLayoutName
) / sizeof(const char*)))
608 namestr
.Format("UNKNOWN LAYOUT:%i", namepos
);
610 namestr
= PCMLayoutName
[namepos
];