- more dependency removal, this time from the MIDI devices.

# Conflicts:
#	src/sound/mididevices/music_win_mididevice.cpp

# Conflicts:
#	src/sound/mididevices/music_softsynth_mididevice.cpp
#	src/sound/mididevices/music_timidity_mididevice.cpp
#	src/sound/mididevices/music_timiditypp_mididevice.cpp
#	src/sound/mididevices/music_wavewriter_mididevice.cpp
This commit is contained in:
Christoph Oelckers 2019-09-28 16:50:00 +02:00 committed by drfrag
parent d3bfffbdab
commit c52b516c6f
29 changed files with 508 additions and 497 deletions

View file

@ -269,7 +269,6 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes)
io->chips[i]->Update(samples1, samplesleft); io->chips[i]->Update(samples1, samplesleft);
} }
OffsetSamples(samples1, samplesleft << stereoshift); OffsetSamples(samples1, samplesleft << stereoshift);
assert(NextTickIn == ticky);
NextTickIn -= samplesleft; NextTickIn -= samplesleft;
assert (NextTickIn >= 0); assert (NextTickIn >= 0);
numsamples -= samplesleft; numsamples -= samplesleft;

View file

@ -1,3 +1,4 @@
#pragma once
#include <stdint.h> #include <stdint.h>
#if defined(__GNUC__) #if defined(__GNUC__)

View file

@ -587,13 +587,40 @@ int Instruments::read_config_file(const char *name)
return 0; return 0;
} }
// When loading DMXGUS the sfreader's default file must be the DMXGUS file, not the config as for patch sets. static char* gets(const char *&input, const char *eof, char* strbuf, size_t len)
{
if (ptrdiff_t(len) > eof - input) len = eof - input;
if (len <= 0) return nullptr;
int Instruments::LoadDMXGUS(int gus_memsize) char* p = strbuf;
while (len > 1)
{
if (*input == 0)
{
input++;
break;
}
if (*input != '\r')
{
*p++ = *input;
len--;
if (*input == '\n')
{
input++;
break;
}
}
input++;
}
if (p == strbuf) return nullptr;
*p++ = 0;
return strbuf;
}
int Instruments::LoadDMXGUS(int gus_memsize, const char* dmxgusdata, size_t dmxgussize)
{ {
char readbuffer[1024]; char readbuffer[1024];
auto data = sfreader->open_timidity_file(nullptr); const char* eof = dmxgusdata + dmxgussize;
long size = (data->seek(0, SEEK_END), data->tell());
long read = 0; long read = 0;
uint8_t remap[256]; uint8_t remap[256];
@ -604,9 +631,7 @@ int Instruments::LoadDMXGUS(int gus_memsize)
int status = -1; int status = -1;
int gusbank = (gus_memsize >= 1 && gus_memsize <= 4) ? gus_memsize : -1; int gusbank = (gus_memsize >= 1 && gus_memsize <= 4) ? gus_memsize : -1;
data->seek(0, SEEK_SET); while (gets(dmxgusdata, eof, readbuffer, 1024))
while (data->gets(readbuffer, 1024) && read < size)
{ {
int i = 0; int i = 0;
while (readbuffer[i] != 0 && i < 1024) while (readbuffer[i] != 0 && i < 1024)

View file

@ -175,7 +175,7 @@ public:
int LoadConfig() { return read_config_file(nullptr); } int LoadConfig() { return read_config_file(nullptr); }
int read_config_file(const char* name); int read_config_file(const char* name);
int LoadDMXGUS(int gus_memsize); int LoadDMXGUS(int gus_memsize, const char *dmxgusdata, size_t dmxgussize);
void font_freeall(); void font_freeall();
FontFile* font_find(const char* filename); FontFile* font_find(const char* filename);

View file

@ -48,7 +48,7 @@ public:
// A minimalistic sound font reader interface. Normally this should be replaced with something better tied into the host application. // A minimalistic sound font reader interface. Normally this should be replaced with something better tied into the host application.
#ifdef USE_BASE_INTERFACE #if 1//def USE_BASE_INTERFACE
// Base version of timidity_file using stdio's FILE. // Base version of timidity_file using stdio's FILE.
struct timidity_file_FILE : public timidity_file struct timidity_file_FILE : public timidity_file
@ -100,7 +100,8 @@ class BaseSoundFontReader : public SoundFontReaderInterface
return 0; return 0;
} }
struct timidity_file* open_timidityplus_file(const char* fn) public:
struct timidity_file* open_timidity_file(const char* fn)
{ {
FILE *f = nullptr; FILE *f = nullptr;
std::string fullname; std::string fullname;
@ -129,7 +130,7 @@ class BaseSoundFontReader : public SoundFontReaderInterface
return tf; return tf;
} }
void timidityplus_add_path(const char* path) void timidity_add_path(const char* path)
{ {
std::string p = path; std::string p = path;
if (p.back() != '/' && p.back() != '\\') p += '/'; // always let it end with a slash. if (p.back() != '/' && p.back() != '\\') p += '/'; // always let it end with a slash.

View file

@ -153,22 +153,6 @@ private:
SoundDecoder& operator=(const SoundDecoder &rhs); SoundDecoder& operator=(const SoundDecoder &rhs);
}; };
enum EMidiDevice
{
MDEV_DEFAULT = -1,
MDEV_MMAPI = 0,
MDEV_OPL = 1,
MDEV_SNDSYS = 2,
MDEV_TIMIDITY = 3,
MDEV_FLUIDSYNTH = 4,
MDEV_GUS = 5,
MDEV_WILDMIDI = 6,
MDEV_ADL = 7,
MDEV_OPN = 8,
MDEV_COUNT
};
class MusInfo; class MusInfo;
struct MusPlayingInfo struct MusPlayingInfo
{ {

View file

@ -915,3 +915,6 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args)
return true; return true;
} }
// This one is for Win32 MMAPI.
CVAR(Bool, snd_midiprecache, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);

View file

@ -0,0 +1,129 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
struct ADLConfig
{
int adl_chips_count = 6;
int adl_emulator_id = 0;
int adl_bank = 14;
int adl_volume_model = 3; // DMX
bool adl_run_at_pcm_rate = 0;
bool adl_fullpan = 1;
std::string adl_custom_bank;
};
struct FluidConfig
{
std::string fluid_lib;
std::vector<std::string> fluid_patchset;
bool fluid_reverb = false;
bool fluid_chorus = false;
int fluid_voices = 128;
int fluid_interp = 1;
int fluid_samplerate = 0;
int fluid_threads = 1;
int fluid_chorus_voices = 3;
int fluid_chorus_type = 0;
float fluid_gain = 0.5f;
float fluid_reverb_roomsize = 0.61f;
float fluid_reverb_damping = 0.23f;
float fluid_reverb_width = 0.76f;
float fluid_reverb_level = 0.57f;
float fluid_chorus_level = 1.2f;
float fluid_chorus_speed = 0.3f;
float fluid_chorus_depth = 8;
};
#include "oplsynth/genmidi.h"
struct OPLMidiConfig
{
int numchips = 2;
int core = 0;
bool fullpan = true;
struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL];
};
struct OpnConfig
{
int opn_chips_count = 8;
int opn_emulator_id = 0;
bool opn_run_at_pcm_rate = false;
bool opn_fullpan = 1;
std::string opn_custom_bank;
std::vector<uint8_t> default_bank;
};
namespace Timidity
{
class Instruments;
class SoundFontReaderInterface;
}
struct GUSConfig
{
// This one is a bit more complex because it also implements the instrument cache.
int midi_voices = 32;
int gus_memsize = 0;
void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr;
Timidity::SoundFontReaderInterface *reader;
std::string readerName;
std::vector<uint8_t> dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data.
std::string gus_patchdir;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, both 'reader' and 'dmxgus' fields should be left empty.
std::string loadedConfig;
std::shared_ptr<Timidity::Instruments> instruments; // this is held both by the config and the device
};
namespace TimidityPlus
{
class Instruments;
class SoundFontReaderInterface;
}
struct TimidityConfig
{
void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr;
TimidityPlus::SoundFontReaderInterface* reader;
std::string readerName;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty.
std::string loadedConfig;
std::shared_ptr<TimidityPlus::Instruments> instruments; // this is held both by the config and the device
};
namespace WildMidi
{
struct Instruments;
class SoundFontReaderInterface;
}
struct WildMidiConfig
{
bool reverb = false;
bool enhanced_resampling = true;
void (*errorfunc)(const char* wmfmt, va_list args) = nullptr;
WildMidi::SoundFontReaderInterface* reader;
std::string readerName;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty.
std::string loadedConfig;
std::shared_ptr<WildMidi::Instruments> instruments; // this is held both by the config and the device
};

