1 /*
2  *  Copyright (C) 2005-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 "AudioDecoder.h"
10 
11 #include "Application.h"
12 #include "CodecFactory.h"
13 #include "FileItem.h"
14 #include "ServiceBroker.h"
15 #include "music/tags/MusicInfoTag.h"
16 #include "settings/Settings.h"
17 #include "settings/SettingsComponent.h"
18 #include "threads/SingleLock.h"
19 #include "utils/log.h"
20 
21 #include <math.h>
22 
CAudioDecoder()23 CAudioDecoder::CAudioDecoder()
24 {
25   m_codec = NULL;
26   m_rawBuffer = nullptr;
27 
28   m_eof = false;
29 
30   m_status = STATUS_NO_FILE;
31   m_canPlay = false;
32 
33   // output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain)
34   memset(&m_outputBuffer, 0, OUTPUT_SAMPLES * sizeof(float));
35   memset(&m_pcmInputBuffer, 0, INPUT_SIZE * sizeof(unsigned char));
36   memset(&m_inputBuffer, 0, INPUT_SAMPLES * sizeof(float));
37 
38   m_rawBufferSize = 0;
39 }
40 
~CAudioDecoder()41 CAudioDecoder::~CAudioDecoder()
42 {
43   Destroy();
44 }
45 
Destroy()46 void CAudioDecoder::Destroy()
47 {
48   CSingleLock lock(m_critSection);
49   m_status = STATUS_NO_FILE;
50 
51   m_pcmBuffer.Destroy();
52 
53   if ( m_codec )
54     delete m_codec;
55   m_codec = NULL;
56 
57   m_canPlay = false;
58 }
59 
Create(const CFileItem & file,int64_t seekOffset)60 bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset)
61 {
62   Destroy();
63 
64   CSingleLock lock(m_critSection);
65 
66   // reset our playback timing variables
67   m_eof = false;
68 
69   // get correct cache size
70   const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
71   unsigned int filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_INTERNET);
72   if ( file.IsHD() )
73     filecache = settings->GetInt(CSettings::SETTING_CACHE_HARDDISK);
74   else if ( file.IsOnDVD() )
75     filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_DVDROM);
76   else if ( file.IsOnLAN() )
77     filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_LAN);
78 
79   // create our codec
80   m_codec=CodecFactory::CreateCodecDemux(file, filecache * 1024);
81 
82   if (!m_codec || !m_codec->Init(file, filecache * 1024))
83   {
84     CLog::Log(LOGERROR, "CAudioDecoder: Unable to Init Codec while loading file %s", file.GetDynPath().c_str());
85     Destroy();
86     return false;
87   }
88   unsigned int blockSize = (m_codec->m_bitsPerSample >> 3) * m_codec->m_format.m_channelLayout.Count();
89 
90   if (blockSize == 0)
91   {
92     CLog::Log(LOGERROR, "CAudioDecoder: Codec provided invalid parameters (%d-bit, %u channels)",
93               m_codec->m_bitsPerSample, GetFormat().m_channelLayout.Count());
94     return false;
95   }
96 
97   /* allocate the pcmBuffer for 2 seconds of audio */
98   m_pcmBuffer.Create(2 * blockSize * m_codec->m_format.m_sampleRate);
99 
100   if (file.HasMusicInfoTag())
101   {
102     // set total time from the given tag
103     if (file.GetMusicInfoTag()->GetDuration())
104       m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration());
105 
106     // update ReplayGain from the given tag if it's better then original (cuesheet)
107     ReplayGain rgInfo = m_codec->m_tag.GetReplayGain();
108     bool anySet = false;
109     if (!rgInfo.Get(ReplayGain::ALBUM).Valid()
110       && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM).Valid())
111     {
112       rgInfo.Set(ReplayGain::ALBUM, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM));
113       anySet = true;
114     }
115     if (!rgInfo.Get(ReplayGain::TRACK).Valid()
116       && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK).Valid())
117     {
118       rgInfo.Set(ReplayGain::TRACK, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK));
119       anySet = true;
120     }
121     if (anySet)
122       m_codec->m_tag.SetReplayGain(rgInfo);
123   }
124 
125   if (seekOffset)
126     m_codec->Seek(seekOffset);
127 
128   m_status = STATUS_QUEUING;
129 
130   m_rawBufferSize = 0;
131 
132   return true;
133 }
134 
GetFormat()135 AEAudioFormat CAudioDecoder::GetFormat()
136 {
137   AEAudioFormat format;
138   if (!m_codec)
139     return format;
140   return m_codec->m_format;
141 }
142 
Seek(int64_t time)143 int64_t CAudioDecoder::Seek(int64_t time)
144 {
145   m_pcmBuffer.Clear();
146   m_rawBufferSize = 0;
147   if (!m_codec)
148     return 0;
149   if (time < 0) time = 0;
150   if (time > m_codec->m_TotalTime) time = m_codec->m_TotalTime;
151   return m_codec->Seek(time);
152 }
153 
SetTotalTime(int64_t time)154 void CAudioDecoder::SetTotalTime(int64_t time)
155 {
156   if (m_codec)
157     m_codec->m_TotalTime = time;
158 }
159 
TotalTime()160 int64_t CAudioDecoder::TotalTime()
161 {
162   if (m_codec)
163     return m_codec->m_TotalTime;
164   return 0;
165 }
166 
GetDataSize(bool checkPktSize)167 unsigned int CAudioDecoder::GetDataSize(bool checkPktSize)
168 {
169   if (m_status == STATUS_QUEUING || m_status == STATUS_NO_FILE)
170     return 0;
171 
172   if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
173   {
174     // check for end of file and end of buffer
175     if (m_status == STATUS_ENDING)
176     {
177       if (m_pcmBuffer.getMaxReadSize() == 0)
178         m_status = STATUS_ENDED;
179       else if (checkPktSize && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE)
180         m_status = STATUS_ENDED;
181     }
182     return std::min(m_pcmBuffer.getMaxReadSize() / (m_codec->m_bitsPerSample >> 3), (unsigned int)OUTPUT_SAMPLES);
183   }
184   else
185   {
186     if (m_status == STATUS_ENDING)
187       m_status = STATUS_ENDED;
188     return m_rawBufferSize;
189   }
190 }
191 
GetData(unsigned int samples)192 void *CAudioDecoder::GetData(unsigned int samples)
193 {
194   unsigned int size  = samples * (m_codec->m_bitsPerSample >> 3);
195   if (size > sizeof(m_outputBuffer))
196   {
197     CLog::Log(LOGERROR, "CAudioDecoder::GetData - More data was requested then we have space to buffer!");
198     return NULL;
199   }
200 
201   if (size > m_pcmBuffer.getMaxReadSize())
202   {
203     CLog::Log(LOGWARNING, "CAudioDecoder::GetData() more bytes/samples (%i) requested than we have to give (%i)!", size, m_pcmBuffer.getMaxReadSize());
204     size = m_pcmBuffer.getMaxReadSize();
205   }
206 
207   if (m_pcmBuffer.ReadData((char *)m_outputBuffer, size))
208   {
209     if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() == 0)
210       m_status = STATUS_ENDED;
211 
212     return m_outputBuffer;
213   }
214 
215   CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with %i samples", samples);
216   return NULL;
217 }
218 
GetRawData(int & size)219 uint8_t *CAudioDecoder::GetRawData(int &size)
220 {
221   if (m_status == STATUS_ENDING)
222     m_status = STATUS_ENDED;
223 
224   if (m_rawBufferSize)
225   {
226     size = m_rawBufferSize;
227     m_rawBufferSize = 0;
228     return m_rawBuffer;
229   }
230   return nullptr;
231 }
232 
ReadSamples(int numsamples)233 int CAudioDecoder::ReadSamples(int numsamples)
234 {
235   if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED)
236     return RET_SLEEP;             // nothing loaded yet
237 
238   // start playing once we're fully queued and we're ready to go
239   if (m_status == STATUS_QUEUED && m_canPlay)
240     m_status = STATUS_PLAYING;
241 
242   // grab a lock to ensure the codec is created at this point.
243   CSingleLock lock(m_critSection);
244 
245   if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
246   {
247     // Read in more data
248     int maxsize = std::min<int>(INPUT_SAMPLES, m_pcmBuffer.getMaxWriteSize() / (m_codec->m_bitsPerSample >> 3));
249     numsamples = std::min<int>(numsamples, maxsize);
250     numsamples -= (numsamples % GetFormat().m_channelLayout.Count());  // make sure it's divisible by our number of channels
251     if (numsamples)
252     {
253       int readSize = 0;
254       int result = m_codec->ReadPCM(m_pcmInputBuffer, numsamples * (m_codec->m_bitsPerSample >> 3), &readSize);
255 
256       if (result != READ_ERROR && readSize)
257       {
258         // move it into our buffer
259         m_pcmBuffer.WriteData((char *)m_pcmInputBuffer, readSize);
260 
261         // update status
262         if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9)
263         {
264           CLog::Log(LOGINFO, "AudioDecoder: File is queued");
265           m_status = STATUS_QUEUED;
266         }
267 
268         if (result == READ_EOF) // EOF reached
269         {
270           // setup ending if we're within set time of the end (currently just EOF)
271           m_eof = true;
272           if (m_status < STATUS_ENDING)
273             m_status = STATUS_ENDING;
274         }
275 
276         return RET_SUCCESS;
277       }
278       if (result == READ_ERROR)
279       {
280         // error decoding, lets finish up and get out
281         CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding %i", result);
282         return RET_ERROR;
283       }
284       if (result == READ_EOF)
285       {
286         m_eof = true;
287         // setup ending if we're within set time of the end (currently just EOF)
288         if (m_status < STATUS_ENDING)
289           m_status = STATUS_ENDING;
290       }
291     }
292   }
293   else
294   {
295     if (m_rawBufferSize == 0)
296     {
297       int result = m_codec->ReadRaw(&m_rawBuffer, &m_rawBufferSize);
298       if (result == READ_SUCCESS && m_rawBufferSize)
299       {
300         //! @todo trash this useless ringbuffer
301         if (m_status == STATUS_QUEUING)
302         {
303           m_status = STATUS_QUEUED;
304         }
305         return RET_SUCCESS;
306       }
307       else if (result == READ_ERROR)
308       {
309         // error decoding, lets finish up and get out
310         CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding %i", result);
311         return RET_ERROR;
312       }
313       else if (result == READ_EOF)
314       {
315         m_eof = true;
316         // setup ending if we're within set time of the end (currently just EOF)
317         if (m_status < STATUS_ENDING)
318           m_status = STATUS_ENDING;
319       }
320     }
321   }
322   return RET_SLEEP; // nothing to do
323 }
324 
GetReplayGain(float & peakVal)325 float CAudioDecoder::GetReplayGain(float &peakVal)
326 {
327 #define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
328   const ReplayGainSettings &replayGainSettings = g_application.GetReplayGainSettings();
329   if (replayGainSettings.iType == ReplayGain::NONE)
330     return 1.0f;
331 
332   // Compute amount of gain
333   float replaydB = (float)replayGainSettings.iNoGainPreAmp;
334   float peak = 1.0f;
335   const ReplayGain& rgInfo = m_codec->m_tag.GetReplayGain();
336   if (replayGainSettings.iType == ReplayGain::ALBUM)
337   {
338     if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
339     {
340       replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
341       if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
342         peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
343     }
344     else if (rgInfo.Get(ReplayGain::TRACK).HasGain())
345     {
346       replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
347       if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
348         peak = rgInfo.Get(ReplayGain::TRACK).Peak();
349     }
350   }
351   else if (replayGainSettings.iType == ReplayGain::TRACK)
352   {
353     if (rgInfo.Get(ReplayGain::TRACK).HasGain())
354     {
355       replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
356       if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
357         peak = rgInfo.Get(ReplayGain::TRACK).Peak();
358     }
359     else if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
360     {
361       replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
362       if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
363         peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
364     }
365   }
366   // convert to a gain type
367   float replaygain = pow(10.0f, (replaydB - REPLAY_GAIN_DEFAULT_LEVEL)* 0.05f);
368 
369   CLog::Log(LOGDEBUG, "AudioDecoder::GetReplayGain - Final Replaygain applied: %f, Track/Album Gain %f, Peak %f", replaygain, replaydB, peak);
370 
371   peakVal = peak;
372   return replaygain;
373 }
374 
375