mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-10 23:02:03 +00:00
- deleted duplicated music code.
Apparently this got back in with some cherry pick.
This commit is contained in:
parent
0d7fc1263e
commit
f658b0f33d
7 changed files with 0 additions and 2605 deletions
|
@ -1,489 +0,0 @@
|
|||
/*
|
||||
** i_music.cpp
|
||||
** Plays music
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2010 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "m_argv.h"
|
||||
#include "w_wad.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "templates.h"
|
||||
#include "stats.h"
|
||||
#include "c_cvars.h"
|
||||
#include "c_console.h"
|
||||
#include "vm.h"
|
||||
#include "v_text.h"
|
||||
#include "i_sound.h"
|
||||
#include "i_soundfont.h"
|
||||
#include "s_music.h"
|
||||
#include "doomstat.h"
|
||||
#include "zmusic/zmusic.h"
|
||||
#include "zmusic/musinfo.h"
|
||||
#include "streamsources/streamsource.h"
|
||||
#include "filereadermusicinterface.h"
|
||||
#include "../libraries/zmusic/midisources/midisource.h"
|
||||
|
||||
|
||||
|
||||
void I_InitSoundFonts();
|
||||
|
||||
EXTERN_CVAR (Int, snd_samplerate)
|
||||
EXTERN_CVAR (Int, snd_mididevice)
|
||||
|
||||
static bool ungzip(uint8_t *data, int size, std::vector<uint8_t> &newdata);
|
||||
|
||||
int nomusic = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
void I_InitMusicWin32();
|
||||
|
||||
#include "musicformats/win32/i_cd.h"
|
||||
//==========================================================================
|
||||
//
|
||||
// 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 snd_musicvolume
|
||||
//
|
||||
// Maximum volume of MOD/stream music.
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR (Float, snd_musicvolume, 0.5f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < 0.f)
|
||||
self = 0.f;
|
||||
else if (self > 1.f)
|
||||
self = 1.f;
|
||||
else
|
||||
{
|
||||
// Set general music volume.
|
||||
ChangeMusicSetting(ZMusic::snd_musicvolume, nullptr, self);
|
||||
if (GSnd != nullptr)
|
||||
{
|
||||
GSnd->SetMusicVolume(clamp<float>(self * relative_volume * snd_mastervolume, 0, 1));
|
||||
}
|
||||
// For music not implemented through the digital sound system,
|
||||
// let them know about the change.
|
||||
if (mus_playing.handle != nullptr)
|
||||
{
|
||||
mus_playing.handle->MusicVolumeChanged();
|
||||
}
|
||||
else
|
||||
{ // If the music was stopped because volume was 0, start it now.
|
||||
S_RestartMusic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Callbacks for the music system.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void tim_printfunc(int type, int verbosity_level, const char* fmt, ...)
|
||||
{
|
||||
if (verbosity_level >= 3/*Timidity::VERB_DEBUG*/) return; // Don't waste time on diagnostics.
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
FString msg;
|
||||
msg.VFormat(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 2:// Timidity::CMSG_ERROR:
|
||||
Printf(TEXTCOLOR_RED "%s\n", msg.GetChars());
|
||||
break;
|
||||
|
||||
case 1://Timidity::CMSG_WARNING:
|
||||
Printf(TEXTCOLOR_YELLOW "%s\n", msg.GetChars());
|
||||
break;
|
||||
|
||||
case 0://Timidity::CMSG_INFO:
|
||||
DPrintf(DMSG_SPAMMY, "%s\n", msg.GetChars());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void wm_printfunc(const char* wmfmt, va_list args)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED);
|
||||
VPrintf(PRINT_HIGH, wmfmt, args);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// other callbacks
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static short* dumb_decode_vorbis_(int outlen, const void* oggstream, int sizebytes)
|
||||
{
|
||||
return GSnd->DecodeSample(outlen, oggstream, sizebytes, CODEC_Vorbis);
|
||||
}
|
||||
|
||||
static std::string mus_NicePath(const char* str)
|
||||
{
|
||||
FString strv = NicePath(str);
|
||||
return strv.GetChars();
|
||||
}
|
||||
|
||||
static const char* mus_pathToSoundFont(const char* sfname, int type)
|
||||
{
|
||||
auto info = sfmanager.FindSoundFont(sfname, type);
|
||||
return info ? info->mFilename.GetChars() : nullptr;
|
||||
}
|
||||
|
||||
static MusicIO::SoundFontReaderInterface* mus_openSoundFont(const char* sfname, int type)
|
||||
{
|
||||
return sfmanager.OpenSoundFont(sfname, type);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Pass some basic working data to the music backend
|
||||
// We do this once at startup for everything available.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void SetupGenMidi()
|
||||
{
|
||||
// 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 = Wads.CheckNumForName("GENMIDI", ns_global);
|
||||
if (lump < 0)
|
||||
{
|
||||
Printf("No GENMIDI lump found. OPL playback not available.");
|
||||
return;
|
||||
}
|
||||
auto data = Wads.OpenLumpReader(lump);
|
||||
|
||||
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 = Wads.CheckNumForFullName("xg.wopn");
|
||||
if (lump < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FMemLump data = Wads.ReadLump(lump);
|
||||
ZMusic_SetWgOpn(data.GetMem(), (uint32_t)data.GetSize());
|
||||
}
|
||||
|
||||
static void SetupDMXGUS()
|
||||
{
|
||||
int lump = Wads.CheckNumForFullName("DMXGUS");
|
||||
if (lump < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FMemLump data = Wads.ReadLump(lump);
|
||||
ZMusic_SetDmxGus(data.GetMem(), (uint32_t)data.GetSize());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void I_InitMusic (void)
|
||||
{
|
||||
I_InitSoundFonts();
|
||||
|
||||
snd_musicvolume.Callback ();
|
||||
|
||||
nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound");
|
||||
|
||||
#ifdef _WIN32
|
||||
I_InitMusicWin32 ();
|
||||
#endif // _WIN32
|
||||
|
||||
Callbacks callbacks;
|
||||
|
||||
callbacks.Fluid_MessageFunc = Printf;
|
||||
callbacks.GUS_MessageFunc = callbacks.Timidity_Messagefunc = tim_printfunc;
|
||||
callbacks.WildMidi_MessageFunc = wm_printfunc;
|
||||
callbacks.NicePath = mus_NicePath;
|
||||
callbacks.PathForSoundfont = mus_pathToSoundFont;
|
||||
callbacks.OpenSoundFont = mus_openSoundFont;
|
||||
callbacks.DumbVorbisDecode = dumb_decode_vorbis_;
|
||||
|
||||
ZMusic_SetCallbacks(&callbacks);
|
||||
SetupGenMidi();
|
||||
SetupDMXGUS();
|
||||
SetupWgOpn();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void I_SetRelativeVolume(float vol)
|
||||
{
|
||||
relative_volume = (float)vol;
|
||||
ChangeMusicSetting(ZMusic::relative_volume, nullptr, (float)vol);
|
||||
snd_musicvolume.Callback();
|
||||
}
|
||||
//==========================================================================
|
||||
//
|
||||
// Sets relative music volume. Takes $musicvolume in SNDINFO into consideration
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void I_SetMusicVolume (double factor)
|
||||
{
|
||||
factor = clamp(factor, 0., 2.0);
|
||||
I_SetRelativeVolume((float)factor);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// test a relative music volume
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD(testmusicvol)
|
||||
{
|
||||
if (argv.argc() > 1)
|
||||
{
|
||||
I_SetRelativeVolume((float)strtod(argv[1], nullptr));
|
||||
}
|
||||
else
|
||||
Printf("Current relative volume is %1.2f\n", relative_volume);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// STAT music
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ADD_STAT(music)
|
||||
{
|
||||
if (mus_playing.handle != nullptr)
|
||||
{
|
||||
return FString(mus_playing.handle->GetStats().c_str());
|
||||
}
|
||||
return "No song playing";
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Common loader for the dumpers.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static MIDISource *GetMIDISource(const char *fn)
|
||||
{
|
||||
FString src = fn;
|
||||
if (src.Compare("*") == 0) src = mus_playing.name;
|
||||
|
||||
auto lump = Wads.CheckNumForName(src, ns_music);
|
||||
if (lump < 0) lump = Wads.CheckNumForFullName(src);
|
||||
if (lump < 0)
|
||||
{
|
||||
Printf("Cannot find MIDI lump %s.\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wlump = Wads.OpenLumpReader(lump);
|
||||
|
||||
uint32_t id[32 / 4];
|
||||
|
||||
if (wlump.Read(id, 32) != 32 || wlump.Seek(-32, FileReader::SeekCur) != 0)
|
||||
{
|
||||
Printf("Unable to read lump %s\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
auto type = IdentifyMIDIType(id, 32);
|
||||
if (type == MIDI_NOTMIDI)
|
||||
{
|
||||
Printf("%s is not MIDI-based.\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = wlump.Read();
|
||||
auto source = CreateMIDISource(data.Data(), data.Size(), type);
|
||||
|
||||
if (source == nullptr)
|
||||
{
|
||||
Printf("%s is not MIDI-based.\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD writewave
|
||||
//
|
||||
// If the current song can be represented as a waveform, dump it to
|
||||
// the specified file on disk. The sample rate parameter is merely a
|
||||
// suggestion, and the dumper is free to ignore it.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
UNSAFE_CCMD (writewave)
|
||||
{
|
||||
if (argv.argc() >= 3 && argv.argc() <= 7)
|
||||
{
|
||||
auto source = GetMIDISource(argv[1]);
|
||||
if (source == nullptr) return;
|
||||
|
||||
EMidiDevice dev = MDEV_DEFAULT;
|
||||
|
||||
if (argv.argc() >= 6)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
// We must stop the currently playing music to avoid interference between two synths.
|
||||
auto savedsong = mus_playing;
|
||||
S_StopMusic(true);
|
||||
if (dev == MDEV_DEFAULT && snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave.
|
||||
try
|
||||
{
|
||||
MIDIDumpWave(source, dev, argv.argc() < 6 ? nullptr : argv[6], argv[2], argv.argc() < 4 ? 0 : (int)strtol(argv[3], nullptr, 10), argv.argc() < 5 ? 0 : (int)strtol(argv[4], nullptr, 10));
|
||||
}
|
||||
catch (const std::runtime_error& err)
|
||||
{
|
||||
Printf("MIDI dump failed: %s\n", err.what());
|
||||
}
|
||||
|
||||
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Usage: writewave <midi> <filename> [subsong] [sample rate] [synth] [soundfont]\n"
|
||||
" - use '*' as song name to dump the currently playing song\n"
|
||||
" - use 0 for subsong and sample rate to play the default\n");
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD writemidi
|
||||
//
|
||||
// Writes a given MIDI song to disk. This does not affect playback anymore,
|
||||
// like older versions did.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
UNSAFE_CCMD(writemidi)
|
||||
{
|
||||
if (argv.argc() != 3)
|
||||
{
|
||||
Printf("Usage: writemidi <midisong> <filename> - use '*' as song name to dump the currently playing song\n");
|
||||
return;
|
||||
}
|
||||
auto source = GetMIDISource(argv[1]);
|
||||
if (source == nullptr) return;
|
||||
|
||||
std::vector<uint8_t> midi;
|
||||
bool success;
|
||||
|
||||
source->CreateSMF(midi, 1);
|
||||
auto f = FileWriter::Open(argv[2]);
|
||||
if (f == nullptr)
|
||||
{
|
||||
Printf("Could not open %s.\n", argv[2]);
|
||||
return;
|
||||
}
|
||||
success = (f->Write(&midi[0], midi.size()) == midi.size());
|
||||
delete f;
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Printf("Could not write to music file %s.\n", argv[2]);
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
** i_music.h
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifndef __I_MUSIC_H__
|
||||
#define __I_MUSIC_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;
|
||||
|
||||
#endif //__I_MUSIC_H__
|
|
@ -1,546 +0,0 @@
|
|||
/*
|
||||
** i_soundfont.cpp
|
||||
** The sound font manager for the MIDI synths
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2018 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** 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 <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/zmusic.h"
|
||||
#include "resourcefiles/resourcefile.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FSoundFontManager sfmanager;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// returns both a file reader and the full name of the looked up file
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
std::pair<FileReader, FString> FSoundFontReader::LookupFile(const char *name)
|
||||
{
|
||||
if (!IsAbsPath(name))
|
||||
{
|
||||
for(int i = mPaths.Size()-1; i>=0; i--)
|
||||
{
|
||||
FString fullname = mPaths[i] + name;
|
||||
auto fr = OpenFile(fullname);
|
||||
if (fr.isOpen()) return std::make_pair(std::move(fr), fullname);
|
||||
}
|
||||
}
|
||||
auto fr = OpenFile(name);
|
||||
if (!fr.isOpen()) name = "";
|
||||
return std::make_pair(std::move(fr), name);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This adds a directory to the path list
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSoundFontReader::AddPath(const char *strp)
|
||||
{
|
||||
if (*strp == 0) return;
|
||||
if (!mAllowAbsolutePaths && IsAbsPath(strp)) return; // of no use so we may just discard it right away
|
||||
int i = 0;
|
||||
FString str = strp;
|
||||
FixPathSeperator(str);
|
||||
if (str.Back() != '/') str += '/'; // always let it end with a slash.
|
||||
for (auto &s : mPaths)
|
||||
{
|
||||
if (pathcmp(s.GetChars(), str) == 0)
|
||||
{
|
||||
// move string to the back.
|
||||
mPaths.Delete(i);
|
||||
mPaths.Push(str);
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
mPaths.Push(str);
|
||||
}
|
||||
|
||||
int FSoundFontReader::pathcmp(const char *p1, const char *p2)
|
||||
{
|
||||
return mCaseSensitivePaths? strcmp(p1, p2) : stricmp(p1, p2);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FileReader FSoundFontReader::Open(const char *name, std::string& filename)
|
||||
{
|
||||
FileReader fr;
|
||||
if (name == nullptr)
|
||||
{
|
||||
fr = OpenMainConfigFile();
|
||||
filename = MainConfigFileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto res = LookupFile(name);
|
||||
fr = std::move(res.first);
|
||||
filename = res.second;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MusicIO::FileInterface* FSoundFontReader::open_interface(const char* name)
|
||||
{
|
||||
std::string filename;
|
||||
|
||||
FileReader fr = Open(name, filename);
|
||||
if (!fr.isOpen()) return nullptr;
|
||||
auto fri = new FileReaderMusicInterface(fr);
|
||||
fri->filename = std::move(filename);
|
||||
return fri;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// The file interface for the backend
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct MusicIO::FileInterface* FSoundFontReader::open_file(const char* name)
|
||||
{
|
||||
return open_interface(name);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Note that the file type has already been checked
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FSF2Reader::FSF2Reader(const char *fn)
|
||||
{
|
||||
mMainConfigForSF2.Format("soundfont \"%s\"\n", fn);
|
||||
mFilename = fn;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FileReader FSF2Reader::OpenMainConfigFile()
|
||||
{
|
||||
FileReader fr;
|
||||
if (mMainConfigForSF2.IsNotEmpty())
|
||||
{
|
||||
fr.OpenMemory(mMainConfigForSF2.GetChars(), mMainConfigForSF2.Len());
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
FileReader FSF2Reader::OpenFile(const char *name)
|
||||
{
|
||||
FileReader fr;
|
||||
if (mFilename.CompareNoCase(name) == 0)
|
||||
{
|
||||
fr.OpenFile(name);
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FZipPatReader::FZipPatReader(const char *filename)
|
||||
{
|
||||
resf = FResourceFile::OpenResourceFile(filename, true);
|
||||
}
|
||||
|
||||
FZipPatReader::~FZipPatReader()
|
||||
{
|
||||
if (resf != nullptr) delete resf;
|
||||
}
|
||||
|
||||
FileReader FZipPatReader::OpenMainConfigFile()
|
||||
{
|
||||
return OpenFile("timidity.cfg");
|
||||
}
|
||||
|
||||
FileReader FZipPatReader::OpenFile(const char *name)
|
||||
{
|
||||
FileReader fr;
|
||||
if (resf != nullptr)
|
||||
{
|
||||
auto lump = resf->FindLump(name);
|
||||
if (lump != nullptr)
|
||||
{
|
||||
return lump->NewReader();
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FPatchSetReader::FPatchSetReader(const char *filename)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
mCaseSensitivePaths = true;
|
||||
const char *paths[] = {
|
||||
"/usr/local/lib/timidity",
|
||||
"/etc/timidity",
|
||||
"/etc"
|
||||
};
|
||||
#else
|
||||
const char *paths[] = {
|
||||
"C:/TIMIDITY",
|
||||
"/TIMIDITY",
|
||||
progdir
|
||||
};
|
||||
#endif
|
||||
mAllowAbsolutePaths = true;
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(filename))
|
||||
{
|
||||
mFullPathToConfig = filename;
|
||||
}
|
||||
else if (!IsAbsPath(filename))
|
||||
{
|
||||
for(auto c : paths)
|
||||
{
|
||||
FStringf fullname("%s/%s", c, filename);
|
||||
if (fr.OpenFile(fullname))
|
||||
{
|
||||
mFullPathToConfig = fullname;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mFullPathToConfig.Len() > 0)
|
||||
{
|
||||
FixPathSeperator(mFullPathToConfig);
|
||||
mBasePath = ExtractFilePath(mFullPathToConfig);
|
||||
if (mBasePath.Len() > 0 && mBasePath.Back() != '/') mBasePath += '/';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FileReader FPatchSetReader::OpenMainConfigFile()
|
||||
{
|
||||
FileReader fr;
|
||||
fr.OpenFile(mFullPathToConfig);
|
||||
return fr;
|
||||
}
|
||||
|
||||
FileReader FPatchSetReader::OpenFile(const char *name)
|
||||
{
|
||||
FString path;
|
||||
if (IsAbsPath(name)) path = name;
|
||||
else path = mBasePath + name;
|
||||
FileReader fr;
|
||||
fr.OpenFile(path);
|
||||
return fr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FLumpPatchSetReader::FLumpPatchSetReader(const char *filename)
|
||||
{
|
||||
mLumpIndex = Wads.CheckNumForFullName(filename);
|
||||
|
||||
mBasePath = filename;
|
||||
FixPathSeperator(mBasePath);
|
||||
mBasePath = ExtractFilePath(mBasePath);
|
||||
if (mBasePath.Len() > 0 && mBasePath.Back() != '/') mBasePath += '/';
|
||||
}
|
||||
|
||||
FileReader FLumpPatchSetReader::OpenMainConfigFile()
|
||||
{
|
||||
return Wads.ReopenLumpReader(mLumpIndex);
|
||||
}
|
||||
|
||||
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 = Wads.CheckNumForFullName(path);
|
||||
if (index < 0) return FileReader();
|
||||
return Wads.ReopenLumpReader(index);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// collects everything out of the soundfonts directory.
|
||||
// This may either be .sf2 files or zipped GUS patch sets with a
|
||||
// 'timidity.cfg' in the root directory.
|
||||
// Other compression types are not supported, in particular not 7z because
|
||||
// due to the solid nature of its archives would be too slow.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSoundFontManager::ProcessOneFile(const FString &fn)
|
||||
{
|
||||
auto fb = ExtractFileBase(fn, false);
|
||||
auto fbe = ExtractFileBase(fn, true);
|
||||
for (auto &sfi : soundfonts)
|
||||
{
|
||||
// We already got a soundfont with this name. Do not add again.
|
||||
if (!sfi.mName.CompareNoCase(fb)) return;
|
||||
}
|
||||
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(fn))
|
||||
{
|
||||
// Try to identify. We only accept .sf2 and .zip by content. All other archives are intentionally ignored.
|
||||
char head[16] = { 0};
|
||||
fr.Read(head, 16);
|
||||
if (!memcmp(head, "RIFF", 4) && !memcmp(head+8, "sfbkLIST", 8))
|
||||
{
|
||||
FSoundFontInfo sft = { fb, fbe, fn, SF_SF2 };
|
||||
soundfonts.Push(sft);
|
||||
}
|
||||
if (!memcmp(head, "WOPL3-BANK\0", 11))
|
||||
{
|
||||
FSoundFontInfo sft = { fb, fbe, fn, SF_WOPL };
|
||||
soundfonts.Push(sft);
|
||||
}
|
||||
if (!memcmp(head, "WOPN2-BANK\0", 11) || !memcmp(head, "WOPN2-B2NK\0", 11))
|
||||
{
|
||||
FSoundFontInfo sft = { fb, fbe, fn, SF_WOPN };
|
||||
soundfonts.Push(sft);
|
||||
}
|
||||
else if (!memcmp(head, "PK", 2))
|
||||
{
|
||||
auto zip = FResourceFile::OpenResourceFile(fn, true);
|
||||
if (zip != nullptr)
|
||||
{
|
||||
if (zip->LumpCount() > 1) // Anything with just one lump cannot possibly be a packed GUS patch set so skip it right away and simplify the lookup code
|
||||
{
|
||||
auto zipl = zip->FindLump("timidity.cfg");
|
||||
if (zipl != nullptr)
|
||||
{
|
||||
// It seems like this is what we are looking for
|
||||
FSoundFontInfo sft = { fb, fbe, fn, SF_GUS };
|
||||
soundfonts.Push(sft);
|
||||
}
|
||||
}
|
||||
delete zip;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FSoundFontManager::CollectSoundfonts()
|
||||
{
|
||||
findstate_t c_file;
|
||||
void *file;
|
||||
|
||||
if (GameConfig != NULL && GameConfig->SetSection ("SoundfontSearch.Directories"))
|
||||
{
|
||||
const char *key;
|
||||
const char *value;
|
||||
|
||||
while (GameConfig->NextInSection (key, value))
|
||||
{
|
||||
if (stricmp (key, "Path") == 0)
|
||||
{
|
||||
FString dir;
|
||||
|
||||
dir = NicePath(value);
|
||||
FixPathSeperator(dir);
|
||||
if (dir.IsNotEmpty())
|
||||
{
|
||||
if (dir.Back() != '/') dir += '/';
|
||||
FString mask = dir + '*';
|
||||
if ((file = I_FindFirst(mask, &c_file)) != ((void *)(-1)))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (!(I_FindAttr(&c_file) & FA_DIREC))
|
||||
{
|
||||
FStringf name("%s%s", dir.GetChars(), I_FindName(&c_file));
|
||||
ProcessOneFile(name);
|
||||
}
|
||||
} while (I_FindNext(file, &c_file) == 0);
|
||||
I_FindClose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (soundfonts.Size() == 0)
|
||||
{
|
||||
ProcessOneFile(NicePath("$PROGDIR/soundfonts/gzdoom.sf2"));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const FSoundFontInfo *FSoundFontManager::FindSoundFont(const char *name, int allowed) const
|
||||
{
|
||||
for(auto &sfi : soundfonts)
|
||||
{
|
||||
// an empty name will pick the first one in a compatible format.
|
||||
if (allowed & sfi.type && (name == nullptr || *name == 0 || !sfi.mName.CompareNoCase(name) || !sfi.mNameExt.CompareNoCase(name)))
|
||||
{
|
||||
return &sfi;
|
||||
}
|
||||
}
|
||||
// We did not find what we were looking for. Let's just return the first valid item that works with the given device.
|
||||
for (auto &sfi : soundfonts)
|
||||
{
|
||||
if (allowed & sfi.type)
|
||||
{
|
||||
return &sfi;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FSoundFontReader *FSoundFontManager::OpenSoundFont(const char *name, int allowed)
|
||||
{
|
||||
|
||||
// First check if the given name is inside the loaded resources.
|
||||
// To avoid clashes this will only be done if the name has the '.cfg' extension.
|
||||
// Sound fonts cannot be loaded this way.
|
||||
if (name != nullptr)
|
||||
{
|
||||
const char *p = name + strlen(name) - 4;
|
||||
if (p > name && !stricmp(p, ".cfg") && Wads.CheckNumForFullName(name) >= 0)
|
||||
{
|
||||
return new FLumpPatchSetReader(name);
|
||||
}
|
||||
}
|
||||
|
||||
auto sfi = FindSoundFont(name, allowed);
|
||||
if (sfi != nullptr)
|
||||
{
|
||||
if (sfi->type == SF_SF2) return new FSF2Reader(sfi->mFilename);
|
||||
else return new FZipPatReader(sfi->mFilename);
|
||||
}
|
||||
// The sound font collection did not yield any good results.
|
||||
// Next check if the file is a .sf file
|
||||
if (allowed & SF_SF2)
|
||||
{
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(name))
|
||||
{
|
||||
char head[16] = { 0};
|
||||
fr.Read(head, 16);
|
||||
fr.Close();
|
||||
if (!memcmp(head, "RIFF", 4) && !memcmp(head+8, "sfbkLIST", 8))
|
||||
{
|
||||
return new FSF2Reader(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (allowed & SF_GUS)
|
||||
{
|
||||
FileReader fr;
|
||||
if (fr.OpenFile(name))
|
||||
{
|
||||
char head[16] = { 0 };
|
||||
fr.Read(head, 2);
|
||||
fr.Close();
|
||||
if (!memcmp(head, "PK", 2)) // The only reason for this check is to block non-Zips. The actual validation will be done by FZipFile.
|
||||
{
|
||||
auto r = new FZipPatReader(name);
|
||||
if (r->isOk()) return r;
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
|
||||
// Config files are only accepted if they are named '.cfg', because they are impossible to validate.
|
||||
const char *p = name + strlen(name) - 4;
|
||||
if (p > name && !stricmp(p, ".cfg") && FileExists(name))
|
||||
{
|
||||
return new FPatchSetReader(name);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
}
|
||||
|
||||
void I_InitSoundFonts()
|
||||
{
|
||||
sfmanager.CollectSoundfonts();
|
||||
}
|
||||
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "w_wad.h"
|
||||
#include "files.h"
|
||||
#include "filereadermusicinterface.h"
|
||||
|
||||
struct FSoundFontInfo
|
||||
{
|
||||
FString mName; // This is what the sounfont is identified with. It's the extension-less base file name
|
||||
FString mNameExt; // Same with extension. Used for comparing with input names so they can be done with or without extension.
|
||||
FString mFilename; // Full path to the backing file - this is needed by FluidSynth to load the sound font.
|
||||
int type;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FSoundFontReader : public MusicIO::SoundFontReaderInterface
|
||||
// Yes, it's 3 copies of essentially the same interface, but since we want to keep the 3 renderers as isolated modules we have to pull in their own implementations here.
|
||||
{
|
||||
protected:
|
||||
// This is only doable for loose config files that get set as sound fonts. All other cases read from a contained environment where this does not apply.
|
||||
bool mAllowAbsolutePaths = false;
|
||||
// This has only meaning if being run on a platform with a case sensitive file system and loose files.
|
||||
// When reading from an archive it will always be case insensitive, just like the lump manager.
|
||||
bool mCaseSensitivePaths = false;
|
||||
TArray<FString> mPaths;
|
||||
|
||||
|
||||
int pathcmp(const char *p1, const char *p2);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
virtual ~FSoundFontReader() {}
|
||||
virtual FileReader OpenMainConfigFile() = 0; // this is special because it needs to be synthesized for .sf files and set some restrictions for patch sets
|
||||
virtual FString MainConfigFileName()
|
||||
{
|
||||
return basePath() + "timidity.cfg";
|
||||
}
|
||||
|
||||
virtual FileReader OpenFile(const char *name) = 0;
|
||||
std::pair<FileReader , FString> LookupFile(const char *name);
|
||||
void AddPath(const char *str);
|
||||
virtual FString basePath() const
|
||||
{
|
||||
return ""; // archived patch sets do not use paths
|
||||
}
|
||||
|
||||
virtual FileReader Open(const char* name, std::string &filename);
|
||||
|
||||
// Timidity++ interface
|
||||
struct MusicIO::FileInterface* open_file(const char* name) override;
|
||||
void add_search_path(const char* name) override
|
||||
{
|
||||
return AddPath(name);
|
||||
}
|
||||
|
||||
MusicIO::FileInterface* open_interface(const char* name);
|
||||
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FSF2Reader : public FSoundFontReader
|
||||
{
|
||||
FString mMainConfigForSF2;
|
||||
FString mFilename;
|
||||
public:
|
||||
FSF2Reader(const char *filename);
|
||||
virtual FileReader OpenMainConfigFile() override;
|
||||
virtual FileReader OpenFile(const char *name) override;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FZipPatReader : public FSoundFontReader
|
||||
{
|
||||
FResourceFile *resf;
|
||||
public:
|
||||
FZipPatReader(const char *filename);
|
||||
~FZipPatReader();
|
||||
virtual FileReader OpenMainConfigFile() override;
|
||||
virtual FileReader OpenFile(const char *name) override;
|
||||
bool isOk() { return resf != nullptr; }
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FLumpPatchSetReader : public FSoundFontReader
|
||||
{
|
||||
int mLumpIndex;;
|
||||
FString mBasePath;
|
||||
|
||||
public:
|
||||
FLumpPatchSetReader(const char *filename);
|
||||
virtual FileReader OpenMainConfigFile() override;
|
||||
virtual FileReader OpenFile(const char *name) override;
|
||||
virtual FString basePath() const override
|
||||
{
|
||||
return mBasePath;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FPatchSetReader : public FSoundFontReader
|
||||
{
|
||||
FString mBasePath;
|
||||
FString mFullPathToConfig;
|
||||
|
||||
public:
|
||||
FPatchSetReader(FileReader &reader);
|
||||
FPatchSetReader(const char *filename);
|
||||
virtual FileReader OpenMainConfigFile() override;
|
||||
virtual FileReader OpenFile(const char *name) override;
|
||||
virtual FString basePath() const override
|
||||
{
|
||||
return mBasePath;
|
||||
}
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
class FSoundFontManager
|
||||
{
|
||||
TArray<FSoundFontInfo> soundfonts;
|
||||
|
||||
void ProcessOneFile(const FString & fn);
|
||||
|
||||
public:
|
||||
void CollectSoundfonts();
|
||||
const FSoundFontInfo *FindSoundFont(const char *name, int allowedtypes) const;
|
||||
FSoundFontReader *OpenSoundFont(const char *name, int allowedtypes);
|
||||
const auto &GetList() const { return soundfonts; } // This is for the menu
|
||||
|
||||
};
|
||||
|
||||
|
||||
extern FSoundFontManager sfmanager;
|
|
@ -1,490 +0,0 @@
|
|||
/*
|
||||
** music_config.cpp
|
||||
** This forwards all CVAR changes to the music system.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1999-2016 Randy Heit
|
||||
** Copyright 2005-2019 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** 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 <stdint.h>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include "c_cvars.h"
|
||||
#include "s_music.h"
|
||||
#include "zmusic/zmusic.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ADL Midi device
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
#define FORWARD_CVAR(key) \
|
||||
decltype(*self) newval; \
|
||||
auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle, *self, &newval); \
|
||||
self = (decltype(*self))newval; \
|
||||
if (ret) S_MIDIDeviceChanged(-1, true);
|
||||
|
||||
#define FORWARD_BOOL_CVAR(key) \
|
||||
int newval; \
|
||||
auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self, &newval); \
|
||||
self = !!newval; \
|
||||
if (ret) S_MIDIDeviceChanged(-1, true);
|
||||
|
||||
#define FORWARD_STRING_CVAR(key) \
|
||||
auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self); \
|
||||
if (ret) S_MIDIDeviceChanged(-1, true);
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Fluidsynth MIDI device
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(String, fluid_lib, "", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_STRING_CVAR(fluid_lib);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(String, fluid_patchset, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_STRING_CVAR(fluid_patchset);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_gain, 0.5, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_gain);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, fluid_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(fluid_reverb);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, fluid_chorus, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(fluid_chorus);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_voices, 128, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_voices);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_interp, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_interp);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_samplerate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_samplerate);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_threads, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_threads);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_reverb_roomsize, 0.61f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_reverb_roomsize);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_reverb_damping, 0.23f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_reverb_damping);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_reverb_width, 0.76f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_reverb_width);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_reverb_level, 0.57f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_reverb_level);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_chorus_voices, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_chorus_voices);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_chorus_level, 1.2f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_chorus_level);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, fluid_chorus_speed, 0.3f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_chorus_speed);
|
||||
}
|
||||
|
||||
// depth is in ms and actual maximum depends on the sample rate
|
||||
CUSTOM_CVAR(Float, fluid_chorus_depth, 8, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_chorus_depth);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, fluid_chorus_type, 0/*FLUID_CHORUS_DEFAULT_TYPE*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(fluid_chorus_type);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPL MIDI device
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Int, opl_numchips, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(opl_numchips);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, opl_core, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(opl_core);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, opl_fullpan, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(opl_fullpan);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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, "gzdoom", 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, min_sustain_time, 5000, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(min_sustain_time);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(String, timidity_config, "gzdoom", CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_STRING_CVAR(timidity_config);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// This one is for Win32 MMAPI.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(snd_midiprecache);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GME
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Float, gme_stereodepth, 0.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(gme_stereodepth);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// sndfile
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Int, snd_streambuffersize, 64, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(snd_streambuffersize);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Dumb
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CUSTOM_CVAR(Int, mod_samplerate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_samplerate);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mod_volramp, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_volramp);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mod_interp, 2/*DUMB_LQ_CUBIC*/, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_interp);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Bool, mod_autochip, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_BOOL_CVAR(mod_autochip);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mod_autochip_size_force, 100, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_autochip_size_force);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mod_autochip_size_scan, 500, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_autochip_size_scan);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Int, mod_autochip_scan_threshold, 12, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_autochip_scan_threshold);
|
||||
}
|
||||
|
||||
CUSTOM_CVAR(Float, mod_dumb_mastervolume, 1.f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_VIRTUAL)
|
||||
{
|
||||
FORWARD_CVAR(mod_dumb_mastervolume);
|
||||
}
|
||||
|
|
@ -1,229 +0,0 @@
|
|||
/*
|
||||
** music_midi_base.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2010 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
#include "c_dispatch.h"
|
||||
|
||||
#include "v_text.h"
|
||||
#include "menu/menu.h"
|
||||
#include "zmusic/zmusic.h"
|
||||
#include "s_music.h"
|
||||
|
||||
static uint32_t nummididevices;
|
||||
static bool nummididevicesset;
|
||||
|
||||
#define NUM_DEF_DEVICES 7
|
||||
|
||||
static void AddDefaultMidiDevices(FOptionValues *opt)
|
||||
{
|
||||
FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(NUM_DEF_DEVICES)];
|
||||
pair[0].Text = "FluidSynth";
|
||||
pair[0].Value = -5.0;
|
||||
pair[1].Text = "TiMidity++";
|
||||
pair[1].Value = -2.0;
|
||||
pair[2].Text = "WildMidi";
|
||||
pair[2].Value = -6.0;
|
||||
pair[3].Text = "GUS";
|
||||
pair[3].Value = -4.0;
|
||||
pair[4].Text = "OPL Synth Emulation";
|
||||
pair[4].Value = -3.0;
|
||||
pair[5].Text = "libADL";
|
||||
pair[5].Value = -7.0;
|
||||
pair[6].Text = "libOPN";
|
||||
pair[6].Value = -8.0;
|
||||
|
||||
}
|
||||
|
||||
#define DEF_MIDIDEV -5
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
CUSTOM_CVAR (Int, snd_mididevice, DEF_MIDIDEV, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (!nummididevicesset)
|
||||
return;
|
||||
|
||||
if ((self >= (signed)nummididevices) || (self < -8))
|
||||
{
|
||||
// Don't do repeated message spam if there is no valid device.
|
||||
if (self != 0)
|
||||
{
|
||||
Printf("ID out of range. Using default device.\n");
|
||||
self = DEF_MIDIDEV;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (self == -1) self = DEF_MIDIDEV;
|
||||
ChangeMusicSetting(ZMusic::snd_mididevice, nullptr, self);
|
||||
S_MIDIDeviceChanged(self, false);
|
||||
}
|
||||
|
||||
void I_InitMusicWin32 ()
|
||||
{
|
||||
nummididevices = midiOutGetNumDevs ();
|
||||
nummididevicesset = true;
|
||||
snd_mididevice.Callback ();
|
||||
}
|
||||
|
||||
void I_BuildMIDIMenuList (FOptionValues *opt)
|
||||
{
|
||||
AddDefaultMidiDevices(opt);
|
||||
|
||||
for (uint32_t id = 0; id < nummididevices; ++id)
|
||||
{
|
||||
MIDIOUTCAPS caps;
|
||||
MMRESULT res;
|
||||
|
||||
res = midiOutGetDevCaps (id, &caps, sizeof(caps));
|
||||
assert(res == MMSYSERR_NOERROR);
|
||||
if (res == MMSYSERR_NOERROR)
|
||||
{
|
||||
FOptionValues::Pair *pair = &opt->mValues[opt->mValues.Reserve(1)];
|
||||
pair->Text = caps.szPname;
|
||||
pair->Value = (float)id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintMidiDevice (int id, const char *name, uint16_t tech, uint32_t support)
|
||||
{
|
||||
if (id == snd_mididevice)
|
||||
{
|
||||
Printf (TEXTCOLOR_BOLD);
|
||||
}
|
||||
Printf ("% 2d. %s : ", id, name);
|
||||
switch (tech)
|
||||
{
|
||||
case MIDIDEV_MIDIPORT: Printf ("MIDIPORT"); break;
|
||||
case MIDIDEV_SYNTH: Printf ("SYNTH"); break;
|
||||
case MIDIDEV_SQSYNTH: Printf ("SQSYNTH"); break;
|
||||
case MIDIDEV_FMSYNTH: Printf ("FMSYNTH"); break;
|
||||
case MIDIDEV_MAPPER: Printf ("MAPPER"); break;
|
||||
case MIDIDEV_WAVETABLE: Printf ("WAVETABLE"); break;
|
||||
case MIDIDEV_SWSYNTH: Printf ("SWSYNTH"); break;
|
||||
}
|
||||
if (support & MIDICAPS_CACHE)
|
||||
{
|
||||
Printf (" CACHE");
|
||||
}
|
||||
if (support & MIDICAPS_LRVOLUME)
|
||||
{
|
||||
Printf (" LRVOLUME");
|
||||
}
|
||||
if (support & MIDICAPS_STREAM)
|
||||
{
|
||||
Printf (" STREAM");
|
||||
}
|
||||
if (support & MIDICAPS_VOLUME)
|
||||
{
|
||||
Printf (" VOLUME");
|
||||
}
|
||||
Printf (TEXTCOLOR_NORMAL "\n");
|
||||
}
|
||||
|
||||
CCMD (snd_listmididevices)
|
||||
{
|
||||
UINT id;
|
||||
MIDIOUTCAPS caps;
|
||||
MMRESULT res;
|
||||
|
||||
PrintMidiDevice(-8, "libOPN", MIDIDEV_FMSYNTH, 0);
|
||||
PrintMidiDevice(-7, "libADL", MIDIDEV_FMSYNTH, 0);
|
||||
PrintMidiDevice (-6, "WildMidi", MIDIDEV_SWSYNTH, 0);
|
||||
PrintMidiDevice (-5, "FluidSynth", MIDIDEV_SWSYNTH, 0);
|
||||
PrintMidiDevice (-4, "Gravis Ultrasound Emulation", MIDIDEV_SWSYNTH, 0);
|
||||
PrintMidiDevice (-3, "Emulated OPL FM Synth", MIDIDEV_FMSYNTH, 0);
|
||||
PrintMidiDevice (-2, "TiMidity++", MIDIDEV_SWSYNTH, 0);
|
||||
if (nummididevices != 0)
|
||||
{
|
||||
for (id = 0; id < nummididevices; ++id)
|
||||
{
|
||||
FString text;
|
||||
res = midiOutGetDevCaps (id, &caps, sizeof(caps));
|
||||
if (res == MMSYSERR_NODRIVER)
|
||||
text = "<Driver not installed>";
|
||||
else if (res == MMSYSERR_NOMEM)
|
||||
text = "<No memory for description>";
|
||||
else if (res == MMSYSERR_NOERROR)
|
||||
text = caps.szPname;
|
||||
else
|
||||
continue;
|
||||
|
||||
PrintMidiDevice (id, text, caps.wTechnology, caps.dwSupport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Everything but Windows uses this code.
|
||||
|
||||
CUSTOM_CVAR(Int, snd_mididevice, DEF_MIDIDEV, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < -8)
|
||||
self = -8;
|
||||
else if (self > -2)
|
||||
self = -2;
|
||||
else
|
||||
{
|
||||
ChangeMusicSetting(ZMusic::snd_mididevice, nullptr, self);
|
||||
S_MIDIDeviceChanged(self, false);
|
||||
}
|
||||
}
|
||||
|
||||
void I_BuildMIDIMenuList (FOptionValues *opt)
|
||||
{
|
||||
AddDefaultMidiDevices(opt);
|
||||
}
|
||||
|
||||
CCMD (snd_listmididevices)
|
||||
{
|
||||
Printf("%s-8. libOPN\n", -8 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-7. libADL\n", -7 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-6. WildMidi\n", -6 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-5. FluidSynth\n", -5 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-4. Gravis Ultrasound Emulation\n", -4 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : "");
|
||||
}
|
||||
#endif
|
|
@ -1,633 +0,0 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright 1993-1996 id Software
|
||||
// Copyright 1999-2016 Randy Heit
|
||||
// Copyright 2002-2016 Christoph Oelckers
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// DESCRIPTION: none
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* For code that originates from ZDoom the following applies:
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
** 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include "musicformats/win32/i_cd.h"
|
||||
#endif
|
||||
|
||||
#include "i_system.h"
|
||||
#include "i_sound.h"
|
||||
#include "i_music.h"
|
||||
|
||||
#include "s_sound.h"
|
||||
#include "s_sndseq.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "m_random.h"
|
||||
#include "w_wad.h"
|
||||
#include "p_local.h"
|
||||
#include "doomstat.h"
|
||||
#include "cmdlib.h"
|
||||
#include "v_video.h"
|
||||
#include "v_text.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "gstrings.h"
|
||||
#include "gi.h"
|
||||
#include "po_man.h"
|
||||
#include "serializer.h"
|
||||
#include "d_player.h"
|
||||
#include "g_levellocals.h"
|
||||
#include "vm.h"
|
||||
#include "g_game.h"
|
||||
#include "atterm.h"
|
||||
#include "s_music.h"
|
||||
#include "filereadermusicinterface.h"
|
||||
#include "zmusic/musinfo.h"
|
||||
#include "zmusic/zmusic.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
|
||||
extern float S_GetMusicVolume (const char *music);
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static bool MusicPaused; // whether music is paused
|
||||
MusPlayingInfo mus_playing; // music currently being played
|
||||
float relative_volume = 1.f;
|
||||
float saved_relative_volume = 1.0f; // this could be used to implement an ACS FadeMusic function
|
||||
|
||||
DEFINE_GLOBAL_NAMED(mus_playing, musplaying);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder);
|
||||
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop);
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
// Create a sound system stream for the currently playing song
|
||||
//==========================================================================
|
||||
|
||||
static std::unique_ptr<SoundStream> musicStream;
|
||||
|
||||
static bool FillStream(SoundStream* stream, void* buff, int len, void* userdata)
|
||||
{
|
||||
bool written = mus_playing.handle? mus_playing.handle->ServiceStream(buff, len) : 0;
|
||||
if (!written)
|
||||
{
|
||||
memset((char*)buff, 0, len);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void S_CreateStream()
|
||||
{
|
||||
if (!mus_playing.handle) return;
|
||||
auto fmt = mus_playing.handle->GetStreamInfo();
|
||||
if (fmt.mBufferSize > 0)
|
||||
{
|
||||
int flags = fmt.mNumChannels < 0 ? 0 : SoundStream::Float;
|
||||
if (abs(fmt.mNumChannels) < 2) flags |= SoundStream::Mono;
|
||||
|
||||
musicStream.reset(GSnd->CreateStream(FillStream, fmt.mBufferSize, flags, fmt.mSampleRate, nullptr));
|
||||
if (musicStream) musicStream->Play(true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void S_PauseStream(bool paused)
|
||||
{
|
||||
if (musicStream) musicStream->SetPaused(paused);
|
||||
}
|
||||
|
||||
void S_StopStream()
|
||||
{
|
||||
if (musicStream)
|
||||
{
|
||||
musicStream->Stop();
|
||||
musicStream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// starts playing this song
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void S_StartMusicPlaying(MusInfo* song, bool loop, float rel_vol, int subsong)
|
||||
{
|
||||
if (rel_vol > 0.f)
|
||||
{
|
||||
float factor = relative_volume / saved_relative_volume;
|
||||
saved_relative_volume = rel_vol;
|
||||
I_SetRelativeVolume(saved_relative_volume * factor);
|
||||
}
|
||||
song->Stop();
|
||||
song->Play(loop, subsong);
|
||||
song->m_NotStartedYet = false;
|
||||
|
||||
// Notify the sound system of the changed relative volume
|
||||
snd_musicvolume.Callback();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_PauseSound
|
||||
//
|
||||
// Stop music and sound effects, during game PAUSE.
|
||||
//==========================================================================
|
||||
|
||||
void S_PauseMusic ()
|
||||
{
|
||||
if (mus_playing.handle && !MusicPaused)
|
||||
{
|
||||
mus_playing.handle->Pause();
|
||||
S_PauseStream(true);
|
||||
MusicPaused = true;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_ResumeSound
|
||||
//
|
||||
// Resume music and sound effects, after game PAUSE.
|
||||
//==========================================================================
|
||||
|
||||
void S_ResumeMusic ()
|
||||
{
|
||||
if (mus_playing.handle && MusicPaused)
|
||||
{
|
||||
mus_playing.handle->Resume();
|
||||
S_PauseStream(false);
|
||||
MusicPaused = false;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_UpdateSound
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void S_UpdateMusic ()
|
||||
{
|
||||
if (mus_playing.handle != nullptr)
|
||||
{
|
||||
mus_playing.handle->Update();
|
||||
|
||||
if (!mus_playing.handle->IsPlaying())
|
||||
{
|
||||
S_StopMusic(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_Start
|
||||
//
|
||||
// Per level startup code. Kills playing sounds at start of level
|
||||
// and starts new music.
|
||||
//==========================================================================
|
||||
|
||||
void S_StartMusic ()
|
||||
{
|
||||
// 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);
|
||||
|
||||
// start new music for the level
|
||||
MusicPaused = false;
|
||||
|
||||
// Don't start the music if loading a savegame, because the music is stored there.
|
||||
// Don't start the music if revisiting a level in a hub for the same reason.
|
||||
if (!primaryLevel->IsReentering())
|
||||
{
|
||||
primaryLevel->SetMusic();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_StartMusic
|
||||
//
|
||||
// Starts some music with the given name.
|
||||
//==========================================================================
|
||||
|
||||
bool S_StartMusic (const char *m_id)
|
||||
{
|
||||
return S_ChangeMusic (m_id, 0, false);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_ChangeMusic
|
||||
//
|
||||
// Starts playing a music, possibly looping.
|
||||
//
|
||||
// [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)
|
||||
{
|
||||
if (nomusic) return false; // skip the entire procedure if music is globally disabled.
|
||||
|
||||
// allow specifying "*" as a placeholder to play the level's default music.
|
||||
if (musicname != nullptr && !strcmp(musicname, "*"))
|
||||
{
|
||||
if (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL)
|
||||
{
|
||||
musicname = primaryLevel->Music;
|
||||
order = primaryLevel->musicorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
musicname = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (musicname == nullptr || musicname[0] == 0)
|
||||
{
|
||||
// Don't choke if the map doesn't have a song attached
|
||||
S_StopMusic (true);
|
||||
mus_playing.name = "";
|
||||
mus_playing.LastSong = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
FString DEH_Music;
|
||||
if (musicname[0] == '$')
|
||||
{
|
||||
// handle dehacked replacement.
|
||||
// Any music name defined this way needs to be prefixed with 'D_' because
|
||||
// Doom.exe does not contain the prefix so these strings don't either.
|
||||
const char * mus_string = GStrings[musicname+1];
|
||||
if (mus_string != nullptr)
|
||||
{
|
||||
DEH_Music << "D_" << mus_string;
|
||||
musicname = DEH_Music;
|
||||
}
|
||||
}
|
||||
|
||||
FName *aliasp = MusicAliases.CheckKey(musicname);
|
||||
if (aliasp != nullptr)
|
||||
{
|
||||
if (*aliasp == NAME_None)
|
||||
{
|
||||
return true; // flagged to be ignored
|
||||
}
|
||||
musicname = aliasp->GetChars();
|
||||
}
|
||||
|
||||
if (!mus_playing.name.IsEmpty() &&
|
||||
mus_playing.handle != nullptr &&
|
||||
stricmp (mus_playing.name, musicname) == 0 &&
|
||||
mus_playing.handle->m_Looping == looping)
|
||||
{
|
||||
if (order != mus_playing.baseorder)
|
||||
{
|
||||
if (mus_playing.handle->SetSubsong(order))
|
||||
{
|
||||
mus_playing.baseorder = order;
|
||||
}
|
||||
}
|
||||
else if (!mus_playing.handle->IsPlaying())
|
||||
{
|
||||
try
|
||||
{
|
||||
mus_playing.handle->Play(looping, order);
|
||||
S_CreateStream();
|
||||
}
|
||||
catch (const std::runtime_error& err)
|
||||
{
|
||||
Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), err.what());
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
int lumpnum = -1;
|
||||
int length = 0;
|
||||
MusInfo *handle = nullptr;
|
||||
MidiDeviceSetting *devp = MidiDevices.CheckKey(musicname);
|
||||
|
||||
// Strip off any leading file:// component.
|
||||
if (strncmp(musicname, "file://", 7) == 0)
|
||||
{
|
||||
musicname += 7;
|
||||
}
|
||||
|
||||
FileReader reader;
|
||||
if (!FileExists (musicname))
|
||||
{
|
||||
if ((lumpnum = Wads.CheckNumForFullName (musicname, true, ns_music)) == -1)
|
||||
{
|
||||
Printf ("Music \"%s\" not found\n", musicname);
|
||||
return false;
|
||||
}
|
||||
if (handle == nullptr)
|
||||
{
|
||||
if (Wads.LumpLength (lumpnum) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
reader = Wads.ReopenLumpReader(lumpnum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load an external file.
|
||||
if (!reader.OpenFile(musicname))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// shutdown old music
|
||||
S_StopMusic (true);
|
||||
|
||||
// Just record it if volume is 0
|
||||
if (snd_musicvolume <= 0)
|
||||
{
|
||||
mus_playing.loop = looping;
|
||||
mus_playing.name = musicname;
|
||||
mus_playing.baseorder = order;
|
||||
mus_playing.LastSong = musicname;
|
||||
return true;
|
||||
}
|
||||
|
||||
// load & register it
|
||||
if (handle != nullptr)
|
||||
{
|
||||
mus_playing.handle = handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
auto mreader = new FileReaderMusicInterface(reader);
|
||||
mus_playing.handle = ZMusic_OpenSong(mreader, devp? (EMidiDevice)devp->device : MDEV_DEFAULT, devp? devp->args.GetChars() : "");
|
||||
}
|
||||
catch (const std::runtime_error& err)
|
||||
{
|
||||
Printf("Unable to load %s: %s\n", mus_playing.name.GetChars(), err.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mus_playing.loop = looping;
|
||||
mus_playing.name = musicname;
|
||||
mus_playing.baseorder = 0;
|
||||
mus_playing.LastSong = "";
|
||||
|
||||
if (mus_playing.handle != 0)
|
||||
{ // play it
|
||||
try
|
||||
{
|
||||
S_StartMusicPlaying(mus_playing.handle, looping, S_GetMusicVolume(musicname), order);
|
||||
S_CreateStream();
|
||||
mus_playing.baseorder = order;
|
||||
}
|
||||
catch (const std::runtime_error& err)
|
||||
{
|
||||
Printf("Unable to start %s: %s\n", mus_playing.name.GetChars(), err.what());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_RestartMusic
|
||||
//
|
||||
// Must only be called from snd_reset in i_sound.cpp!
|
||||
//==========================================================================
|
||||
|
||||
void S_RestartMusic ()
|
||||
{
|
||||
if (!mus_playing.LastSong.IsEmpty())
|
||||
{
|
||||
FString song = mus_playing.LastSong;
|
||||
mus_playing.LastSong = "";
|
||||
S_ChangeMusic (song, mus_playing.baseorder, mus_playing.loop, true);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_MIDIDeviceChanged
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
|
||||
void S_MIDIDeviceChanged(int newdev, bool force)
|
||||
{
|
||||
static int oldmididev = INT_MIN;
|
||||
|
||||
// If a song is playing, move it to the new device.
|
||||
if (oldmididev != newdev || force)
|
||||
{
|
||||
if (mus_playing.handle != nullptr && mus_playing.handle->IsMIDI())
|
||||
{
|
||||
MusInfo* song = mus_playing.handle;
|
||||
if (song->m_Status == MusInfo::STATE_Playing)
|
||||
{
|
||||
if (song->GetDeviceType() == MDEV_FLUIDSYNTH && force)
|
||||
{
|
||||
// FluidSynth must reload the song to change the patch set.
|
||||
auto mi = mus_playing;
|
||||
S_StopMusic(true);
|
||||
S_ChangeMusic(mi.name, mi.baseorder, mi.loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
song->Stop();
|
||||
S_StartMusicPlaying(song, song->m_Looping, -1, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 'force'
|
||||
if (!force) oldmididev = newdev;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_GetMusic
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int S_GetMusic (const char **name)
|
||||
{
|
||||
int order;
|
||||
|
||||
if (mus_playing.name.IsNotEmpty())
|
||||
{
|
||||
*name = mus_playing.name;
|
||||
order = mus_playing.baseorder;
|
||||
}
|
||||
else
|
||||
{
|
||||
*name = nullptr;
|
||||
order = 0;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// S_StopMusic
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void S_StopMusic (bool force)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (mus_playing.handle != nullptr)
|
||||
{
|
||||
S_ResumeMusic();
|
||||
S_StopStream();
|
||||
mus_playing.handle->Stop();
|
||||
delete mus_playing.handle;
|
||||
mus_playing.handle = nullptr;
|
||||
}
|
||||
mus_playing.LastSong = std::move(mus_playing.name);
|
||||
}
|
||||
catch (const std::runtime_error& )
|
||||
{
|
||||
//Printf("Unable to stop %s: %s\n", mus_playing.name.GetChars(), err.what());
|
||||
if (mus_playing.handle != nullptr)
|
||||
{
|
||||
delete mus_playing.handle;
|
||||
mus_playing.handle = nullptr;
|
||||
}
|
||||
mus_playing.name = "";
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD changemus
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD (changemus)
|
||||
{
|
||||
if (!nomusic)
|
||||
{
|
||||
if (argv.argc() > 1)
|
||||
{
|
||||
S_ChangeMusic (argv[1], argv.argc() > 2 ? atoi (argv[2]) : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *currentmus = mus_playing.name.GetChars();
|
||||
if(currentmus != nullptr && *currentmus != 0)
|
||||
{
|
||||
Printf ("currently playing %s\n", currentmus);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("no music playing\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Music is disabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD stopmus
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD (stopmus)
|
||||
{
|
||||
S_StopMusic (false);
|
||||
mus_playing.LastSong = ""; // forget the last played song so that it won't get restarted if some volume changes occur
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
CCMD(currentmusic)
|
||||
{
|
||||
if (mus_playing.name.IsNotEmpty())
|
||||
{
|
||||
Printf("Currently playing music '%s'\n", mus_playing.name.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Currently no music playing\n");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue