2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
22 #include "CodecFactory.h"
24 #include "settings/AdvancedSettings.h"
25 #include "settings/Settings.h"
26 #include "music/tags/MusicInfoTag.h"
27 #include "utils/TimeUtils.h"
28 #include "utils/log.h"
29 #include "utils/MathUtils.h"
30 #include "utils/JobManager.h"
32 #include "threads/SingleLock.h"
33 #include "cores/AudioEngine/AEFactory.h"
34 #include "cores/AudioEngine/Utils/AEUtil.h"
35 #include "cores/AudioEngine/Interfaces/AEStream.h"
37 #define TIME_TO_CACHE_NEXT_FILE 5000 /* 5 seconds before end of song, start caching the next song */
38 #define FAST_XFADE_TIME 80 /* 80 milliseconds */
39 #define MAX_SKIP_XFADE_TIME 2000 /* max 2 seconds crossfade on track skip */
41 CAEChannelInfo ICodec::GetChannelInfo()
43 return CAEUtil::GuessChLayout(m_Channels);
46 class CQueueNextFileJob : public CJob
52 CQueueNextFileJob(const CFileItem& item, PAPlayer &player)
53 : m_item(item), m_player(player) {}
54 virtual ~CQueueNextFileJob() {}
57 return m_player.QueueNextFileEx(m_item, true, true);
61 // PAP: Psycho-acoustic Audio Player
62 // Supporting all open audio codec standards.
63 // First one being nullsoft's nsv audio decoder format
65 PAPlayer::PAPlayer(IPlayerCallback& callback) :
68 m_signalSpeedChange (false),
73 m_defaultCrossfadeMS (0),
74 m_upcomingCrossfadeMS(0),
75 m_currentStream (NULL ),
76 m_audioCallback (NULL ),
77 m_FileItem (new CFileItem()),
79 m_continueStream (false)
81 memset(&m_playerGUIData, 0, sizeof(m_playerGUIData));
90 bool PAPlayer::HandlesType(const CStdString &type)
92 ICodec* codec = CodecFactory::CreateCodec(type);
93 if (codec && codec->CanInit())
102 void PAPlayer::SoftStart(bool wait/* = false */)
104 CSharedLock lock(m_streamsLock);
105 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
107 StreamInfo* si = *itt;
108 if (si->m_fadeOutTriggered)
111 si->m_stream->Resume();
112 si->m_stream->FadeVolume(0.0f, 1.0f, FAST_XFADE_TIME);
117 /* wait for them to fade in */
119 Sleep(FAST_XFADE_TIME);
122 /* be sure they have faded in */
126 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
128 StreamInfo* si = *itt;
129 if (si->m_stream->IsFading())
142 void PAPlayer::SoftStop(bool wait/* = false */, bool close/* = true */)
144 /* fade all the streams out fast for a nice soft stop */
145 CSharedLock lock(m_streamsLock);
146 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
148 StreamInfo* si = *itt;
150 si->m_stream->FadeVolume(1.0f, 0.0f, FAST_XFADE_TIME);
154 si->m_prepareTriggered = true;
155 si->m_playNextTriggered = true;
156 si->m_fadeOutTriggered = true;
160 /* if we are going to wait for them to finish fading */
163 /* wait for them to fade out */
165 Sleep(FAST_XFADE_TIME);
168 /* be sure they have faded out */
169 while(wait && !CAEFactory::IsSuspended())
172 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
174 StreamInfo* si = *itt;
175 if (si->m_stream && si->m_stream->IsFading())
186 /* if we are not closing the streams, pause them */
189 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
191 StreamInfo* si = *itt;
192 si->m_stream->Pause();
198 void PAPlayer::CloseAllStreams(bool fade/* = true */)
202 CExclusiveLock lock(m_streamsLock);
203 while(!m_streams.empty())
205 StreamInfo* si = m_streams.front();
206 m_streams.pop_front();
210 CAEFactory::FreeStream(si->m_stream);
214 si->m_decoder.Destroy();
218 while(!m_finishing.empty())
220 StreamInfo* si = m_finishing.front();
221 m_finishing.pop_front();
225 CAEFactory::FreeStream(si->m_stream);
229 si->m_decoder.Destroy();
232 m_currentStream = NULL;
236 SoftStop(false, true);
237 CExclusiveLock lock(m_streamsLock);
238 m_currentStream = NULL;
242 bool PAPlayer::OpenFile(const CFileItem& file, const CPlayerOptions &options)
244 m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
246 if (m_streams.size() > 1 || !m_defaultCrossfadeMS || m_isPaused)
248 CloseAllStreams(!m_isPaused);
250 m_isPaused = false; // Make sure to reset the pause state
253 if (!QueueNextFileEx(file, false))
256 CSharedLock lock(m_streamsLock);
257 if (m_streams.size() == 2)
259 //do a short crossfade on trackskip, set to max 2 seconds for these prev/next transitions
260 m_upcomingCrossfadeMS = std::min(m_defaultCrossfadeMS, (unsigned int)MAX_SKIP_XFADE_TIME);
262 //start transition to next track
263 StreamInfo* si = m_streams.front();
264 si->m_playNextAtFrame = si->m_framesSent; //start next track at current frame
265 si->m_prepareTriggered = true; //next track is ready to go
272 /* trigger playback start */
278 void PAPlayer::UpdateCrossfadeTime(const CFileItem& file)
280 // we explicitely disable crossfading for audio cds
282 m_upcomingCrossfadeMS = 0;
284 m_upcomingCrossfadeMS = m_defaultCrossfadeMS = CSettings::Get().GetInt("musicplayer.crossfade") * 1000;
285 if (m_upcomingCrossfadeMS)
287 if (m_streams.size() == 0 ||
289 file.HasMusicInfoTag() && !CSettings::Get().GetBool("musicplayer.crossfadealbumtracks") &&
290 m_FileItem->HasMusicInfoTag() &&
291 (m_FileItem->GetMusicInfoTag()->GetAlbum() != "") &&
292 (m_FileItem->GetMusicInfoTag()->GetAlbum() == file.GetMusicInfoTag()->GetAlbum()) &&
293 (m_FileItem->GetMusicInfoTag()->GetDiscNumber() == file.GetMusicInfoTag()->GetDiscNumber()) &&
294 (m_FileItem->GetMusicInfoTag()->GetTrackNumber() == file.GetMusicInfoTag()->GetTrackNumber() - 1)
298 //do not crossfade when playing consecutive albumtracks
299 m_upcomingCrossfadeMS = 0;
304 bool PAPlayer::QueueNextFile(const CFileItem &file)
307 CExclusiveLock lock(m_streamsLock);
310 CJobManager::GetInstance().AddJob(new CQueueNextFileJob(file, *this), this, CJob::PRIORITY_NORMAL);
314 bool PAPlayer::QueueNextFileEx(const CFileItem &file, bool fadeIn/* = true */, bool job /* = false */)
316 StreamInfo *si = new StreamInfo();
318 // check if we advance a track of a CUE sheet
319 // if this is the case we don't need to open a new stream
320 std::string newURL = file.GetMusicInfoTag() ? file.GetMusicInfoTag()->GetURL() : file.GetPath();
321 std::string oldURL = m_FileItem->GetMusicInfoTag() ? m_FileItem->GetMusicInfoTag()->GetURL() : m_FileItem->GetPath();
322 if (newURL.compare(oldURL) == 0 &&
323 file.m_lStartOffset &&
324 file.m_lStartOffset == m_FileItem->m_lEndOffset &&
325 m_currentStream && m_currentStream->m_prepareTriggered)
327 m_continueStream = true;
328 m_upcomingCrossfadeMS = 0;
334 m_continueStream = false;
337 if (!si->m_decoder.Create(file, (file.m_lStartOffset * 1000) / 75))
339 CLog::Log(LOGWARNING, "PAPlayer::QueueNextFileEx - Failed to create the decoder");
344 m_callback.OnPlayBackStarted();
345 m_callback.OnQueueNextItem();
349 /* decode until there is data-available */
350 si->m_decoder.Start();
351 while(si->m_decoder.GetDataSize() == 0)
353 int status = si->m_decoder.GetStatus();
354 if (status == STATUS_ENDED ||
355 status == STATUS_NO_FILE ||
356 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
358 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error reading samples");
360 si->m_decoder.Destroy();
364 m_callback.OnPlayBackStarted();
365 m_callback.OnQueueNextItem();
369 /* yield our time so that the main PAP thread doesnt stall */
373 /* init the streaminfo struct */
374 si->m_decoder.GetDataFormat(&si->m_channelInfo, &si->m_sampleRate, &si->m_encodedSampleRate, &si->m_dataFormat);
375 si->m_startOffset = file.m_lStartOffset * 1000 / 75;
376 si->m_endOffset = file.m_lEndOffset * 1000 / 75;
377 si->m_bytesPerSample = CAEUtil::DataFormatToBits(si->m_dataFormat) >> 3;
378 si->m_bytesPerFrame = si->m_bytesPerSample * si->m_channelInfo.Count();
379 si->m_started = false;
380 si->m_finishing = false;
381 si->m_framesSent = 0;
382 si->m_seekNextAtFrame = 0;
383 si->m_seekFrame = -1;
385 si->m_volume = (fadeIn && m_upcomingCrossfadeMS) ? 0.0f : 1.0f;
386 si->m_fadeOutTriggered = false;
387 si->m_isSlaved = false;
389 int64_t streamTotalTime = si->m_decoder.TotalTime();
391 streamTotalTime = si->m_endOffset - si->m_startOffset;
393 si->m_prepareNextAtFrame = 0;
394 // cd drives don't really like it to be crossfaded or prepared
397 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
398 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
401 if (m_currentStream && (AE_IS_RAW(m_currentStream->m_dataFormat) || AE_IS_RAW(si->m_dataFormat)))
403 m_currentStream->m_prepareTriggered = false;
404 m_currentStream->m_waitOnDrain = true;
405 m_currentStream->m_prepareNextAtFrame = 0;
406 si->m_decoder.Destroy();
411 UpdateCrossfadeTime(file);
413 si->m_prepareTriggered = false;
414 si->m_playNextAtFrame = 0;
415 si->m_playNextTriggered = false;
416 si->m_waitOnDrain = false;
418 if (!PrepareStream(si))
420 CLog::Log(LOGINFO, "PAPlayer::QueueNextFileEx - Error preparing stream");
422 si->m_decoder.Destroy();
426 m_callback.OnPlayBackStarted();
427 m_callback.OnQueueNextItem();
431 /* add the stream to the list */
432 CExclusiveLock lock(m_streamsLock);
433 m_streams.push_back(si);
434 //update the current stream to start playing the next track at the correct frame.
435 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
442 void PAPlayer::UpdateStreamInfoPlayNextAtFrame(StreamInfo *si, unsigned int crossFadingTime)
446 int64_t streamTotalTime = si->m_decoder.TotalTime();
448 streamTotalTime = si->m_endOffset - si->m_startOffset;
449 if (streamTotalTime < crossFadingTime)
450 si->m_playNextAtFrame = (int)((streamTotalTime / 2) * si->m_sampleRate / 1000.0f);
452 si->m_playNextAtFrame = (int)((streamTotalTime - crossFadingTime) * si->m_sampleRate / 1000.0f);
456 inline bool PAPlayer::PrepareStream(StreamInfo *si)
458 /* if we have a stream we are already prepared */
462 /* get a paused stream */
463 si->m_stream = CAEFactory::MakeStream(
466 si->m_encodedSampleRate,
473 CLog::Log(LOGDEBUG, "PAPlayer::PrepareStream - Failed to get IAEStream");
477 si->m_stream->SetVolume (si->m_volume);
478 si->m_stream->SetReplayGain(si->m_decoder.GetReplayGain());
480 /* if its not the first stream and crossfade is not enabled */
481 if (m_currentStream && m_currentStream != si && !m_upcomingCrossfadeMS)
483 /* slave the stream for gapless */
484 si->m_isSlaved = true;
485 m_currentStream->m_stream->RegisterSlave(si->m_stream);
488 /* fill the stream's buffer */
489 while(si->m_stream->IsBuffering())
491 int status = si->m_decoder.GetStatus();
492 if (status == STATUS_ENDED ||
493 status == STATUS_NO_FILE ||
494 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR)
496 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Stream Finished");
503 /* yield our time so that the main PAP thread doesnt stall */
507 CLog::Log(LOGINFO, "PAPlayer::PrepareStream - Ready");
512 bool PAPlayer::CloseFile(bool reopen)
515 CAEFactory::KeepConfiguration(3000);
518 SoftStop(true, true);
519 CloseAllStreams(false);
521 /* wait for the thread to terminate */
522 StopThread(true);//true - wait for end of thread
526 void PAPlayer::Process()
528 if (!m_startEvent.WaitMSec(100))
530 CLog::Log(LOGDEBUG, "PAPlayer::Process - Failed to receive start event");
534 CLog::Log(LOGDEBUG, "PAPlayer::Process - Playback started");
535 while(m_isPlaying && !m_bStop)
537 /* this needs to happen outside of any locks to prevent deadlocks */
538 if (m_signalSpeedChange)
540 m_callback.OnPlayBackSpeedChanged(m_playbackSpeed);
541 m_signalSpeedChange = false;
544 double freeBufferTime = 0.0;
545 ProcessStreams(freeBufferTime);
547 // if none of our streams wants at least 10ms of data, we sleep
548 if (freeBufferTime < 0.01)
553 GetTimeInternal(); //update for GUI
556 // wait for any pending jobs to complete
558 CSharedLock lock(m_streamsLock);
559 while (m_jobCounter > 0)
562 m_jobEvent.WaitMSec(100);
567 if(m_isFinished && !m_bStop)
568 m_callback.OnPlayBackEnded();
570 m_callback.OnPlayBackStopped();
573 inline void PAPlayer::ProcessStreams(double &freeBufferTime)
575 CSharedLock sharedLock(m_streamsLock);
576 if (m_isFinished && m_streams.empty() && m_finishing.empty())
579 freeBufferTime = 1.0;
583 /* destroy any drained streams */
584 for(StreamList::iterator itt = m_finishing.begin(); itt != m_finishing.end();)
586 StreamInfo* si = *itt;
587 if (si->m_stream->IsDrained())
589 itt = m_finishing.erase(itt);
590 CAEFactory::FreeStream(si->m_stream);
592 CLog::Log(LOGDEBUG, "PAPlayer::ProcessStreams - Stream Freed");
599 CExclusiveLock lock(m_streamsLock);
601 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
603 StreamInfo* si = *itt;
604 if (!m_currentStream && !si->m_started)
606 m_currentStream = si;
607 UpdateGUIData(si); //update for GUI
609 /* if the stream is finishing */
610 if ((si->m_playNextTriggered && si->m_stream && !si->m_stream->IsFading()) || !ProcessStream(si, freeBufferTime))
612 if (!si->m_prepareTriggered)
614 if (si->m_waitOnDrain)
616 si->m_stream->Drain(true);
617 si->m_waitOnDrain = false;
619 si->m_prepareTriggered = true;
620 m_callback.OnQueueNextItem();
623 /* remove the stream */
624 itt = m_streams.erase(itt);
625 /* if its the current stream */
626 if (si == m_currentStream)
628 /* if it was the last stream */
629 if (itt == m_streams.end())
631 /* if it didnt trigger the next queue item */
632 if (!si->m_prepareTriggered)
634 if (si->m_waitOnDrain)
636 si->m_stream->Drain(true);
637 si->m_waitOnDrain = false;
639 m_callback.OnQueueNextItem();
640 si->m_prepareTriggered = true;
642 m_currentStream = NULL;
646 m_currentStream = *itt;
647 UpdateGUIData(*itt); //update for GUI
651 /* unregister the audio callback */
652 si->m_stream->UnRegisterAudioCallback();
653 si->m_decoder.Destroy();
654 si->m_stream->Drain(false);
655 m_finishing.push_back(si);
662 /* is it time to prepare the next stream? */
663 if (si->m_prepareNextAtFrame > 0 && !si->m_prepareTriggered && si->m_framesSent >= si->m_prepareNextAtFrame)
665 si->m_prepareTriggered = true;
666 m_callback.OnQueueNextItem();
669 /* it is time to start playing the next stream? */
670 if (si->m_playNextAtFrame > 0 && !si->m_playNextTriggered && !m_continueStream && si->m_framesSent >= si->m_playNextAtFrame)
672 if (!si->m_prepareTriggered)
674 si->m_prepareTriggered = true;
675 m_callback.OnQueueNextItem();
680 if (m_upcomingCrossfadeMS)
682 si->m_stream->FadeVolume(1.0f, 0.0f, m_upcomingCrossfadeMS);
683 si->m_fadeOutTriggered = true;
685 m_currentStream = NULL;
687 /* unregister the audio callback */
688 si->m_stream->UnRegisterAudioCallback();
691 si->m_playNextTriggered = true;
696 inline bool PAPlayer::ProcessStream(StreamInfo *si, double &freeBufferTime)
698 /* if playback needs to start on this stream, do it */
699 if (si == m_currentStream && !si->m_started)
701 si->m_started = true;
702 si->m_stream->RegisterAudioCallback(m_audioCallback);
704 si->m_stream->Resume();
705 si->m_stream->FadeVolume(0.0f, 1.0f, m_upcomingCrossfadeMS);
706 m_callback.OnPlayBackStarted();
709 /* if we have not started yet and the stream has been primed */
710 unsigned int space = si->m_stream->GetSpace();
711 if (!si->m_started && !space)
714 /* see if it is time yet to FF/RW or a direct seek */
715 if (!si->m_playNextTriggered && ((m_playbackSpeed != 1 && si->m_framesSent >= si->m_seekNextAtFrame) || si->m_seekFrame > -1))
717 int64_t time = (int64_t)0;
718 /* if its a direct seek */
719 if (si->m_seekFrame > -1)
721 time = (int64_t)((float)si->m_seekFrame / (float)si->m_sampleRate * 1000.0f);
722 si->m_framesSent = (int)(si->m_seekFrame - ((float)si->m_startOffset * (float)si->m_sampleRate) / 1000.0f);
723 si->m_seekFrame = -1;
724 m_playerGUIData.m_time = time; //update for GUI
725 si->m_seekNextAtFrame = 0;
730 si->m_framesSent += si->m_sampleRate * (m_playbackSpeed - 1);
731 si->m_seekNextAtFrame = si->m_framesSent + si->m_sampleRate / 2;
732 time = (int64_t)(((float)si->m_framesSent / (float)si->m_sampleRate * 1000.0f) + (float)si->m_startOffset);
735 /* if we are seeking back before the start of the track start normal playback */
736 if (time < si->m_startOffset || si->m_framesSent < 0)
738 time = si->m_startOffset;
739 si->m_framesSent = 0;
740 si->m_seekNextAtFrame = 0;
744 si->m_decoder.Seek(time);
747 int status = si->m_decoder.GetStatus();
748 if (status == STATUS_ENDED ||
749 status == STATUS_NO_FILE ||
750 si->m_decoder.ReadSamples(PACKET_SIZE) == RET_ERROR ||
751 ((si->m_endOffset) && (si->m_framesSent / si->m_sampleRate >= (si->m_endOffset - si->m_startOffset) / 1000)))
753 if (si == m_currentStream && m_continueStream)
755 // update current stream with info of next track
756 si->m_startOffset = m_FileItem->m_lStartOffset * 1000 / 75;
757 if (m_FileItem->m_lEndOffset)
758 si->m_endOffset = m_FileItem->m_lEndOffset * 1000 / 75;
761 si->m_framesSent = 0;
763 int64_t streamTotalTime = si->m_decoder.TotalTime() - si->m_startOffset;
765 streamTotalTime = si->m_endOffset - si->m_startOffset;
767 // calculate time when to prepare next stream
768 si->m_prepareNextAtFrame = 0;
769 if (streamTotalTime >= TIME_TO_CACHE_NEXT_FILE + m_defaultCrossfadeMS)
770 si->m_prepareNextAtFrame = (int)((streamTotalTime - TIME_TO_CACHE_NEXT_FILE - m_defaultCrossfadeMS) * si->m_sampleRate / 1000.0f);
772 si->m_prepareTriggered = false;
773 si->m_playNextAtFrame = 0;
774 si->m_playNextTriggered = false;
775 si->m_seekNextAtFrame = 0;
777 //update the current stream to start playing the next track at the correct frame.
778 UpdateStreamInfoPlayNextAtFrame(m_currentStream, m_upcomingCrossfadeMS);
781 m_callback.OnPlayBackStarted();
782 m_continueStream = false;
786 CLog::Log(LOGINFO, "PAPlayer::ProcessStream - Stream Finished");
794 /* update free buffer time if we are running */
797 if (si->m_stream->IsBuffering())
798 freeBufferTime = 1.0;
801 double free_space = (double)(si->m_stream->GetSpace() / si->m_bytesPerSample) / si->m_sampleRate;
802 freeBufferTime = std::max(freeBufferTime , free_space);
809 bool PAPlayer::QueueData(StreamInfo *si)
811 unsigned int space = si->m_stream->GetSpace();
812 unsigned int samples = std::min(si->m_decoder.GetDataSize(), space / si->m_bytesPerSample);
816 void* data = si->m_decoder.GetData(samples);
819 CLog::Log(LOGERROR, "PAPlayer::QueueData - Failed to get data from the decoder");
823 unsigned int added = si->m_stream->AddData(data, samples * si->m_bytesPerSample);
824 si->m_framesSent += added / si->m_bytesPerFrame;
826 const ICodec* codec = si->m_decoder.GetCodec();
827 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0; //update for GUI
832 void PAPlayer::OnExit()
837 void PAPlayer::RegisterAudioCallback(IAudioCallback* pCallback)
839 CSharedLock lock(m_streamsLock);
840 m_audioCallback = pCallback;
841 if (m_currentStream && m_currentStream->m_stream)
842 m_currentStream->m_stream->RegisterAudioCallback(pCallback);
845 void PAPlayer::UnRegisterAudioCallback()
847 CSharedLock lock(m_streamsLock);
848 /* only one stream should have the callback, but we do it to all just incase */
849 for(StreamList::iterator itt = m_streams.begin(); itt != m_streams.end(); ++itt)
850 if ((*itt)->m_stream)
851 (*itt)->m_stream->UnRegisterAudioCallback();
852 m_audioCallback = NULL;
855 void PAPlayer::OnNothingToQueueNotify()
860 bool PAPlayer::IsPlaying() const
865 bool PAPlayer::IsPaused() const
870 void PAPlayer::Pause()
876 m_callback.OnPlayBackResumed();
881 SoftStop(true, false);
882 m_callback.OnPlayBackPaused();
886 void PAPlayer::SetVolume(float volume)
891 void PAPlayer::SetDynamicRangeCompression(long drc)
896 void PAPlayer::ToFFRW(int iSpeed)
898 m_playbackSpeed = iSpeed;
899 m_signalSpeedChange = true;
902 int64_t PAPlayer::GetTimeInternal()
904 CSharedLock lock(m_streamsLock);
905 if (!m_currentStream)
908 double time = ((double)m_currentStream->m_framesSent / (double)m_currentStream->m_sampleRate);
909 if (m_currentStream->m_stream)
910 time -= m_currentStream->m_stream->GetDelay();
911 time = time * 1000.0;
913 m_playerGUIData.m_time = (int64_t)time; //update for GUI
915 return (int64_t)time;
918 int64_t PAPlayer::GetTime()
920 return m_playerGUIData.m_time;
923 int64_t PAPlayer::GetTotalTime64()
925 CSharedLock lock(m_streamsLock);
926 if (!m_currentStream)
929 int64_t total = m_currentStream->m_decoder.TotalTime();
930 if (m_currentStream->m_endOffset)
931 total = m_currentStream->m_endOffset;
932 total -= m_currentStream->m_startOffset;
936 int64_t PAPlayer::GetTotalTime()
938 return m_playerGUIData.m_totalTime;
941 int PAPlayer::GetCacheLevel() const
943 return m_playerGUIData.m_cacheLevel;
946 void PAPlayer::GetAudioStreamInfo(int index, SPlayerAudioStreamInfo &info)
948 info.bitrate = m_playerGUIData.m_audioBitrate;
949 info.channels = m_playerGUIData.m_channelCount;
950 info.audioCodecName = m_playerGUIData.m_codec;
951 info.samplerate = m_playerGUIData.m_sampleRate;
952 info.bitspersample = m_playerGUIData.m_bitsPerSample;
955 bool PAPlayer::CanSeek()
957 return m_playerGUIData.m_canSeek;
960 void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride)
962 if (!CanSeek()) return;
965 if (g_advancedSettings.m_musicUseTimeSeeking && GetTotalTime() > 2 * g_advancedSettings.m_musicTimeSeekForwardBig)
968 seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig;
970 seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward;
978 percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig;
980 percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward;
981 seek = (__int64)(GetTotalTime64() * (GetPercentage() + percent) / 100);
987 void PAPlayer::SeekTime(int64_t iTime /*=0*/)
989 if (!CanSeek()) return;
991 CSharedLock lock(m_streamsLock);
992 if (!m_currentStream)
995 int seekOffset = (int)(iTime - GetTimeInternal());
997 if (m_playbackSpeed != 1)
1000 m_currentStream->m_seekFrame = (int)((float)m_currentStream->m_sampleRate * ((float)iTime + (float)m_currentStream->m_startOffset) / 1000.0f);
1001 m_callback.OnPlayBackSeek((int)iTime, seekOffset);
1004 void PAPlayer::SeekPercentage(float fPercent /*=0*/)
1006 if (fPercent < 0.0f ) fPercent = 0.0f;
1007 if (fPercent > 100.0f) fPercent = 100.0f;
1008 SeekTime((int64_t)(fPercent * 0.01f * (float)GetTotalTime64()));
1011 float PAPlayer::GetPercentage()
1013 if (m_playerGUIData.m_totalTime > 0)
1014 return m_playerGUIData.m_time * 100.0f / m_playerGUIData.m_totalTime;
1019 bool PAPlayer::SkipNext()
1024 void PAPlayer::UpdateGUIData(StreamInfo *si)
1026 /* Store data need by external threads in member
1027 * structure to prevent locking conflicts when
1028 * data required by GUI and main application
1030 CSharedLock lock(m_streamsLock);
1032 m_playerGUIData.m_sampleRate = si->m_sampleRate;
1033 m_playerGUIData.m_bitsPerSample = si->m_bytesPerSample << 3;
1034 m_playerGUIData.m_channelCount = si->m_channelInfo.Count();
1035 m_playerGUIData.m_canSeek = si->m_decoder.CanSeek();
1037 const ICodec* codec = si->m_decoder.GetCodec();
1039 m_playerGUIData.m_audioBitrate = codec ? codec->m_Bitrate : 0;
1040 strncpy(m_playerGUIData.m_codec,codec ? codec->m_CodecName : "",20);
1041 m_playerGUIData.m_cacheLevel = codec ? codec->GetCacheLevel() : 0;
1043 int64_t total = si->m_decoder.TotalTime();
1044 if (si->m_endOffset)
1045 total = m_currentStream->m_endOffset;
1046 total -= m_currentStream->m_startOffset;
1047 m_playerGUIData.m_totalTime = total;
1050 void PAPlayer::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1052 CExclusiveLock lock(m_streamsLock);