From 3888535c831b54710984c9af3a141fb52476777f Mon Sep 17 00:00:00 2001 From: casey langen Date: Sat, 5 Jan 2019 16:36:06 -0800 Subject: [PATCH] Added global looping option to the GmeDecoder and fixed the way the end of song is detected. --- src/musikcube/app/window/TransportWindow.cpp | 50 ++++++++++++++------ src/plugins/gmedecoder/Constants.h | 3 ++ src/plugins/gmedecoder/GmeDecoder.cpp | 46 +++++++++++++----- src/plugins/gmedecoder/GmeDecoder.h | 7 ++- 4 files changed, 78 insertions(+), 28 deletions(-) diff --git a/src/musikcube/app/window/TransportWindow.cpp b/src/musikcube/app/window/TransportWindow.cpp index 5aff80528..9a1b79eaa 100755 --- a/src/musikcube/app/window/TransportWindow.cpp +++ b/src/musikcube/app/window/TransportWindow.cpp @@ -189,7 +189,16 @@ struct musik::cube::TransportDisplayCache { return stringToColumns[str]; } + std::string CurrentTime(int secondsCurrent) { + if (secondsTotal != INT_MIN) { + secondsCurrent = std::min(secondsCurrent, secondsTotal); + } + return musik::core::duration::Duration(secondsCurrent); + } + void Update(ITransport& transport, TrackPtr track) { + /* some params don't update regularly at all, so we can safely + cache them as long as the track hasn't actually changed. */ if (this->track != track) { this->Reset(); @@ -207,21 +216,32 @@ struct musik::cube::TransportDisplayCache { artist = this->track->GetString(constants::Track::ARTIST); artist = artist.size() ? artist : Strings.EMPTY_ARTIST; artistCols = u8cols(artist); - - secondsTotal = (int)transport.GetDuration(); - if (secondsTotal <= 0) { - std::string duration = - this->track->GetString(constants::Track::DURATION); - - if (duration.size()) { - secondsTotal = boost::lexical_cast(duration); - } - } - - totalTime = musik::core::duration::Duration(secondsTotal); - totalTimeCols = u8cols(totalTime); } } + + /* we check duration even if the track is the same because + looping params may have changed. */ + auto updatedTotal = (int)transport.GetDuration(); + if (updatedTotal != secondsTotal) { + secondsTotal = updatedTotal; + if (secondsTotal <= 0 && secondsTotal != INT_MIN) { + std::string duration = + this->track->GetString(constants::Track::DURATION); + + if (duration.size()) { + secondsTotal = boost::lexical_cast(duration); + } + } + + if (secondsTotal >= 0) { + totalTime = musik::core::duration::Duration(secondsTotal); + } + else { + totalTime = "∞"; + } + + totalTimeCols = u8cols(totalTime); + } } }; @@ -657,8 +677,8 @@ void TransportWindow::Update(TimeMode timeMode) { secondsCurrent = (int) round(this->lastTime); } - const std::string currentTime = musik::core::duration::Duration( - std::min(secondsCurrent, displayCache->secondsTotal)); + const std::string currentTime = + displayCache->CurrentTime(secondsCurrent); const std::string replayGain = replayGainEnabled ? "rg" : ""; diff --git a/src/plugins/gmedecoder/Constants.h b/src/plugins/gmedecoder/Constants.h index dff79d7a5..09cdcbba1 100644 --- a/src/plugins/gmedecoder/Constants.h +++ b/src/plugins/gmedecoder/Constants.h @@ -57,6 +57,8 @@ static const char* PLUGIN_NAME = "GME IDecoder"; +static const char* KEY_ALWAYS_LOOP_FOREVER = "always_loop_forever"; +static const bool DEFAULT_ALWAYS_LOOP_FOREVER = false; static const char* KEY_DEFAULT_TRACK_LENGTH = "default_track_length_secs"; static const double DEFAULT_TRACK_LENGTH = 60.0 * 3.0; static const char* KEY_TRACK_FADE_OUT_LENGTH = "track_fade_out_length_secs"; @@ -94,6 +96,7 @@ static inline std::wstring u8to16(const char* utf8) { static inline musik::core::sdk::ISchema* CreateSchema() { auto schema = new musik::core::sdk::TSchema<>(); + schema->AddBool(KEY_ALWAYS_LOOP_FOREVER, DEFAULT_ALWAYS_LOOP_FOREVER); schema->AddDouble(KEY_DEFAULT_TRACK_LENGTH, DEFAULT_TRACK_LENGTH); schema->AddDouble(KEY_TRACK_FADE_OUT_LENGTH, DEFAULT_FADE_OUT_LENGTH); schema->AddBool(KEY_ENABLE_M3U, DEFAULT_ENABLE_M3U); diff --git a/src/plugins/gmedecoder/GmeDecoder.cpp b/src/plugins/gmedecoder/GmeDecoder.cpp index d2dbd6e37..21cccb5ab 100644 --- a/src/plugins/gmedecoder/GmeDecoder.cpp +++ b/src/plugins/gmedecoder/GmeDecoder.cpp @@ -35,15 +35,18 @@ #include "Constants.h" #include "GmeDecoder.h" #include +#include #include static const int BUFFER_SAMPLE_COUNT = 2048; static const int CHANNELS = 2; -static const int SAMPLE_RATE = 44100; +static const int SAMPLE_RATE = 48000; static const int SAMPLES_PER_MS = (SAMPLE_RATE * CHANNELS) / 1000; +static const double LENGTH_FOREVER = INT_MIN; static const float F_SHRT_MAX = (float) SHRT_MAX; extern IPreferences* prefs; +extern IDebug* debug; GmeDecoder::GmeDecoder() { this->buffer = new short[BUFFER_SAMPLE_COUNT]; @@ -84,7 +87,10 @@ bool GmeDecoder::Open(musik::core::sdk::IDataStream *stream) { this->info = nullptr; } else { - if (this->info->length == -1) { + if (prefs->GetBool(KEY_ALWAYS_LOOP_FOREVER, DEFAULT_ALWAYS_LOOP_FOREVER)) { + this->length = LENGTH_FOREVER; + } + else if (this->info->length == -1) { this->length = prefs->GetDouble( KEY_DEFAULT_TRACK_LENGTH, DEFAULT_TRACK_LENGTH); @@ -97,9 +103,11 @@ bool GmeDecoder::Open(musik::core::sdk::IDataStream *stream) { (int)(fadeLength * 1000.0)); } else { - this->length = (double) this->info->length / 1000.0; + this->length = (double) this->info->play_length / 1000.0; } } + + this->totalSamples = (int)(this->length * SAMPLE_RATE * CHANNELS); } } delete[] data; @@ -111,9 +119,13 @@ void GmeDecoder::Release() { } double GmeDecoder::SetPosition(double seconds) { + std::unique_lockmutex)> lock(this->mutex); if (this->gme) { - gme_seek(this->gme, (int)(seconds * 1000.0)); - return (double) gme_tell(this->gme) / 1000.0; + auto err = gme_seek(this->gme, (int)(seconds * 1000.0)); + if (err) { debug->Error(PLUGIN_NAME, err); } + double position = (double)gme_tell(this->gme) / 1000.0; + this->samplesPlayed = (int)(position * SAMPLE_RATE * CHANNELS); + return position; } return 0.0; } @@ -123,26 +135,36 @@ double GmeDecoder::GetDuration() { } bool GmeDecoder::GetBuffer(IBuffer *target) { + std::unique_lockmutex)> lock(this->mutex); if (this->gme) { - if (!gme_track_ended(this->gme) && - !gme_play(this->gme, BUFFER_SAMPLE_COUNT, this->buffer)) + int samplesRemaining = totalSamples - samplesPlayed; + + bool playFullBuffer = + (samplesRemaining > BUFFER_SAMPLE_COUNT) || + (this->length == LENGTH_FOREVER); + + int bufferSamples = playFullBuffer + ? BUFFER_SAMPLE_COUNT : samplesRemaining; + + if (bufferSamples > 0 && + !gme_play(this->gme, bufferSamples, this->buffer)) { target->SetChannels(CHANNELS); target->SetSampleRate(SAMPLE_RATE); - target->SetSamples(BUFFER_SAMPLE_COUNT); + target->SetSamples(bufferSamples); + float* dst = target->BufferPointer(); for (size_t i = 0; i < BUFFER_SAMPLE_COUNT; i++) { dst[i] = (float) this->buffer[i] / F_SHRT_MAX; } + samplesPlayed += bufferSamples; return true; } } + this->exhausted = true; return false; } bool GmeDecoder::Exhausted() { - if (this->gme) { - return gme_track_ended(this->gme); - } - return true; + return this->exhausted; } diff --git a/src/plugins/gmedecoder/GmeDecoder.h b/src/plugins/gmedecoder/GmeDecoder.h index 134753e1b..f71edcfdd 100644 --- a/src/plugins/gmedecoder/GmeDecoder.h +++ b/src/plugins/gmedecoder/GmeDecoder.h @@ -40,6 +40,7 @@ #include "GmeDataStream.h" #include #include +#include using namespace musik::core::sdk; @@ -60,5 +61,9 @@ class GmeDecoder: public musik::core::sdk::IDecoder { gme_t* gme { nullptr }; gme_info_t* info { nullptr }; short* buffer; - double length = -1.0; + double length{ -1.0 }; + int totalSamples { 0 }; + int samplesPlayed { 0 }; + bool exhausted { false }; + std::mutex mutex; };