Skip to content

Commit

Permalink
Don't write temporary music files to disk, use memory streams instead
Browse files Browse the repository at this point in the history
Refactored the `I_SDL_RegisterSong` function to eliminate the use of temporary files written to disk.
Instead, music data is now processed and loaded directly from memory using SDL_RWops.
This approach improves performance and avoids potential issues with file system permissions or clutter.

- Replaced temporary file creation (`M_TempFile`) with memory streams (`SDL_RWFromConstMem`).
- Added support for converting MUS to MIDI entirely in memory using `mem_fopen` and `mem_get_buf`.
- Updated error handling and logging for better diagnostics.
- Preserves compatibility with existing workflows while removing dependency on disk operations.

Thanks @JNechaevsky for suggestion.
  • Loading branch information
pvictress committed Dec 12, 2024
1 parent f50e22c commit 3d32c71
Showing 1 changed file with 64 additions and 38 deletions.
102 changes: 64 additions & 38 deletions src/i_sdlmusic.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,8 @@ static void I_SDL_UnRegisterSong(void *handle)
}
}

// [PN] No longer used in function below
/*
static boolean ConvertMus(byte *musdata, int len, const char *filename)
{
MEMFILE *instream;
Expand All @@ -360,62 +362,86 @@ static boolean ConvertMus(byte *musdata, int len, const char *filename)
return result;
}
*/

// -----------------------------------------------------------------------------
// I_SDL_RegisterSong
// [PN] Registers a music for playback without writing temporary files to disk.
//
// Depending on the format of the input data, the function:
// - Converts MUS data to MIDI in memory if necessary.
// - Directly loads other music formats from memory.
//
// This implementation avoids the use of temporary disk files, using SDL_RWops
// to create memory streams instead. The memory stream is automatically freed
// after loading the music.
// -----------------------------------------------------------------------------

static void *I_SDL_RegisterSong(void *data, int len)
{
char *filename;
Mix_Music *music;
Mix_Music *music = NULL;
SDL_RWops *rw = NULL;

if (!music_initialized)
{
return NULL;
}

// MUS files begin with "MUS"
// Reject anything which doesnt have this signature

filename = M_TempFile("doom"); // [crispy] generic filename

// [crispy] Reverse Choco's logic from "if (MIDI)" to "if (not MUS)"
// MUS is the only format that requires conversion,
// let SDL_Mixer figure out the others
/*
if (IsMid(data, len) && len < MAXMIDLENGTH)
*/
if (!IsMus(data, len)) // [crispy] MUS_HEADER_MAGIC
{
M_WriteFile(filename, data, len);
}
else
// [PN] Check if the data is a MUS file (MUS files start with "MUS")
if (IsMus(data, len))
{
// Assume a MUS file and try to convert
MEMFILE *instream = mem_fopen_read(data, len);
MEMFILE *outstream = mem_fopen_write();
void *outbuf;
size_t outbuf_len;

ConvertMus(data, len, filename);
if (instream && outstream)
{
const int result = mus2mid(instream, outstream);
if (result == 0)
{
// [PN] Retrieve converted data from memory stream
mem_get_buf(outstream, &outbuf, &outbuf_len);
rw = SDL_RWFromConstMem(outbuf, (int)outbuf_len);
if (rw == NULL)
{
fprintf(stderr, "Error creating SDL_RWops: %s\n", SDL_GetError());
}
}
else
{
fprintf(stderr, "Error converting MUS to MIDI.\n");
}

// [PN] Close memory streams
mem_fclose(instream);
mem_fclose(outstream);
}
else
{
fprintf(stderr, "Error opening memory streams for MUS conversion.\n");
}
}

// Load the MIDI. In an ideal world we'd be using Mix_LoadMUS_RW()
// by now, but Mix_SetMusicCMD() only works with Mix_LoadMUS(), so
// we have to generate a temporary file.

music = Mix_LoadMUS(filename);
if (music == NULL)
else
{
// Failed to load
fprintf(stderr, "Error loading midi: %s\n", Mix_GetError());
// [PN] If not MUS, use the data directly
rw = SDL_RWFromConstMem(data, len);
if (rw == NULL)
{
fprintf(stderr, "Error creating SDL_RWops: %s\n", SDL_GetError());
}
}

// Remove the temporary MIDI file; however, when using an external
// MIDI program we can't delete the file. Otherwise, the program
// won't find the file to play. This means we leave a mess on
// disk :(

if (strlen(snd_musiccmd) == 0)
if (rw)
{
M_remove(filename);
// [PN] Load the music from memory stream
music = Mix_LoadMUS_RW(rw, 1); // Automatically frees rw after loading
if (music == NULL)
{
fprintf(stderr, "Error loading music: %s\n", Mix_GetError());
}
}

free(filename);

return music;
}

Expand Down

0 comments on commit 3d32c71

Please sign in to comment.