Skip to content
1 change: 1 addition & 0 deletions include/metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ struct TrackMetadata {
~TrackMetadata();

int64_t Length; // milliseconds
int64_t LengthSamples;
int64_t Fadeout;
std::string Title;
std::string Artist;
Expand Down
1 change: 1 addition & 0 deletions include/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct PluginState {
~PluginState();

DB_fileinfo_t fFileInfo;
int readsample;
bool fInit;
uint32_t hints;
TrackMetadata fMetadata;
Expand Down
2 changes: 1 addition & 1 deletion src/metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand Down
15 changes: 12 additions & 3 deletions src/play.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
9 changes: 6 additions & 3 deletions src/psflib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down