View file

@ -0,0 +1,181 @@
#pragma once
#include <mutex>
#include "midiconfig.h"
#include "mididefs.h"
void TimidityPP_Shutdown();
typedef void(*MidiCallback)(void *);
// A device that provides a WinMM-like MIDI streaming interface -------------
enum EMidiDevice
{
MDEV_DEFAULT = -1,
MDEV_MMAPI = 0,
MDEV_OPL = 1,
MDEV_SNDSYS = 2,
MDEV_TIMIDITY = 3,
MDEV_FLUIDSYNTH = 4,
MDEV_GUS = 5,
MDEV_WILDMIDI = 6,
MDEV_ADL = 7,
MDEV_OPN = 8,
MDEV_COUNT
};
struct MidiHeader
{
uint8_t *lpData;
uint32_t dwBufferLength;
uint32_t dwBytesRecorded;
MidiHeader *lpNext;
};
struct SoundStreamInfo
{
// Format is always 32 bit float. If mBufferSize is 0, the song doesn't use streaming but plays through a different interface.
int mBufferSize;
int mSampleRate;
int mNumChannels;
};
class MIDIDevice
{
public:
MIDIDevice() = default;
virtual ~MIDIDevice();
void SetCallback(MidiCallback callback, void* userdata)
{
Callback = callback;
CallbackData = userdata;
}
virtual int Open() = 0;
virtual void Close() = 0;
virtual bool IsOpen() const = 0;
virtual int GetTechnology() const = 0;
virtual int SetTempo(int tempo) = 0;
virtual int SetTimeDiv(int timediv) = 0;
virtual int StreamOut(MidiHeader *data) = 0;
virtual int StreamOutSync(MidiHeader *data) = 0;
virtual int Resume() = 0;
virtual void Stop() = 0;
virtual int PrepareHeader(MidiHeader *data);
virtual int UnprepareHeader(MidiHeader *data);
virtual bool FakeVolume();
virtual bool Pause(bool paused) = 0;
virtual void InitPlayback();
virtual bool Update();
virtual void PrecacheInstruments(const uint16_t *instruments, int count);
virtual void ChangeSettingInt(const char *setting, int value);
virtual void ChangeSettingNum(const char *setting, double value);
virtual void ChangeSettingString(const char *setting, const char *value);
virtual std::string GetStats();
virtual int GetDeviceType() const { return MDEV_DEFAULT; }
virtual bool CanHandleSysex() const { return true; }
virtual SoundStreamInfo GetStreamInfo() const;
protected:
MidiCallback Callback;
void* CallbackData;
};
// Base class for software synthesizer MIDI output devices ------------------
class SoftSynthMIDIDevice : public MIDIDevice
{
friend class MIDIWaveWriter;
public:
SoftSynthMIDIDevice(int samplerate, int minrate = 1, int maxrate = 1000000 /* something higher than any valid value */);
~SoftSynthMIDIDevice();
void Close();
bool IsOpen() const;
int GetTechnology() const;
int SetTempo(int tempo);
int SetTimeDiv(int timediv);
int StreamOut(MidiHeader *data);
int StreamOutSync(MidiHeader *data);
int Resume();
void Stop();
bool Pause(bool paused);
virtual int Open();
virtual bool ServiceStream(void* buff, int numbytes);
int GetSampleRate() const { return SampleRate; }
SoundStreamInfo GetStreamInfo() const override;
protected:
std::mutex CritSec;
double Tempo;
double Division;
double SamplesPerTick;
double NextTickIn;
MidiHeader *Events;
bool Started;
bool isMono = false; // only relevant for OPL.
bool isOpen = false;
uint32_t Position;
int SampleRate;
int StreamBlockSize = 2;
virtual void CalcTickRate();
int PlayTick();
virtual int OpenRenderer() = 0;
virtual void HandleEvent(int status, int parm1, int parm2) = 0;
virtual void HandleLongEvent(const uint8_t *data, int len) = 0;
virtual void ComputeOutput(float *buffer, int len) = 0;
};
// Internal disk writing version of a MIDI device ------------------
class MIDIWaveWriter : public SoftSynthMIDIDevice
{
public:
MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse);
//~MIDIWaveWriter();
bool CloseFile();
int Resume() override;
int Open() override
{
return playDevice->Open();
}
int OpenRenderer() override { return playDevice->OpenRenderer(); }
void Stop() override;
void HandleEvent(int status, int parm1, int parm2) override { playDevice->HandleEvent(status, parm1, parm2); }
void HandleLongEvent(const uint8_t *data, int len) override { playDevice->HandleLongEvent(data, len); }
void ComputeOutput(float *buffer, int len) override { playDevice->ComputeOutput(buffer, len); }
int StreamOutSync(MidiHeader *data) override { return playDevice->StreamOutSync(data); }
int StreamOut(MidiHeader *data) override { return playDevice->StreamOut(data); }
int GetDeviceType() const override { return playDevice->GetDeviceType(); }
bool ServiceStream (void *buff, int numbytes) override { return playDevice->ServiceStream(buff, numbytes); }
int GetTechnology() const override { return playDevice->GetTechnology(); }
int SetTempo(int tempo) override { return playDevice->SetTempo(tempo); }
int SetTimeDiv(int timediv) override { return playDevice->SetTimeDiv(timediv); }
bool IsOpen() const override { return playDevice->IsOpen(); }
void CalcTickRate() override { playDevice->CalcTickRate(); }
protected:
FILE *File;
SoftSynthMIDIDevice *playDevice;
};
// MIDI devices
MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config, int (*printfunc)(const char*, ...));
MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config);
MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args);
MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config);
MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate);
MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate);
MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate);
#ifdef _WIN32
MIDIDevice* CreateWinMIDIDevice(int mididevice, bool precache);
#endif

