1 /*
2  *  Copyright (C) 2010-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "AEChannelInfo.h"
10 
11 #include <algorithm>
12 #include <assert.h>
13 #include <limits>
14 #include <string.h>
15 
CAEChannelInfo()16 CAEChannelInfo::CAEChannelInfo()
17 {
18   Reset();
19 }
20 
CAEChannelInfo(const enum AEChannel * rhs)21 CAEChannelInfo::CAEChannelInfo(const enum AEChannel* rhs)
22 {
23   *this = rhs;
24 }
25 
CAEChannelInfo(const AEStdChLayout rhs)26 CAEChannelInfo::CAEChannelInfo(const AEStdChLayout rhs)
27 {
28   *this = rhs;
29 }
30 
ResolveChannels(const CAEChannelInfo & rhs)31 void CAEChannelInfo::ResolveChannels(const CAEChannelInfo& rhs)
32 {
33   /* mono gets upmixed to dual mono */
34   if (m_channelCount == 1 && m_channels[0] == AE_CH_FC)
35   {
36     Reset();
37     *this += AE_CH_FL;
38     *this += AE_CH_FR;
39     return;
40   }
41 
42   bool srcHasSL = false;
43   bool srcHasSR = false;
44   bool srcHasRL = false;
45   bool srcHasRR = false;
46   bool srcHasBC = false;
47 
48   bool dstHasSL = false;
49   bool dstHasSR = false;
50   bool dstHasRL = false;
51   bool dstHasRR = false;
52   bool dstHasBC = false;
53 
54   for (unsigned int c = 0; c < rhs.m_channelCount; ++c)
55     switch(rhs.m_channels[c])
56     {
57       case AE_CH_SL: dstHasSL = true; break;
58       case AE_CH_SR: dstHasSR = true; break;
59       case AE_CH_BL: dstHasRL = true; break;
60       case AE_CH_BR: dstHasRR = true; break;
61       case AE_CH_BC: dstHasBC = true; break;
62       default:
63         break;
64     }
65 
66   CAEChannelInfo newInfo;
67   for (unsigned int i = 0; i < m_channelCount; ++i)
68   {
69     switch (m_channels[i])
70     {
71       case AE_CH_SL: srcHasSL = true; break;
72       case AE_CH_SR: srcHasSR = true; break;
73       case AE_CH_BL: srcHasRL = true; break;
74       case AE_CH_BR: srcHasRR = true; break;
75       case AE_CH_BC: srcHasBC = true; break;
76       default:
77         break;
78     }
79 
80     bool found = false;
81     for (unsigned int c = 0; c < rhs.m_channelCount; ++c)
82       if (m_channels[i] == rhs.m_channels[c])
83       {
84         found = true;
85         break;
86       }
87 
88     if (found)
89       newInfo += m_channels[i];
90   }
91 
92   /* we need to ensure we end up with rear or side channels for downmix to work */
93   if (srcHasSL && !dstHasSL && dstHasRL && !newInfo.HasChannel(AE_CH_BL))
94     newInfo += AE_CH_BL;
95   if (srcHasSR && !dstHasSR && dstHasRR && !newInfo.HasChannel(AE_CH_BR))
96     newInfo += AE_CH_BR;
97   if (srcHasRL && !dstHasRL && dstHasSL && !newInfo.HasChannel(AE_CH_SL))
98     newInfo += AE_CH_SL;
99   if (srcHasRR && !dstHasRR && dstHasSR && !newInfo.HasChannel(AE_CH_SR))
100     newInfo += AE_CH_SR;
101 
102   // mix back center if not available in destination layout
103   // prefer mixing into backs if available
104   if (srcHasBC && !dstHasBC)
105   {
106     if (dstHasRL && !newInfo.HasChannel(AE_CH_BL))
107       newInfo += AE_CH_BL;
108     else if (dstHasSL && !newInfo.HasChannel(AE_CH_SL))
109       newInfo += AE_CH_SL;
110 
111     if (dstHasRR && !newInfo.HasChannel(AE_CH_BR))
112       newInfo += AE_CH_BR;
113     else if (dstHasSR && !newInfo.HasChannel(AE_CH_SR))
114       newInfo += AE_CH_SR;
115   }
116 
117   *this = newInfo;
118 }
119 
Reset()120 void CAEChannelInfo::Reset()
121 {
122   m_channelCount = 0;
123   for (AEChannel& channel : m_channels)
124     channel = AE_CH_NULL;
125 }
126 
operator =(const CAEChannelInfo & rhs)127 CAEChannelInfo& CAEChannelInfo::operator=(const CAEChannelInfo& rhs)
128 {
129   if (this == &rhs)
130     return *this;
131 
132   /* clone the information */
133   m_channelCount = rhs.m_channelCount;
134   memcpy(m_channels, rhs.m_channels, sizeof(enum AEChannel) * rhs.m_channelCount);
135 
136   return *this;
137 }
138 
operator =(const enum AEChannel * rhs)139 CAEChannelInfo& CAEChannelInfo::operator=(const enum AEChannel* rhs)
140 {
141   Reset();
142   if (rhs == NULL)
143     return *this;
144 
145   while (m_channelCount < AE_CH_MAX && rhs[m_channelCount] != AE_CH_NULL)
146   {
147     m_channels[m_channelCount] = rhs[m_channelCount];
148     ++m_channelCount;
149   }
150 
151   /* the last entry should be NULL, if not we were passed a non null terminated list */
152   assert(rhs[m_channelCount] == AE_CH_NULL);
153 
154   return *this;
155 }
156 
operator =(const enum AEStdChLayout rhs)157 CAEChannelInfo& CAEChannelInfo::operator=(const enum AEStdChLayout rhs)
158 {
159   assert(rhs > AE_CH_LAYOUT_INVALID && rhs < AE_CH_LAYOUT_MAX);
160 
161   static enum AEChannel layouts[AE_CH_LAYOUT_MAX][9] = {
162     {AE_CH_FC, AE_CH_NULL},
163     {AE_CH_FL, AE_CH_FR, AE_CH_NULL},
164     {AE_CH_FL, AE_CH_FR, AE_CH_LFE, AE_CH_NULL},
165     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_NULL},
166     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_LFE, AE_CH_NULL},
167     {AE_CH_FL, AE_CH_FR, AE_CH_BL , AE_CH_BR , AE_CH_NULL},
168     {AE_CH_FL, AE_CH_FR, AE_CH_BL , AE_CH_BR , AE_CH_LFE, AE_CH_NULL},
169     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_NULL},
170     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_LFE,  AE_CH_BL , AE_CH_BR , AE_CH_NULL},
171     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_BL , AE_CH_BR , AE_CH_SL , AE_CH_SR, AE_CH_NULL},
172     {AE_CH_FL, AE_CH_FR, AE_CH_FC , AE_CH_LFE, AE_CH_BL , AE_CH_BR , AE_CH_SL , AE_CH_SR, AE_CH_NULL}
173   };
174 
175   *this = layouts[rhs];
176   return *this;
177 }
178 
operator ==(const CAEChannelInfo & rhs) const179 bool CAEChannelInfo::operator==(const CAEChannelInfo& rhs) const
180 {
181   /* if the channel count doesn't match, no need to check further */
182   if (m_channelCount != rhs.m_channelCount)
183     return false;
184 
185   /* make sure the channel order is the same */
186   for (unsigned int i = 0; i < m_channelCount; ++i)
187     if (m_channels[i] != rhs.m_channels[i])
188       return false;
189 
190   return true;
191 }
192 
operator !=(const CAEChannelInfo & rhs) const193 bool CAEChannelInfo::operator!=(const CAEChannelInfo& rhs) const
194 {
195   return !(*this == rhs);
196 }
197 
operator +=(const enum AEChannel & rhs)198 CAEChannelInfo& CAEChannelInfo::operator+=(const enum AEChannel& rhs)
199 {
200   assert(m_channelCount < AE_CH_MAX);
201   assert(rhs > AE_CH_NULL && rhs < AE_CH_MAX);
202 
203   m_channels[m_channelCount++] = rhs;
204   return *this;
205 }
206 
operator -=(const enum AEChannel & rhs)207 CAEChannelInfo& CAEChannelInfo::operator-=(const enum AEChannel& rhs)
208 {
209   assert(rhs > AE_CH_NULL && rhs < AE_CH_MAX);
210 
211   unsigned int i = 0;
212   while(i < m_channelCount && m_channels[i] != rhs)
213     i++;
214   if (i >= m_channelCount)
215     return *this; // Channel not found
216 
217   for(; i < m_channelCount-1; i++)
218     m_channels[i] = m_channels[i+1];
219 
220   m_channels[i] = AE_CH_NULL;
221   m_channelCount--;
222   return *this;
223 }
224 
operator [](unsigned int i) const225 enum AEChannel CAEChannelInfo::operator[](unsigned int i) const
226 {
227   assert(i < m_channelCount);
228   return m_channels[i];
229 }
230 
operator std::string() const231 CAEChannelInfo::operator std::string() const
232 {
233   if (m_channelCount == 0)
234     return "NULL";
235 
236   std::string s;
237   for (unsigned int i = 0; i < m_channelCount - 1; ++i)
238   {
239     s.append(GetChName(m_channels[i]));
240     s.append(", ");
241   }
242   s.append(GetChName(m_channels[m_channelCount-1]));
243 
244   return s;
245 }
246 
IsChannelValid(const unsigned int pos)247 bool CAEChannelInfo::IsChannelValid(const unsigned int pos)
248 {
249   assert(pos < m_channelCount);
250   bool isValid = false;
251   if (m_channels[pos] > AE_CH_NULL && m_channels[pos] < AE_CH_UNKNOWN1)
252     isValid = true;
253 
254   return isValid;
255 }
256 
IsLayoutValid()257 bool CAEChannelInfo::IsLayoutValid()
258 {
259   if (m_channelCount == 0)
260     return false;
261 
262   for (unsigned int i = 0; i < m_channelCount; ++i)
263   {
264     // we need at least one valid channel
265     if (IsChannelValid(i))
266       return true;
267   }
268   return false;
269 }
270 
GetChName(const enum AEChannel ch)271 const char* CAEChannelInfo::GetChName(const enum AEChannel ch)
272 {
273   assert(ch >= 0 && ch < AE_CH_MAX);
274 
275   static const char* channels[AE_CH_MAX] =
276   {
277     "RAW" ,
278     "FL"  , "FR" , "FC" , "LFE", "BL"  , "BR"  , "FLOC",
279     "FROC", "BC" , "SL" , "SR" , "TFL" , "TFR" , "TFC" ,
280     "TC"  , "TBL", "TBR", "TBC", "BLOC", "BROC",
281 
282     /* p16v devices */
283     "UNKNOWN1" , "UNKNOWN2" , "UNKNOWN3" , "UNKNOWN4" ,
284     "UNKNOWN5" , "UNKNOWN6" , "UNKNOWN7" , "UNKNOWN8" ,
285     "UNKNOWN9" , "UNKNOWN10", "UNKNOWN11", "UNKNOWN12",
286     "UNKNOWN13", "UNKNOWN14", "UNKNOWN15", "UNKNOWN16",
287     "UNKNOWN17", "UNKNOWN18", "UNKNOWN19", "UNKNOWN20",
288     "UNKNOWN21", "UNKNOWN22", "UNKNOWN23", "UNKNOWN24",
289     "UNKNOWN25", "UNKNOWN26", "UNKNOWN27", "UNKNOWN28",
290     "UNKNOWN29", "UNKNOWN30", "UNKNOWN31", "UNKNOWN32",
291     "UNKNOWN33", "UNKNOWN34", "UNKNOWN35", "UNKNOWN36",
292     "UNKNOWN37", "UNKNOWN38", "UNKNOWN39", "UNKNOWN40",
293     "UNKNOWN41", "UNKNOWN42", "UNKNOWN43", "UNKNOWN44",
294     "UNKNOWN45", "UNKNOWN46", "UNKNOWN47", "UNKNOWN48",
295     "UNKNOWN49", "UNKNOWN50", "UNKNOWN51", "UNKNOWN52",
296     "UNKNOWN53", "UNKNOWN54", "UNKNOWN55", "UNKNOWN56",
297     "UNKNOWN57", "UNKNOWN58", "UNKNOWN59", "UNKNOWN60",
298     "UNKNOWN61", "UNKNOWN62", "UNKNOWN63", "UNKNOWN64"
299   };
300 
301   return channels[ch];
302 }
303 
HasChannel(const enum AEChannel ch) const304 bool CAEChannelInfo::HasChannel(const enum AEChannel ch) const
305 {
306   for (unsigned int i = 0; i < m_channelCount; ++i)
307     if (m_channels[i] == ch)
308       return true;
309   return false;
310 }
311 
ContainsChannels(const CAEChannelInfo & rhs) const312 bool CAEChannelInfo::ContainsChannels(const CAEChannelInfo& rhs) const
313 {
314   for (unsigned int i = 0; i < rhs.m_channelCount; ++i)
315   {
316     if (!HasChannel(rhs.m_channels[i]))
317       return false;
318   }
319   return true;
320 }
321 
ReplaceChannel(const enum AEChannel from,const enum AEChannel to)322 void CAEChannelInfo::ReplaceChannel(const enum AEChannel from, const enum AEChannel to)
323 {
324   for (unsigned int i = 0; i < m_channelCount; ++i)
325   {
326     if (m_channels[i] == from)
327     {
328       m_channels[i] = to;
329       break;
330     }
331   }
332 }
333 
BestMatch(const std::vector<CAEChannelInfo> & dsts,int * score) const334 int CAEChannelInfo::BestMatch(const std::vector<CAEChannelInfo>& dsts, int* score) const
335 {
336   CAEChannelInfo availableDstChannels;
337   for (unsigned int i = 0; i < dsts.size(); i++)
338     availableDstChannels.AddMissingChannels(dsts[i]);
339 
340   /* if we have channels not existing in any destination layout but that
341    * are remappable (e.g. RC => RL+RR), do those remaps */
342   CAEChannelInfo src(*this);
343   src.ResolveChannels(availableDstChannels);
344 
345   bool remapped = (src != *this);
346   /* good enough approximation (does not account for added channels) */
347   int dropped = std::max((int)src.Count() - (int)Count(), 0);
348 
349   int bestScore = std::numeric_limits<int>::min();
350   int bestMatch = -1;
351 
352   for (unsigned int i = 0; i < dsts.size(); i++)
353   {
354     const CAEChannelInfo& dst = dsts[i];
355     int okChannels = 0;
356 
357     for (unsigned int j = 0; j < src.Count(); j++)
358       okChannels += dst.HasChannel(src[j]);
359 
360     int missingChannels = src.Count() - okChannels;
361     int extraChannels = dst.Count() - okChannels;
362 
363     int curScore = 0 - (missingChannels + dropped) * 1000 - extraChannels * 10 - (remapped ? 1 : 0);
364 
365     if (curScore > bestScore)
366     {
367       bestScore = curScore;
368       bestMatch = i;
369       if (curScore == 0)
370         break;
371     }
372   }
373 
374   if (score)
375     *score = bestScore;
376 
377   return bestMatch;
378 }
379 
AddMissingChannels(const CAEChannelInfo & rhs)380 void CAEChannelInfo::AddMissingChannels(const CAEChannelInfo& rhs)
381 {
382   for (unsigned int i = 0; i < rhs.Count(); i++)
383     if (!HasChannel(rhs[i]))
384       *this += rhs[i];
385 }
386