- added the rest of GZDoom's music interface.

This still needs to be integrated.
This commit is contained in:
Christoph Oelckers 2019-11-11 00:23:52 +01:00
parent 9f9748ede6
commit 5e95ef5322
8 changed files with 2015 additions and 16 deletions

View file

@ -168,13 +168,6 @@ CUSTOM_CVARD(Int, snd_speech, 5, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "enables/disabl
else if (self > 5) self = 5;
}
CUSTOM_CVARD(Int, mus_volume, 255, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "controls music volume")
{
if (self < 0) self = 0;
if (self > 255) self = 255;
Mus_SetVolume(self/255.f);
}
int MusicDevice = ASS_WinMM;
CUSTOM_CVARD(Int, mus_device, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "selects music device")
{

View file

@ -0,0 +1,452 @@
/*
** 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 "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)
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 mus_volume
//
// Maximum volume of MOD/stream music.
//==========================================================================
CUSTOM_CVARD(Int, mus_volume, 255, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "controls music volume")
{
if (self < 0) self = 0;
else if (self > 255) self = 255;
else
{
// Set general music volume.
ChangeMusicSetting(ZMusic::mus_volume, nullptr, self / 255.f);
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)
{
ZMusic_VolumeChanged(mus_playing.handle);
}
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)
{
// This really should be done internally in zmusic...
return nullptr;// 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 = fileSystem.FindFile("demolition/genmidi.txt");
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);
}
//==========================================================================
//
//
//
//==========================================================================
void I_InitMusic (void)
{
I_InitSoundFonts();
mus_volume.Callback ();
#ifdef _WIN32
I_InitMusicWin32 ();
#endif // _WIN32
snd_mididevice.Callback();
Callbacks callbacks;
callbacks.Fluid_MessageFunc = Printf;
callbacks.Timidity_Messagefunc = tim_printfunc;
callbacks.NicePath = mus_NicePath;
callbacks.PathForSoundfont = mus_pathToSoundFont;
callbacks.OpenSoundFont = mus_openSoundFont;
//callbacks.DumbVorbisDecode = dumb_decode_vorbis_;
ZMusic_SetCallbacks(&callbacks);
SetupGenMidi();
}
//==========================================================================
//
//
//
//==========================================================================
void I_SetRelativeVolume(float vol)
{
relative_volume = (float)vol;
ChangeMusicSetting(ZMusic::relative_volume, nullptr, (float)vol);
mus_volume.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(ZMusic_GetStats(mus_playing.handle).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 = fileSystem.FindFile(src);
if (lump < 0)
{
Printf("Cannot find MIDI lump %s.\n", src.GetChars());
return nullptr;
}
auto wlump = fileSystem.OpenFile(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.
//
//==========================================================================
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], "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
{
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.
//
//==========================================================================
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]);
}
}

View file

@ -0,0 +1,48 @@
/*
** 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;
//
// MUSIC I/O
//
void I_InitMusic ();
// Volume.
void I_SetRelativeVolume(float);
void I_SetMusicVolume (double volume);
#endif //__I_MUSIC_H__

View file

@ -0,0 +1,545 @@
/*
** 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 "cmdlib.h"
#include "i_system.h"
#include "gameconfigfile.h"
#include "filereadermusicinterface.h"
#include "zmusic/zmusic.h"
#include "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;
str.Substitute("\\", "/");
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)
{
mFullPathToConfig.Substitute("\\", "/");
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 = fileSystem.FindFile(filename);
mBasePath = filename;
mBasePath.Substitute("\\", "/");
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 = fileSystem.FindFile(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);
dir.Substitute("\\", "/");
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/demolition.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") && fileSystem.FindFile(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();
}

View file

@ -0,0 +1,166 @@
#pragma once
#include "zstring.h"
#include "tarray.h"
#include "filesystem.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;

View file

@ -1,3 +1,58 @@
//-----------------------------------------------------------------------------
//
// 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 "zmusic/zmusic.h"
#include "z_music.h"
#include "zstring.h"
@ -15,12 +70,171 @@ MusicAliasMap MusicAliases;
MidiDeviceMap MidiDevices;
//==========================================================================
//
// 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);
}
ZMusic_Stop(song);
ZMusic_Start(song, subsong, loop);
// 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)
{
ZMusic_Pause(mus_playing.handle);
S_PauseStream(true);
MusicPaused = true;
}
}
//==========================================================================
//
// S_ResumeSound
//
// Resume music and sound effects, after game PAUSE.
//==========================================================================
void S_ResumeMusic ()
{
if (mus_playing.handle && MusicPaused)
{
ZMusic_Resume(mus_playing.handle);
S_PauseStream(false);
MusicPaused = false;
}
}
//==========================================================================
//
// S_UpdateSound
//
//==========================================================================
void S_UpdateMusic ()
{
if (mus_playing.handle != nullptr)
{
ZMusic_Update(mus_playing.handle);
// [RH] Update music and/or playlist. IsPlaying() must be called
// to attempt to reconnect to broken net streams and to advance the
// 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_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_ChangeCDMusic
//
// Starts a CD track as music.
//==========================================================================
bool S_ChangeCDMusic (int track, unsigned int id, bool looping)
{
char temp[32];
if (id != 0)
{
mysnprintf (temp, countof(temp), ",CD,%d,%x", track, id);
}
else
{
mysnprintf (temp, countof(temp), ",CD,%d", track);
}
return S_ChangeMusic (temp, 0, looping);
}
//==========================================================================
//
// 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 (musicname == nullptr || musicname[0] == 0)
{
// Don't choke if the map doesn't have a song attached
//S_StopMusic(true);
S_StopMusic (true);
mus_playing.name = "";
mus_playing.LastSong = "";
return true;
@ -55,7 +269,7 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
try
{
ZMusic_Start(mus_playing.handle, looping, order);
//S_CreateStream();
S_CreateStream();
}
catch (const std::runtime_error & err)
{
@ -76,8 +290,8 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
{
id = strtoul(more + 1, nullptr, 16);
}
//S_StopMusic(true);
mus_playing.handle = ZMusic_OpenCDSong(track, id);
S_StopMusic (true);
mus_playing.handle = ZMusic_OpenCDSong (track, id);
}
else
{
@ -119,7 +333,7 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
}
// shutdown old music
//S_StopMusic(true);
S_StopMusic (true);
// Just record it if volume is 0
if (mus_volume <= 0)
@ -159,8 +373,8 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
{ // play it
try
{
//S_StartMusicPlaying(mus_playing.handle, looping, S_GetMusicVolume(musicname), order);
//S_CreateStream();
S_StartMusicPlaying(mus_playing.handle, looping, S_GetMusicVolume(musicname), order);
S_CreateStream();
mus_playing.baseorder = order;
}
catch (const std::runtime_error & err)
@ -172,11 +386,152 @@ bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force)
return false;
}
void Mus_Play(const char* fn, bool loop)
//==========================================================================
//
// 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)
{
MusInfo* song = mus_playing.handle;
if (song != nullptr && ZMusic_IsMIDI(song) && ZMusic_IsPlaying(song))
{
// Reload the song to change the device
auto mi = mus_playing;
S_StopMusic(true);
S_ChangeMusic(mi.name, mi.baseorder, mi.loop);
}
}
//==========================================================================
//
// 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
{
// [RH] Don't stop if a playlist is active.
if ((force || PlayList.GetNumSongs() == 0) && !mus_playing.name.IsEmpty())
{
if (mus_playing.handle != nullptr)
{
S_ResumeMusic();
S_StopStream();
ZMusic_Stop(mus_playing.handle);
auto h = mus_playing.handle;
mus_playing.handle = nullptr;
ZMusic_Close(h);
}
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)
{
auto h = mus_playing.handle;
mus_playing.handle = nullptr;
ZMusic_Close(h);
}
mus_playing.name = "";
}
}
}
void Mus_Stop()
//==========================================================================
//
// CCMD changemus
//
//==========================================================================
CCMD (changemus)
{
if (!nomusic)
{
if (argv.argc() > 1)
{
PlayList.Clear();
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)
{
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
}
{
}

View file

@ -0,0 +1,359 @@
/*
** 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"
//==========================================================================
//
//
//
//==========================================================================
#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);
#define FORWARD_BOOL_CVAR(key) \
int newval; \
auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self, &newval); \
self = !!newval; \
if (ret) S_MIDIDeviceChanged(-1);
#define FORWARD_STRING_CVAR(key) \
auto ret = ChangeMusicSetting(ZMusic::key, mus_playing.handle,*self); \
if (ret) S_MIDIDeviceChanged(-1);
//==========================================================================
//
// 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);
}
//==========================================================================
//
// 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);
}
//==========================================================================
//
// 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);
}

View file

@ -0,0 +1,81 @@
/*
** 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.
**---------------------------------------------------------------------------
**
*/
#define DEF_MIDIDEV -5
EXTERN_CVAR(Int, snd_mididevice)
static uint32_t nummididevices;
#define NUM_DEF_DEVICES 7
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
void I_InitMusicWin32 ()
{
nummididevices = midiOutGetNumDevs ();
}
#endif
#include "c_dispatch.h"
#include "v_text.h"
#include "zmusic/zmusic.h"
#include "s_music.h"
CUSTOM_CVAR (Int, snd_mididevice, DEF_MIDIDEV, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
{
if ((self >= (signed)nummididevices) || (self < -5))
{
// 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;
return;
}
bool change = ChangeMusicSetting(ZMusic::snd_mididevice, nullptr, self);
if (change) S_MIDIDeviceChanged(self);
}