View file

@ -34,7 +34,7 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "adlmidi.h" #include "adlmidi.h"
class ADLMIDIDevice : public SoftSynthMIDIDevice class ADLMIDIDevice : public SoftSynthMIDIDevice

View file

@ -35,10 +35,7 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "templates.h"
#include "doomerrors.h"
#include "v_text.h"
//========================================================================== //==========================================================================
@ -71,21 +68,6 @@ void MIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
{ {
} }
//==========================================================================
//
// MIDIDevice :: Preprocess
//
// Gives the MIDI device a chance to do some processing with the song before
// it starts playing it. Returns true if MIDIStreamer should perform its
// standard playback startup sequence.
//
//==========================================================================
bool MIDIDevice::Preprocess(MIDIStreamer *song, bool looping)
{
return true;
}
//========================================================================== //==========================================================================
// //
// MIDIDevice :: PrepareHeader // MIDIDevice :: PrepareHeader
@ -183,7 +165,7 @@ void MIDIDevice::ChangeSettingString(const char *setting, const char *value)
// //
//========================================================================== //==========================================================================
FString MIDIDevice::GetStats() std::string MIDIDevice::GetStats()
{ {
return "This MIDI device does not have any stats."; return "This MIDI device does not have any stats.";
} }

View file

@ -34,7 +34,10 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include <mutex>
#include <stdio.h>
#include "mididevice.h"
#include "mus2midi.h"
// FluidSynth implementation of a MIDI device ------------------------------- // FluidSynth implementation of a MIDI device -------------------------------
@ -55,7 +58,7 @@ public:
~FluidSynthMIDIDevice(); ~FluidSynthMIDIDevice();
int OpenRenderer(); int OpenRenderer();
FString GetStats(); std::string GetStats() override;
void ChangeSettingInt(const char *setting, int value); void ChangeSettingInt(const char *setting, int value);
void ChangeSettingNum(const char *setting, double value); void ChangeSettingNum(const char *setting, double value);
void ChangeSettingString(const char *setting, const char *value); void ChangeSettingString(const char *setting, const char *value);
@ -504,13 +507,12 @@ void FluidSynthMIDIDevice::ChangeSettingString(const char *setting, const char *
// //
//========================================================================== //==========================================================================
FString FluidSynthMIDIDevice::GetStats() std::string FluidSynthMIDIDevice::GetStats()
{ {
if (FluidSynth == NULL || FluidSettings == NULL) if (FluidSynth == NULL || FluidSettings == NULL)
{ {
return "FluidSynth is invalid"; return "FluidSynth is invalid";
} }
FString out;
std::lock_guard<std::mutex> lock(CritSec); std::lock_guard<std::mutex> lock(CritSec);
int polyphony = fluid_synth_get_polyphony(FluidSynth); int polyphony = fluid_synth_get_polyphony(FluidSynth);
@ -521,7 +523,8 @@ FString FluidSynthMIDIDevice::GetStats()
fluid_settings_getint(FluidSettings, "synth.reverb.active", &reverb); fluid_settings_getint(FluidSettings, "synth.reverb.active", &reverb);
fluid_settings_getint(FluidSettings, "synth.polyphony", &maxpoly); fluid_settings_getint(FluidSettings, "synth.polyphony", &maxpoly);
out.Format("Voices: %3d/%3d(%3d) %6.2f%% CPU Reverb: %3s Chorus: %3s", char out[100];
snprintf(out, 100,"Voices: %3d/%3d(%3d) %6.2f%% CPU Reverb: %3s Chorus: %3s",
voices, polyphony, maxpoly, load, reverb ? "yes" : "no", chorus ? "yes" : "no"); voices, polyphony, maxpoly, load, reverb ? "yes" : "no", chorus ? "yes" : "no");
return out; return out;
} }

View file

@ -35,8 +35,10 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "mus2midi.h"
#include "oplsynth/opl.h" #include "oplsynth/opl.h"
#include "oplsynth/opl_mus_player.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -59,7 +61,7 @@ public:
int OpenRenderer(); int OpenRenderer();
void Close(); void Close();
int GetTechnology() const; int GetTechnology() const;
FString GetStats(); std::string GetStats() override;
protected: protected:
void CalcTickRate(); void CalcTickRate();
@ -285,9 +287,9 @@ bool OPLMIDIDevice::ServiceStream(void *buff, int numbytes)
// //
//========================================================================== //==========================================================================
FString OPLMIDIDevice::GetStats() std::string OPLMIDIDevice::GetStats()
{ {
FString out; std::string out;
for (uint32_t i = 0; i < io->NumChannels; ++i) for (uint32_t i = 0; i < io->NumChannels; ++i)
{ {
char star = '*'; char star = '*';

View file

@ -34,11 +34,8 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "w_wad.h"
#include "doomerrors.h"
#include "opnmidi.h" #include "opnmidi.h"
#include "i_soundfont.h"
class OPNMIDIDevice : public SoftSynthMIDIDevice class OPNMIDIDevice : public SoftSynthMIDIDevice
{ {

View file

@ -34,13 +34,8 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include <mutex>
#include "templates.h" #include "mididevice.h"
#include "doomdef.h"
#include "m_swap.h"
#include "w_wad.h"
#include "v_text.h"
#include "i_system.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -383,7 +378,7 @@ bool SoftSynthMIDIDevice::ServiceStream (void *buff, int numbytes)
{ {
double ticky = NextTickIn; double ticky = NextTickIn;
int tick_in = int(NextTickIn); int tick_in = int(NextTickIn);
int samplesleft = MIN(numsamples, tick_in); int samplesleft = std::min(numsamples, tick_in);
if (samplesleft > 0) if (samplesleft > 0)
{ {

View file

@ -34,17 +34,12 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h"
#include "w_wad.h"
#include "v_text.h"
#include "timidity/timidity.h" #include "timidity/timidity.h"
#include "timidity/playmidi.h" #include "timidity/playmidi.h"
#include "timidity/instrum.h" #include "timidity/instrum.h"
#include "i_soundfont.h" #define USE_BASE_INTERFACE
#include "doomerrors.h" #include "timidity/timidity_file.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -101,9 +96,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
FString ultradir = getenv("ULTRADIR"); FString ultradir = getenv("ULTRADIR");
if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0) if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0)
{ {
FileReader fr; auto psreader = new Timidity::BaseSoundFontReader;
fr.OpenMemory((const char *)config->dmxgus.data(), (long)config->dmxgus.size());
auto psreader = new FPatchSetReader(fr);
// The GUS put its patches in %ULTRADIR%/MIDI so we can try that // The GUS put its patches in %ULTRADIR%/MIDI so we can try that
if (ultradir.IsNotEmpty()) if (ultradir.IsNotEmpty())
@ -115,7 +108,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
if (config->gus_patchdir.length() != 0) psreader->timidity_add_path(config->gus_patchdir.c_str()); if (config->gus_patchdir.length() != 0) psreader->timidity_add_path(config->gus_patchdir.c_str());
config->instruments.reset(new Timidity::Instruments(psreader)); config->instruments.reset(new Timidity::Instruments(psreader));
bool success = config->instruments->LoadDMXGUS(config->gus_memsize) >= 0; bool success = config->instruments->LoadDMXGUS(config->gus_memsize, (const char*)config->dmxgus.data(), config->dmxgus.size()) >= 0;
config->dmxgus.clear(); config->dmxgus.clear();

View file

@ -32,19 +32,9 @@
** **
*/ */
#include <string> #include "mididevice.h"
#include <vector> #include <vector>
#include "i_musicinterns.h"
#include "c_cvars.h"
#include "cmdlib.h"
#include "templates.h"
#include "version.h"
#include "m_misc.h"
#include "v_text.h"
#include "doomerrors.h"
#include "i_soundfont.h"
#include "timiditypp/timidity.h" #include "timiditypp/timidity.h"
#include "timiditypp/instrum.h" #include "timiditypp/instrum.h"
#include "timiditypp/playmidi.h" #include "timiditypp/playmidi.h"
@ -61,7 +51,7 @@ public:
int OpenRenderer(); int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count); void PrecacheInstruments(const uint16_t *instruments, int count);
//FString GetStats(); //std::string GetStats();
int GetDeviceType() const override { return MDEV_TIMIDITY; } int GetDeviceType() const override { return MDEV_TIMIDITY; }
double test[3] = { 0, 0, 0 }; double test[3] = { 0, 0, 0 };

View file

@ -35,13 +35,8 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h" #include "m_swap.h"
#include "w_wad.h"
#include "v_text.h"
#include "timidity/timidity.h"
#include <errno.h> #include <errno.h>
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -50,7 +45,7 @@
struct FmtChunk struct FmtChunk
{ {
uint32_t ChunkID; //uint32_t ChunkID;
uint32_t ChunkLen; uint32_t ChunkLen;
uint16_t FormatTag; uint16_t FormatTag;
uint16_t Channels; uint16_t Channels;
@ -80,32 +75,48 @@ struct FmtChunk
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
#ifdef _WIN32
// I'd rather not include Windows.h for just this.
extern "C" __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned CodePage, unsigned long dwFlags, const char* lpMultiByteStr, int cbMultiByte, const wchar_t* lpWideCharStr, int cchWideChar);
#endif
static FILE* my_fopen(const char* filename)
{
#ifndef _WIN32
return fopen(filename, "wb");
#else
// This is supposed to handle UTF-8 which Windows does not support with fopen. :(
const int CP_UTF8 = 65001;
std::wstring filePathW;
auto len = strlen(filename);
filePathW.resize(len);
int newSize = MultiByteToWideChar(CP_UTF8, 0, filename, (int)len, const_cast<wchar_t*>(filePathW.c_str()), (int)len);
filePathW.resize(newSize);
return _wfopen(filePathW.c_str(), L"wb");
#endif
}
//========================================================================== //==========================================================================
// //
// TimidityWaveWriterMIDIDevice Constructor // MIDIWaveWriter Constructor
// //
//========================================================================== //==========================================================================
MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playdevice) MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playdevice)
: SoftSynthMIDIDevice(playdevice->GetSampleRate()) : SoftSynthMIDIDevice(playdevice->GetSampleRate())
{ {
File = FileWriter::Open(filename); File = my_fopen(filename);
playDevice = playdevice; playDevice = playdevice;
if (File != nullptr) if (File != nullptr)
{ // Write wave header { // Write wave header
uint32_t work[3];
FmtChunk fmt; FmtChunk fmt;
work[0] = MAKE_ID('R','I','F','F'); if (fwrite("RIFF\0\0\0\0WAVEfmt ", 1, 16, File) != 16) goto fail;
work[1] = 0; // filled in later
work[2] = MAKE_ID('W','A','V','E');
if (4*3 != File->Write(work, 4 * 3)) goto fail;
playDevice->CalcTickRate(); playDevice->CalcTickRate();
fmt.ChunkID = MAKE_ID('f','m','t',' '); fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 4));
fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 8));
fmt.FormatTag = LittleShort((uint16_t)0xFFFE); // WAVE_FORMAT_EXTENSIBLE fmt.FormatTag = LittleShort((uint16_t)0xFFFE); // WAVE_FORMAT_EXTENSIBLE
fmt.Channels = LittleShort((uint16_t)2); fmt.Channels = LittleShort((uint16_t)2);
fmt.SamplesPerSec = LittleLong(SampleRate); fmt.SamplesPerSec = LittleLong(SampleRate);
@ -126,58 +137,60 @@ MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playde
fmt.SubFormatD[5] = 0x38; fmt.SubFormatD[5] = 0x38;
fmt.SubFormatD[6] = 0x9b; fmt.SubFormatD[6] = 0x9b;
fmt.SubFormatD[7] = 0x71; fmt.SubFormatD[7] = 0x71;
if (sizeof(fmt) != File->Write(&fmt, sizeof(fmt))) goto fail; if (sizeof(fmt) != fwrite(&fmt, 1, sizeof(fmt), File)) goto fail;
work[0] = MAKE_ID('d','a','t','a'); if (fwrite("data\0\0\0\0", 1, 8, File) != 8) goto fail;
work[1] = 0; // filled in later
if (8 !=File->Write(work, 8)) goto fail;
return; return;
fail: fail:
Printf("Failed to write %s: %s\n", filename, strerror(errno)); char buffer[80];
delete File; fclose(File);
File = nullptr; File = nullptr;
snprintf(buffer, 80, "Failed to write %s: %s\n", filename, strerror(errno));
throw std::runtime_error(buffer);
} }
} }
//========================================================================== //==========================================================================
// //
// TimidityWaveWriterMIDIDevice Destructor // MIDIWaveWriter Destructor
// //
//========================================================================== //==========================================================================
MIDIWaveWriter::~MIDIWaveWriter() bool MIDIWaveWriter::CloseFile()
{ {
if (File != nullptr) if (File != nullptr)
{ {
long pos = File->Tell(); auto pos = ftell(File);
uint32_t size; uint32_t size;
// data chunk size // data chunk size
size = LittleLong(uint32_t(pos - 8)); size = LittleLong(uint32_t(pos - 8));
if (0 == File->Seek(4, SEEK_SET)) if (0 == fseek(File, 4, SEEK_SET))
{ {
if (4 == File->Write(&size, 4)) if (4 == fwrite(&size, 1, 4, File))
{ {
size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8)); size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8));
if (0 == File->Seek(4 + sizeof(FmtChunk) + 4, SEEK_CUR)) if (0 == fseek(File, 4 + sizeof(FmtChunk) + 4, SEEK_CUR))
{ {
if (4 == File->Write(&size, 4)) if (4 == fwrite(&size, 1, 5, File))
{ {
delete File; fclose(File);
return; File = nullptr;
return true;
} }
} }
} }
} }
Printf("Could not finish writing wave file: %s\n", strerror(errno)); fclose(File);
delete File; File = nullptr;
return false;
} }
} }
//========================================================================== //==========================================================================
// //
// TimidityWaveWriterMIDIDevice :: Resume // MIDIWaveWriter :: Resume
// //
//========================================================================== //==========================================================================
@ -187,10 +200,13 @@ int MIDIWaveWriter::Resume()
while (ServiceStream(writebuffer, sizeof(writebuffer))) while (ServiceStream(writebuffer, sizeof(writebuffer)))
{ {
if (File->Write(writebuffer, sizeof(writebuffer)) != sizeof(writebuffer)) if (fwrite(writebuffer, 1, sizeof(writebuffer), File) != sizeof(writebuffer))
{ {
Printf("Could not write entire wave file: %s\n", strerror(errno)); fclose(File);
return 1; File = nullptr;
char buffer[80];
snprintf(buffer, 80, "Could not write entire wave file: %s\n", strerror(errno));
throw std::runtime_error(buffer);
} }
} }
return 0; return 0;
@ -198,7 +214,7 @@ int MIDIWaveWriter::Resume()
//========================================================================== //==========================================================================
// //
// TimidityWaveWriterMIDIDevice Stop // MIDIWaveWriter Stop
// //
//========================================================================== //==========================================================================

View file

@ -34,12 +34,8 @@
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "doomerrors.h" #include "wildmidi/wildmidi_lib.h"
#include "i_soundfont.h"
#include "c_console.h"
#include "v_text.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -55,7 +51,7 @@ public:
int OpenRenderer(); int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count); void PrecacheInstruments(const uint16_t *instruments, int count);
FString GetStats(); std::string GetStats();
int GetDeviceType() const override { return MDEV_WILDMIDI; } int GetDeviceType() const override { return MDEV_WILDMIDI; }
protected: protected:
@ -71,22 +67,6 @@ protected:
}; };
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
//========================================================================== //==========================================================================
@ -228,16 +208,16 @@ void WildMIDIDevice::ComputeOutput(float *buffer, int len)
// //
//========================================================================== //==========================================================================
FString WildMIDIDevice::GetStats() std::string WildMIDIDevice::GetStats()
{ {
FString out; char out[20];
out.Format("%3d voices", Renderer->GetVoiceCount()); snprintf(out, 20, "%3d voices", Renderer->GetVoiceCount());
return out; return out;
} }
//========================================================================== //==========================================================================
// //
// WildMIDIDevice :: GetStats // WildMIDIDevice :: ChangeSettingInt
// //
//========================================================================== //==========================================================================

View file

@ -37,14 +37,15 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <mmsystem.h> #include <mmsystem.h>
#include <algorithm>
#include <mutex>
#include <assert.h>
// HEADER FILES ------------------------------------------------------------ // HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h" #include "mididevice.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h" #include "m_swap.h"
#include "i_system.h" #include "mus2midi.h"
#ifndef __GNUC__ #ifndef __GNUC__
#include <mmdeviceapi.h> #include <mmdeviceapi.h>
@ -68,7 +69,7 @@ static bool IgnoreMIDIVolume(UINT id);
class WinMIDIDevice : public MIDIDevice class WinMIDIDevice : public MIDIDevice
{ {
public: public:
WinMIDIDevice(int dev_id); WinMIDIDevice(int dev_id, bool precache);
~WinMIDIDevice(); ~WinMIDIDevice();
int Open(); int Open();
void Close(); void Close();
@ -104,6 +105,7 @@ public:
MIDIHDR WinMidiHeaders[2]; MIDIHDR WinMidiHeaders[2];
int HeaderIndex; int HeaderIndex;
bool VolumeWorks; bool VolumeWorks;
bool Precache;
HANDLE BufferDoneEvent; HANDLE BufferDoneEvent;
HANDLE ExitEvent; HANDLE ExitEvent;
@ -114,8 +116,6 @@ public:
// PUBLIC DATA DEFINITIONS ------------------------------------------------- // PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR (Bool, snd_midiprecache, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
// CODE -------------------------------------------------------------------- // CODE --------------------------------------------------------------------
//========================================================================== //==========================================================================
@ -124,22 +124,25 @@ CVAR (Bool, snd_midiprecache, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
// //
//========================================================================== //==========================================================================
WinMIDIDevice::WinMIDIDevice(int dev_id) WinMIDIDevice::WinMIDIDevice(int dev_id, bool precache)
{ {
DeviceID = MAX<DWORD>(dev_id, 0); DeviceID = std::max<DWORD>(dev_id, 0);
MidiOut = 0; MidiOut = 0;
HeaderIndex = 0; HeaderIndex = 0;
Precache = precache;
memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders)); memset(WinMidiHeaders, 0, sizeof(WinMidiHeaders));
BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); BufferDoneEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (BufferDoneEvent == nullptr) if (BufferDoneEvent == nullptr)
{ {
Printf(PRINT_BOLD, "Could not create buffer done event for MIDI playback\n"); throw std::runtime_error("Could not create buffer done event for MIDI playback");
} }
ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); ExitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (ExitEvent == nullptr) if (ExitEvent == nullptr)
{ {
Printf(PRINT_BOLD, "Could not create exit event for MIDI playback\n"); CloseHandle(BufferDoneEvent);
BufferDoneEvent = nullptr;
throw std::runtime_error("Could not create exit event for MIDI playback");
} }
PlayerThread = nullptr; PlayerThread = nullptr;
} }
@ -297,9 +300,8 @@ int WinMIDIDevice::Resume()
PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid); PlayerThread = CreateThread(nullptr, 0, PlayerProc, this, 0, &tid);
if (PlayerThread == nullptr) if (PlayerThread == nullptr)
{ {
Printf("Creating MIDI thread failed\n");
Stop(); Stop();
return MMSYSERR_NOTSUPPORTED; throw std::runtime_error("Creating MIDI thread failed\n");
} }
} }
return ret; return ret;
@ -400,7 +402,7 @@ void WinMIDIDevice::PrecacheInstruments(const uint16_t *instruments, int count)
{ {
// Setting snd_midiprecache to false disables this precaching, since it // Setting snd_midiprecache to false disables this precaching, since it
// does involve sleeping for more than a miniscule amount of time. // does involve sleeping for more than a miniscule amount of time.
if (!snd_midiprecache) if (!Precache)
{ {
return; return;
} }
@ -606,20 +608,21 @@ bool WinMIDIDevice::Update()
GetExitCodeThread(PlayerThread, &code); GetExitCodeThread(PlayerThread, &code);
CloseHandle(PlayerThread); CloseHandle(PlayerThread);
PlayerThread = nullptr; PlayerThread = nullptr;
Printf("MIDI playback failure: "); char errmsg[100];
if (code < countof(MMErrorCodes)) const char *m = "MIDI playback failure: ";
if (code < 8)
{ {
Printf("%s\n", MMErrorCodes[code]); snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code]);
} }
else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + countof(MidiErrorCodes)) else if (code >= MIDIERR_BASE && code < MIDIERR_BASE + 8)
{ {
Printf("%s\n", MidiErrorCodes[code - MIDIERR_BASE]); snprintf(errmsg, 100, "%s%s", m, MMErrorCodes[code - MIDIERR_BASE]);
} }
else else
{ {
Printf("%08x\n", code); snprintf(errmsg, 100, "%s%08x", m, code);
} }
return false; throw std::runtime_error(errmsg);
} }
return true; return true;
} }
@ -683,15 +686,9 @@ static bool IgnoreMIDIVolume(UINT id)
return false; return false;
} }
MIDIDevice *CreateWinMIDIDevice(int mididevice) MIDIDevice *CreateWinMIDIDevice(int mididevice, bool precache)
{ {
auto d = new WinMIDIDevice(mididevice); return new WinMIDIDevice(mididevice, precache);
if (d->BufferDoneEvent == nullptr || d->ExitEvent == nullptr)
{
delete d;
I_Error("failed to create MIDI events");
}
return d;
} }
#endif #endif

