void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride) { if (!CanSeek()) return; __int64 seek; if (g_advancedSettings.m_musicUseTimeSeeking && GetTotalTime() > 2 * g_advancedSettings.m_musicTimeSeekForwardBig) { if (bLargeStep) seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig; else seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward; seek *= 1000; seek += GetTime(); } else { float percent; if (bLargeStep) percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig; else percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward; seek = (__int64)(GetTotalTime64() * (GetPercentage() + percent) / 100); } SeekTime(seek); }
void PAPlayer::Seek(bool bPlus, bool bLargeStep, bool bChapterOverride) { if (!CanSeek()) return; long long seek; if (g_advancedSettings.m_musicUseTimeSeeking && m_playerGUIData.m_totalTime > 2 * g_advancedSettings.m_musicTimeSeekForwardBig) { if (bLargeStep) seek = bPlus ? g_advancedSettings.m_musicTimeSeekForwardBig : g_advancedSettings.m_musicTimeSeekBackwardBig; else seek = bPlus ? g_advancedSettings.m_musicTimeSeekForward : g_advancedSettings.m_musicTimeSeekBackward; seek *= 1000; seek += m_playerGUIData.m_time; } else { float percent; if (bLargeStep) percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForwardBig : (float)g_advancedSettings.m_musicPercentSeekBackwardBig; else percent = bPlus ? (float)g_advancedSettings.m_musicPercentSeekForward : (float)g_advancedSettings.m_musicPercentSeekBackward; seek = static_cast<long long>(GetTotalTime64() * (GetPercentage() + percent) / 100); } SeekTime(seek); }
float PAPlayer::GetPercentage() { float percent = (float)GetTime() * 100.0f / GetTotalTime64(); return percent; }
void PAPlayer::SeekPercentage(float fPercent /*=0*/) { if (fPercent < 0.0f) fPercent = 0.0f; if (fPercent > 100.0f) fPercent = 100.0f; SeekTime((__int64)(fPercent * 0.01f * (float)GetTotalTime64())); }
int PAPlayer::GetTotalTime() { return (int)(GetTotalTime64()/1000); }
bool PAPlayer::ProcessPAP() { /* * Here's what we should be doing in each player loop: * * 1. Run DoWork() on our audio device to actually output audio. * * 2. Pass our current buffer to the audio device to see if it wants anything, * and if so, reduce our buffer size accordingly. * * 3. Check whether we have space in our buffer for more data, and if so, * read some more in. * * 4. Check for end of file and return false if we reach it. * * 5. Perform any seeking and ffwd/rewding as necessary. * * 6. If we don't do anything in 2...5, we can take a breather and break out for sleeping. */ while (true) { if (m_bStop) return false; // Check for .cue sheet item end if (m_currentFile->m_lEndOffset && GetTime() >= GetTotalTime64()) { CLog::Log(LOGINFO, "PAPlayer: Passed end of track in a .cue sheet item"); m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED); } // check whether we need to send off our callbacks etc. int status = m_decoder[m_currentDecoder].GetStatus(); if (status == STATUS_NO_FILE) return false; UpdateCacheLevel(); // check whether we should queue the next file up if ((GetTotalTime64() > 0) && GetTotalTime64() - GetTime() < TIME_TO_CACHE_NEXT_FILE + m_crossFading * 1000L && !m_cachingNextFile) { // request the next file from our application m_callback.OnQueueNextItem(); m_cachingNextFile = true; } if (m_crossFading && m_decoder[0].GetChannels() == m_decoder[1].GetChannels()) { if (((GetTotalTime64() - GetTime() < m_crossFading * 1000L) || (m_forceFadeToNext)) && !m_currentlyCrossFading) { // request the next file from our application if (m_decoder[1 - m_currentDecoder].GetStatus() == STATUS_QUEUED && m_pAudioDecoder[1 - m_currentStream]) { m_currentlyCrossFading = true; if (m_forceFadeToNext) { m_forceFadeToNext = false; m_crossFadeLength = m_crossFading * 1000L; } else { m_crossFadeLength = GetTotalTime64() - GetTime(); } m_currentDecoder = 1 - m_currentDecoder; m_decoder[m_currentDecoder].Start(); m_currentStream = 1 - m_currentStream; CLog::Log(LOGDEBUG, "Starting Crossfade - resuming stream %i", m_currentStream); m_pAudioDecoder[m_currentStream]->Resume(); m_callback.OnPlayBackStarted(); m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75; m_bytesSentOut = 0; *m_currentFile = *m_nextFile; m_nextFile->Reset(); m_cachingNextFile = false; } } } // Check for EOF and queue the next track if applicable if (m_decoder[m_currentDecoder].GetStatus() == STATUS_ENDED) { // time to swap tracks if (m_nextFile->m_strPath != m_currentFile->m_strPath || !m_nextFile->m_lStartOffset || m_nextFile->m_lStartOffset != m_currentFile->m_lEndOffset) { // don't have a .cue sheet item int nextstatus = m_decoder[1 - m_currentDecoder].GetStatus(); if (nextstatus == STATUS_QUEUED || nextstatus == STATUS_QUEUING || nextstatus == STATUS_PLAYING) { // swap streams CLog::Log(LOGDEBUG, "PAPlayer: Swapping tracks %i to %i", m_currentDecoder, 1-m_currentDecoder); if (!m_crossFading || m_decoder[0].GetChannels() != m_decoder[1].GetChannels()) { // playing gapless (we use only the 1 output stream in this case) int prefixAmount = m_decoder[m_currentDecoder].GetDataSize(); CLog::Log(LOGDEBUG, "PAPlayer::Prefixing %i samples of old data to new track for gapless playback", prefixAmount); m_decoder[1 - m_currentDecoder].PrefixData(m_decoder[m_currentDecoder].GetData(prefixAmount), prefixAmount); // check if we need to change the resampler (due to format change) unsigned int channels, samplerate, bitspersample; m_decoder[m_currentDecoder].GetDataFormat(&channels, &samplerate, &bitspersample); unsigned int channels2, samplerate2, bitspersample2; m_decoder[1 - m_currentDecoder].GetDataFormat(&channels2, &samplerate2, &bitspersample2); // change of channels - reinitialize our speaker configuration if (channels != channels2 || (g_advancedSettings.m_musicResample == 0 && (samplerate != samplerate2 || bitspersample != bitspersample2))) { CLog::Log(LOGINFO, "PAPlayer: Stream properties have changed, restarting stream"); FreeStream(m_currentStream); if (!CreateStream(m_currentStream, channels2, samplerate2, bitspersample2)) { CLog::Log(LOGERROR, "PAPlayer: Error creating stream!"); return false; } m_pAudioDecoder[m_currentStream]->Resume(); } else if (samplerate != samplerate2 || bitspersample != bitspersample2) { CLog::Log(LOGINFO, "PAPlayer: Restarting resampler due to a change in data format"); m_resampler[m_currentStream].DeInitialize(); if (!m_resampler[m_currentStream].InitConverter(samplerate2, bitspersample2, channels2, g_advancedSettings.m_musicResample, 16, PACKET_SIZE)) { CLog::Log(LOGERROR, "PAPlayer: Error initializing resampler!"); return false; } } CLog::Log(LOGINFO, "PAPlayer: Starting new track"); m_decoder[m_currentDecoder].Destroy(); m_decoder[1 - m_currentDecoder].Start(); m_callback.OnPlayBackStarted(); m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75; m_bytesSentOut = 0; *m_currentFile = *m_nextFile; m_nextFile->Reset(); m_cachingNextFile = false; m_currentDecoder = 1 - m_currentDecoder; } else { // cross fading - shouldn't ever get here - if we do, return false if (!m_currentlyCrossFading) { CLog::Log(LOGERROR, "End of file Reached before crossfading kicked in!"); return false; } else { CLog::Log(LOGINFO, "End of file reached before crossfading finished!"); return false; } } } else { if (GetTotalTime64() <= 0 && !m_bQueueFailed) { //we did not know the duration so didn't queue the next song, try queueing it now if (!m_cachingNextFile) {// request the next file from our application m_callback.OnQueueNextItem(); m_cachingNextFile = true; } } else { // no track queued - return and get another one once we are finished // with the current stream WaitForStream(); return false; } } } else { // set the next track playing (.cue sheet) m_decoder[m_currentDecoder].SetStatus(STATUS_PLAYING); m_callback.OnPlayBackStarted(); m_timeOffset = m_nextFile->m_lStartOffset * 1000 / 75; m_bytesSentOut = 0; *m_currentFile = *m_nextFile; m_nextFile->Reset(); m_cachingNextFile = false; } } // handle seeking and ffwd/rewding. HandleSeeking(); if (!HandleFFwdRewd()) { // need to skip to the next track - let's see if we already have another one m_decoder[m_currentDecoder].SetStatus(STATUS_ENDED); continue; // loop around to start the next track } if (!m_bPaused) { // Let our decoding stream(s) do their thing int retVal = m_decoder[m_currentDecoder].ReadSamples(PACKET_SIZE); if (retVal == RET_ERROR) { m_decoder[m_currentDecoder].Destroy(); return false; } int retVal2 = m_decoder[1 - m_currentDecoder].ReadSamples(PACKET_SIZE); if (retVal2 == RET_ERROR) { m_decoder[1 - m_currentDecoder].Destroy(); } // if we're cross-fading, then we do this for both streams, otherwise // we do it just for the one stream. if (m_currentlyCrossFading) { if (GetTime() >= m_crossFadeLength) // finished { CLog::Log(LOGDEBUG, "Finished Crossfading"); m_currentlyCrossFading = false; SetStreamVolume(m_currentStream, g_settings.m_nVolumeLevel); FreeStream(1 - m_currentStream); m_decoder[1 - m_currentDecoder].Destroy(); } else { float fraction = (float)(m_crossFadeLength - GetTime()) / (float)m_crossFadeLength - 0.5f; // make sure we can take valid logs. if (fraction > 0.499f) fraction = 0.499f; if (fraction < -0.499f) fraction = -0.499f; float volumeCurrent = 2000.0f * log10(0.5f - fraction); float volumeNext = 2000.0f * log10(0.5f + fraction); SetStreamVolume(m_currentStream, g_settings.m_nVolumeLevel + (int)volumeCurrent); SetStreamVolume(1 - m_currentStream, g_settings.m_nVolumeLevel + (int)volumeNext); if (AddPacketsToStream(1 - m_currentStream, m_decoder[1 - m_currentDecoder])) retVal2 = RET_SUCCESS; } } // add packets as necessary if (AddPacketsToStream(m_currentStream, m_decoder[m_currentDecoder])) retVal = RET_SUCCESS; if (retVal == RET_SLEEP && retVal2 == RET_SLEEP) { float maximumSleepTime = m_pAudioDecoder[m_currentStream]->GetCacheTime(); if (m_pAudioDecoder[1 - m_currentStream]) maximumSleepTime = std::min(maximumSleepTime, m_pAudioDecoder[1 - m_currentStream]->GetCacheTime()); int sleep = std::max((int)((maximumSleepTime / 2.0f) * 1000.0f), 1); Sleep(std::min(sleep, 15)); } } else Sleep(100); } return true; }
float PAPlayer::GetPercentage() { return GetTime() * 100.0f / GetTotalTime64(); }