- update of music code, in particular separating the engine-specific lookup from the backend.

# Conflicts:
#	source/core/music/music.cpp

# Conflicts:
#	source/build/src/palette.cpp
This commit is contained in:
Christoph Oelckers 2020-04-12 08:07:48 +02:00
parent d0406e27b6
commit 6a8efb7520
32 changed files with 891 additions and 347 deletions

View file

@ -698,6 +698,7 @@ set (PCH_SOURCES
core/quotes.cpp
core/screenshot.cpp
core/serializer.cpp
core/raze_music.cpp
core/2d/v_2ddrawer.cpp
core/2d/v_draw.cpp

View file

@ -48,7 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "view.h"
#include "nnexts.h"
#include "savegamehelp.h"
#include "z_music.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "aibat.h"

View file

@ -30,7 +30,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "resource.h"
#include "sound.h"
#include "baselayer.h"
#include "z_music.h"
#include "raze_music.h"
#include "sfx.h"
#include "sound/s_soundinternal.h"

View file

@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#pragma once
#include "resource.h"
#include "z_music.h"
#include "raze_music.h"
BEGIN_BLD_NS

View file

@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "c_console.h"
#include "c_dispatch.h"
#include "i_specialpaths.h"
#include "z_music.h"
#include "raze_music.h"
#include "statistics.h"
#include "menu.h"
#include "gstrings.h"
@ -96,6 +96,8 @@ void InitFileSystem(TArray<GrpEntry>&);
void I_SetWindowTitle(const char* caption);
void InitENet();
void ShutdownENet();
void S_ParseSndInfo();
bool AppActive;
int chatmodeon; // needed by the common console code.
@ -273,9 +275,7 @@ void UserConfig::ProcessOptions()
AddArt.reset(Args->GatherFiles("-art"));
nologo = Args->CheckParm("-nologo") || Args->CheckParm("-quick");
nomusic = Args->CheckParm("-nomusic");
nosound = Args->CheckParm("-nosfx");
if (Args->CheckParm("-nosound")) nomusic = nosound = true;
nosound = Args->CheckParm("-nosfx") || Args->CheckParm("-nosound");
if (Args->CheckParm("-setup")) queryiwad = 1;
else if (Args->CheckParm("-nosetup")) queryiwad = 0;
@ -714,7 +714,10 @@ int RunGame()
V_InitFonts();
C_CON_SetAliases();
sfx_empty = fileSystem.FindFile("engine/dsempty.lmp"); // this must be done outside the sound code because it's initialized late.
Mus_Init();
I_InitSound();
Mus_InitMusic();
timerSetCallback(Mus_UpdateMusic);
S_ParseSndInfo();
InitStatistics();
M_Init();
SetDefaultStrings();

View file

@ -63,7 +63,7 @@ struct UserConfig
bool nomonsters = false;
bool nosound = false;
bool nomusic = false;
//bool nomusic = false;
bool nologo = false;
int setupstate = -1;
@ -77,9 +77,10 @@ struct UserConfig
extern UserConfig userConfig;
extern int nomusic;
inline bool MusicEnabled()
{
return !userConfig.nomusic;
return !nomusic;
}
inline bool SoundEnabled()

View file

@ -42,7 +42,7 @@
#include "m_argv.h"
#include "rts.h"
#include "stats.h"
#include "z_music.h"
#include "raze_music.h"
#include "c_dispatch.h"
#include "gstrings.h"
#include "quotemgr.h"

View file

@ -39,6 +39,7 @@
#include <zlib.h>
#include <zmusic.h>
#include "m_argv.h"
#include "filesystem.h"
#include "c_dispatch.h"
@ -48,69 +49,28 @@
#include "c_cvars.h"
#include "c_console.h"
#include "v_text.h"
#include "i_sound.h"
#include "i_soundfont.h"
#include "s_music.h"
#include "printf.h"
#include "timer.h"
#include "backend/i_sound.h"
#include <zmusic.h>
#include "filereadermusicinterface.h"
void I_InitSoundFonts();
void S_SetStreamVolume(float);
void S_ParseSndInfo();
EXTERN_CVAR (Int, snd_samplerate)
EXTERN_CVAR (Int, snd_mididevice)
EXTERN_CVAR(Float, snd_mastervolume)
float relative_volume = 1.f, saved_relative_volume = 1.f;
#ifdef _WIN32
int nomusic = 0;
//==========================================================================
//
// CVAR: cd_drive
//
// Which drive (letter) to use for CD audio. If not a valid drive letter,
// let the operating system decide for us.
//
//==========================================================================
EXTERN_CVAR(Bool, cd_enabled);
CUSTOM_CVAR(String, cd_drive, "", CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
{
if (cd_enabled && !Args->CheckParm("-nocdaudio")) CD_Enable(self);
}
//==========================================================================
//
// CVAR: cd_enabled
//
// Use the CD device? Can be overridden with -nocdaudio on the command line
//
//==========================================================================
CUSTOM_CVAR(Bool, cd_enabled, true, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
{
if (self && !Args->CheckParm("-nocdaudio"))
CD_Enable(cd_drive);
else
CD_Enable(nullptr);
}
#endif
//==========================================================================
//
// CVAR mus_volume
// CVAR snd_musicvolume
//
// Maximum volume of MOD/stream music.
//==========================================================================
CUSTOM_CVARD(Float, mus_volume, 0.5, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "controls music volume")
CUSTOM_CVARD(Float, snd_musicvolume, 0.5, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "controls music volume")
{
if (self < 0.f)
self = 0.f;
@ -202,20 +162,68 @@ static void mus_sfclose(void* handle)
reinterpret_cast<FSoundFontReader*>(handle)->close();
}
#ifndef ZMUSIC_LITE
//==========================================================================
//
//
// Pass some basic working data to the music backend
// We do this once at startup for everything available.
//
//==========================================================================
void Mus_Init(void)
static void SetupGenMidi()
{
I_InitSound();
I_InitSoundFonts();
S_ParseSndInfo();
// The OPL renderer should not care about where this comes from.
// Note: No I_Error here - this needs to be consistent with the rest of the music code.
auto lump = fileSystem.CheckNumForName("GENMIDI", ns_global);
if (lump < 0)
{
Printf("No GENMIDI lump found. OPL playback not available.\n");
return;
}
auto data = fileSystem.OpenFileReader(lump);
mus_volume.Callback ();
auto genmidi = data.Read();
if (genmidi.Size() < 8 + 175 * 36 || memcmp(genmidi.Data(), "#OPL_II#", 8)) return;
ZMusic_SetGenMidi(genmidi.Data()+8);
}
static void SetupWgOpn()
{
int lump = fileSystem.CheckNumForFullName("xg.wopn");
if (lump < 0)
{
return;
}
FileData data = fileSystem.ReadFile(lump);
ZMusic_SetWgOpn(data.GetMem(), (uint32_t)data.GetSize());
}
static void SetupDMXGUS()
{
int lump = fileSystem.CheckNumForFullName("DMXGUS");
if (lump < 0)
{
return;
}
FileData data = fileSystem.ReadFile(lump);
ZMusic_SetDmxGus(data.GetMem(), (uint32_t)data.GetSize());
}
#endif
//==========================================================================
//
//
//
//==========================================================================
void I_InitMusic(void)
{
I_InitSoundFonts();
snd_musicvolume.Callback ();
nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound");
snd_mididevice.Callback();
@ -230,7 +238,11 @@ void Mus_Init(void)
callbacks.SF_Close = mus_sfclose;
ZMusic_SetCallbacks(&callbacks);
timerSetCallback(S_UpdateMusic);
#ifndef ZMUSIC_LITE
SetupGenMidi();
SetupDMXGUS();
SetupWgOpn();
#endif
}
@ -244,7 +256,7 @@ void I_SetRelativeVolume(float vol)
{
relative_volume = (float)vol;
ChangeMusicSetting(zmusic_relative_volume, nullptr, (float)vol);
mus_volume.Callback();
snd_musicvolume.Callback();
}
//==========================================================================
//
@ -300,7 +312,8 @@ static ZMusic_MidiSource GetMIDISource(const char *fn)
FString src = fn;
if (src.Compare("*") == 0) src = mus_playing.name;
auto lump = fileSystem.FindFile(src);
auto lump = fileSystem.CheckNumForName(src, ns_music);
if (lump < 0) lump = fileSystem.CheckNumForFullName(src);
if (lump < 0)
{
Printf("Cannot find MIDI lump %s.\n", src.GetChars());
@ -344,7 +357,7 @@ static ZMusic_MidiSource GetMIDISource(const char *fn)
//
//==========================================================================
CCMD (writewave)
UNSAFE_CCMD (writewave)
{
if (argv.argc() >= 3 && argv.argc() <= 7)
{
@ -352,18 +365,23 @@ CCMD (writewave)
if (source == nullptr) return;
EMidiDevice dev = MDEV_DEFAULT;
#ifndef ZMUSIC_LITE
if (argv.argc() >= 6)
{
if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY;
if (!stricmp(argv[5], "WildMidi")) dev = MDEV_WILDMIDI;
else if (!stricmp(argv[5], "GUS")) dev = MDEV_GUS;
else if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY;
else if (!stricmp(argv[5], "FluidSynth")) dev = MDEV_FLUIDSYNTH;
else if (!stricmp(argv[5], "OPL")) dev = MDEV_OPL;
else if (!stricmp(argv[5], "OPN")) dev = MDEV_OPN;
else if (!stricmp(argv[5], "ADL")) dev = MDEV_ADL;
else
{
Printf("%s: Unknown MIDI device\n", argv[5]);
return;
}
}
#endif
// We must stop the currently playing music to avoid interference between two synths.
auto savedsong = mus_playing;
S_StopMusic(true);
@ -392,7 +410,7 @@ CCMD (writewave)
//
//==========================================================================
CCMD(writemidi)
UNSAFE_CCMD(writemidi)
{
if (argv.argc() != 3)
{

View file

@ -34,14 +34,26 @@
#ifndef __I_MUSIC_H__
#define __I_MUSIC_H__
#include "c_cvars.h"
class FileReader;
struct FOptionValues;
//
// MUSIC I/O
//
void I_InitMusic ();
void I_BuildMIDIMenuList (FOptionValues *);
// Volume.
void I_SetRelativeVolume(float);
void I_SetMusicVolume (double volume);
extern int nomusic;
EXTERN_CVAR(Bool, mus_enabled)
EXTERN_CVAR(Float, snd_musicvolume)
#endif //__I_MUSIC_H__

View file

@ -35,7 +35,9 @@
#include <ctype.h>
#include <assert.h>
#include "i_soundfont.h"
#include "i_soundinternal.h"
#include "cmdlib.h"
#include "i_system.h"
#include "gameconfigfile.h"
#include "filereadermusicinterface.h"
#include <zmusic.h>
@ -292,7 +294,7 @@ FileReader FPatchSetReader::OpenFile(const char *name)
FLumpPatchSetReader::FLumpPatchSetReader(const char *filename)
{
mLumpIndex = fileSystem.FindFile(filename);
mLumpIndex = fileSystem.CheckNumForFullName(filename);
mBasePath = filename;
FixPathSeperator(mBasePath);
@ -310,7 +312,7 @@ FileReader FLumpPatchSetReader::OpenFile(const char *name)
FString path;
if (IsAbsPath(name)) return FileReader(); // no absolute paths in the lump directory.
path = mBasePath + name;
auto index = fileSystem.FindFile(path);
auto index = fileSystem.CheckNumForFullName(path);
if (index < 0) return FileReader();
return fileSystem.ReopenFileReader(index);
}
@ -470,7 +472,7 @@ FSoundFontReader *FSoundFontManager::OpenSoundFont(const char *name, int allowed
if (name != nullptr)
{
const char *p = name + strlen(name) - 4;
if (p > name && !stricmp(p, ".cfg") && fileSystem.FindFile(name) >= 0)
if (p > name && !stricmp(p, ".cfg") && fileSystem.CheckNumForFullName(name) >= 0)
{
return new FLumpPatchSetReader(name);
}

View file

@ -2,7 +2,7 @@
**
** music.cpp
**
** music engine - borrowed from GZDoom
** music engine
**
** Copyright 1999-2016 Randy Heit
** Copyright 2002-2016 Christoph Oelckers
@ -35,51 +35,35 @@
**
*/
#include <zmusic.h>
#include "z_music.h"
#include "zstring.h"
#include "backend/i_sound.h"
#include "name.h"
#include "s_music.h"
#include <stdio.h>
#include <stdlib.h>
#include "i_sound.h"
#include "i_music.h"
#include "printf.h"
#include "files.h"
#include "s_playlist.h"
#include "c_dispatch.h"
#include "filesystem.h"
#include "cmdlib.h"
#include "gamecvars.h"
#include "c_dispatch.h"
#include "gamecontrol.h"
#include "s_music.h"
#include "filereadermusicinterface.h"
#include "v_text.h"
#include "mapinfo.h"
#include "serializer.h"
#include <zmusic.h>
MusPlayingInfo mus_playing;
MusicAliasMap MusicAliases;
MidiDeviceMap MidiDevices;
static bool MusicPaused; // whether music is paused
MusPlayingInfo mus_playing; // music currently being played
static FPlayList PlayList;
float relative_volume = 1.f;
float saved_relative_volume = 1.0f; // this could be used to implement an ACS FadeMusic function
MusicVolumeMap MusicVolumes;
MusicAliasMap LevelMusicAliases;
bool MusicPaused;
static bool mus_blocked;
static FString lastStartedMusic;
EXTERN_CVAR(Float, mus_volume)
CVAR(Bool, printmusicinfo, false, 0)
CVAR(Bool, mus_extendedlookup, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
MidiDeviceMap MidiDevices;
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// Order is: streaming formats, module formats, emulated formats and MIDI formats - for external files the first one found wins so ambiguous names should be avoided
static const char* knownMusicExts[] = {
"OGG", "FLAC", "MP3", "MP2", "XA", "XM", "MOD",
"IT", "S3M", "MTM", "STM", "669", "PTM", "AMF",
"OKT", "DSM", "AMFF", "SPC", "VGM", "VGZ", "AY",
"GBS", "GYM", "HES", "KSS", "NSF", "NSFE", "SAP",
"MID", "HMP", "HMI", "XMI", "VOC"
};
extern float S_GetMusicVolume (const char *music);
static void S_ActivatePlayList(bool goBack);
FString G_SetupFilenameBasedMusic(const char* fn, const char* defmusic)
{
FString name = StripExtension(fn);
FString test;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// Test if a real file with this name exists with all known extensions for music.
for (auto& ext : knownMusicExts)
@ -94,77 +78,25 @@ FString G_SetupFilenameBasedMusic(const char* fn, const char* defmusic)
return defmusic;
}
FString MusicFileExists(const char* fn)
static FileReader DefaultOpenMusic(const char* fn)
{
if (mus_extendedlookup) return G_SetupFilenameBasedMusic(fn, nullptr);
if (FileExists(fn)) return fn;
return FString();
// This is the minimum needed to make the music system functional.
FileReader fr;
fr.OpenFile(fn);
return fr;
}
static MusicCallbacks mus_cb = { nullptr, DefaultOpenMusic };
int LookupMusicLump(const char* fn)
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE --------------------------------------------------------------------
void S_SetMusicCallbacks(MusicCallbacks* cb)
{
if (mus_extendedlookup)
{
FString name = StripExtension(fn);
int l = fileSystem.FindFileWithExtensions(name, knownMusicExts, countof(knownMusicExts));
if (l >= 0) return l;
}
return fileSystem.FindFile(fn);
}
//==========================================================================
//
// Music lookup in various places.
//
//==========================================================================
FileReader LookupMusic(const char* musicname)
{
FileReader reader;
FString mus = MusicFileExists(musicname);
if (mus.IsNotEmpty())
{
// Load an external file.
reader.OpenFile(mus);
}
if (!reader.isOpen())
{
int lumpnum = LookupMusicLump(musicname);
if (mus_extendedlookup && lumpnum >= 0)
{
// EDuke also looks in a subfolder named after the main game resource. Do this as well if extended lookup is active.
auto rfn = fileSystem.GetResourceFileName(fileSystem.GetFileContainer(lumpnum));
auto rfbase = ExtractFileBase(rfn);
FStringf aliasMusicname("music/%s/%s", rfbase.GetChars(), musicname);
lumpnum = LookupMusicLump(aliasMusicname);
}
if (lumpnum == -1)
{
// Always look in the 'music' subfolder as well. This gets used by multiple setups to store ripped CD tracks.
FStringf aliasMusicname("music/%s", musicname);
lumpnum = LookupMusicLump(aliasMusicname);
}
if (lumpnum == -1 && (g_gameType & GAMEFLAG_SW))
{
// Some Shadow Warrioe distributions have the music in a subfolder named 'classic'. Check that, too.
FStringf aliasMusicname("classic/music/%s", musicname);
lumpnum = fileSystem.FindFile(aliasMusicname);
}
if (lumpnum > -1)
{
if (fileSystem.FileLength(lumpnum) >= 0)
{
reader = fileSystem.ReopenFileReader(lumpnum);
if (!reader.isOpen())
{
Printf(TEXTCOLOR_RED "Unable to play music " TEXTCOLOR_WHITE "\"%s\"\n", musicname);
}
else if (printmusicinfo) Printf("Playing music from file system %s:%s\n", fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(lumpnum)), fileSystem.GetFileFullPath(lumpnum).GetChars());
}
}
}
else if (printmusicinfo) Printf("Playing music from external file %s\n", musicname);
return reader;
mus_cb = *cb;
if (mus_cb.OpenMusic == nullptr) mus_cb.OpenMusic = DefaultOpenMusic; // without this we are dead in the water.
}
//==========================================================================
@ -240,7 +172,7 @@ static bool S_StartMusicPlaying(ZMusic_MusicStream song, bool loop, float rel_vo
}
// Notify the sound system of the changed relative volume
mus_volume.Callback();
snd_musicvolume.Callback();
return true;
}
@ -287,7 +219,6 @@ void S_ResumeMusic ()
void S_UpdateMusic ()
{
mus_blocked = false;
if (mus_playing.handle != nullptr)
{
ZMusic_Update(mus_playing.handle);
@ -297,31 +228,61 @@ void S_UpdateMusic ()
// playlist when the current song finishes.
if (!ZMusic_IsPlaying(mus_playing.handle))
{
if (PlayList.GetNumSongs())
{
PlayList.Advance();
S_ActivatePlayList(false);
}
else
{
S_StopMusic(true);
}
}
}
}
//==========================================================================
//
// S_ChangeCDMusic
// Resets the music player if music playback was paused.
//
// Starts a CD track as music.
//==========================================================================
bool S_ChangeCDMusic (int track, unsigned int id, bool looping)
void S_ResetMusic ()
{
char temp[32];
// stop the old music if it has been paused.
// This ensures that the new music is started from the beginning
// if it's the same as the last one and it has been paused.
if (MusicPaused) S_StopMusic(true);
if (id != 0)
// start new music for the level
MusicPaused = false;
}
//==========================================================================
//
// S_ActivatePlayList
//
// Plays the next song in the playlist. If no songs in the playlist can be
// played, then it is deleted.
//==========================================================================
void S_ActivatePlayList (bool goBack)
{
int startpos, pos;
startpos = pos = PlayList.GetPosition ();
S_StopMusic (true);
while (!S_ChangeMusic (PlayList.GetSong (pos), 0, false, true))
{
mysnprintf (temp, countof(temp), ",CD,%d,%x", track, id);
}
else
pos = goBack ? PlayList.Backup () : PlayList.Advance ();
if (pos == startpos)
{
mysnprintf (temp, countof(temp), ",CD,%d", track);
PlayList.Clear();
Printf ("Cannot play anything in the playlist.\n");
return;
}
}
return S_ChangeMusic (temp, 0, looping);
}
//==========================================================================
@ -340,16 +301,26 @@ bool S_StartMusic (const char *m_id)
//
// S_ChangeMusic
//
// Starts playing a music, possibly looping.
// initiates playback of a song
//
// [RH] If music is a MOD, starts it at position order. If name is of the
// format ",CD,<track>,[cd id]" song is a CD track, and if [cd id] is
// specified, it will only be played if the specified CD is in a drive.
//==========================================================================
bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
{
lastStartedMusic = musicname; // remember the last piece of music that was requested to be played.
if (nomusic) return false; // skip the entire procedure if music is globally disabled.
if (!force && PlayList.GetNumSongs())
{ // Don't change if a playlist is active
return false;
}
// Do game specific lookup.
FString musicname_;
if (mus_cb.LookupFileName)
{
musicname_ = mus_cb.LookupFileName(musicname, order);
musicname = musicname_.GetChars();
}
if (musicname == nullptr || musicname[0] == 0)
{
// Don't choke if the map doesn't have a song attached
@ -358,13 +329,10 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
mus_playing.LastSong = "";
return true;
}
if (*musicname == '/') musicname++;
FString DEH_Music;
if (!mus_playing.name.IsEmpty() &&
mus_playing.handle != nullptr &&
stricmp (mus_playing.name, musicname) == 0 &&
stricmp(mus_playing.name, musicname) == 0 &&
ZMusic_IsLooping(mus_playing.handle) == zmusic_bool(looping))
{
if (order != mus_playing.baseorder)
@ -386,29 +354,10 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
return true;
}
if (strnicmp (musicname, ",CD,", 4) == 0)
{
int track = strtoul (musicname+4, nullptr, 0);
const char *more = strchr (musicname+4, ',');
unsigned int id = 0;
if (more != nullptr)
{
id = strtoul (more+1, nullptr, 16);
}
S_StopMusic (true);
mus_playing.handle = ZMusic_OpenCDSong (track, id);
if (mus_playing.handle == nullptr)
{
Printf("Unable to start CD Audio for track #%d, ID %d\n", track, id);
}
}
else
{
int lumpnum = -1;
int length = 0;
ZMusic_MusicStream handle = nullptr;
MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname);
MidiDeviceSetting* devp = MidiDevices.CheckKey(musicname);
// Strip off any leading file:// component.
if (strncmp(musicname, "file://", 7) == 0)
@ -416,14 +365,15 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
musicname += 7;
}
FileReader reader = LookupMusic(musicname);
// opening the music must be done by the game because it's different depending on the game's file system use.
FileReader reader = mus_cb.OpenMusic(musicname);
if (!reader.isOpen()) return false;
// shutdown old music
S_StopMusic (true);
S_StopMusic(true);
// Just record it if volume is 0 or music was disabled
if (mus_volume <= 0 || !mus_enabled)
if (snd_musicvolume <= 0 || !mus_enabled)
{
mus_playing.loop = looping;
mus_playing.name = musicname;
@ -440,13 +390,12 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
else
{
auto mreader = GetMusicReader(reader); // this passes the file reader to the newly created wrapper.
mus_playing.handle = ZMusic_OpenSong(mreader, devp? (EMidiDevice)devp->device : MDEV_DEFAULT, devp? devp->args.GetChars() : "");
mus_playing.handle = ZMusic_OpenSong(mreader, devp ? (EMidiDevice)devp->device : MDEV_DEFAULT, devp ? devp->args.GetChars() : "");
if (mus_playing.handle == nullptr)
{
Printf("Unable to load %s: %s\n", mus_playing.name.GetChars(), ZMusic_GetLastError());
}
}
}
mus_playing.loop = looping;
mus_playing.name = musicname;
@ -477,7 +426,8 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
void S_RestartMusic ()
{
if (!mus_playing.LastSong.IsEmpty() && mus_volume > 0 && mus_enabled)
if (snd_musicvolume <= 0) return;
if (!mus_playing.LastSong.IsEmpty() && mus_enabled)
{
FString song = mus_playing.LastSong;
mus_playing.LastSong = "";
@ -542,7 +492,7 @@ void S_StopMusic (bool force)
try
{
// [RH] Don't stop if a playlist is active.
if (!mus_playing.name.IsEmpty())
if ((force || PlayList.GetNumSongs() == 0) && !mus_playing.name.IsEmpty())
{
if (mus_playing.handle != nullptr)
{
@ -556,7 +506,7 @@ void S_StopMusic (bool force)
mus_playing.LastSong = std::move(mus_playing.name);
}
}
catch (const CRecoverableError& )
catch (const std::runtime_error& )
{
//Printf("Unable to stop %s: %s\n", mus_playing.name.GetChars(), err.what());
if (mus_playing.handle != nullptr)
@ -577,10 +527,11 @@ void S_StopMusic (bool force)
CCMD (changemus)
{
if (MusicEnabled())
if (!nomusic)
{
if (argv.argc() > 1)
{
PlayList.Clear();
S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0);
}
else
@ -610,104 +561,136 @@ CCMD (changemus)
CCMD (stopmus)
{
PlayList.Clear();
S_StopMusic (false);
mus_playing.LastSong = ""; // forget the last played song so that it won't get restarted if some volume changes occur
}
static FString lastMusicLevel, lastMusic;
int Mus_Play(const char *mapname, const char *fn, bool loop)
//==========================================================================
//
// CCMD playlist
//
//==========================================================================
UNSAFE_CCMD (playlist)
{
if (mus_blocked) return 1; // Caller should believe it succeeded.
// Store the requested names for resuming.
lastMusicLevel = mapname;
lastMusic = fn;
int argc = argv.argc();
if (!MusicEnabled())
if (argc < 2 || argc > 3)
{
return 0;
Printf ("playlist <playlist.m3u> [<position>|shuffle]\n");
}
// Allow per level music substitution.
// For most cases using $musicalias would be sufficient, but that method only works if a level actually has some music defined at all.
// This way it can be done with an add-on definition lump even in cases like Redneck Rampage where no music definitions exist
// or where music gets reused for multiple levels but replacement is wanted individually.
if (mapname && *mapname)
else
{
if (*mapname == '/') mapname++;
FName *check = LevelMusicAliases.CheckKey(FName(mapname, true));
if (check) fn = check->GetChars();
}
// Now perform music aliasing. This also needs to be done before checking identities because multiple names can map to the same song.
FName* aliasp = MusicAliases.CheckKey(fn);
if (aliasp != nullptr)
{
if (*aliasp == NAME_None)
if (!PlayList.ChangeList(argv[1]))
{
return true; // flagged to be ignored
}
fn = aliasp->GetChars();
Printf("Could not open " TEXTCOLOR_BOLD "%s" TEXTCOLOR_NORMAL ": %s\n", argv[1], strerror(errno));
return;
}
if (!mus_restartonload)
if (PlayList.GetNumSongs () > 0)
{
// If the currently playing piece of music is the same, do not restart. Note that there's still edge cases where this may fail to detect identities.
if (mus_playing.handle != nullptr && lastStartedMusic.CompareNoCase(fn) == 0 && mus_playing.loop)
return true;
}
return S_ChangeMusic(fn, 0, loop, true);
}
bool Mus_IsPlaying()
{
return mus_playing.handle != nullptr;
}
void Mus_Stop()
{
if (mus_blocked) return;
S_StopMusic(true);
}
void Mus_Fade(double seconds)
{
// Todo: Blood uses this, but the streamer cannot currently fade the volume.
Mus_Stop();
}
void Mus_SetPaused(bool on)
{
if (on) S_PauseMusic();
else S_ResumeMusic();
}
void Mus_Serialize(FSerializer &arc)
{
if (arc.BeginObject("music"))
{
if (arc.isWriting())
if (argc == 3)
{
FString music = mus_playing.name;
if (music.IsEmpty()) music = mus_playing.LastSong;
arc.AddString("music", music);
if (stricmp (argv[2], "shuffle") == 0)
{
PlayList.Shuffle ();
}
else
{
PlayList.SetPosition (atoi (argv[2]));
}
}
S_ActivatePlayList (false);
}
else arc("music", mus_playing.LastSong);
arc("baseorder", mus_playing.baseorder)
("loop", mus_playing.loop)
.EndObject();
// this is to prevent scripts from resetting the music after it has been loaded from the savegame.
if (arc.isReading()) mus_blocked = true;
// Actual music resuming cannot be performed here, it must be done in the game code.
}
}
void Mus_ResumeSaved()
//==========================================================================
//
// CCMD playlistpos
//
//==========================================================================
static bool CheckForPlaylist ()
{
S_RestartMusic();
if (PlayList.GetNumSongs() == 0)
{
Printf ("No playlist is playing.\n");
return false;
}
return true;
}
CCMD (playlistpos)
{
if (CheckForPlaylist() && argv.argc() > 1)
{
PlayList.SetPosition (atoi (argv[1]) - 1);
S_ActivatePlayList (false);
}
}
//==========================================================================
//
// CCMD playlistnext
//
//==========================================================================
CCMD (playlistnext)
{
if (CheckForPlaylist())
{
PlayList.Advance ();
S_ActivatePlayList (false);
}
}
//==========================================================================
//
// CCMD playlistprev
//
//==========================================================================
CCMD (playlistprev)
{
if (CheckForPlaylist())
{
PlayList.Backup ();
S_ActivatePlayList (true);
}
}
//==========================================================================
//
// CCMD playliststatus
//
//==========================================================================
CCMD (playliststatus)
{
if (CheckForPlaylist ())
{
Printf ("Song %d of %d:\n%s\n",
PlayList.GetPosition () + 1,
PlayList.GetNumSongs (),
PlayList.GetSong (PlayList.GetPosition ()));
}
}
//==========================================================================
//
//
//
//==========================================================================
CCMD(currentmusic)
{
if (mus_playing.name.IsNotEmpty())
{
Printf("Currently playing music '%s'\n", mus_playing.name.GetChars());
}
else
{
Printf("Currently no music playing\n");
}
}

View file

@ -43,7 +43,7 @@
//==========================================================================
//
//
// ADL Midi device
//
//==========================================================================
@ -63,6 +63,47 @@
auto ret = ChangeMusicSetting(zmusic_##key, mus_playing.handle,*self); \
if (ret) S_MIDIDeviceChanged(-1);
#ifndef ZMUSIC_LITE
CUSTOM_CVAR(Int, adl_chips_count, 6, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(adl_chips_count);
}
CUSTOM_CVAR(Int, adl_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(adl_emulator_id);
}
CUSTOM_CVAR(Bool, adl_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(adl_run_at_pcm_rate);
}
CUSTOM_CVAR(Bool, adl_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(adl_fullpan);
}
CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(adl_bank);
}
CUSTOM_CVAR(Bool, adl_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(adl_use_custom_bank);
}
CUSTOM_CVAR(String, adl_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(adl_custom_bank);
}
CUSTOM_CVAR(Int, adl_volume_model, 3/*ADLMIDI_VolumeModel_DMX*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(adl_bank);
}
#endif
//==========================================================================
//
// Fluidsynth MIDI device
@ -182,10 +223,194 @@ CUSTOM_CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIR
FORWARD_BOOL_CVAR(opl_fullpan);
}
#ifndef ZMUSIC_LITE
//==========================================================================
//
// OPN MIDI device
//
//==========================================================================
CUSTOM_CVAR(Int, opn_chips_count, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(opn_chips_count);
}
CUSTOM_CVAR(Int, opn_emulator_id, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(opn_emulator_id);
}
CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(opn_run_at_pcm_rate);
}
CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(opn_fullpan);
}
CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(opn_use_custom_bank);
}
CUSTOM_CVAR(String, opn_custom_bank, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(opn_custom_bank);
}
//==========================================================================
//
// GUS MIDI device
//
//==========================================================================
CUSTOM_CVAR(String, midi_config, GAMENAMELOWERCASE, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(gus_config);
}
CUSTOM_CVAR(Bool, midi_dmxgus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL) // This was 'true' but since it requires special setup that's not such a good idea.
{
FORWARD_BOOL_CVAR(gus_dmxgus);
}
CUSTOM_CVAR(String, gus_patchdir, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(gus_patchdir);
}
CUSTOM_CVAR(Int, midi_voices, 32, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(gus_midi_voices);
}
CUSTOM_CVAR(Int, gus_memsize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(gus_memsize);
}
//==========================================================================
//
// Timidity++ device
//
//==========================================================================
CUSTOM_CVAR(Bool, timidity_modulation_wheel, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_modulation_wheel);
}
CUSTOM_CVAR(Bool, timidity_portamento, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_portamento);
}
CUSTOM_CVAR(Int, timidity_reverb, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_reverb);
}
CUSTOM_CVAR(Int, timidity_reverb_level, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_reverb_level);
}
CUSTOM_CVAR(Int, timidity_chorus, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_chorus);
}
CUSTOM_CVAR(Bool, timidity_surround_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_surround_chorus);
}
CUSTOM_CVAR(Bool, timidity_channel_pressure, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_channel_pressure);
}
CUSTOM_CVAR(Int, timidity_lpf_def, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_lpf_def);
}
CUSTOM_CVAR(Bool, timidity_temper_control, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_temper_control);
}
CUSTOM_CVAR(Bool, timidity_modulation_envelope, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_modulation_envelope);
}
CUSTOM_CVAR(Bool, timidity_overlap_voice_allow, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_overlap_voice_allow);
}
CUSTOM_CVAR(Bool, timidity_drum_effect, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_drum_effect);
}
CUSTOM_CVAR(Bool, timidity_pan_delay, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(timidity_pan_delay);
}
CUSTOM_CVAR(Float, timidity_drum_power, 1.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL) /* coef. of drum amplitude */
{
FORWARD_CVAR(timidity_drum_power);
}
CUSTOM_CVAR(Int, timidity_key_adjust, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_key_adjust);
}
CUSTOM_CVAR(Float, timidity_tempo_adjust, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_tempo_adjust);
}
CUSTOM_CVAR(Float, timidity_min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_CVAR(timidity_min_sustain_time);
}
#endif
CUSTOM_CVAR(String, timidity_config, GAMENAMELOWERCASE, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(timidity_config);
}
#ifndef ZMUSIC_LITE
//==========================================================================
//
// WildMidi
//
//==========================================================================
CUSTOM_CVAR(String, wildmidi_config, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_STRING_CVAR(wildmidi_config);
}
CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(wildmidi_reverb);
}
CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
{
FORWARD_BOOL_CVAR(wildmidi_enhanced_resampling);
}
#endif
//==========================================================================
//

View file

@ -43,6 +43,8 @@
#include "sc_man.h"
#include <zmusic.h>
#include "raze_music.h"
// MACROS ------------------------------------------------------------------
enum SICommands

View file

@ -7,6 +7,15 @@
#include "name.h"
#include <zmusic.h>
class FileReader;
struct MusicCallbacks
{
FString(*LookupFileName)(const char* fn, int &order);
FileReader(*OpenMusic)(const char* fn);
};
void S_SetMusicCallbacks(MusicCallbacks* cb);
void S_CreateStream();
void S_PauseStream(bool pause);
void S_StopStream();
@ -15,7 +24,7 @@ void S_SetStreamVolume(float vol);
//
void S_InitMusic ();
void S_StartMusic ();
void S_ResetMusic ();
// Start music using <music_name>
@ -24,9 +33,6 @@ bool S_StartMusic (const char *music_name);
// Start music using <music_name>, and set whether looping
bool S_ChangeMusic (const char *music_name, int order=0, bool looping=true, bool force=false);
// Start playing a cd track as music
bool S_ChangeCDMusic (int track, unsigned int id=0, bool looping=true);
void S_RestartMusic ();
void S_MIDIDeviceChanged(int newdev);
@ -50,11 +56,9 @@ struct MidiDeviceSetting
FString args;
};
typedef TMap<FName, FName> MusicAliasMap;
typedef TMap<FName, MidiDeviceSetting> MidiDeviceMap;
typedef TMap<FName, float> MusicVolumeMap;
extern MusicAliasMap MusicAliases;
extern MidiDeviceMap MidiDevices;
extern MusicVolumeMap MusicVolumes;
@ -71,8 +75,5 @@ extern MusPlayingInfo mus_playing;
extern float relative_volume, saved_relative_volume;
// Note for later when the OPL player is ported.
// DN3D and related games use "d3dtimbr.tmb"
#endif

282
source/core/raze_music.cpp Normal file
View file

@ -0,0 +1,282 @@
/*
**
** raze_music.cpp
** music player for Build games
**
** Copyright 2019-2020 Christoph Oelckers
**
**---------------------------------------------------------------------------
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "raze_music.h"
#include "s_music.h"
#include "c_cvars.h"
#include "cmdlib.h"
#include "filesystem.h"
#include "files.h"
#include "i_music.h"
#include "gamecontrol.h"
#include "serializer.h"
static bool mus_blocked;
static FString lastStartedMusic;
MusicAliasMap MusicAliases;
MusicAliasMap LevelMusicAliases;
CVAR(Bool, printmusicinfo, false, 0)
CVAR(Bool, mus_extendedlookup, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
// Order is: streaming formats, module formats, emulated formats and MIDI formats - for external files the first one found wins so ambiguous names should be avoided
static const char* knownMusicExts[] = {
"OGG", "FLAC", "MP3", "MP2", "XA", "XM", "MOD",
"IT", "S3M", "MTM", "STM", "669", "PTM", "AMF",
"OKT", "DSM", "AMFF", "SPC", "VGM", "VGZ", "AY",
"GBS", "GYM", "HES", "KSS", "NSF", "NSFE", "SAP",
"MID", "HMP", "HMI", "XMI", "VOC"
};
//==========================================================================
//
// Music file name lookup
//
//==========================================================================
FString G_SetupFilenameBasedMusic(const char* fn, const char* defmusic)
{
FString name = StripExtension(fn);
FString test;
// Test if a real file with this name exists with all known extensions for music.
for (auto& ext : knownMusicExts)
{
test.Format("%s.%s", name.GetChars(), ext);
if (FileExists(test)) return test;
#ifdef __unix__
test.Format("%s.%s", name.GetChars(), FString(ext.GetChars()).MakeLower().GetChars());
if (FileExists(test)) return test;
#endif
}
return defmusic;
}
FString MusicFileExists(const char* fn)
{
if (mus_extendedlookup) return G_SetupFilenameBasedMusic(fn, nullptr);
if (FileExists(fn)) return fn;
return FString();
}
int LookupMusicLump(const char* fn)
{
if (mus_extendedlookup)
{
FString name = StripExtension(fn);
int l = fileSystem.FindFileWithExtensions(name, knownMusicExts, countof(knownMusicExts));
if (l >= 0) return l;
}
return fileSystem.CheckNumForFullName(fn, true, ns_music);
}
//==========================================================================
//
// Music lookup in various places.
//
//==========================================================================
FileReader OpenMusic(const char* musicname)
{
FileReader reader;
if (!mus_restartonload)
{
// If the currently playing piece of music is the same, do not restart. Note that there's still edge cases where this may fail to detect identities.
if (mus_playing.handle != nullptr && lastStartedMusic.CompareNoCase(musicname) == 0 && mus_playing.loop)
return reader;
}
lastStartedMusic = musicname; // remember the last piece of music that was requested to be played.
FString mus = MusicFileExists(musicname);
if (mus.IsNotEmpty())
{
// Load an external file.
reader.OpenFile(mus);
}
if (!reader.isOpen())
{
int lumpnum = LookupMusicLump(musicname);
if (mus_extendedlookup && lumpnum >= 0)
{
// EDuke also looks in a subfolder named after the main game resource. Do this as well if extended lookup is active.
auto rfn = fileSystem.GetResourceFileName(fileSystem.GetFileContainer(lumpnum));
auto rfbase = ExtractFileBase(rfn);
FStringf aliasMusicname("music/%s/%s", rfbase.GetChars(), musicname);
lumpnum = LookupMusicLump(aliasMusicname);
}
if (lumpnum == -1)
{
// Always look in the 'music' subfolder as well. This gets used by multiple setups to store ripped CD tracks.
FStringf aliasMusicname("music/%s", musicname);
lumpnum = LookupMusicLump(aliasMusicname);
}
if (lumpnum == -1 && (g_gameType & GAMEFLAG_SW))
{
// Some Shadow Warrioe distributions have the music in a subfolder named 'classic'. Check that, too.
FStringf aliasMusicname("classic/music/%s", musicname);
lumpnum = fileSystem.FindFile(aliasMusicname);
}
if (lumpnum > -1)
{
if (fileSystem.FileLength(lumpnum) >= 0)
{
reader = fileSystem.ReopenFileReader(lumpnum);
if (!reader.isOpen())
{
Printf(TEXTCOLOR_RED "Unable to play music " TEXTCOLOR_WHITE "\"%s\"\n", musicname);
}
else if (printmusicinfo) Printf("Playing music from file system %s:%s\n", fileSystem.GetResourceFileFullName(fileSystem.GetFileContainer(lumpnum)), fileSystem.GetFileFullPath(lumpnum).GetChars());
}
}
}
else if (printmusicinfo) Printf("Playing music from external file %s\n", musicname);
return reader;
}
static FString LookupMusic(const char* musicname, int& order)
{
// Now perform music aliasing. This also needs to be done before checking identities because multiple names can map to the same song.
FName* aliasp = MusicAliases.CheckKey(musicname);
if (aliasp != nullptr)
{
if (*aliasp == NAME_None)
{
return true; // flagged to be ignored
}
return aliasp->GetChars();
}
return musicname;
}
static FString lastMusicLevel, lastMusic;
int Mus_Play(const char *mapname, const char *fn, bool loop)
{
if (mus_blocked) return 1; // Caller should believe it succeeded.
// Store the requested names for resuming.
lastMusicLevel = mapname;
lastMusic = fn;
if (!MusicEnabled())
{
return 0;
}
// Allow per level music substitution.
// For most cases using $musicalias would be sufficient, but that method only works if a level actually has some music defined at all.
// This way it can be done with an add-on definition lump even in cases like Redneck Rampage where no music definitions exist
// or where music gets reused for multiple levels but replacement is wanted individually.
if (mapname && *mapname)
{
if (*mapname == '/') mapname++;
FName *check = LevelMusicAliases.CheckKey(FName(mapname, true));
if (check) fn = check->GetChars();
}
return S_ChangeMusic(fn, 0, loop, true);
}
bool Mus_IsPlaying()
{
return mus_playing.handle != nullptr;
}
void Mus_Stop()
{
if (mus_blocked) return;
S_StopMusic(true);
}
void Mus_Fade(double seconds)
{
// Todo: Blood uses this, but the streamer cannot currently fade the volume.
Mus_Stop();
}
void Mus_SetPaused(bool on)
{
if (on) S_PauseMusic();
else S_ResumeMusic();
}
void Mus_Serialize(FSerializer &arc)
{
if (arc.BeginObject("music"))
{
if (arc.isWriting())
{
FString music = mus_playing.name;
if (music.IsEmpty()) music = mus_playing.LastSong;
arc.AddString("music", music);
}
else arc("music", mus_playing.LastSong);
arc("baseorder", mus_playing.baseorder)
("loop", mus_playing.loop)
.EndObject();
// this is to prevent scripts from resetting the music after it has been loaded from the savegame.
if (arc.isReading()) mus_blocked = true;
// Actual music resuming cannot be performed here, it must be done in the game code.
}
}
void Mus_ResumeSaved()
{
S_RestartMusic();
}
void Mus_UpdateMusic()
{
mus_blocked = false;
S_UpdateMusic();
}
void Mus_InitMusic()
{
I_InitMusic();
static MusicCallbacks mus_cb =
{
LookupMusic,
OpenMusic
};
S_SetMusicCallbacks(&mus_cb);
}

View file

@ -1,10 +1,15 @@
#pragma once
#include "zstring.h"
#include "tarray.h"
#include "name.h"
typedef TMap<FName, FName> MusicAliasMap;
extern MusicAliasMap MusicAliases;
// Totally minimalistic interface - should be all the game modules need.
void Mus_Init();
void Mus_InitMusic();
void Mus_UpdateMusic();
int Mus_Play(const char *mapname, const char *fn, bool loop);
void Mus_Stop();
bool Mus_IsPlaying();

View file

@ -49,7 +49,7 @@
#include "m_argv.h"
#include "serializer.h"
#include "version.h"
#include "z_music.h"
#include "raze_music.h"
#include "s_soundinternal.h"
static CompositeSavegameWriter savewriter;

View file

@ -48,13 +48,13 @@
#include "c_cvars.h"
#include "stats.h"
#include "s_music.h"
#include "z_music.h"
#include "raze_music.h"
#include "gamecvars.h"
#include "gamecontrol.h"
#include <zmusic.h>
EXTERN_CVAR (Float, snd_sfxvolume)
EXTERN_CVAR(Float, mus_volume)
EXTERN_CVAR(Float, snd_musicvolume)
CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, snd_hrtf, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
@ -95,7 +95,7 @@ CUSTOM_CVAR(Float, snd_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVA
ChangeMusicSetting(zmusic_snd_mastervolume, nullptr, self);
snd_sfxvolume.Callback();
mus_volume.Callback();
snd_musicvolume.Callback();
}
//==========================================================================

View file

@ -33,7 +33,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "savegamehelp.h"
#include "menu/menu.h"
#include "mapinfo.h"
#include "z_music.h"
#include "raze_music.h"
BEGIN_DUKE_NS

View file

@ -46,6 +46,13 @@ double g_moveActorsTime, g_moveWorldTime; // in ms, smoothed
int32_t g_noLogoAnim = 0;
int32_t g_noLogo = 0;
void PlayBonusMusic()
{
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
}
////////// OFTEN-USED FEW-LINERS //////////
#ifndef EDUKE32_STANDALONE
static void G_HandleEventsWhileNoInput(void)
@ -1921,8 +1928,7 @@ void G_BonusScreen(int32_t bonusonly)
videoClearScreen(0);
G_DisplayMPResultsScreen();
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
PlayBonusMusic();
videoNextPage();
inputState.ClearAllInput();
@ -1960,8 +1966,7 @@ void G_BonusScreen(int32_t bonusonly)
gametext_center_shade(192, GStrings("PRESSKEY"), quotepulseshade);
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
PlayBonusMusic();
videoNextPage();
inputState.ClearAllInput();

View file

@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "compat.h"
#include "duke3d.h"
#include "z_music.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "sound/s_soundinternal.h"

View file

@ -31,7 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sounds_common.h"
#include "sound/s_soundinternal.h"
#include "z_music.h"
#include "raze_music.h"
BEGIN_DUKE_NS

View file

@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "exhumed.h"
#include <stdio.h>
#include <stdlib.h>
#include "z_music.h"
#include "raze_music.h"
BEGIN_PS_NS

View file

@ -28,7 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "i_specialpaths.h"
#include "gamecontrol.h"
#include "version.h"
#include "z_music.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "savegamehelp.h"

View file

@ -46,6 +46,13 @@ double g_moveActorsTime, g_moveWorldTime; // in ms, smoothed
int32_t g_noLogoAnim = 0;
int32_t g_noLogo = 0;
void PlayBonusMusic()
{
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
}
////////// OFTEN-USED FEW-LINERS //////////
static void G_HandleEventsWhileNoInput(void)
{
@ -1930,8 +1937,7 @@ void G_BonusScreen(int32_t bonusonly)
videoClearScreen(0);
G_DisplayMPResultsScreen();
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
PlayBonusMusic();
videoNextPage();
inputState.ClearAllInput();
@ -1970,10 +1976,9 @@ void G_BonusScreen(int32_t bonusonly)
menutext_center(20-6, lastmapname);
menutext_center(36-6, GStrings("Completed"));
gametext_center_shade(192, GStrings("PRESSKEY"), quotepulseshade);
gametext_center_shade(192, GStrings("PRESSKEY"), quotepulseshade);
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
PlayBonusMusic();
}
else
{
@ -2504,8 +2509,7 @@ void G_BonusScreenRRRA(int32_t bonusonly)
videoClearScreen(0);
G_DisplayMPResultsScreen();
if (MusicEnabled() && mus_enabled)
S_PlaySound(BONUSMUSIC, CHAN_AUTO, CHANF_UI);
PlayBonusMusic();
videoNextPage();
inputState.ClearAllInput();

View file

@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "compat.h"
#include "duke3d.h"
#include "z_music.h"
#include "raze_music.h"
#include "mapinfo.h"
#include "sound/s_soundinternal.h"

View file

@ -31,7 +31,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "sounds_common.h"
#include "sound/s_soundinternal.h"
#include "z_music.h"
#include "raze_music.h"
BEGIN_RR_NS

View file

@ -94,7 +94,7 @@ Things required to make savegames work:
#include "m_argv.h"
#include "debugbreak.h"
#include "menu/menu.h"
#include "z_music.h"
#include "raze_music.h"
#include "statistics.h"
#include "gstrings.h"
#include "mapinfo.h"

View file

@ -65,7 +65,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
#include "interpso.h"
#include "menu/menu.h"
#include "gstrings.h"
#include "z_music.h"
#include "raze_music.h"
BEGIN_SW_NS

View file

@ -54,7 +54,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
#include "player.h"
#include "i_specialpaths.h"
#include "savegamehelp.h"
#include "z_music.h"
#include "raze_music.h"
#include "mapinfo.h"
//void TimerFunc(task * Task);

View file

@ -49,7 +49,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
#include "menus.h"
#include "config.h"
#include "menu/menu.h"
#include "z_music.h"
#include "raze_music.h"
#include "sound/s_soundinternal.h"
#include "filesystem.h"
#include "serializer.h"

View file

@ -1387,7 +1387,7 @@ OptionMenu SoundOptions //protected
Option "$SNDMNU_SNDENABLED", "snd_enabled", "YesNo"
Slider "$SNDMNU_SFXVOLUME", "snd_sfxvolume", 0, 1, 0.05, 2
Option "$SNDMNU_MUSENABLED", "mus_enabled", "YesNo"
Slider "$SNDMNU_MUSICVOLUME", "mus_volume", 0, 1, 0.05, 2
Slider "$SNDMNU_MUSICVOLUME", "snd_musicvolume", 0, 1, 0.05, 2
Option "$SNDMNU_MENUSOUND", "menu_sounds", "OnOff" // placeholder until the slider can be made to work
//Slider "$SNDMNU_MENUVOLUME", "snd_menuvolume", 0, 1, 0.05, 2
StaticText " "