View file

@ -11,8 +11,8 @@
#include <stdint.h> #include <stdint.h>
#include <functional> #include <functional>
#include <vector> #include <vector>
#include "mus2midi.h" #include "mididevices/mus2midi.h"
#include "mididefs.h" #include "mididevices/mididefs.h"
extern char MIDI_EventLengths[7]; extern char MIDI_EventLengths[7];
extern char MIDI_CommonLengths[15]; extern char MIDI_CommonLengths[15];

View file

@ -734,6 +734,8 @@ UNSAFE_CCMD (writewave)
else if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY; 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], "FluidSynth")) dev = MDEV_FLUIDSYNTH;
else if (!stricmp(argv[5], "OPL")) dev = MDEV_OPL; 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 else
{ {
Printf("%s: Unknown MIDI device\n", argv[5]); Printf("%s: Unknown MIDI device\n", argv[5]);
@ -744,12 +746,18 @@ UNSAFE_CCMD (writewave)
auto savedsong = mus_playing; auto savedsong = mus_playing;
S_StopMusic(true); S_StopMusic(true);
if (dev == MDEV_DEFAULT && snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave. if (dev == MDEV_DEFAULT && snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave.
auto streamer = new MIDIStreamer(dev, argv.argc() < 6 ? nullptr : argv[6]); try
streamer->SetMIDISource(source); {
streamer->DumpWave(argv[2], argv.argc() <4? 0: (int)strtol(argv[3], nullptr, 10), argv.argc() <5 ? 0 : (int)strtol(argv[4], nullptr, 10)); MIDIStreamer streamer(dev, argv.argc() < 6 ? nullptr : argv[6]);
delete streamer; streamer.SetMIDISource(source);
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true); streamer.DumpWave(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 else
{ {

View file

@ -75,7 +75,7 @@ public:
virtual bool SetPosition (unsigned int ms); virtual bool SetPosition (unsigned int ms);
virtual bool SetSubsong (int subsong); virtual bool SetSubsong (int subsong);
virtual void Update(); virtual void Update();
virtual int GetDeviceType() const { return MDEV_DEFAULT; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart. virtual int GetDeviceType() const { return -1; } // MDEV_DEFAULT stands in for anything that cannot change playback parameters which needs a restart.
virtual FString GetStats(); virtual FString GetStats();
virtual MusInfo *GetOPLDumper(const char *filename); virtual MusInfo *GetOPLDumper(const char *filename);
virtual MusInfo *GetWaveDumper(const char *filename, int rate); virtual MusInfo *GetWaveDumper(const char *filename, int rate);

View file

@ -3,13 +3,14 @@
#include <string> #include <string>
#include "oplsynth/opl_mus_player.h" #include "oplsynth/opl_mus_player.h"
#include "c_cvars.h" #include "c_cvars.h"
#include "mus2midi.h" #include "mididevices/mus2midi.h"
#include "i_sound.h" #include "i_sound.h"
#include "i_music.h" #include "i_music.h"
#include "s_sound.h" #include "s_sound.h"
#include "files.h" #include "files.h"
#include "wildmidi/wildmidi_lib.h" #include "wildmidi/wildmidi_lib.h"
#include "midisources/midisource.h" #include "midisources/midisource.h"
#include "mididevices/mididevice.h"
void I_InitMusicWin32 (); void I_InitMusicWin32 ();
@ -17,283 +18,17 @@ extern float relative_volume;
class MIDISource; class MIDISource;
// A device that provides a WinMM-like MIDI streaming interface -------------
struct MidiHeader
{
uint8_t *lpData;
uint32_t dwBufferLength;
uint32_t dwBytesRecorded;
MidiHeader *lpNext;
};
struct ADLConfig
{
int adl_chips_count = 6;
int adl_emulator_id = 0;
int adl_bank = 14;
int adl_volume_model = 3; // DMX
bool adl_run_at_pcm_rate = 0;
bool adl_fullpan = 1;
std::string adl_custom_bank;
};
extern ADLConfig adlConfig; extern ADLConfig adlConfig;
struct FluidConfig
{
std::string fluid_lib;
std::vector<std::string> fluid_patchset;
bool fluid_reverb = false;
bool fluid_chorus = false;
int fluid_voices = 128;
int fluid_interp = 1;
int fluid_samplerate = 0;
int fluid_threads = 1;
int fluid_chorus_voices = 3;
int fluid_chorus_type = 0;
float fluid_gain = 0.5f;
float fluid_reverb_roomsize = 0.61f;
float fluid_reverb_damping = 0.23f;
float fluid_reverb_width = 0.76f;
float fluid_reverb_level = 0.57f;
float fluid_chorus_level = 1.2f;
float fluid_chorus_speed = 0.3f;
float fluid_chorus_depth = 8;
};
extern FluidConfig fluidConfig; extern FluidConfig fluidConfig;
struct OPLMidiConfig
{
int numchips = 2;
int core = 0;
bool fullpan = true;
struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL];
};
extern OPLMidiConfig oplMidiConfig; extern OPLMidiConfig oplMidiConfig;
struct OpnConfig
{
int opn_chips_count = 8;
int opn_emulator_id = 0;
bool opn_run_at_pcm_rate = false;
bool opn_fullpan = 1;
std::string opn_custom_bank;
std::vector<uint8_t> default_bank;
};
extern OpnConfig opnConfig; extern OpnConfig opnConfig;
namespace Timidity
{
class Instruments;
class SoundFontReaderInterface;
}
struct GUSConfig
{
// This one is a bit more complex because it also implements the instrument cache.
int midi_voices = 32;
int gus_memsize = 0;
void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr;
Timidity::SoundFontReaderInterface *reader;
std::string readerName;
std::vector<uint8_t> dmxgus; // can contain the contents of a DMXGUS lump that may be used as the instrument set. In this case gus_patchdir must point to the location of the GUS data.
std::string gus_patchdir;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, both 'reader' and 'dmxgus' fields should be left empty.
std::string loadedConfig;
std::shared_ptr<Timidity::Instruments> instruments; // this is held both by the config and the device
};
extern GUSConfig gusConfig; extern GUSConfig gusConfig;
namespace TimidityPlus
{
class Instruments;
class SoundFontReaderInterface;
}
struct TimidityConfig
{
void (*errorfunc)(int type, int verbosity_level, const char* fmt, ...) = nullptr;
TimidityPlus::SoundFontReaderInterface* reader;
std::string readerName;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty.
std::string loadedConfig;
std::shared_ptr<TimidityPlus::Instruments> instruments; // this is held both by the config and the device
};
extern TimidityConfig timidityConfig; extern TimidityConfig timidityConfig;
struct WildMidiConfig
{
bool reverb = false;
bool enhanced_resampling = true;
void (*errorfunc)(const char* wmfmt, va_list args) = nullptr;
WildMidi::SoundFontReaderInterface* reader;
std::string readerName;
// These next two fields are for caching the instruments for repeated use. The GUS device will work without them being cached in the config but it'd require reloading the instruments each time.
// Thus, this config should always be stored globally to avoid this.
// If the last loaded instrument set is to be reused or the caller wants to manage them itself, 'reader' should be left empty.
std::string loadedConfig;
std::shared_ptr<WildMidi::Instruments> instruments; // this is held both by the config and the device
};
extern WildMidiConfig wildMidiConfig; extern WildMidiConfig wildMidiConfig;
struct SoundStreamInfo
{
// Format is always 32 bit float. If mBufferSize is 0, the song doesn't use streaming but plays through a different interface.
int mBufferSize;
int mSampleRate;
int mNumChannels;
};
class MIDIStreamer; class MIDIStreamer;
typedef void(*MidiCallback)(void *);
class MIDIDevice
{
public:
MIDIDevice() = default;
virtual ~MIDIDevice();
void SetCallback(MidiCallback callback, void* userdata)
{
Callback = callback;
CallbackData = userdata;
}
virtual int Open() = 0;
virtual void Close() = 0;
virtual bool IsOpen() const = 0;
virtual int GetTechnology() const = 0;
virtual int SetTempo(int tempo) = 0;
virtual int SetTimeDiv(int timediv) = 0;
virtual int StreamOut(MidiHeader *data) = 0;
virtual int StreamOutSync(MidiHeader *data) = 0;
virtual int Resume() = 0;
virtual void Stop() = 0;
virtual int PrepareHeader(MidiHeader *data);
virtual int UnprepareHeader(MidiHeader *data);
virtual bool FakeVolume();
virtual bool Pause(bool paused) = 0;
virtual void InitPlayback();
virtual bool Update();
virtual void PrecacheInstruments(const uint16_t *instruments, int count);
virtual void ChangeSettingInt(const char *setting, int value);
virtual void ChangeSettingNum(const char *setting, double value);
virtual void ChangeSettingString(const char *setting, const char *value);
virtual bool Preprocess(MIDIStreamer *song, bool looping);
virtual FString GetStats();
virtual int GetDeviceType() const { return MDEV_DEFAULT; }
virtual bool CanHandleSysex() const { return true; }
virtual SoundStreamInfo GetStreamInfo() const;
protected:
MidiCallback Callback;
void* CallbackData;
};
void TimidityPP_Shutdown();
// Base class for software synthesizer MIDI output devices ------------------
class SoftSynthMIDIDevice : public MIDIDevice
{
friend class MIDIWaveWriter;
public:
SoftSynthMIDIDevice(int samplerate, int minrate = 1, int maxrate = 1000000 /* something higher than any valid value */);
~SoftSynthMIDIDevice();
void Close();
bool IsOpen() const;
int GetTechnology() const;
int SetTempo(int tempo);
int SetTimeDiv(int timediv);
int StreamOut(MidiHeader *data);
int StreamOutSync(MidiHeader *data);
int Resume();
void Stop();
bool Pause(bool paused);
virtual int Open();
virtual bool ServiceStream(void* buff, int numbytes);
int GetSampleRate() const { return SampleRate; }
SoundStreamInfo GetStreamInfo() const override;
protected:
std::mutex CritSec;
double Tempo;
double Division;
double SamplesPerTick;
double NextTickIn;
MidiHeader *Events;
bool Started;
bool isMono = false; // only relevant for OPL.
bool isOpen = false;
uint32_t Position;
int SampleRate;
int StreamBlockSize = 2;
virtual void CalcTickRate();
int PlayTick();
virtual int OpenRenderer() = 0;
virtual void HandleEvent(int status, int parm1, int parm2) = 0;
virtual void HandleLongEvent(const uint8_t *data, int len) = 0;
virtual void ComputeOutput(float *buffer, int len) = 0;
};
// Internal disk writing version of a MIDI device ------------------
class MIDIWaveWriter : public SoftSynthMIDIDevice
{
public:
MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *devtouse);
~MIDIWaveWriter();
int Resume() override;
int Open() override
{
return playDevice->Open();
}
int OpenRenderer() override { return playDevice->OpenRenderer(); }
void Stop() override;
void HandleEvent(int status, int parm1, int parm2) override { playDevice->HandleEvent(status, parm1, parm2); }
void HandleLongEvent(const uint8_t *data, int len) override { playDevice->HandleLongEvent(data, len); }
void ComputeOutput(float *buffer, int len) override { playDevice->ComputeOutput(buffer, len); }
int StreamOutSync(MidiHeader *data) override { return playDevice->StreamOutSync(data); }
int StreamOut(MidiHeader *data) override { return playDevice->StreamOut(data); }
int GetDeviceType() const override { return playDevice->GetDeviceType(); }
bool ServiceStream (void *buff, int numbytes) override { return playDevice->ServiceStream(buff, numbytes); }
int GetTechnology() const override { return playDevice->GetTechnology(); }
int SetTempo(int tempo) override { return playDevice->SetTempo(tempo); }
int SetTimeDiv(int timediv) override { return playDevice->SetTimeDiv(timediv); }
bool IsOpen() const override { return playDevice->IsOpen(); }
void CalcTickRate() override { playDevice->CalcTickRate(); }
protected:
FileWriter *File;
SoftSynthMIDIDevice *playDevice;
};
// Base class for streaming MUS and MIDI files ------------------------------ // Base class for streaming MUS and MIDI files ------------------------------
@ -448,20 +183,6 @@ public:
CDDAFile (FileReader &reader); CDDAFile (FileReader &reader);
}; };
// MIDI devices
MIDIDevice *CreateFluidSynthMIDIDevice(int samplerate, const FluidConfig* config, int (*printfunc)(const char*, ...));
MIDIDevice *CreateADLMIDIDevice(const ADLConfig* config);
MIDIDevice *CreateOPNMIDIDevice(const OpnConfig *args);
MIDIDevice *CreateOplMIDIDevice(const OPLMidiConfig* config);
MIDIDevice *CreateTimidityMIDIDevice(GUSConfig *config, int samplerate);
MIDIDevice *CreateTimidityPPMIDIDevice(TimidityConfig *config, int samplerate);
MIDIDevice *CreateWildMIDIDevice(WildMidiConfig *config, int samplerate);
#ifdef _WIN32
MIDIDevice* CreateWinMIDIDevice(int mididevice);
#endif
// Data interface // Data interface
void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback); void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback);

View file

@ -57,7 +57,7 @@
static void WriteVarLen (TArray<uint8_t> &file, uint32_t value); static void WriteVarLen (TArray<uint8_t> &file, uint32_t value);
// EXTERNAL DATA DECLARATIONS ---------------------------------------------- // EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR(Bool, snd_midiprecache);
EXTERN_CVAR(Float, snd_musicvolume) EXTERN_CVAR(Float, snd_musicvolume)
EXTERN_CVAR(Int, snd_mididevice) EXTERN_CVAR(Int, snd_mididevice)
@ -209,7 +209,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
case MDEV_MMAPI: case MDEV_MMAPI:
#ifdef _WIN32 #ifdef _WIN32
dev = CreateWinMIDIDevice(mididevice); dev = CreateWinMIDIDevice(mididevice, snd_midiprecache);
break; break;
#endif #endif
// Intentional fall-through for non-Windows systems. // Intentional fall-through for non-Windows systems.
@ -318,8 +318,16 @@ bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate)
throw std::runtime_error("MMAPI device is not supported"); throw std::runtime_error("MMAPI device is not supported");
} }
auto iMIDI = CreateMIDIDevice(devtype, samplerate); auto iMIDI = CreateMIDIDevice(devtype, samplerate);
MIDI.reset(new MIDIWaveWriter(filename, static_cast<SoftSynthMIDIDevice *>(iMIDI))); auto writer = new MIDIWaveWriter(filename, static_cast<SoftSynthMIDIDevice*>(iMIDI));
return InitPlayback(); MIDI.reset(writer);
bool res = InitPlayback();
if (!writer->CloseFile())
{
char buffer[80];
snprintf(buffer, 80, "Could not finish writing wave file: %s\n", strerror(errno));
throw std::runtime_error(buffer);
}
return res;
} }
//========================================================================== //==========================================================================
@ -351,13 +359,10 @@ bool MIDIStreamer::InitPlayback()
Stream.reset(GSnd->CreateStream(FillStream, streamInfo.mBufferSize, streamInfo.mNumChannels == 1 ? SoundStream::Float | SoundStream::Mono : SoundStream::Float, streamInfo.mSampleRate, MIDI.get())); Stream.reset(GSnd->CreateStream(FillStream, streamInfo.mBufferSize, streamInfo.mNumChannels == 1 ? SoundStream::Float | SoundStream::Mono : SoundStream::Float, streamInfo.mSampleRate, MIDI.get()));
} }
if (MIDI->Preprocess(this, m_Looping)) StartPlayback();
{ if (MIDI == nullptr)
StartPlayback(); { // The MIDI file had no content and has been automatically closed.
if (MIDI == nullptr) return false;
{ // The MIDI file had no content and has been automatically closed.
return false;
}
} }
int res = 1; int res = 1;
@ -895,7 +900,8 @@ FString MIDIStreamer::GetStats()
{ {
return "No MIDI device in use."; return "No MIDI device in use.";
} }
return MIDI->GetStats(); auto s = MIDI->GetStats();
return s.c_str();
} }
//========================================================================== //==========================================================================

