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

This commit is contained in:
Christoph Oelckers 2019-09-28 16:50:00 +02:00
parent 820cbcc689
commit fc6eba0c26
29 changed files with 509 additions and 479 deletions

View file

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

View file

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

View file

@ -587,13 +587,40 @@ int Instruments::read_config_file(const char *name)
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];
auto data = sfreader->open_timidity_file(nullptr);
long size = (data->seek(0, SEEK_END), data->tell());
const char* eof = dmxgusdata + dmxgussize;
long read = 0;
uint8_t remap[256];
@ -604,9 +631,7 @@ int Instruments::LoadDMXGUS(int gus_memsize)
int status = -1;
int gusbank = (gus_memsize >= 1 && gus_memsize <= 4) ? gus_memsize : -1;
data->seek(0, SEEK_SET);
while (data->gets(readbuffer, 1024) && read < size)
while (gets(dmxgusdata, eof, readbuffer, 1024))
{
int i = 0;
while (readbuffer[i] != 0 && i < 1024)

View file

@ -175,7 +175,7 @@ public:
int LoadConfig() { return read_config_file(nullptr); }
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();
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.
#ifdef USE_BASE_INTERFACE
#if 1//def USE_BASE_INTERFACE
// Base version of timidity_file using stdio's FILE.
struct timidity_file_FILE : public timidity_file
@ -89,7 +89,7 @@ struct timidity_file_FILE : public timidity_file
class BaseSoundFontReader : public SoundFontReaderInterface
{
std::vector<std::string> paths;
bool IsAbsPath(const char *name)
{
if (name[0] == '/' || name[0] == '\\') return true;
@ -100,7 +100,8 @@ class BaseSoundFontReader : public SoundFontReaderInterface
return 0;
}
struct timidity_file* open_timidityplus_file(const char* fn)
public:
struct timidity_file* open_timidity_file(const char* fn)
{
FILE *f = nullptr;
std::string fullname;
@ -129,7 +130,7 @@ class BaseSoundFontReader : public SoundFontReaderInterface
return tf;
}
void timidityplus_add_path(const char* path)
void timidity_add_path(const char* path)
{
std::string p = path;
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);
};
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;
struct MusPlayingInfo
{

View file

@ -915,3 +915,6 @@ bool WildMidi_SetupConfig(WildMidiConfig* config, const char* args)
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 ------------------------------------------------------------
#include "i_musicinterns.h"
#include "mididevice.h"
#include "adlmidi.h"
class ADLMIDIDevice : public SoftSynthMIDIDevice

View file

@ -35,10 +35,7 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "templates.h"
#include "doomerrors.h"
#include "v_text.h"
#include "mididevice.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
@ -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.";
}

View file

@ -34,7 +34,10 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include <mutex>
#include <stdio.h>
#include "mididevice.h"
#include "mus2midi.h"
// FluidSynth implementation of a MIDI device -------------------------------
@ -55,7 +58,7 @@ public:
~FluidSynthMIDIDevice();
int OpenRenderer();
FString GetStats();
std::string GetStats() override;
void ChangeSettingInt(const char *setting, int value);
void ChangeSettingNum(const char *setting, double 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)
{
return "FluidSynth is invalid";
}
FString out;
std::lock_guard<std::mutex> lock(CritSec);
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.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");
return out;
}

View file

