Support MS ADPCM playback for files in Music/ game folder #710

Closed
fdelapena opened this Issue Jan 4, 2016 · 2 comments

Projects

None yet

1 participant

@fdelapena
Member

SDL can play MS ADPCM WAV files, however SDL_mixer is not supporting them for music playback, however they can be played in a channel.

Because current file format detection is using SDL_Mixer to play music and files must be loaded first to detect the music type with SDL_LoadMUS_RW, mixer will return SDL_error about unknown WAVE data format. SDL_LoadWAV does not have audio type format

There is also a known double free crash bug in SDL_mixer when passing 1 value to automatically free the data source when calling SDL_LoadMUS_RW to detect the file format, which is the reason of the Yume 2kki or the game 星の海で crash as these games use MS ADPCM WAV files for music.

A simple workaround could be reading the first four bytes of the file to check WAV format before doing the bogus SDL_LoadMUS_RW detection then directly do the BGS_Play call when those first four bytes are "RIFF".

@fdelapena fdelapena added the Crash label Jan 4, 2016
@fdelapena fdelapena added this to the 0.4.1 milestone Jan 4, 2016
@fdelapena fdelapena added the Audio label Jan 4, 2016
@fdelapena
Member

This patch allows to reproduce MS ADPCM:

diff --git a/src/platform/sdl_audio.cpp b/src/platform/sdl_audio.cpp
index c5d69f8..693b2ef 100644
--- a/src/platform/sdl_audio.cpp
+++ b/src/platform/sdl_audio.cpp
@@ -145,6 +145,21 @@ void SdlAudio::BGM_Play(std::string const& file, int volume, int /* pitch */, in
        }

        SDL_RWops *rw = SDL_RWFromFile(path.c_str(), "rb");
+
+       // SDL_mixer does not detect MS ADPCM in Mix_LoadMUS_RW
+       // and does not return the desired bgm structure to check the music type.
+       // To work around this, check valid WAV header and play BGS.
+       char header[4];
+       if (rw->read(rw, header, 4, 1) > 0) {
+               if (strncmp((char*)header, "RIFF", 4) == 0) {
+                       // SDL2_mixer bug, see above
+                       if (bgs_playing) {
+                               BGS_Stop();
+                       }
+                       BGS_Play(file, volume, 0, fadein);
+                       return;
+               }
+       }
 #if SDL_MIXER_MAJOR_VERSION>1
        bgm.reset(Mix_LoadMUS_RW(rw, 1), &Mix_FreeMusic);
 #else

However, fade out is not working properly because there is a bug in BGM_fade:

#if SDL_MAJOR_VERSION>1
    // SDL2_mixer bug, see above
    Mix_MusicType mtype = Mix_GetMusicType(bgm.get());
    if (mtype == MUS_WAV || mtype == MUS_OGG) {
        BGS_Fade(fade);
        return;
    }
#endif

Mix_GetMusicType() is getting the music type from the music special channel, which is not a normal channel. All WAV and OGG are being played from channels, if a game is not playing a WAV or an OGG in the music special channel, this bug will be hearable.

A solution would be reading again the file header from here or maybe a private class member to store the previously detected format. What do you suggest?

There is also an issue with midi playback due to this, it reports a nonsense error message:

Warning: Couldn't load [filename] BGM.
No SoundFonts have been requested

And a similar XAudio nonsense message on Win32.

@fdelapena
Member

The crash can be worked around by passing 0 instead of 1 to SDL_LoadMUS_RW() and the free call is already done from the shared_ptr reset deleter parameter.

Warning: Couldn't load j17r BGM.
Unknown WAVE data format

Now it is easy to work around by getting the SDL_GetError() and call BGS_Play().

@fdelapena fdelapena added a commit to fdelapena/easyrpg-player that referenced this issue Feb 22, 2016
@fdelapena fdelapena Don't free from Mix_LoadMUS_RW() and play ADPCM with BGS_Play
Fixes #710.
4f2b8c9
@Ghabry Ghabry closed this in #773 Feb 22, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment