2014-06-01 17:55:23 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
|
|
|
|
* 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.
|
|
|
|
*/
|
2006-10-17 20:52:09 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
*/
|
|
|
|
|
2014-08-27 02:52:21 +00:00
|
|
|
#include <memory>
|
2013-06-30 02:22:56 +00:00
|
|
|
#include <SDL.h>
|
|
|
|
#include <SDL_mixer.h>
|
2006-10-17 20:52:09 +00:00
|
|
|
#include <string.h>
|
2008-01-23 17:25:09 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2006-10-17 20:52:09 +00:00
|
|
|
#include "args.h"
|
2010-07-18 20:28:15 +00:00
|
|
|
#include "hmp.h"
|
2007-09-18 13:37:39 +00:00
|
|
|
#include "digi_mixer_music.h"
|
2013-12-26 04:18:28 +00:00
|
|
|
#include "strutil.h"
|
2008-01-23 17:25:09 +00:00
|
|
|
#include "u_mem.h"
|
2011-06-01 07:59:51 +00:00
|
|
|
#include "console.h"
|
2006-10-17 20:52:09 +00:00
|
|
|
|
2015-01-23 03:55:06 +00:00
|
|
|
namespace {
|
|
|
|
|
2014-08-27 02:52:21 +00:00
|
|
|
class current_music_t
|
|
|
|
{
|
|
|
|
struct RWops_delete
|
|
|
|
{
|
2015-02-28 22:34:07 +00:00
|
|
|
void operator()(SDL_RWops *o)
|
|
|
|
{
|
|
|
|
SDL_RWclose(o);
|
|
|
|
}
|
2014-08-27 02:52:21 +00:00
|
|
|
};
|
|
|
|
struct Music_delete
|
|
|
|
{
|
|
|
|
void operator()(Mix_Music *m) { Mix_FreeMusic(m); }
|
|
|
|
};
|
|
|
|
typedef SDL_RWops *rpointer;
|
|
|
|
typedef Mix_Music *mpointer;
|
|
|
|
std::unique_ptr<SDL_RWops, RWops_delete> m_ops;
|
|
|
|
std::unique_ptr<Mix_Music, Music_delete> 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<bool>(m_music); }
|
|
|
|
mpointer get() { return m_music.get(); }
|
|
|
|
};
|
|
|
|
|
2015-01-23 03:55:06 +00:00
|
|
|
}
|
|
|
|
|
2014-08-27 02:52:21 +00:00
|
|
|
static current_music_t current_music;
|
2014-09-20 23:14:03 +00:00
|
|
|
static std::vector<uint8_t> current_music_hndlbuf;
|
2006-10-17 20:52:09 +00:00
|
|
|
|
2008-01-23 17:25:09 +00:00
|
|
|
/*
|
2010-08-22 13:27:47 +00:00
|
|
|
* Plays a music file from an absolute path or a relative path
|
2008-01-23 17:25:09 +00:00
|
|
|
*/
|
2006-10-17 20:52:09 +00:00
|
|
|
|
2013-06-08 22:24:17 +00:00
|
|
|
int mix_play_file(const char *filename, int loop, void (*hook_finished_track)())
|
2010-06-14 08:13:16 +00:00
|
|
|
{
|
2010-06-27 14:30:12 +00:00
|
|
|
SDL_RWops *rw = NULL;
|
2010-10-29 15:40:21 +00:00
|
|
|
char full_path[PATH_MAX];
|
2013-06-08 22:24:17 +00:00
|
|
|
const char *fptr;
|
2010-10-29 15:40:21 +00:00
|
|
|
unsigned int bufsize = 0;
|
2007-03-12 21:56:41 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
mix_free_music(); // stop and free what we're already playing, if anything
|
2007-03-12 21:56:41 +00:00
|
|
|
|
2010-06-14 10:43:30 +00:00
|
|
|
fptr = strrchr(filename, '.');
|
2008-01-23 17:25:09 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
if (fptr == NULL)
|
|
|
|
return 0;
|
2008-01-23 17:25:09 +00:00
|
|
|
|
2010-11-28 15:49:32 +00:00
|
|
|
// It's a .hmp!
|
2012-05-18 23:36:41 +00:00
|
|
|
if (!d_stricmp(fptr, ".hmp"))
|
2010-06-14 08:13:16 +00:00
|
|
|
{
|
2014-09-20 23:14:03 +00:00
|
|
|
hmp2mid(filename, current_music_hndlbuf);
|
|
|
|
rw = SDL_RWFromConstMem(¤t_music_hndlbuf[0], current_music_hndlbuf.size()*sizeof(char));
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset(Mix_LoadMUS_RW(rw), rw);
|
2010-06-14 08:13:16 +00:00
|
|
|
}
|
2007-03-12 21:56:41 +00:00
|
|
|
|
2010-07-05 07:41:30 +00:00
|
|
|
// try loading music via given filename
|
2010-10-29 15:40:21 +00:00
|
|
|
if (!current_music)
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset(Mix_LoadMUS(filename));
|
2010-07-05 07:41:30 +00:00
|
|
|
|
2011-04-18 12:30:46 +00:00
|
|
|
// 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)]);
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset(Mix_LoadMUS(full_path));
|
2011-04-18 12:30:46 +00:00
|
|
|
if (current_music)
|
|
|
|
filename = full_path; // used later for possible error reporting
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-29 15:40:21 +00:00
|
|
|
// no luck. so it might be in Searchpath. So try to build absolute path
|
2010-07-05 07:41:30 +00:00
|
|
|
if (!current_music)
|
2010-08-22 13:27:47 +00:00
|
|
|
{
|
|
|
|
PHYSFSX_getRealPath(filename, full_path);
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset(Mix_LoadMUS(full_path));
|
2010-08-22 13:27:47 +00:00
|
|
|
if (current_music)
|
|
|
|
filename = full_path; // used later for possible error reporting
|
|
|
|
}
|
|
|
|
|
2010-10-29 15:40:21 +00:00
|
|
|
// still nothin'? Let's open via PhysFS in case it's located inside an archive
|
2010-08-22 13:27:47 +00:00
|
|
|
if (!current_music)
|
2010-06-14 08:13:16 +00:00
|
|
|
{
|
2015-01-17 18:31:42 +00:00
|
|
|
if (RAIIPHYSFS_File filehandle{PHYSFS_openRead(filename)})
|
2010-10-29 15:40:21 +00:00
|
|
|
{
|
2012-11-24 16:26:53 +00:00
|
|
|
unsigned len = PHYSFS_fileLength(filehandle);
|
2014-09-20 23:14:03 +00:00
|
|
|
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));
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset(Mix_LoadMUS_RW(rw), rw);
|
2010-10-29 15:40:21 +00:00
|
|
|
}
|
2010-06-14 08:13:16 +00:00
|
|
|
}
|
2006-10-17 20:52:09 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
if (current_music)
|
|
|
|
{
|
2014-08-27 02:52:21 +00:00
|
|
|
Mix_PlayMusic(current_music.get(), (loop ? -1 : 1));
|
2010-06-14 08:13:16 +00:00
|
|
|
Mix_HookMusicFinished(hook_finished_track ? hook_finished_track : mix_free_music);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-07 00:47:27 +00:00
|
|
|
con_printf(CON_CRITICAL,"Music %s could not be loaded: %s", filename, Mix_GetError());
|
2010-10-29 15:40:21 +00:00
|
|
|
mix_stop_music();
|
2010-06-14 08:13:16 +00:00
|
|
|
}
|
2008-03-23 08:42:24 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2008-03-23 08:42:24 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
// What to do when stopping song playback
|
|
|
|
void mix_free_music()
|
2008-03-23 08:42:24 +00:00
|
|
|
{
|
2010-06-14 08:13:16 +00:00
|
|
|
Mix_HaltMusic();
|
2014-08-27 02:52:21 +00:00
|
|
|
current_music.reset();
|
2014-09-20 23:14:03 +00:00
|
|
|
current_music_hndlbuf.clear();
|
2010-06-14 08:13:16 +00:00
|
|
|
}
|
2008-03-23 08:42:24 +00:00
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
void mix_set_music_volume(int vol)
|
|
|
|
{
|
2010-08-19 15:54:19 +00:00
|
|
|
vol *= MIX_MAX_VOLUME/8;
|
2010-06-14 08:13:16 +00:00
|
|
|
Mix_VolumeMusic(vol);
|
2008-03-23 08:42:24 +00:00
|
|
|
}
|
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
void mix_stop_music()
|
|
|
|
{
|
|
|
|
Mix_HaltMusic();
|
2014-09-20 23:14:03 +00:00
|
|
|
current_music_hndlbuf.clear();
|
2008-01-10 20:35:59 +00:00
|
|
|
}
|
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
void mix_pause_music()
|
|
|
|
{
|
|
|
|
Mix_PauseMusic();
|
2006-10-17 20:52:09 +00:00
|
|
|
}
|
|
|
|
|
2010-06-14 08:13:16 +00:00
|
|
|
void mix_resume_music()
|
|
|
|
{
|
|
|
|
Mix_ResumeMusic();
|
|
|
|
}
|
|
|
|
|
|
|
|
void mix_pause_resume_music()
|
|
|
|
{
|
|
|
|
if (Mix_PausedMusic())
|
|
|
|
Mix_ResumeMusic();
|
|
|
|
else if (Mix_PlayingMusic())
|
|
|
|
Mix_PauseMusic();
|
2006-10-17 20:52:09 +00:00
|
|
|
}
|