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