Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 57 additions & 12 deletions src/decoder_xmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ XMPDecoder::~XMPDecoder() {

bool XMPDecoder::Open(Filesystem_Stream::InputStream stream) {
finished = false;
frame_bytes_left = 0;

if (!ctx)
return false;
Expand Down Expand Up @@ -106,6 +107,7 @@ bool XMPDecoder::Seek(std::streamoff offset, std::ios_base::seekdir origin) {

if (offset == 0 && origin == std::ios_base::beg) {
xmp_restart_module(ctx);
frame_bytes_left = 0;
finished = false;
return true;
}
Expand Down Expand Up @@ -138,6 +140,8 @@ bool XMPDecoder::SetFormat(int freq, AudioDecoder::Format frmt, int chans) {

// restart the player, apply new format flags
xmp_end_player(ctx);
frame_bytes_left = 0;
finished = false;

channels = chans;
format = frmt;
Expand Down Expand Up @@ -176,21 +180,62 @@ int XMPDecoder::FillBuffer(uint8_t* buffer, int length) {
if (!ctx)
return -1;

/* FIXME: `xmp_play_buffer()` is a loop around `xmp_play_frame()` that may add
* silence at the end of the buffer, when there is not enough audio data left.
* We may need to use the latter directly, to have no gap between two loops.
*/
int ret = xmp_play_buffer(ctx, buffer, length, 1);
if (finished)
return 0;

// end of file
if (ret == -XMP_END)
finished = true;
int filled = 0;

// xmp_play_frame renders a single frame of the module
// xmp_play_buffer is not used here as this inserts silence at the end
// Also gives more control about the loop handling
while (filled < length) {
if (frame_bytes_left == 0) {
int ret = xmp_play_frame(ctx);

// error
if (ret == -XMP_ERROR_STATE)
return -1;

// end of module
if (ret < 0) {
finished = true;
break;
}

xmp_frame_info info;
xmp_get_frame_info(ctx, &info);

// libxmp handles jump commands (built-in loops) automatically
// When looping is enabled for the AudioDecoder manually increment
// our own loop counter
if (info.loop_count != GetLoopCount()) {
if (!GetLooping()) {
// Play the module once, then report it as finished
finished = true;
break;
}

// Manually set the loop count as IsFinished which handles this
// normally will never return true
loop_count = info.loop_count;
}

frame_buffer = reinterpret_cast<const uint8_t*>(info.buffer);
frame_bytes_left = info.buffer_size;
}

// error
if (ret == -XMP_ERROR_STATE)
return -1;
int copy_size = length - filled;
if (copy_size > frame_bytes_left)
copy_size = frame_bytes_left;

memcpy(buffer + filled, frame_buffer, copy_size);

frame_buffer += copy_size;
frame_bytes_left -= copy_size;
filled += copy_size;
}

return length;
return filled;
}

#endif
4 changes: 4 additions & 0 deletions src/decoder_xmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class XMPDecoder : public AudioDecoder {
#ifdef HAVE_LIBXMP
xmp_context ctx = nullptr;
#endif
/** Buffer filled by xmp_play_frame */
const uint8_t* frame_buffer = nullptr;
/** Remaining bytes in that buffer */
int frame_bytes_left = 0;
bool finished = false;

// defaults
Expand Down
4 changes: 2 additions & 2 deletions src/filefinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
namespace FileFinder {
constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz");
constexpr const auto MUSIC_TYPES = Utils::MakeSvArray(
".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma");
".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".mod", ".xm", ".s3m", ".it", ".wma");
constexpr const auto SOUND_TYPES = Utils::MakeSvArray(
".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma");
".opus", ".oga", ".ogg", ".wav", ".mp3", ".mod", ".xm", ".s3m", ".it", ".wma");
constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff");
constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ""); // "" = Complete Filename (incl. extension) provided by the user

Expand Down
Loading