diff --git a/include/metadata.h b/include/metadata.h index b7a3d27..67a4d55 100644 --- a/include/metadata.h +++ b/include/metadata.h @@ -10,6 +10,7 @@ struct TrackMetadata { ~TrackMetadata(); int64_t Length; // milliseconds + int64_t LengthSamples; int64_t Fadeout; std::string Title; std::string Artist; diff --git a/include/plugin.h b/include/plugin.h index 64502a1..43482d5 100644 --- a/include/plugin.h +++ b/include/plugin.h @@ -26,6 +26,7 @@ struct PluginState { ~PluginState(); DB_fileinfo_t fFileInfo; + int readsample; bool fInit; uint32_t hints; TrackMetadata fMetadata; diff --git a/src/metadata.cpp b/src/metadata.cpp index 3dc53bc..17d68bb 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -3,7 +3,7 @@ #include "psflib.h" TrackMetadata::TrackMetadata() - : Length(0), Fadeout(0), Title(""), Artist(""), Year(""), Game(""), Comment(""), set_RG_album(false), RG_AGAIN(0), RG_APEAK(1), set_RG_track(false), RG_TGAIN(0), RG_TPEAK(1) { + : Length(0), LengthSamples(0), Fadeout(0), Title(""), Artist(""), Year(""), Game(""), Comment(""), set_RG_album(false), RG_AGAIN(0), RG_APEAK(1), set_RG_track(false), RG_TGAIN(0), RG_TPEAK(1) { // explicitly clear the map to avoid any issues with reused memory OtherMeta.clear(); } diff --git a/src/play.cpp b/src/play.cpp index 5c14449..892292e 100644 --- a/src/play.cpp +++ b/src/play.cpp @@ -131,13 +131,13 @@ int gsf_read(DB_fileinfo_t *_info, char *buffer, int nbytes) { } #ifdef BUILD_DEBUG - tracedbg("GSF DEBUG: readpos: %d, length: %d\n", _info->readpos, state->fMetadata.Length / 1000); + tracedbg("GSF DEBUG: readsample: %d, length: %d\n", state->readsample, state->fMetadata.LengthSamples); #ifdef STDERR_DEBUGGING - std::cerr << "GSF DEBUG: readpos: " << _info->readpos << ", length: " << state->fMetadata.Length / 1000 << std::endl; + std::cerr << "GSF DEBUG: readsample: " << state->readsample << ", length: " << state->fMetadata.LengthSamples << std::endl; #endif #endif if (!(deadbeef->streamer_get_repeat () == DDB_REPEAT_SINGLE) || !(state->hints & DDB_DECODER_HINT_CAN_LOOP)) { - if (_info->readpos >= (float)state->fMetadata.Length / 1000) { + if (state->readsample >= (float)state->fMetadata.LengthSamples) { #ifdef BUILD_DEBUG tracedbg("GSF DEBUG: end of track\n"); #endif @@ -177,6 +177,11 @@ int gsf_read(DB_fileinfo_t *_info, char *buffer, int nbytes) { std::cerr << "GSF DEBUG: Must copy " << to_copy << " bytes" << std::endl; #endif #endif + // if we would copy more samples than the length of the file, we + // need to trim the buffer + size_t remaining_samples = state->fMetadata.LengthSamples - state->readsample; + if (to_copy > remaining_samples) + to_copy = remaining_samples; unsigned char *head_sample = &state->output.sample_buffer[0]; std::copy(head_sample, head_sample+to_copy, buffer); @@ -194,6 +199,7 @@ int gsf_read(DB_fileinfo_t *_info, char *buffer, int nbytes) { // 16-bit samples, stereo, so 4 bytes per sample // 44100 samples per second + state->readsample += nbytes / 4; _info->readpos += (float)nbytes / 44100 / 4; return to_copy; @@ -208,6 +214,7 @@ int gsf_seek(DB_fileinfo_t *info, float seconds) { if (info->readpos > seconds) { CPUReset(&state->fEmulator); info->readpos = 0; + state->readsample = 0; } float to_seek = seconds - info->readpos; @@ -231,6 +238,7 @@ int gsf_seek(DB_fileinfo_t *info, float seconds) { #endif #endif // discard the entire buffer if there's less data than we need + state->readsample += in_buffer / 4; in_buffer = 0; to_seek -= seconds_in_buffer; continue; @@ -246,6 +254,7 @@ int gsf_seek(DB_fileinfo_t *info, float seconds) { std::copy(head_sample + bytes_needed, head_sample + in_buffer, head_sample); + state->readsample += in_buffer / 4; in_buffer -= bytes_needed; to_seek = 0; break; diff --git a/src/plugin.cpp b/src/plugin.cpp index 9116ea2..bc9be76 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -9,7 +9,7 @@ // plugin state will often occupy the same memory as a previous state, // must be careful to ensure everything is properly initialised to // avoid segfaults -PluginState::PluginState() : fFileInfo(), fInit(false), hints(0), fMetadata(), output(), ROM(), entry_point(0), set_entry(false), fEmulator() {} +PluginState::PluginState() : fFileInfo(), readsample(0), fInit(false), hints(0), fMetadata(), output(), ROM(), entry_point(0), set_entry(false), fEmulator() {} PluginState::~PluginState() { if (fInit) { diff --git a/src/psflib.cpp b/src/psflib.cpp index 8b901c9..afc0d87 100644 --- a/src/psflib.cpp +++ b/src/psflib.cpp @@ -161,6 +161,8 @@ int gsf_load_callback(void *context, const uint8_t *exe, size_t exe_size, return 0; } +constexpr int sample_rate = 44100; // samples per second + // metadata callback for psflib int gsf_info_callback(void *context, const char *name, const char *value) { TrackMetadata *meta = (TrackMetadata*)context; @@ -169,9 +171,10 @@ int gsf_info_callback(void *context, const char *name, const char *value) { // ignore -- we don't want the _lib entries showing up, as they // are internal use only return 0; - else if (!strcasecmp(name, "length")) - meta->Length = parse_time(value); - else if (!strcasecmp(name, "fade")) + else if (!strcasecmp(name, "length")) { + meta->Length = parse_time(value); // milliseconds + meta->LengthSamples = 44100 * meta->Length / 1000; + } else if (!strcasecmp(name, "fade")) meta->Fadeout = parse_time(value); else if (!strcasecmp(name, "title")) meta->Title = value;