@ -35,8 +35,10 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "mididevice.h"
#include "mus2midi.h"
#include "oplsynth/opl.h"
#include "oplsynth/opl_mus_player.h"
// MACROS ------------------------------------------------------------------
@ -59,7 +61,7 @@ public:
int OpenRenderer();
void Close();
int GetTechnology() const;
FString GetStats();
std::string GetStats() override;
protected:
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)
{
char star = '*';

View file

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

View file

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

View file

@ -34,13 +34,12 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "v_text.h"
#include "mididevice.h"
#include "timidity/timidity.h"
#include "timidity/playmidi.h"
#include "timidity/instrum.h"
#include "i_soundfont.h"
#include "doomerrors.h"
#define USE_BASE_INTERFACE
#include "timidity/timidity_file.h"
// MACROS ------------------------------------------------------------------
@ -97,9 +96,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
FString ultradir = getenv("ULTRADIR");
if (ultradir.IsNotEmpty() || config->gus_patchdir.length() != 0)
{
FileReader fr;
fr.OpenMemory((const char *)config->dmxgus.data(), (long)config->dmxgus.size());
auto psreader = new FPatchSetReader(fr);
auto psreader = new Timidity::BaseSoundFontReader;
// The GUS put its patches in %ULTRADIR%/MIDI so we can try that
if (ultradir.IsNotEmpty())
@ -111,7 +108,7 @@ void TimidityMIDIDevice::LoadInstruments(GUSConfig *config)
if (config->gus_patchdir.length() != 0) psreader->timidity_add_path(config->gus_patchdir.c_str());
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();

View file

@ -32,12 +32,7 @@
**
*/
#include <string>
#include "i_musicinterns.h"
#include "v_text.h"
#include "doomerrors.h"
#include "i_soundfont.h"
#include "mididevice.h"
#include "timiditypp/timidity.h"
#include "timiditypp/instrum.h"
@ -55,7 +50,7 @@ public:
int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count);
//FString GetStats();
//std::string GetStats();
int GetDeviceType() const override { return MDEV_TIMIDITY; }
double test[3] = { 0, 0, 0 };

View file

@ -35,7 +35,8 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "mididevice.h"
#include "m_swap.h"
#include <errno.h>
// MACROS ------------------------------------------------------------------
@ -44,7 +45,7 @@
struct FmtChunk
{
uint32_t ChunkID;
//uint32_t ChunkID;
uint32_t ChunkLen;
uint16_t FormatTag;
uint16_t Channels;
@ -74,32 +75,48 @@ struct FmtChunk
// PUBLIC DATA DEFINITIONS -------------------------------------------------
// 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)
: SoftSynthMIDIDevice(playdevice->GetSampleRate())
{
File = FileWriter::Open(filename);
File = my_fopen(filename);
playDevice = playdevice;
if (File != nullptr)
{ // Write wave header
uint32_t work[3];
FmtChunk fmt;
work[0] = MAKE_ID('R','I','F','F');
work[1] = 0; // filled in later
work[2] = MAKE_ID('W','A','V','E');
if (4*3 != File->Write(work, 4 * 3)) goto fail;
if (fwrite("RIFF\0\0\0\0WAVEfmt ", 1, 16, File) != 16) goto fail;
playDevice->CalcTickRate();
fmt.ChunkID = MAKE_ID('f','m','t',' ');
fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 8));
fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 4));
fmt.FormatTag = LittleShort((uint16_t)0xFFFE); // WAVE_FORMAT_EXTENSIBLE
fmt.Channels = LittleShort((uint16_t)2);
fmt.SamplesPerSec = LittleLong(SampleRate);
@ -120,58 +137,60 @@ MIDIWaveWriter::MIDIWaveWriter(const char *filename, SoftSynthMIDIDevice *playde
fmt.SubFormatD[5] = 0x38;
fmt.SubFormatD[6] = 0x9b;
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');
work[1] = 0; // filled in later
if (8 !=File->Write(work, 8)) goto fail;
if (fwrite("data\0\0\0\0", 1, 8, File) != 8) goto fail;
return;
fail:
Printf("Failed to write %s: %s\n", filename, strerror(errno));
delete File;
fail:
char buffer[80];
fclose(File);
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)
{
long pos = File->Tell();
auto pos = ftell(File);
uint32_t size;
// data chunk size
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));
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;
return;
fclose(File);
File = nullptr;
return true;
}
}
}
}
Printf("Could not finish writing wave file: %s\n", strerror(errno));
delete File;
fclose(File);
File = nullptr;
return false;
}
}
//==========================================================================
//
// TimidityWaveWriterMIDIDevice :: Resume
// MIDIWaveWriter :: Resume
//
//==========================================================================
@ -181,10 +200,13 @@ int MIDIWaveWriter::Resume()
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));
return 1;
fclose(File);
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;
@ -192,7 +214,7 @@ int MIDIWaveWriter::Resume()
//==========================================================================
//
// TimidityWaveWriterMIDIDevice Stop
// MIDIWaveWriter Stop
//
//==========================================================================

View file

@ -34,12 +34,8 @@
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "doomerrors.h"
#include "i_soundfont.h"
#include "c_console.h"
#include "v_text.h"
#include "mididevice.h"
#include "wildmidi/wildmidi_lib.h"
// MACROS ------------------------------------------------------------------
@ -55,7 +51,7 @@ public:
int OpenRenderer();
void PrecacheInstruments(const uint16_t *instruments, int count);
FString GetStats();
std::string GetStats();
int GetDeviceType() const override { return MDEV_WILDMIDI; }
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 --------------------------------------------------------------------
//==========================================================================
@ -228,16 +208,16 @@ void WildMIDIDevice::ComputeOutput(float *buffer, int len)
//
//==========================================================================
FString WildMIDIDevice::GetStats()
std::string WildMIDIDevice::GetStats()
{
FString out;
out.Format("%3d voices", Renderer->GetVoiceCount());
char out[20];
snprintf(out, 20, "%3d voices", Renderer->GetVoiceCount());
return out;
}
//==========================================================================
//
// WildMIDIDevice :: GetStats
// WildMIDIDevice :: ChangeSettingInt
//
//==========================================================================

