changed: Improve (fallback) logic for subtitle downloading
[vuplus_xbmc] / xbmc / cores / AudioEngine / Engines / CoreAudio / CoreAudioMixMap.cpp
1 /*
2  *      Copyright (C) 2011-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
21 #include "CoreAudioMixMap.h"
22
23 #include "CoreAudioUnit.h"
24 #include "CoreAudioAEHAL.h"
25 #include "utils/log.h"
26
27
28 #include <AudioToolbox/AudioToolbox.h>
29 #include <sstream>
30
31 CCoreAudioMixMap::CCoreAudioMixMap() :
32   m_isValid(false)
33 {
34   m_pMap = (Float32*)calloc(sizeof(AudioChannelLayout), 1);
35 }
36
37 CCoreAudioMixMap::CCoreAudioMixMap(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout) :
38   m_isValid(false)
39 {
40   Rebuild(inLayout, outLayout);
41 }
42
43 CCoreAudioMixMap::~CCoreAudioMixMap()
44 {
45   free(m_pMap);
46   m_pMap = NULL;
47 }
48
49 void CCoreAudioMixMap::Rebuild(AudioChannelLayout& inLayout, AudioChannelLayout& outLayout)
50 {
51   // map[in][out] = mix-level of input_channel[in] into output_channel[out]
52
53   free(m_pMap);
54   m_pMap = NULL;
55
56   m_inChannels  = CCoreAudioChannelLayout::GetChannelCountForLayout(inLayout);
57   m_outChannels = CCoreAudioChannelLayout::GetChannelCountForLayout(outLayout);
58
59   // Try to find a 'well-known' matrix
60   const AudioChannelLayout* layouts[] = {&inLayout, &outLayout};
61   UInt32 propSize = 0;
62   AudioFormatGetPropertyInfo(kAudioFormatProperty_MatrixMixMap,
63     sizeof(layouts), layouts, &propSize);
64   m_pMap = (Float32*)calloc(1,propSize);
65
66   // Try and get a predefined mixmap
67   OSStatus ret = AudioFormatGetProperty(kAudioFormatProperty_MatrixMixMap,
68     sizeof(layouts), layouts, &propSize, m_pMap);
69   if (ret)
70   {
71     // If we for some reason don't find a predefined matrix let's build a diagonal matrix,
72     // basically guessing here, but we need to have a mixmap that matches the output and input
73     CLog::Log(LOGDEBUG, "CCoreAudioMixMap::CreateMap: No pre-defined mapping from %d to %d channels, building diagonal matrix.", m_inChannels, m_outChannels);
74     for (UInt32 chan = 0; chan < std::min(m_inChannels, m_outChannels); ++chan)
75     {
76       Float32 *vol = m_pMap + (chan * m_outChannels + chan);
77       CLog::Log(LOGDEBUG, "CCoreAudioMixMap::Rebuild %d = %f", chan, *vol);
78       *vol = 1.;
79     }
80   }
81   m_isValid = true;
82 }
83
84 CCoreAudioMixMap *CCoreAudioMixMap::CreateMixMap(CAUOutputDevice  *audioUnit, AEAudioFormat &format, AudioChannelLayoutTag layoutTag)
85 {
86   if (!audioUnit)
87     return NULL;
88
89   AudioStreamBasicDescription fmt;
90   AudioStreamBasicDescription inputFormat;
91
92   // get the stream input format
93   audioUnit->GetFormatDesc(format, &inputFormat, &fmt);
94
95   unsigned int channels = format.m_channelLayout.Count();
96   CAEChannelInfo channelLayout = format.m_channelLayout;
97   bool hasLFE = false;
98   // Convert XBMC input channel layout format to CoreAudio layout format
99   AudioChannelLayout* pInLayout = (AudioChannelLayout*)malloc(sizeof(AudioChannelLayout) + sizeof(AudioChannelDescription) * channels);
100   pInLayout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
101   pInLayout->mChannelBitmap = 0;
102   pInLayout->mNumberChannelDescriptions = channels;
103   for (unsigned int chan=0; chan < channels; chan++)
104   {
105     AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
106     // Convert from XBMC channel tag to CoreAudio channel tag
107     pDesc->mChannelLabel = g_LabelMap[(unsigned int)channelLayout[chan]];
108     pDesc->mChannelFlags = kAudioChannelFlags_AllOff;
109     pDesc->mCoordinates[0] = 0.0f;
110     pDesc->mCoordinates[1] = 0.0f;
111     pDesc->mCoordinates[2] = 0.0f;
112     if (pDesc->mChannelLabel == kAudioChannelLabel_LFEScreen)
113       hasLFE = true;
114   }
115   // HACK: Fix broken channel layouts coming from some aac sources
116   // that include rear channel but no side channels.
117   // 5.1 streams should include front and side channels.
118   // Rear channels are added by 6.1 and 7.1, so any 5.1
119   // source that claims to have rear channels is wrong.
120   if (inputFormat.mChannelsPerFrame == 6 && hasLFE)
121   {
122     // Check for 5.1 configuration (as best we can without getting too silly)
123     for (unsigned int chan=0; chan < inputFormat.mChannelsPerFrame; chan++)
124     {
125       AudioChannelDescription* pDesc = &pInLayout->mChannelDescriptions[chan];
126       if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurround || pDesc->mChannelLabel == kAudioChannelLabel_RightSurround)
127         break; // Required condition cannot be true
128
129       if (pDesc->mChannelLabel == kAudioChannelLabel_LeftSurroundDirect)
130       {
131         // Change [Back Left] to [Side Left]
132         pDesc->mChannelLabel = kAudioChannelLabel_LeftSurround;
133         CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: "
134           "Detected faulty input channel map...fixing(Back Left-->Side Left)");
135       }
136       if (pDesc->mChannelLabel == kAudioChannelLabel_RightSurroundDirect)
137       {
138         // Change [Back Left] to [Side Left]
139         pDesc->mChannelLabel = kAudioChannelLabel_RightSurround;
140         CLog::Log(LOGINFO, "CCoreAudioGraph::CreateMixMap: "
141           "Detected faulty input channel map...fixing(Back Right-->Side Right)");
142       }
143     }
144   }
145
146   CCoreAudioChannelLayout sourceLayout(*pInLayout);
147   free(pInLayout);
148   pInLayout = NULL;
149
150   std::string strInLayout;
151   CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: Source Stream Layout: %s",
152     CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)sourceLayout, strInLayout));
153
154   // Get User-Configured (XBMC) Speaker Configuration
155   AudioChannelLayout guiLayout;
156   guiLayout.mChannelLayoutTag = layoutTag;
157   CCoreAudioChannelLayout userLayout(guiLayout);
158   std::string strUserLayout;
159   CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: User-Configured Speaker Layout: %s",
160     CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)userLayout, strUserLayout));
161
162   // Get OS-Configured (Audio MIDI Setup) Speaker Configuration (Channel Layout)
163   CCoreAudioChannelLayout deviceLayout;
164   if (!audioUnit->GetPreferredChannelLayout(deviceLayout))
165     return NULL;
166
167   // When all channels on the output device are unknown take the gui layout
168   //if(deviceLayout.AllChannelUnknown())
169   //  deviceLayout.CopyLayout(guiLayout);
170
171   std::string strOutLayout;
172   CLog::Log(LOGDEBUG, "CCoreAudioGraph::CreateMixMap: Output Device Layout: %s",
173     CCoreAudioChannelLayout::ChannelLayoutToString(*(AudioChannelLayout*)deviceLayout, strOutLayout));
174
175   // TODO:
176   // Reconcile the OS and GUI layout configurations. Clamp to the minimum number of speakers
177   // For each OS-defined output, see if it exists in the GUI configuration
178   // If it does, add it to the 'union' layout (bitmap?)
179   // User may have configured 5.1 in GUI, but only 2.0 in OS
180   // Resulting layout would be {FL, FR}
181   // User may have configured 2.0 in GUI, and 5.1 in OS
182   // Resulting layout would be {FL, FR}
183
184   // Correct any configuration incompatibilities
185   //if (CCoreAudioChannelLayout::GetChannelCountForLayout(guiLayout) < CCoreAudioChannelLayout::GetChannelCountForLayout(deviceLayout))
186   //  deviceLayout.CopyLayout(guiLayout);
187
188   // TODO: Skip matrix mixer if input/output are compatible
189   CCoreAudioMixMap *mixMap = new CCoreAudioMixMap();
190   mixMap->Rebuild(*sourceLayout, *(AudioChannelLayout*)deviceLayout);
191   return mixMap;
192 }
193
194 bool CCoreAudioMixMap::SetMixingMatrix(CAUMatrixMixer *mixerUnit,
195   CCoreAudioMixMap *mixMap, AudioStreamBasicDescription *inputFormat,
196   AudioStreamBasicDescription *fmt, int channelOffset)
197 {
198   if (!mixerUnit || !inputFormat || !fmt)
199     return false;
200
201   // Fetch the mixing unit size
202   UInt32 dims[2];
203   UInt32 size = sizeof(dims);
204   AudioUnitGetProperty(mixerUnit->GetUnit(),
205     kAudioUnitProperty_MatrixDimensions, kAudioUnitScope_Global, 0, dims, &size);
206
207   if(inputFormat->mChannelsPerFrame + channelOffset > dims[0])
208   {
209     CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - input format doesn't fit mixer size %u+%u > %u"
210                       , inputFormat->mChannelsPerFrame, channelOffset, dims[0]);
211     return false;
212   }
213
214   if(fmt->mChannelsPerFrame > dims[1])
215   {
216     CLog::Log(LOGERROR, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't fit mixer size %u > %u"
217               , fmt->mChannelsPerFrame, dims[1]);
218     return false;
219   }
220
221   if(fmt->mChannelsPerFrame < dims[1])
222   {
223     CLog::Log(LOGWARNING, "CCoreAudioMixMap::SetMixingMatrix - ouput format doesn't specify all outputs %u < %u"
224               , fmt->mChannelsPerFrame, dims[1]);
225   }
226
227   // Configure the mixing matrix
228   // The return from kAudioFormatProperty_MatrixMixMap (See Rebuild above) 
229   // is a Float32* which is laid out like this:
230   //
231   // mapping 2 chan -> 2 chan
232   // 1 0 0 1
233   //
234   // or better represented in a tow dimensional array:
235   //
236   // 1 0
237   // 0 1
238   //
239   // mapping 6 chan -> 6 chan:
240   // 1 0 0 0 0 0
241   // 0 1 0 0 0 0
242   // 0 0 1 0 0 0
243   // ....
244
245   Float32* val = (Float32*)*mixMap;
246   for (UInt32 i = 0; i < inputFormat->mChannelsPerFrame; ++i)
247   {
248     UInt32 j = 0;
249     std::stringstream layoutStr;
250     for (; j < fmt->mChannelsPerFrame; ++j)
251     {
252       Float32 *vol = val + (i * mixMap->m_outChannels + j);
253       layoutStr << *vol << ", ";
254       AudioUnitSetParameter(mixerUnit->GetUnit(),
255         kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, *vol, 0);
256     }
257     // zero out additional outputs from this input
258     for (; j < dims[1]; ++j)
259     {
260       AudioUnitSetParameter(mixerUnit->GetUnit(),
261         kMatrixMixerParam_Volume, kAudioUnitScope_Global, ( (i + channelOffset) << 16 ) | j, 0.0f, 0);
262       layoutStr << "0, ";
263     }
264
265     CLog::Log(LOGDEBUG, "CCoreAudioMixMap::SetMixingMatrix channel %d = [%s]", i, layoutStr.str().c_str());
266   }
267
268   CLog::Log(LOGDEBUG, "CCoreAudioGraph::Open: "
269     "Mixer Output Format: %d channels, %0.1f kHz, %d bits, %d bytes per frame",
270     (int)fmt->mChannelsPerFrame, fmt->mSampleRate / 1000.0f, (int)fmt->mBitsPerChannel, (int)fmt->mBytesPerFrame);
271
272   if (!mixerUnit->InitMatrixMixerVolumes())
273     return false;
274
275   return true;
276 }
277