View file

@ -57,6 +57,7 @@
#include "vm.h" #include "vm.h"
#include "atterm.h" #include "atterm.h"
#include "s_music.h" #include "s_music.h"
#include "mididevices/mididevice.h"
// MACROS ------------------------------------------------------------------ // MACROS ------------------------------------------------------------------
@ -1469,6 +1470,8 @@ static void S_AddSNDINFO (int lump)
else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH; else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH;
else if (sc.Compare("gus")) devset.device = MDEV_GUS; else if (sc.Compare("gus")) devset.device = MDEV_GUS;
else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI; else if (sc.Compare("wildmidi")) devset.device = MDEV_WILDMIDI;
else if (sc.Compare("adl")) devset.device = MDEV_ADL;
else if (sc.Compare("opn")) devset.device = MDEV_OPN;
else sc.ScriptError("Unknown MIDI device %s\n", sc.String); else sc.ScriptError("Unknown MIDI device %s\n", sc.String);
if (sc.CheckString(",")) if (sc.CheckString(","))

View file

@ -72,11 +72,6 @@ struct MidiDeviceSetting
{ {
int device; int device;
FString args; FString args;
MidiDeviceSetting()
{
device = MDEV_DEFAULT;
}
}; };
typedef TMap<FName, FName> MusicAliasMap; typedef TMap<FName, FName> MusicAliasMap;