View file

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

View file

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

View file

@ -718,6 +718,8 @@ UNSAFE_CCMD (writewave)
else if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY;
else if (!stricmp(argv[5], "FluidSynth")) dev = MDEV_FLUIDSYNTH;
else if (!stricmp(argv[5], "OPL")) dev = MDEV_OPL;
else if (!stricmp(argv[5], "OPN")) dev = MDEV_OPN;
else if (!stricmp(argv[5], "ADL")) dev = MDEV_ADL;
else
{
Printf("%s: Unknown MIDI device\n", argv[5]);
@ -728,12 +730,18 @@ UNSAFE_CCMD (writewave)
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.
auto streamer = new MIDIStreamer(dev, argv.argc() < 6 ? nullptr : argv[6]);
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));
delete streamer;
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true);
try
{
MIDIStreamer streamer(dev, argv.argc() < 6 ? nullptr : argv[6]);
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));
}
catch (const std::runtime_error& err)
{
Printf("MIDI dump failed: %s\n", err.what());
}
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true);
}
else
{

View file

@ -75,7 +75,7 @@ public:
virtual bool SetPosition (unsigned int ms);
virtual bool SetSubsong (int subsong);
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 MusInfo *GetOPLDumper(const char *filename);
virtual MusInfo *GetWaveDumper(const char *filename, int rate);

View file

@ -3,13 +3,14 @@
#include <string>
#include "oplsynth/opl_mus_player.h"
#include "c_cvars.h"
#include "mus2midi.h"
#include "mididevices/mus2midi.h"
#include "i_sound.h"
#include "i_music.h"
#include "s_sound.h"
#include "files.h"
#include "wildmidi/wildmidi_lib.h"
#include "midisources/midisource.h"
#include "mididevices/mididevice.h"
void I_InitMusicWin32 ();
@ -17,283 +18,17 @@ extern float relative_volume;
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;
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;
struct OPLMidiConfig
{
int numchips = 2;
int core = 0;
bool fullpan = true;
struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL];
};
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;
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;
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;
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;
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;
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 ------------------------------
@ -448,20 +183,6 @@ public:
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
void Fluid_SetupConfig(FluidConfig *config, const char* patches, bool systemfallback);

View file

@ -55,7 +55,7 @@
static void WriteVarLen (TArray<uint8_t> &file, uint32_t value);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR(Bool, snd_midiprecache);
EXTERN_CVAR(Float, snd_musicvolume)
EXTERN_CVAR(Int, snd_mididevice)
@ -207,7 +207,7 @@ MIDIDevice *MIDIStreamer::CreateMIDIDevice(EMidiDevice devtype, int samplerate)
case MDEV_MMAPI:
#ifdef _WIN32
dev = CreateWinMIDIDevice(mididevice);
dev = CreateWinMIDIDevice(mididevice, snd_midiprecache);
break;
#endif
// Intentional fall-through for non-Windows systems.
@ -316,8 +316,16 @@ bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate)
throw std::runtime_error("MMAPI device is not supported");
}
auto iMIDI = CreateMIDIDevice(devtype, samplerate);
MIDI.reset(new MIDIWaveWriter(filename, static_cast<SoftSynthMIDIDevice *>(iMIDI)));
return InitPlayback();
auto writer = new MIDIWaveWriter(filename, static_cast<SoftSynthMIDIDevice*>(iMIDI));
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;
}
//==========================================================================
@ -349,13 +357,10 @@ bool MIDIStreamer::InitPlayback()
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)
{ // The MIDI file had no content and has been automatically closed.
return false;
}
StartPlayback();
if (MIDI == nullptr)
{ // The MIDI file had no content and has been automatically closed.
return false;
}
int res = 1;
@ -893,7 +898,8 @@ FString MIDIStreamer::GetStats()
{
return "No MIDI device in use.";
}
return MIDI->GetStats();
auto s = MIDI->GetStats();
return s.c_str();
}
//==========================================================================

View file

@ -50,6 +50,7 @@
#include "i_system.h"
#include "atterm.h"
#include "s_music.h"
#include "mididevices/mididevice.h"
// MACROS ------------------------------------------------------------------
@ -1462,6 +1463,8 @@ static void S_AddSNDINFO (int lump)
else if (sc.Compare("fluidsynth")) devset.device = MDEV_FLUIDSYNTH;
else if (sc.Compare("gus")) devset.device = MDEV_GUS;
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);
if (sc.CheckString(","))

View file

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