/* * This file is part of the DXX-Rebirth project . * It is copyright by its individual contributors, as recorded in the * project's Git history. See COPYING.txt at the top level for license * terms and a link to the Git history. */ /* * This is an alternate backend for the music system. * It uses SDL_mixer to provide a more reliable playback, * and allow processing of multiple audio formats. * * -- MD2211 (2006-04-24) */ #include #include #include #include #include #include "args.h" #include "hmp.h" #include "digi_mixer_music.h" #include "strutil.h" #include "u_mem.h" #include "console.h" namespace { class current_music_t { struct RWops_delete { void operator()(SDL_RWops *o) { SDL_RWclose(o); } }; struct Music_delete { void operator()(Mix_Music *m) { Mix_FreeMusic(m); } }; typedef SDL_RWops *rpointer; typedef Mix_Music *mpointer; std::unique_ptr m_ops; std::unique_ptr m_music; public: void reset(mpointer m = mpointer(), rpointer r = rpointer()) noexcept(noexcept(m_music.reset(m)) && noexcept(m_ops.reset(r))) { /* Clear music first in case it needs the old ops * Clear old ops * If no new music, clear new ops immediately. This only * happens if the new music fails to load. */ m_music.reset(m); m_ops.reset(r); if (!m) m_ops.reset(); } bool operator!() const { return !m_music; } explicit operator bool() const { return static_cast(m_music); } mpointer get() { return m_music.get(); } }; } static current_music_t current_music; static std::vector current_music_hndlbuf; /* * Plays a music file from an absolute path or a relative path */ int mix_play_file(const char *filename, int loop, void (*hook_finished_track)()) { SDL_RWops *rw = NULL; char full_path[PATH_MAX]; const char *fptr; unsigned int bufsize = 0; mix_free_music(); // stop and free what we're already playing, if anything fptr = strrchr(filename, '.'); if (fptr == NULL) return 0; // It's a .hmp! if (!d_stricmp(fptr, ".hmp")) { hmp2mid(filename, current_music_hndlbuf); rw = SDL_RWFromConstMem(¤t_music_hndlbuf[0], current_music_hndlbuf.size()*sizeof(char)); current_music.reset(Mix_LoadMUS_RW(rw), rw); } // try loading music via given filename if (!current_music) current_music.reset(Mix_LoadMUS(filename)); // allow the shell convention tilde character to mean the user's home folder // chiefly used for default jukebox level song music referenced in 'descent.m3u' for Mac OS X if (!current_music && *filename == '~') { snprintf(full_path, PATH_MAX, "%s%s", PHYSFS_getUserDir(), &filename[1 + (!strncmp(&filename[1], PHYSFS_getDirSeparator(), strlen(PHYSFS_getDirSeparator())) ? strlen(PHYSFS_getDirSeparator()) : 0)]); current_music.reset(Mix_LoadMUS(full_path)); if (current_music) filename = full_path; // used later for possible error reporting } // no luck. so it might be in Searchpath. So try to build absolute path if (!current_music) { PHYSFSX_getRealPath(filename, full_path); current_music.reset(Mix_LoadMUS(full_path)); if (current_music) filename = full_path; // used later for possible error reporting } // still nothin'? Let's open via PhysFS in case it's located inside an archive if (!current_music) { if (RAIIPHYSFS_File filehandle{PHYSFS_openRead(filename)}) { unsigned len = PHYSFS_fileLength(filehandle); current_music_hndlbuf.resize(len); bufsize = PHYSFS_read(filehandle, ¤t_music_hndlbuf[0], sizeof(char), len); rw = SDL_RWFromConstMem(¤t_music_hndlbuf[0], bufsize*sizeof(char)); current_music.reset(Mix_LoadMUS_RW(rw), rw); } } if (current_music) { Mix_PlayMusic(current_music.get(), (loop ? -1 : 1)); Mix_HookMusicFinished(hook_finished_track ? hook_finished_track : mix_free_music); return 1; } else { con_printf(CON_CRITICAL,"Music %s could not be loaded: %s", filename, Mix_GetError()); mix_stop_music(); } return 0; } // What to do when stopping song playback void mix_free_music() { Mix_HaltMusic(); current_music.reset(); current_music_hndlbuf.clear(); } void mix_set_music_volume(int vol) { vol *= MIX_MAX_VOLUME/8; Mix_VolumeMusic(vol); } void mix_stop_music() { Mix_HaltMusic(); current_music_hndlbuf.clear(); } void mix_pause_music() { Mix_PauseMusic(); } void mix_resume_music() { Mix_ResumeMusic(); } void mix_pause_resume_music() { if (Mix_PausedMusic()) Mix_ResumeMusic(); else if (Mix_PlayingMusic()) Mix_PauseMusic(); }