[WASAPI] set stream audio category
[xbmc.git] / xbmc / cores / paplayer / AudioDecoder.cpp
blob96075fd67e2c00b8e1dc0177c531a37c6003c461
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "AudioDecoder.h"
11 #include "CodecFactory.h"
12 #include "FileItem.h"
13 #include "ICodec.h"
14 #include "ServiceBroker.h"
15 #include "application/ApplicationComponents.h"
16 #include "application/ApplicationVolumeHandling.h"
17 #include "music/tags/MusicInfoTag.h"
18 #include "settings/Settings.h"
19 #include "settings/SettingsComponent.h"
20 #include "utils/URIUtils.h"
21 #include "utils/log.h"
23 #include <cmath>
24 #include <mutex>
26 using namespace KODI;
28 CAudioDecoder::CAudioDecoder()
30 m_codec = NULL;
31 m_rawBuffer = nullptr;
33 m_eof = false;
35 m_status = STATUS_NO_FILE;
36 m_canPlay = false;
38 // output buffer (for transferring data from the Pcm Buffer to the rest of the audio chain)
39 memset(&m_outputBuffer, 0, OUTPUT_SAMPLES * sizeof(float));
40 memset(&m_pcmInputBuffer, 0, INPUT_SIZE * sizeof(unsigned char));
41 memset(&m_inputBuffer, 0, INPUT_SAMPLES * sizeof(float));
43 m_rawBufferSize = 0;
46 CAudioDecoder::~CAudioDecoder()
48 Destroy();
51 void CAudioDecoder::Destroy()
53 std::unique_lock<CCriticalSection> lock(m_critSection);
54 m_status = STATUS_NO_FILE;
56 m_pcmBuffer.Destroy();
58 if ( m_codec )
59 delete m_codec;
60 m_codec = NULL;
62 m_canPlay = false;
65 bool CAudioDecoder::Create(const CFileItem &file, int64_t seekOffset)
67 Destroy();
69 std::unique_lock<CCriticalSection> lock(m_critSection);
71 // reset our playback timing variables
72 m_eof = false;
74 // get correct cache size
75 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
76 unsigned int filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_INTERNET);
77 if ( file.IsHD() )
78 filecache = settings->GetInt(CSettings::SETTING_CACHE_HARDDISK);
79 else if ( file.IsOnDVD() )
80 filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_DVDROM);
81 else if (URIUtils::IsOnLAN(file.GetPath()))
82 filecache = settings->GetInt(CSettings::SETTING_CACHEAUDIO_LAN);
84 // create our codec
85 m_codec=CodecFactory::CreateCodecDemux(file, filecache * 1024);
87 if (!m_codec || !m_codec->Init(file, filecache * 1024))
89 CLog::Log(LOGERROR, "CAudioDecoder: Unable to Init Codec while loading file {}",
90 file.GetDynPath());
91 Destroy();
92 return false;
94 unsigned int blockSize = (m_codec->m_bitsPerSample >> 3) * m_codec->m_format.m_channelLayout.Count();
96 if (blockSize == 0)
98 CLog::Log(LOGERROR, "CAudioDecoder: Codec provided invalid parameters ({}-bit, {} channels)",
99 m_codec->m_bitsPerSample, GetFormat().m_channelLayout.Count());
100 return false;
103 /* allocate the pcmBuffer for 2 seconds of audio */
104 m_pcmBuffer.Create(2 * blockSize * m_codec->m_format.m_sampleRate);
106 if (file.HasMusicInfoTag())
108 // set total time from the given tag
109 if (file.GetMusicInfoTag()->GetDuration())
110 m_codec->SetTotalTime(file.GetMusicInfoTag()->GetDuration());
112 // update ReplayGain from the given tag if it's better then original (cuesheet)
113 ReplayGain rgInfo = m_codec->m_tag.GetReplayGain();
114 bool anySet = false;
115 if (!rgInfo.Get(ReplayGain::ALBUM).Valid()
116 && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM).Valid())
118 rgInfo.Set(ReplayGain::ALBUM, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::ALBUM));
119 anySet = true;
121 if (!rgInfo.Get(ReplayGain::TRACK).Valid()
122 && file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK).Valid())
124 rgInfo.Set(ReplayGain::TRACK, file.GetMusicInfoTag()->GetReplayGain().Get(ReplayGain::TRACK));
125 anySet = true;
127 if (anySet)
128 m_codec->m_tag.SetReplayGain(rgInfo);
131 if (seekOffset)
132 m_codec->Seek(seekOffset);
134 m_status = STATUS_QUEUING;
136 m_rawBufferSize = 0;
138 return true;
141 AEAudioFormat CAudioDecoder::GetFormat()
143 AEAudioFormat format;
144 if (!m_codec)
145 return format;
146 return m_codec->m_format;
149 unsigned int CAudioDecoder::GetChannels()
151 return GetFormat().m_channelLayout.Count();
154 int64_t CAudioDecoder::Seek(int64_t time)
156 m_pcmBuffer.Clear();
157 m_rawBufferSize = 0;
158 if (!m_codec)
159 return 0;
160 if (time < 0) time = 0;
161 if (time > m_codec->m_TotalTime) time = m_codec->m_TotalTime;
162 return m_codec->Seek(time);
165 void CAudioDecoder::SetTotalTime(int64_t time)
167 if (m_codec)
168 m_codec->m_TotalTime = time;
171 int64_t CAudioDecoder::TotalTime()
173 if (m_codec)
174 return m_codec->m_TotalTime;
175 return 0;
178 unsigned int CAudioDecoder::GetDataSize(bool checkPktSize)
180 if (m_status == STATUS_QUEUING || m_status == STATUS_NO_FILE)
181 return 0;
183 if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
185 // check for end of file and end of buffer
186 if (m_status == STATUS_ENDING)
188 if (m_pcmBuffer.getMaxReadSize() == 0)
189 m_status = STATUS_ENDED;
190 else if (checkPktSize && m_pcmBuffer.getMaxReadSize() < PACKET_SIZE)
191 m_status = STATUS_ENDED;
193 return std::min(m_pcmBuffer.getMaxReadSize() / (m_codec->m_bitsPerSample >> 3), (unsigned int)OUTPUT_SAMPLES);
195 else
197 if (m_status == STATUS_ENDING)
198 m_status = STATUS_ENDED;
199 return m_rawBufferSize;
203 void *CAudioDecoder::GetData(unsigned int samples)
205 unsigned int size = samples * (m_codec->m_bitsPerSample >> 3);
206 if (size > sizeof(m_outputBuffer))
208 CLog::Log(LOGERROR, "CAudioDecoder::GetData - More data was requested then we have space to buffer!");
209 return NULL;
212 if (size > m_pcmBuffer.getMaxReadSize())
214 CLog::Log(
215 LOGWARNING,
216 "CAudioDecoder::GetData() more bytes/samples ({}) requested than we have to give ({})!",
217 size, m_pcmBuffer.getMaxReadSize());
218 size = m_pcmBuffer.getMaxReadSize();
221 if (m_pcmBuffer.ReadData((char *)m_outputBuffer, size))
223 if (m_status == STATUS_ENDING && m_pcmBuffer.getMaxReadSize() == 0)
224 m_status = STATUS_ENDED;
226 return m_outputBuffer;
229 CLog::Log(LOGERROR, "CAudioDecoder::GetData() ReadBinary failed with {} samples", samples);
230 return NULL;
233 uint8_t *CAudioDecoder::GetRawData(int &size)
235 if (m_status == STATUS_ENDING)
236 m_status = STATUS_ENDED;
238 if (m_rawBufferSize)
240 size = m_rawBufferSize;
241 m_rawBufferSize = 0;
242 return m_rawBuffer;
244 return nullptr;
247 int CAudioDecoder::ReadSamples(int numsamples)
249 if (m_status == STATUS_NO_FILE || m_status == STATUS_ENDING || m_status == STATUS_ENDED)
250 return RET_SLEEP; // nothing loaded yet
252 // start playing once we're fully queued and we're ready to go
253 if (m_status == STATUS_QUEUED && m_canPlay)
254 m_status = STATUS_PLAYING;
256 // grab a lock to ensure the codec is created at this point.
257 std::unique_lock<CCriticalSection> lock(m_critSection);
259 if (m_codec->m_format.m_dataFormat != AE_FMT_RAW)
261 // Read in more data
262 int maxsize = std::min<int>(INPUT_SAMPLES, m_pcmBuffer.getMaxWriteSize() / (m_codec->m_bitsPerSample >> 3));
263 numsamples = std::min<int>(numsamples, maxsize);
264 numsamples -= (numsamples % GetFormat().m_channelLayout.Count()); // make sure it's divisible by our number of channels
265 if (numsamples)
267 size_t readSize = 0;
268 int result = m_codec->ReadPCM(
269 m_pcmInputBuffer, static_cast<size_t>(numsamples * (m_codec->m_bitsPerSample >> 3)),
270 &readSize);
272 if (result != READ_ERROR && readSize)
274 // move it into our buffer
275 m_pcmBuffer.WriteData((char *)m_pcmInputBuffer, readSize);
277 // update status
278 if (m_status == STATUS_QUEUING && m_pcmBuffer.getMaxReadSize() > m_pcmBuffer.getSize() * 0.9)
280 CLog::Log(LOGINFO, "AudioDecoder: File is queued");
281 m_status = STATUS_QUEUED;
284 if (result == READ_EOF) // EOF reached
286 // setup ending if we're within set time of the end (currently just EOF)
287 m_eof = true;
288 if (m_status < STATUS_ENDING)
289 m_status = STATUS_ENDING;
292 return RET_SUCCESS;
294 if (result == READ_ERROR)
296 // error decoding, lets finish up and get out
297 CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding {}", result);
298 return RET_ERROR;
300 if (result == READ_EOF)
302 m_eof = true;
303 // setup ending if we're within set time of the end (currently just EOF)
304 if (m_status < STATUS_ENDING)
305 m_status = STATUS_ENDING;
309 else
311 if (m_rawBufferSize == 0)
313 int result = m_codec->ReadRaw(&m_rawBuffer, &m_rawBufferSize);
314 if (result == READ_SUCCESS && m_rawBufferSize)
316 //! @todo trash this useless ringbuffer
317 if (m_status == STATUS_QUEUING)
319 m_status = STATUS_QUEUED;
321 return RET_SUCCESS;
323 else if (result == READ_ERROR)
325 // error decoding, lets finish up and get out
326 CLog::Log(LOGERROR, "CAudioDecoder: Error while decoding {}", result);
327 return RET_ERROR;
329 else if (result == READ_EOF)
331 m_eof = true;
332 // setup ending if we're within set time of the end (currently just EOF)
333 if (m_status < STATUS_ENDING)
334 m_status = STATUS_ENDING;
338 return RET_SLEEP; // nothing to do
341 bool CAudioDecoder::CanSeek()
343 if (m_codec)
344 return m_codec->CanSeek();
345 else
346 return false;
349 float CAudioDecoder::GetReplayGain(float &peakVal)
351 #define REPLAY_GAIN_DEFAULT_LEVEL 89.0f
352 auto& components = CServiceBroker::GetAppComponents();
353 const auto appVolume = components.GetComponent<CApplicationVolumeHandling>();
355 const auto& replayGainSettings = appVolume->GetReplayGainSettings();
356 if (replayGainSettings.iType == ReplayGain::NONE)
357 return 1.0f;
359 // Compute amount of gain
360 float replaydB = (float)replayGainSettings.iNoGainPreAmp;
361 float peak = 1.0f;
362 const ReplayGain& rgInfo = m_codec->m_tag.GetReplayGain();
363 if (replayGainSettings.iType == ReplayGain::ALBUM)
365 if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
367 replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
368 if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
369 peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
371 else if (rgInfo.Get(ReplayGain::TRACK).HasGain())
373 replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
374 if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
375 peak = rgInfo.Get(ReplayGain::TRACK).Peak();
378 else if (replayGainSettings.iType == ReplayGain::TRACK)
380 if (rgInfo.Get(ReplayGain::TRACK).HasGain())
382 replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::TRACK).Gain();
383 if (rgInfo.Get(ReplayGain::TRACK).HasPeak())
384 peak = rgInfo.Get(ReplayGain::TRACK).Peak();
386 else if (rgInfo.Get(ReplayGain::ALBUM).HasGain())
388 replaydB = (float)replayGainSettings.iPreAmp + rgInfo.Get(ReplayGain::ALBUM).Gain();
389 if (rgInfo.Get(ReplayGain::ALBUM).HasPeak())
390 peak = rgInfo.Get(ReplayGain::ALBUM).Peak();
393 // convert to a gain type
394 float replaygain = std::pow(10.0f, (replaydB - REPLAY_GAIN_DEFAULT_LEVEL) * 0.05f);
396 CLog::Log(LOGDEBUG,
397 "AudioDecoder::GetReplayGain - Final Replaygain applied: {:f}, Track/Album Gain {:f}, "
398 "Peak {:f}",
399 replaygain, replaydB, peak);
401 peakVal = peak;
402 return replaygain;