- refactoring of WildMidi to have proper instrument management

Not tested yet, it compiles but may not work as-is.
This commit is contained in:
Christoph Oelckers 2019-09-25 17:27:10 +02:00
parent 1112a69adb
commit 4369220335
16 changed files with 567 additions and 430 deletions

View file

@ -56,7 +56,7 @@ public:
int GetDeviceType() const override { return MDEV_WILDMIDI; }
protected:
WildMidi_Renderer *Renderer;
WildMidi::Renderer *Renderer;
void HandleEvent(int status, int parm1, int parm2);
void HandleLongEvent(const uint8_t *data, int len);
@ -93,13 +93,20 @@ CVAR(Int, wildmidi_frequency, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CUSTOM_CVAR(Bool, wildmidi_reverb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (currSong != NULL)
currSong->WildMidiSetOption(WM_MO_REVERB, *self? WM_MO_REVERB:0);
currSong->WildMidiSetOption(WildMidi::WM_MO_REVERB, *self? WildMidi::WM_MO_REVERB:0);
}
CUSTOM_CVAR(Bool, wildmidi_enhanced_resampling, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (currSong != NULL)
currSong->WildMidiSetOption(WM_MO_ENHANCED_RESAMPLING, *self? WM_MO_ENHANCED_RESAMPLING:0);
currSong->WildMidiSetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING, *self? WildMidi::WM_MO_ENHANCED_RESAMPLING:0);
}
static WildMidi::Instruments *instruments;
void WildMidi_Shutdown()
{
if (instruments) delete instruments;
}
// CODE --------------------------------------------------------------------
@ -117,32 +124,32 @@ WildMIDIDevice::WildMIDIDevice(const char *args, int samplerate)
if (args == NULL || *args == 0) args = wildmidi_config;
if (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != WildMidi_GetSampleRate())
if (instruments == nullptr || (CurrentConfig.CompareNoCase(args) != 0 || SampleRate != instruments->GetSampleRate()))
{
if (CurrentConfig.IsNotEmpty())
{
WildMidi_Shutdown();
CurrentConfig = "";
}
if (instruments) delete instruments;
instruments = nullptr;
CurrentConfig = "";
auto reader = sfmanager.OpenSoundFont(CurrentConfig, SF_GUS);
auto reader = sfmanager.OpenSoundFont(args, SF_GUS);
if (reader == nullptr)
{
I_Error("WildMidi: Unable to open sound font %s\n", CurrentConfig.GetChars());
I_Error("WildMidi: Unable to open sound font %s\n", args);
}
if (!WildMidi_Init(args, SampleRate, 0))
instruments = new WildMidi::Instruments(reader, SampleRate);
if (instruments->LoadConfig(nullptr) < 0)
{
CurrentConfig = args;
I_Error("WildMidi: Unable to load instruments for sound font %s\n", args);
}
CurrentConfig = args;
}
if (CurrentConfig.IsNotEmpty())
{
Renderer = new WildMidi_Renderer();
Renderer = new WildMidi::Renderer(instruments);
int flags = 0;
if (wildmidi_enhanced_resampling) flags |= WM_MO_ENHANCED_RESAMPLING;
if (wildmidi_reverb) flags |= WM_MO_REVERB;
Renderer->SetOption(WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, flags);
if (wildmidi_enhanced_resampling) flags |= WildMidi::WM_MO_ENHANCED_RESAMPLING;
if (wildmidi_reverb) flags |= WildMidi::WM_MO_REVERB;
Renderer->SetOption(WildMidi::WM_MO_ENHANCED_RESAMPLING | WildMidi::WM_MO_REVERB, flags);
}
else
{

View file

@ -90,6 +90,8 @@ public:
void Timidity_Shutdown();
void TimidityPP_Shutdown();
void WildMidi_Shutdown ();
// Base class for software synthesizer MIDI output devices ------------------

View file

@ -42,6 +42,12 @@
#include "resourcefiles/resourcefile.h"
#include "timiditypp/common.h"
//==========================================================================
//
//
//
//==========================================================================
FSoundFontManager sfmanager;
template<class base>
@ -131,6 +137,11 @@ int FSoundFontReader::pathcmp(const char *p1, const char *p2)
return mCaseSensitivePaths? strcmp(p1, p2) : stricmp(p1, p2);
}
//==========================================================================
//
//
//
//==========================================================================
FileReader FSoundFontReader::Open(const char *name, std::string& filename)
{
@ -151,40 +162,45 @@ FileReader FSoundFontReader::Open(const char *name, std::string& filename)
//==========================================================================
//
// This is the interface function for Timidity++
//
//
//==========================================================================
struct TimidityPlus::timidity_file* FSoundFontReader::open_timidityplus_file(const char* name)
template<class interface>
interface* FSoundFontReader::open_interface(const char* name)
{
std::string filename;
FileReader fr = Open(name, filename);
if (!fr.isOpen()) return nullptr;
auto tf = new file_interface<TimidityPlus::timidity_file>;
auto tf = new file_interface<interface>;
tf->fr = std::move(fr);
tf->filename = std::move(filename);
return tf;
}
//==========================================================================
//
// This is the interface function for Timidity(GUS)
// The file interfaces for the different MIDI renderers
// They are all the same except for the class names.
//
//==========================================================================
struct TimidityPlus::timidity_file* FSoundFontReader::open_timidityplus_file(const char* name)
{
return open_interface<TimidityPlus::timidity_file>(name);
}
struct Timidity::timidity_file* FSoundFontReader::open_timidity_file(const char* name)
{
std::string filename;
return open_interface<Timidity::timidity_file>(name);
}
FileReader fr = Open(name, filename);
if (!fr.isOpen()) return nullptr;
auto tf = new file_interface<Timidity::timidity_file>;
tf->fr = std::move(fr);
tf->filename = std::move(filename);
return tf;
struct WildMidi::wildmidi_file* FSoundFontReader::open_wildmidi_file(const char* name)
{
return open_interface<WildMidi::wildmidi_file>(name);
}
//==========================================================================

View file

@ -5,6 +5,7 @@
#include "files.h"
#include "timiditypp/timidity_file.h"
#include "timidity/timidity_file.h"
#include "wildmidi_file.h"
enum
{
@ -28,7 +29,8 @@ struct FSoundFontInfo
//
//==========================================================================
class FSoundFontReader : public TimidityPlus::SoundFontReaderInterface, public Timidity::SoundFontReaderInterface
class FSoundFontReader : public TimidityPlus::SoundFontReaderInterface, public Timidity::SoundFontReaderInterface, public WildMidi::SoundFontReaderInterface
// Yes, it's 3 copies of essentially the same interface, but since we want to keep the 3 renderers as isolated modules we have to pull in their own implementations here.
{
protected:
// This is only doable for loose config files that get set as sound fonts. All other cases read from a contained environment where this does not apply.
@ -75,6 +77,16 @@ public:
return AddPath(name);
}
// WildMidi interface - essentially the same again but yet another namespace
virtual struct WildMidi::wildmidi_file* open_wildmidi_file(const char* name) override;
virtual void wildmidi_add_path(const char* name) override
{
return AddPath(name);
}
template<class interface>
interface* open_interface(const char* name);
};
//==========================================================================

View file

@ -27,22 +27,21 @@
#ifndef __COMMON_H
#define __COMMON_H
#define SAMPLE_16BIT 0x01
#define SAMPLE_UNSIGNED 0x02
#define SAMPLE_LOOP 0x04
#define SAMPLE_PINGPONG 0x08
#define SAMPLE_REVERSE 0x10
#define SAMPLE_SUSTAIN 0x20
#define SAMPLE_ENVELOPE 0x40
#define SAMPLE_CLAMPED 0x80
namespace WildMidi
{
#ifdef DEBUG_SAMPLES
#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx)
#else
#define SAMPLE_CONVERT_DEBUG(dx)
#endif
enum
{
SAMPLE_16BIT = 0x01,
SAMPLE_UNSIGNED = 0x02,
SAMPLE_LOOP = 0x04,
SAMPLE_PINGPONG = 0x08,
SAMPLE_REVERSE = 0x10,
SAMPLE_SUSTAIN = 0x20,
SAMPLE_ENVELOPE = 0x40,
SAMPLE_CLAMPED = 0x80,
};
extern unsigned short int _WM_SampleRate;
struct _sample {
unsigned int data_length;
@ -91,4 +90,6 @@ struct _patch {
#define M_LN2 0.69314718055994530942
#endif
}
#endif /* __COMMON_H */

View file

@ -36,35 +36,27 @@
#include <errno.h>
#include <memory>
#include "files.h"
#include "wm_error.h"
#include "file_io.h"
#include "i_soundfont.h"
std::unique_ptr<FSoundFontReader> wm_sfreader;
#include "wildmidi_file.h"
unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size)
namespace WildMidi
{
FileReader fp;
if (filename == nullptr)
{
fp = wm_sfreader->OpenMainConfigFile();
filename = wm_sfreader->MainConfigFileName();
}
else
{
fp = wm_sfreader->OpenFile(filename);
}
static const int WM_MAXFILESIZE = 0x1fffffff;
if (!fp.isOpen())
unsigned char *_WM_BufferFile(SoundFontReaderInterface *reader, const char *filename, unsigned long int *size, std::string *fullname)
{
auto fp = reader->open_wildmidi_file(filename);
if (!fp)
{
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, filename, errno);
return NULL;
}
auto fsize = fp.GetLength();
auto fsize = (fp->seek(0, SEEK_END), fp->tell());
if (fsize > WM_MAXFILESIZE)
{
@ -81,8 +73,12 @@ unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size)
return NULL;
}
fp.Read(data, fsize);
fp->seek(0, SEEK_SET);
fp->read(data, fsize);
fp->close();
data[fsize] = 0;
*size = (long)fsize;
if (fullname) *fullname = fp->filename;
return data;
}
}

View file

@ -27,8 +27,11 @@
#ifndef __FILE_IO_H
#define __FILE_IO_H
#define WM_MAXFILESIZE 0x1fffffff
extern unsigned char *_WM_BufferFile (const char *filename, unsigned long int *size);
extern bool _WM_InitReader(const char *config_name);
#include "wildmidi_file.h"
namespace WildMidi
{
unsigned char *_WM_BufferFile(SoundFontReaderInterface *reader, const char *filename, unsigned long int *size, std::string *fullname = nullptr);
}
#endif /* __FILE_IO_H */

View file

@ -30,7 +30,54 @@
#include "common.h"
#include "wm_error.h"
#include "file_io.h"
#include "wildmidi_lib.h"
namespace WildMidi
{
/* Guspat Envelope Rate Timings */
float env_time_table[] = {
/* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */
0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f,
0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f,
0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f,
0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f,
0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f,
0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f,
0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f,
0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f,
/* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */
0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f,
0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f,
0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f,
0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f,
0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f,
0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f,
0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f,
0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f,
/* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */
0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f,
0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f,
0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f,
0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f,
0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f,
0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f,
0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f,
0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f,
/* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */
0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f,
5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f,
2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f,
1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f,
1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f,
1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f,
0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f,
0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f
};
#ifdef DEBUG_GUSPAT
#define GUSPAT_FILENAME_DEBUG(dx) fprintf(stderr,"\r%s\n",dx)
@ -48,6 +95,11 @@
#define GUSPAT_END_DEBUG()
#endif
#ifdef DEBUG_SAMPLES
#define SAMPLE_CONVERT_DEBUG(dx) printf("\r%s\n",dx)
#else
#define SAMPLE_CONVERT_DEBUG(dx)
#endif
/*
* sample data conversion functions
* convert data to signed shorts
@ -70,8 +122,7 @@ static int convert_8s(unsigned char *data, struct _sample *gus_sample) {
return 0;
}
_WM_ERROR_NEW("(%s:%i) ERROR: calloc failed (%s)", __FUNCTION__, __LINE__,
strerror(errno));
_WM_ERROR_NEW("Calloc failed (%s)\n", strerror(errno));
return -1;
}
@ -703,7 +754,8 @@ static int convert_16urp(unsigned char *data, struct _sample *gus_sample) {
/* sample loading */
struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) {
struct _sample * Instruments::load_gus_pat(const char *filename)
{
unsigned char *gus_patch;
unsigned long int gus_size;
unsigned long int gus_ptr;
@ -736,7 +788,7 @@ struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) {
SAMPLE_CONVERT_DEBUG(__FUNCTION__); SAMPLE_CONVERT_DEBUG(filename);
if ((gus_patch = _WM_BufferFile(filename, &gus_size)) == NULL) {
if ((gus_patch = _WM_BufferFile(sfreader, filename, &gus_size)) == NULL) {
return NULL;
}
if (gus_size < 239) {
@ -918,8 +970,8 @@ struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) {
/ ((float) _WM_SampleRate * env_time_table[env_rate]));
GUSPAT_INT_DEBUG("Envelope Rate",gus_sample->env_rate[i]); GUSPAT_INT_DEBUG("GUSPAT Rate",env_rate);
if (gus_sample->env_rate[i] == 0) {
_WM_ERROR_NEW("%s: Warning: found invalid envelope(%lu) rate setting in %s. Using %f instead.",
__FUNCTION__, i, filename, env_time_table[63]);
_WM_ERROR_NEW("Warning: found invalid envelope(%lu) rate setting in %s. Using %f instead.\n",
i, filename, env_time_table[63]);
gus_sample->env_rate[i] = (signed long int) (4194303.0
/ ((float) _WM_SampleRate * env_time_table[63]));
GUSPAT_FLOAT_DEBUG("Envelope Time",env_time_table[63]);
@ -979,3 +1031,5 @@ struct _sample * _WM_load_gus_pat(const char *filename, int fix_release) {
free(gus_patch);
return first_gus_sample;
}
}

View file

@ -27,51 +27,14 @@
#ifndef __GUS_PAT_H
#define __GUS_PAT_H
namespace WildMidi
{
class SoundFontReaderInterface;
/* Guspat Envelope Rate Timings */
extern float env_time_table[];
static float env_time_table[] = {
/* Row 1 = (4095.0 / (x * ( 1.0 / (1.6 * 14.0) ))) / 1000000.0 */
0.0f, 0.091728000f, 0.045864000f, 0.030576000f, 0.022932000f, 0.018345600f, 0.015288000f, 0.013104000f,
0.011466000f, 0.010192000f, 0.009172800f, 0.008338909f, 0.007644000f, 0.007056000f, 0.006552000f, 0.006115200f,
0.005733000f, 0.005395765f, 0.005096000f, 0.004827789f, 0.004586400f, 0.004368000f, 0.004169455f, 0.003988174f,
0.003822000f, 0.003669120f, 0.003528000f, 0.003397333f, 0.003276000f, 0.003163034f, 0.003057600f, 0.002958968f,
0.002866500f, 0.002779636f, 0.002697882f, 0.002620800f, 0.002548000f, 0.002479135f, 0.002413895f, 0.002352000f,
0.002293200f, 0.002237268f, 0.002184000f, 0.002133209f, 0.002084727f, 0.002038400f, 0.001994087f, 0.001951660f,
0.001911000f, 0.001872000f, 0.001834560f, 0.001798588f, 0.001764000f, 0.001730717f, 0.001698667f, 0.001667782f,
0.001638000f, 0.001609263f, 0.001581517f, 0.001554712f, 0.001528800f, 0.001503738f, 0.001479484f, 0.001456000f,
/* Row 2 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 8.0 ))) / 1000000.0 */
0.0f, 0.733824000f, 0.366912000f, 0.244608000f, 0.183456000f, 0.146764800f, 0.122304000f, 0.104832000f,
0.091728000f, 0.081536000f, 0.073382400f, 0.066711273f, 0.061152000f, 0.056448000f, 0.052416000f, 0.048921600f,
0.045864000f, 0.043166118f, 0.040768000f, 0.038622316f, 0.036691200f, 0.034944000f, 0.033355636f, 0.031905391f,
0.030576000f, 0.029352960f, 0.028224000f, 0.027178667f, 0.026208000f, 0.025304276f, 0.024460800f, 0.023671742f,
0.022932000f, 0.022237091f, 0.021583059f, 0.020966400f, 0.020384000f, 0.019833081f, 0.019311158f, 0.018816000f,
0.018345600f, 0.017898146f, 0.017472000f, 0.017065674f, 0.016677818f, 0.016307200f, 0.015952696f, 0.015613277f,
0.015288000f, 0.014976000f, 0.014676480f, 0.014388706f, 0.014112000f, 0.013845736f, 0.013589333f, 0.013342255f,
0.013104000f, 0.012874105f, 0.012652138f, 0.012437695f, 0.012230400f, 0.012029902f, 0.011835871f, 0.011648000f,
/* Row 3 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 64.0 ))) / 1000000.0 */
0.0f, 5.870592000f, 2.935296000f, 1.956864000f, 1.467648000f, 1.174118400f, 0.978432000f, 0.838656000f,
0.733824000f, 0.652288000f, 0.587059200f, 0.533690182f, 0.489216000f, 0.451584000f, 0.419328000f, 0.391372800f,
0.366912000f, 0.345328941f, 0.326144000f, 0.308978526f, 0.293529600f, 0.279552000f, 0.266845091f, 0.255243130f,
0.244608000f, 0.234823680f, 0.225792000f, 0.217429333f, 0.209664000f, 0.202434207f, 0.195686400f, 0.189373935f,
0.183456000f, 0.177896727f, 0.172664471f, 0.167731200f, 0.163072000f, 0.158664649f, 0.154489263f, 0.150528000f,
0.146764800f, 0.143185171f, 0.139776000f, 0.136525395f, 0.133422545f, 0.130457600f, 0.127621565f, 0.124906213f,
0.122304000f, 0.119808000f, 0.117411840f, 0.115109647f, 0.112896000f, 0.110765887f, 0.108714667f, 0.106738036f,
0.104832000f, 0.102992842f, 0.101217103f, 0.099501559f, 0.097843200f, 0.096239213f, 0.094686968f, 0.093184000f,
/* Row 4 = (4095.0 / (x * ((1.0 / (1.6 * 14.0)) / 512.0))) / 1000000.0 */
0.0f, 46.964736000f,23.482368000f,15.654912000f,11.741184000f, 9.392947200f, 7.827456000f, 6.709248000f,
5.870592000f, 5.218304000f, 4.696473600f, 4.269521455f, 3.913728000f, 3.612672000f, 3.354624000f, 3.130982400f,
2.935296000f, 2.762631529f, 2.609152000f, 2.471828211f, 2.348236800f, 2.236416000f, 2.134760727f, 2.041945043f,
1.956864000f, 1.878589440f, 1.806336000f, 1.739434667f, 1.677312000f, 1.619473655f, 1.565491200f, 1.514991484f,
1.467648000f, 1.423173818f, 1.381315765f, 1.341849600f, 1.304576000f, 1.269317189f, 1.235914105f, 1.204224000f,
1.174118400f, 1.145481366f, 1.118208000f, 1.092203163f, 1.067380364f, 1.043660800f, 1.020972522f, 0.999249702f,
0.978432000f, 0.958464000f, 0.939294720f, 0.920877176f, 0.903168000f, 0.886127094f, 0.869717333f, 0.853904291f,
0.838656000f, 0.823942737f, 0.809736828f, 0.796012475f, 0.782745600f, 0.769913705f, 0.757495742f, 0.745472000f
};
extern struct _sample * _WM_load_gus_pat (const char *filename, int _fix_release);
}
#endif /* __GUS_PAT_H */

View file

@ -32,6 +32,8 @@
#include "common.h"
#include "reverb.h"
namespace WildMidi
{
/*
reverb function
*/
@ -396,3 +398,4 @@ void _WM_do_reverb(struct _rvb *rvb, signed int *buffer, int size) {
}
}
}

View file

@ -27,6 +27,9 @@
#ifndef __REVERB_H
#define __REVERB_H
namespace WildMidi
{
struct _rvb {
/* filter data */
signed int l_buf_flt_in[8][6][2];
@ -54,4 +57,6 @@ struct _rvb {
extern void _WM_free_reverb (struct _rvb *rvb);
extern void _WM_do_reverb (struct _rvb *rvb, signed int *buffer, int size);
}
#endif /* __REVERB_H */

View file

@ -0,0 +1,146 @@
/*
wildmidi_file.h
Midi Wavetable Processing library
Copyright (C) 2019 Christoph Oelckers
WildMIDI is free software: you can redistribute and/or modify the player
under the terms of the GNU General Public License and you can redistribute
and/or modify the library under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, either version 3 of
the licenses, or(at your option) any later version.
WildMIDI is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License and
the GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License and the
GNU Lesser General Public License along with WildMIDI. If not, see
<http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdio.h>
#include <vector>
#include <string>
namespace WildMidi
{
struct wildmidi_file
{
std::string filename;
virtual ~wildmidi_file() {}
virtual char* gets(char* buff, int n) = 0;
virtual long read(void* buff, int32_t size, int32_t nitems) = 0;
long read(void* buff, int32_t size) { return read(buff, 1, size); }
virtual long seek(long offset, int whence) = 0;
virtual long tell() = 0;
virtual void close() = 0;
};
class SoundFontReaderInterface
{
public:
virtual ~SoundFontReaderInterface() {}
virtual struct wildmidi_file* open_wildmidi_file(const char* fn) = 0;
virtual void wildmidi_add_path(const char* path) = 0;
};
// A minimalistic sound font reader interface. Normally this should be replaced with something better tied into the host application.
#ifdef USE_BASE_INTERFACE
// Base version of timidity_file using stdio's FILE.
struct wildmidi_file_FILE : public wildmidi_file
{
FILE* f = nullptr;
~wildmidi_file_FILE()
{
if (f) fclose(f);
}
char* gets(char* buff, int n) override
{
if (!f) return nullptr;
return fgets(buff, n, f);
}
long read(void* buff, int32_t size, int32_t nitems) override
{
if (!f) return 0;
return (long)fread(buff, size, nitems, f);
}
long seek(long offset, int whence) override
{
if (!f) return 0;
return fseek(f, offset, whence);
}
long tell() override
{
if (!f) return 0;
return ftell(f);
}
void close()
{
if (f) fclose(f);
delete this;
}
};
class BaseSoundFontReader : public SoundFontReaderInterface
{
std::vector<std::string> paths;
bool IsAbsPath(const char *name)
{
if (name[0] == '/' || name[0] == '\\') return true;
#ifdef _WIN32
/* [A-Za-z]: (for Windows) */
if (isalpha(name[0]) && name[1] == ':') return true;
#endif /* _WIN32 */
return 0;
}
struct wildmidi_file* open_wildmidi_file(const char* fn)
{
FILE *f = nullptr;
std::string fullname;
if (!fn)
{
f = fopen("timidity.cfg", "rt");
fullname = "timidity.cfg";
}
else
{
if (!IsAbsPath(fn))
{
for(int i = (int)paths.size()-1; i>=0; i--)
{
fullname = paths[i] + fn;
f = fopen(fullname.c_str(), "rt");
break;
}
}
if (!f) f = fopen(fn, "rt");
}
if (!f) return nullptr;
auto tf = new wildmidi_file_FILE;
tf->f = f;
tf->filename = fullname;
return tf;
}
void wildmidi_add_path(const char* path)
{
std::string p = path;
if (p.back() != '/' && p.back() != '\\') p += '/'; // always let it end with a slash.
paths.push_back(p);
}
};
#endif
}

View file

@ -31,6 +31,7 @@
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>
#ifndef _WIN32
#include <strings.h>
#endif
@ -44,7 +45,9 @@
#include "reverb.h"
#include "gus_pat.h"
#include "wildmidi_lib.h"
#include "i_soundfont.h"
namespace WildMidi
{
#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
#ifdef _WIN32
@ -63,28 +66,8 @@
#define MEM_CHUNK 8192
static int WM_Initialized = 0;
static signed short int WM_MasterVolume = 948;
static unsigned short int WM_MixerOptions = 0;
static char WM_Version[] = "WildMidi Processing Library";
unsigned short int _WM_SampleRate;
static struct _patch *patch[128];
static float reverb_room_width = 16.875f;
static float reverb_room_length = 22.5f;
static float reverb_listen_posx = 8.4375f;
static float reverb_listen_posy = 16.875f;
static int fix_release = 0;
static int auto_amp = 0;
static int auto_amp_with_amp = 0;
static std::mutex patch_lock;
extern std::unique_ptr<FSoundFontReader> wm_sfreader;
static const char WM_Version[] = "WildMidi Processing Library";
struct _channel {
unsigned char bank;
@ -174,10 +157,8 @@ struct _mdi {
/* Gauss Interpolation code adapted from code supplied by Eric. A. Welsh */
static double newt_coeffs[58][58]; /* for start/end of samples */
#define MAX_GAUSS_ORDER 34 /* 34 is as high as we can go before errors crop up */
static double *gauss_table = NULL; /* *gauss_table[1<<FPBITS] */
static int gauss_n = MAX_GAUSS_ORDER;
static std::mutex gauss_lock;
static std::vector<double> gauss_table; /* *gauss_table[1<<FPBITS] */
static const int gauss_n = 34; /* 34 is as high as we can go before errors crop up */
static void init_gauss(void) {
/* init gauss table */
@ -190,8 +171,7 @@ static void init_gauss(void) {
double z[35];
double *gptr, *t;
std::lock_guard<std::mutex> lock(gauss_lock);
if (gauss_table) {
if (gauss_table.size()) {
return;
}
@ -218,7 +198,8 @@ static void init_gauss(void) {
for (j = 0, sign = (int)pow(-1., i); j <= i; j++, sign *= -1)
newt_coeffs[i][j] *= sign;
t = (double*)malloc((1<<FPBITS) * (n + 1) * sizeof(double));
gauss_table.resize((1<<FPBITS) * (n + 1));
t = gauss_table.data();
x_inc = 1.0 / (1<<FPBITS);
for (m = 0, x = 0.0; m < (1<<FPBITS); m++, x += x_inc) {
xz = (x + n_half) / (4 * M_PI);
@ -236,25 +217,11 @@ static void init_gauss(void) {
*gptr++ = ck;
}
}
gauss_table = t;
}
static void free_gauss(void)
{
std::lock_guard<std::mutex> lock(gauss_lock);
free(gauss_table);
gauss_table = NULL;
}
struct _hndl {
void * handle;
struct _hndl *next;
struct _hndl *prev;
};
/* f: ( VOLUME / 127.0 ) * 1024.0 */
static signed short int lin_volume[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72,
static const signed short int lin_volume[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72,
80, 88, 96, 104, 112, 120, 129, 137, 145, 153, 161, 169, 177, 185, 193,
201, 209, 217, 225, 233, 241, 249, 258, 266, 274, 282, 290, 298, 306,
314, 322, 330, 338, 346, 354, 362, 370, 378, 387, 395, 403, 411, 419,
@ -266,7 +233,7 @@ static signed short int lin_volume[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72,
991, 999, 1007, 1015, 1024 };
/* f: As per midi 2 standard */
static float dBm_volume[] = { -999999.999999f, -84.15214884f, -72.11094901f,
static const float dBm_volume[] = { -999999.999999f, -84.15214884f, -72.11094901f,
-65.06729865f, -60.06974919f, -56.19334866f, -53.02609882f, -50.34822724f,
-48.02854936f, -45.98244846f, -44.15214884f, -42.49644143f, -40.984899f,
-39.59441475f, -38.30702741f, -37.10849848f, -35.98734953f, -34.93419198f,
@ -294,7 +261,7 @@ static float dBm_volume[] = { -999999.999999f, -84.15214884f, -72.11094901f,
-0.5559443807f, -0.4152814317f, -0.2757483179f, -0.1373270335f, 0 };
/* f: As per midi 2 standard */
static float dBm_pan_volume[127] = {
static const float dBm_pan_volume[127] = {
-999999.999999f, -87.6945020928f, -73.8331126923f, -65.7264009888f,
-59.9763864074f, -55.5181788833f, -51.8774481743f, -48.8011722841f,
-46.1383198371f, -43.7914727130f, -41.6941147218f, -39.7988027954f,
@ -328,7 +295,7 @@ static float dBm_pan_volume[127] = {
-0.0560023899f, -0.0388794497f, -0.0248770409f, -0.0139907967f,
-0.0062173263f, -0.0015542108f, 0.0000000000f };
static unsigned int freq_table[] = { 837201792, 837685632, 838169728,
static const unsigned int freq_table[] = { 837201792, 837685632, 838169728,
838653568, 839138240, 839623232, 840108480, 840593984, 841079680,
841565184, 842051648, 842538240, 843025152, 843512320, 843999232,
844486976, 844975040, 845463360, 845951936, 846440320, 846929536,
@ -544,19 +511,13 @@ static unsigned int freq_table[] = { 837201792, 837685632, 838169728,
* =========================
*/
static void WM_InitPatches(void) {
int i;
for (i = 0; i < 128; i++) {
patch[i] = NULL;
}
}
static void WM_FreePatches(void) {
void Instruments::FreePatches()
{
int i;
struct _patch * tmp_patch;
struct _sample * tmp_sample;
std::lock_guard<std::mutex> lock(patch_lock);
for (i = 0; i < 128; i++) {
while (patch[i]) {
while (patch[i]->first_sample) {
@ -649,7 +610,8 @@ static char** WM_LC_Tokenize_Line(char *line_data)
return token_data;
}
static int WM_LoadConfig(const char *config_file, bool main) {
int Instruments::LoadConfig(const char *config_parm)
{
unsigned long int config_size = 0;
char *config_buffer = NULL;
const char *dir_end = NULL;
@ -660,20 +622,20 @@ static int WM_LoadConfig(const char *config_file, bool main) {
struct _patch * tmp_patch;
char **line_tokens = NULL;
int token_count = 0;
auto config_parm = config_file;
std::string config_file_s;
config_buffer = (char *)_WM_BufferFile(config_parm, &config_size);
config_buffer = (char *)_WM_BufferFile(sfreader, config_parm, &config_size, &config_file_s);
if (!config_buffer) {
WM_FreePatches();
FreePatches();
return -1;
}
if (config_parm == nullptr) config_file = wm_sfreader->basePath().GetChars(); // Re-get the base path because for archives this is empty.
auto config_file = config_file_s.c_str();
// This part was rewritten because the original depended on a header that was GPL'd.
dir_end = strrchr(config_file, '/');
#ifdef _WIN32
const char *dir_end2 = strrchr(config_file, '\\');
const char *dir_end2 = strrchr(config_file.c, '\\');
if (dir_end2 > dir_end) dir_end = dir_end2;
#endif
@ -683,7 +645,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to parse config",
errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, 0);
WM_FreePatches();
FreePatches();
free(config_buffer);
return -1;
}
@ -714,7 +676,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"(missing name in dir line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
@ -723,7 +685,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
@ -739,7 +701,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"(missing name in source line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
@ -752,7 +714,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -766,13 +728,13 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"to parse config", errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(line_tokens);
free(config_buffer);
return -1;
}
}
if (WM_LoadConfig(new_config, false) == -1) {
if (LoadConfig(new_config) == -1) {
free(new_config);
free(line_tokens);
free(config_buffer);
@ -786,7 +748,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"(syntax error in bank line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -799,7 +761,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"(syntax error in drumset line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -813,7 +775,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -838,7 +800,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -863,7 +825,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -885,7 +847,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -920,7 +882,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
NULL, errno);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -958,7 +920,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_LOAD, config_file,
0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -988,7 +950,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
WM_ERR_MEM, NULL, errno);
_WM_ERROR(__FUNCTION__, __LINE__,
WM_ERR_LOAD, config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -1011,7 +973,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
"(missing name in patch line)", 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -1025,7 +987,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
NULL, 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -1039,7 +1001,7 @@ static int WM_LoadConfig(const char *config_file, bool main) {
NULL, 0);
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD,
config_file, 0);
WM_FreePatches();
FreePatches();
free(config_dir);
free(line_tokens);
free(config_buffer);
@ -1185,7 +1147,8 @@ static int WM_LoadConfig(const char *config_file, bool main) {
/* sample loading */
static int load_sample(struct _patch *sample_patch) {
int Instruments::load_sample(struct _patch *sample_patch)
{
struct _sample *guspat = NULL;
struct _sample *tmp_sample = NULL;
unsigned int i = 0;
@ -1193,7 +1156,7 @@ static int load_sample(struct _patch *sample_patch) {
/* we only want to try loading the guspat once. */
sample_patch->loaded = 1;
if ((guspat = _WM_load_gus_pat(sample_patch->filename, fix_release)) == NULL) {
if ((guspat = load_gus_pat(sample_patch->filename)) == NULL) {
return -1;
}
@ -1305,12 +1268,10 @@ static int load_sample(struct _patch *sample_patch) {
return 0;
}
static struct _patch *
get_patch_data(unsigned short patchid) {
struct _patch *Instruments::get_patch_data(unsigned short patchid)
{
struct _patch *search_patch;
std::lock_guard<std::mutex> lock(patch_lock);
search_patch = patch[patchid & 0x007F];
if (search_patch == NULL) {
@ -1329,7 +1290,8 @@ get_patch_data(unsigned short patchid) {
return NULL;
}
static void load_patch(struct _mdi *mdi, unsigned short patchid) {
void Instruments::load_patch(struct _mdi *mdi, unsigned short patchid)
{
unsigned int i;
struct _patch *tmp_patch = NULL;
@ -1344,7 +1306,6 @@ static void load_patch(struct _mdi *mdi, unsigned short patchid) {
return;
}
std::lock_guard<std::mutex> lock(patch_lock);
if (!tmp_patch->loaded) {
if (load_sample(tmp_patch) == -1) {
return;
@ -1362,12 +1323,11 @@ static void load_patch(struct _mdi *mdi, unsigned short patchid) {
tmp_patch->inuse_count++;
}
static struct _sample *
get_sample_data(struct _patch *sample_patch, unsigned long int freq) {
static struct _sample *get_sample_data(struct _patch *sample_patch, unsigned long int freq)
{
struct _sample *last_sample = NULL;
struct _sample *return_sample = NULL;
std::lock_guard<std::mutex> lock(patch_lock);
if (sample_patch == NULL) {
return NULL;
}
@ -1394,7 +1354,8 @@ get_sample_data(struct _patch *sample_patch, unsigned long int freq) {
}
/* Should be called in any function that effects note volumes */
void _WM_AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte) {
void Renderer::AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte)
{
double premix_dBm;
double premix_lin;
int pan_ofs;
@ -1450,7 +1411,8 @@ void _WM_AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte
/* Should be called in any function that effects channel volumes */
/* Calling this function with a value > 15 will make it adjust notes on all channels */
void _WM_AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch) {
void Renderer::AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch)
{
struct _note *nte = mdi->note;
if (nte != NULL) {
do {
@ -1460,8 +1422,8 @@ void _WM_AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch) {
}
} else {
_DO_ADJUST:
_WM_AdjustNoteVolumes(mdi, ch, nte);
if (nte->replay) _WM_AdjustNoteVolumes(mdi, ch, nte->replay);
AdjustNoteVolumes(mdi, ch, nte);
if (nte->replay) AdjustNoteVolumes(mdi, ch, nte->replay);
}
nte = nte->next;
} while (nte != NULL);
@ -1538,7 +1500,8 @@ static void do_note_off(struct _mdi *mdi, struct _event_data *data) {
}
}
static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) {
unsigned long int Renderer::get_inc(struct _mdi *mdi, struct _note *nte)
{
int ch = nte->noteid >> 8;
signed long int note_f;
unsigned long int freq;
@ -1555,11 +1518,12 @@ static inline unsigned long int get_inc(struct _mdi *mdi, struct _note *nte) {
note_f = 12700;
}
freq = freq_table[(note_f % 1200)] >> (10 - (note_f / 1200));
return (((freq / ((_WM_SampleRate * 100) / 1024)) * 1024
return (((freq / ((instruments->GetSampleRate() * 100) / 1024)) * 1024
/ nte->sample->inc_div));
}
static void do_note_on(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_note_on(struct _mdi *mdi, struct _event_data *data)
{
struct _note *nte;
struct _note *prev_nte;
struct _note *nte_array;
@ -1584,7 +1548,7 @@ static void do_note_on(struct _mdi *mdi, struct _event_data *data) {
}
freq = freq_table[(note % 12) * 100] >> (10 - (note / 12));
} else {
patch = get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80));
patch = instruments->get_patch_data(((mdi->channel[ch].bank << 8) | note | 0x80));
if (patch == NULL) {
return;
}
@ -1648,10 +1612,11 @@ static void do_note_on(struct _mdi *mdi, struct _event_data *data) {
nte->hold = mdi->channel[ch].hold;
nte->replay = NULL;
nte->is_off = 0;
_WM_AdjustNoteVolumes(mdi, ch, nte);
AdjustNoteVolumes(mdi, ch, nte);
}
static void do_aftertouch(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_aftertouch(struct _mdi *mdi, struct _event_data *data)
{
struct _note *nte;
unsigned char ch = data->channel;
@ -1666,10 +1631,10 @@ static void do_aftertouch(struct _mdi *mdi, struct _event_data *data) {
}
nte->velocity = (unsigned char)data->data;
_WM_AdjustNoteVolumes(mdi, ch, nte);
AdjustNoteVolumes(mdi, ch, nte);
if (nte->replay) {
nte->replay->velocity = (unsigned char)data->data;
_WM_AdjustNoteVolumes(mdi, ch, nte->replay);
AdjustNoteVolumes(mdi, ch, nte->replay);
}
}
@ -1692,37 +1657,38 @@ static void do_control_data_entry_course(struct _mdi *mdi,
}
}
static void do_control_channel_volume(struct _mdi *mdi,
struct _event_data *data) {
void Renderer::do_control_channel_volume(struct _mdi *mdi, struct _event_data *data)
{
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
mdi->channel[ch].volume = (unsigned char)data->data;
_WM_AdjustChannelVolumes(mdi, ch);
AdjustChannelVolumes(mdi, ch);
}
static void do_control_channel_balance(struct _mdi *mdi,
struct _event_data *data) {
void Renderer::do_control_channel_balance(struct _mdi *mdi, struct _event_data *data)
{
unsigned char ch = data->channel;
mdi->channel[ch].balance = (signed char)(data->data);
_WM_AdjustChannelVolumes(mdi, ch);
AdjustChannelVolumes(mdi, ch);
}
static void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_control_channel_pan(struct _mdi *mdi, struct _event_data *data)
{
unsigned char ch = data->channel;
mdi->channel[ch].pan = (signed char)(data->data);
_WM_AdjustChannelVolumes(mdi, ch);
AdjustChannelVolumes(mdi, ch);
}
static void do_control_channel_expression(struct _mdi *mdi,
struct _event_data *data) {
void Renderer::do_control_channel_expression(struct _mdi *mdi, struct _event_data *data)
{
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
mdi->channel[ch].expression = (unsigned char)data->data;
_WM_AdjustChannelVolumes(mdi, ch);
AdjustChannelVolumes(mdi, ch);
}
static void do_control_data_entry_fine(struct _mdi *mdi,
@ -1862,8 +1828,8 @@ static void do_control_channel_sound_off(struct _mdi *mdi,
}
}
static void do_control_channel_controllers_off(struct _mdi *mdi,
struct _event_data *data) {
void Renderer::do_control_channel_controllers_off(struct _mdi *mdi, struct _event_data *data)
{
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
@ -1875,7 +1841,7 @@ static void do_control_channel_controllers_off(struct _mdi *mdi,
mdi->channel[ch].pitch_adjust = 0;
mdi->channel[ch].hold = 0;
_WM_AdjustChannelVolumes(mdi, ch);
AdjustChannelVolumes(mdi, ch);
}
static void do_control_channel_notes_off(struct _mdi *mdi,
@ -1911,17 +1877,19 @@ static void do_control_channel_notes_off(struct _mdi *mdi,
}
}
static void do_patch(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_patch(struct _mdi *mdi, struct _event_data *data)
{
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
if (!mdi->channel[ch].isdrum) {
mdi->channel[ch].patch = get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data)));
mdi->channel[ch].patch = instruments->get_patch_data((unsigned short)(((mdi->channel[ch].bank << 8) | data->data)));
} else {
mdi->channel[ch].bank = (unsigned char)data->data;
}
}
static void do_channel_pressure(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_channel_pressure(struct _mdi *mdi, struct _event_data *data)
{
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
@ -1930,17 +1898,18 @@ static void do_channel_pressure(struct _mdi *mdi, struct _event_data *data) {
while (note_data) {
if ((note_data->noteid >> 8) == ch) {
note_data->velocity = (unsigned char)data->data;
_WM_AdjustNoteVolumes(mdi, ch, note_data);
AdjustNoteVolumes(mdi, ch, note_data);
if (note_data->replay) {
note_data->replay->velocity = (unsigned char)data->data;
_WM_AdjustNoteVolumes(mdi, ch, note_data->replay);
AdjustNoteVolumes(mdi, ch, note_data->replay);
}
}
note_data = note_data->next;
}
}
static void do_pitch(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_pitch(struct _mdi *mdi, struct _event_data *data)
{
struct _note *note_data = mdi->note;
unsigned char ch = data->channel;
@ -1965,8 +1934,8 @@ static void do_pitch(struct _mdi *mdi, struct _event_data *data) {
}
}
static void do_sysex_roland_drum_track(struct _mdi *mdi,
struct _event_data *data) {
void Renderer::do_sysex_roland_drum_track(struct _mdi *mdi, struct _event_data *data)
{
unsigned char ch = data->channel;
MIDI_EVENT_DEBUG(__FUNCTION__,ch);
@ -1976,16 +1945,17 @@ static void do_sysex_roland_drum_track(struct _mdi *mdi,
mdi->channel[ch].patch = NULL;
} else {
mdi->channel[ch].isdrum = 0;
mdi->channel[ch].patch = get_patch_data(0);
mdi->channel[ch].patch = instruments->get_patch_data(0);
}
}
static void do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data)
{
int i;
for (i = 0; i < 16; i++) {
mdi->channel[i].bank = 0;
if (i != 9) {
mdi->channel[i].patch = get_patch_data(0);
mdi->channel[i].patch = instruments->get_patch_data(0);
} else {
mdi->channel[i].patch = NULL;
}
@ -2002,22 +1972,24 @@ static void do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data) {
}
/* I would not expect notes to be active when this event
triggers but we'll adjust active notes as well just in case */
_WM_AdjustChannelVolumes(mdi,16); // A setting > 15 adjusts all channels
AdjustChannelVolumes(mdi,16); // A setting > 15 adjusts all channels
mdi->channel[9].isdrum = 1;
UNUSED(data); /* NOOP, to please the compiler gods */
}
static void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data)
{
do_sysex_gm_reset(mdi, data);
}
static void do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data) {
void Renderer::do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data)
{
do_sysex_gm_reset(mdi, data);
}
static struct _mdi *
Init_MDI(void) {
struct _mdi *Renderer::Init_MDI()
{
struct _mdi *mdi;
mdi = new _mdi;
@ -2025,7 +1997,7 @@ Init_MDI(void) {
mdi->info.copyright = NULL;
mdi->info.mixer_options = WM_MixerOptions;
load_patch(mdi, 0x0000);
instruments->load_patch(mdi, 0x0000);
mdi->samples_to_mix = 0;
mdi->info.current_sample = 0;
@ -2037,12 +2009,12 @@ Init_MDI(void) {
return mdi;
}
static void freeMDI(struct _mdi *mdi) {
static void freeMDI(struct _mdi *mdi)
{
struct _sample *tmp_sample;
unsigned long int i;
if (mdi->patch_count != 0) {
std::lock_guard<std::mutex> lock(patch_lock);
for (i = 0; i < mdi->patch_count; i++) {
mdi->patches[i]->inuse_count--;
if (mdi->patches[i]->inuse_count == 0) {
@ -2243,7 +2215,7 @@ static int *WM_Mix_Linear(midi * handle, int * buffer, unsigned long int count)
static int *WM_Mix_Gauss(midi * handle, int * buffer, unsigned long int count)
{
if (!gauss_table) init_gauss();
if (!gauss_table.size()) init_gauss();
struct _mdi *mdi = (struct _mdi *)handle;
unsigned long int data_pos;
@ -2484,118 +2456,32 @@ int *WM_Mix(midi *handle, int *buffer, unsigned long count)
* =========================
*/
WM_SYMBOL const char *
WildMidi_GetString(unsigned short int info) {
switch (info) {
const char *WildMidi_GetString(unsigned short int info)
{
switch (info)
{
case WM_GS_VERSION:
return WM_Version;
}
return NULL;
}
WM_SYMBOL int WildMidi_Init(FSoundFontReader *reader, unsigned short int rate,
unsigned short int options) {
if (WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0);
return -1;
}
WM_InitPatches();
wm_sfreader.reset(reader);
if (WM_LoadConfig(nullptr, true) == -1) {
return -1;
}
if (options & 0x5FF8) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(invalid option)",
0);
WM_FreePatches();
return -1;
}
WM_MixerOptions = options;
if (rate < 11025) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(rate out of bounds, range is 11025 - 65535)", 0);
WM_FreePatches();
return -1;
}
_WM_SampleRate = rate;
WM_Initialized = 1;
return 0;
}
WM_SYMBOL int WildMidi_GetSampleRate(void)
midi * Renderer::NewMidi()
{
return _WM_SampleRate;
}
WM_SYMBOL int WildMidi_MasterVolume(unsigned char master_volume) {
struct _mdi *mdi = NULL;
int i = 0;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (master_volume > 127) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
"(master volume out of range, range is 0-127)", 0);
return -1;
}
WM_MasterVolume = lin_volume[master_volume];
return 0;
}
WM_SYMBOL int WildMidi_Close(midi * handle) {
struct _mdi *mdi = (struct _mdi *) handle;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
return -1;
}
freeMDI(mdi);
return 0;
}
midi *WildMidi_NewMidi() {
midi * ret = NULL;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return NULL;
}
ret = Init_MDI();
if ((((_mdi*)ret)->reverb = _WM_init_reverb(_WM_SampleRate, reverb_room_width,
reverb_room_length, reverb_listen_posx, reverb_listen_posy))
== NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_MEM, "to init reverb", 0);
WildMidi_Close(ret);
ret = NULL;
}
((_mdi*)ret)->reverb = _WM_init_reverb(instruments->GetSampleRate(), instruments->reverb_room_width,
instruments->reverb_room_length, instruments->reverb_listen_posx, instruments->reverb_listen_posy);
return ret;
}
WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options,
unsigned short int setting) {
int Renderer::SetOption(int options, int setting)
{
struct _mdi *mdi;
if (!WM_Initialized) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_NOT_INIT, NULL, 0);
return -1;
}
if (handle == NULL) {
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG, "(NULL handle)",
0);
@ -2618,7 +2504,7 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options,
| (options & setting));
if (options & WM_MO_LOG_VOLUME) {
_WM_AdjustChannelVolumes(mdi, 16); // Settings greater than 15
AdjustChannelVolumes(mdi, 16); // Settings greater than 15
// adjust all channels
} else if (options & WM_MO_REVERB) {
_WM_reset_reverb(mdi->reverb);
@ -2627,42 +2513,20 @@ WM_SYMBOL int WildMidi_SetOption(midi * handle, unsigned short int options,
return 0;
}
WM_SYMBOL int WildMidi_Shutdown(void) {
if (!WM_Initialized) {
// No error if trying to shut down an uninitialized device.
return 0;
}
WM_FreePatches();
free_gauss();
/* reset the globals */
WM_MasterVolume = 948;
WM_MixerOptions = 0;
fix_release = 0;
auto_amp = 0;
auto_amp_with_amp = 0;
reverb_room_width = 16.875f;
reverb_room_length = 22.5f;
reverb_listen_posx = 8.4375f;
reverb_listen_posy = 16.875f;
WM_Initialized = 0;
return 0;
}
WildMidi_Renderer::WildMidi_Renderer()
Renderer::Renderer(Instruments *instr, unsigned mixOpt)
{
handle = WildMidi_NewMidi();
init_gauss();
WM_MixerOptions = mixOpt;
handle = NewMidi();
}
WildMidi_Renderer::~WildMidi_Renderer()
Renderer::~Renderer()
{
WildMidi_Close((midi *)handle);
freeMDI((_mdi *)handle);
}
void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2)
void Renderer::ShortEvent(int status, int parm1, int parm2)
{
_mdi *mdi = (_mdi *)handle;
_event_data ev;
@ -2725,7 +2589,7 @@ void WildMidi_Renderer::ShortEvent(int status, int parm1, int parm2)
}
}
void WildMidi_Renderer::LongEvent(const unsigned char *data, int len)
void Renderer::LongEvent(const unsigned char *data, int len)
{
// Check for Roland SysEx
if (len >= 11 && // Must be at least 11 bytes
@ -2782,7 +2646,7 @@ void WildMidi_Renderer::LongEvent(const unsigned char *data, int len)
}
}
void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len)
void Renderer::ComputeOutput(float *fbuffer, int len)
{
_mdi *mdi = (_mdi *)handle;
int *buffer = (int *)fbuffer;
@ -2797,12 +2661,12 @@ void WildMidi_Renderer::ComputeOutput(float *fbuffer, int len)
}
}
void WildMidi_Renderer::LoadInstrument(int bank, int percussion, int instr)
void Renderer::LoadInstrument(int bank, int percussion, int instr)
{
load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0));
instruments->load_patch((_mdi *)handle, (bank << 8) | instr | (percussion ? 0x80 : 0));
}
int WildMidi_Renderer::GetVoiceCount()
int Renderer::GetVoiceCount()
{
int count = 0;
for (_note *note_data = ((_mdi *)handle)->note; note_data != NULL; note_data = note_data->next)
@ -2812,7 +2676,10 @@ int WildMidi_Renderer::GetVoiceCount()
return count;
}
void WildMidi_Renderer::SetOption(int opt, int set)
void Renderer::SetMasterVolume(unsigned char master_volume)
{
WildMidi_SetOption((_mdi*)handle, (unsigned short)opt, (unsigned short)set);
WM_MasterVolume = lin_volume[std::min<unsigned char>(127, master_volume)];
}
}

View file

@ -27,21 +27,21 @@
#ifndef WILDMIDI_LIB_H
#define WILDMIDI_LIB_H
#define WM_MO_LOG_VOLUME 0x0001
#define WM_MO_ENHANCED_RESAMPLING 0x0002
#define WM_MO_REVERB 0x0004
#define WM_MO_WHOLETEMPO 0x8000
#define WM_MO_ROUNDTEMPO 0x2000
namespace WildMidi
{
enum EMixerOptions
{
WM_MO_LOG_VOLUME = 0x0001,
WM_MO_ENHANCED_RESAMPLING = 0x0002,
WM_MO_REVERB = 0x0004,
WM_MO_WHOLETEMPO = 0x8000,
WM_MO_ROUNDTEMPO = 0x2000,
#define WM_GS_VERSION 0x0001
WM_GS_VERSION = 0x0001,
};
#define WM_SYMBOL // we do not need this in ZDoom
/*
#if defined(__cplusplus)
extern "C" {
#endif
*/
class SoundFontReaderInterface;
struct _WM_Info {
char *copyright;
@ -53,35 +53,88 @@ struct _WM_Info {
typedef void midi;
WM_SYMBOL const char * WildMidi_GetString (unsigned short int info);
WM_SYMBOL int WildMidi_Init (FSoundFontReader *reader, unsigned short int rate, unsigned short int options);
WM_SYMBOL int WildMidi_MasterVolume (unsigned char master_volume);
WM_SYMBOL int WildMidi_SetOption (midi * handle, unsigned short int options, unsigned short int setting);
WM_SYMBOL int WildMidi_Close (midi * handle);
WM_SYMBOL int WildMidi_Shutdown (void);
WM_SYMBOL int WildMidi_GetSampleRate (void);
/*
#if defined(__cplusplus)
}
#endif
*/
class WildMidi_Renderer
struct Instruments
{
SoundFontReaderInterface *sfreader;
struct _patch *patch[128] = {};
float reverb_room_width = 16.875f;
float reverb_room_length = 22.5f;
float reverb_listen_posx = 8.4375f;
float reverb_listen_posy = 16.875f;
int fix_release = 0;
int auto_amp = 0;
int auto_amp_with_amp = 0;
unsigned short int _WM_SampleRate; // WildMidi makes the sample rate a property of the patches, not the renderer. Meaning that the instruments need to be reloaded when it changes... :?
Instruments(SoundFontReaderInterface *reader, int samplerate)
{
sfreader = reader;
_WM_SampleRate = samplerate;
}
int LoadConfig(const char *config_file);
int load_sample(struct _patch *sample_patch);
struct _patch *get_patch_data(unsigned short patchid);
void load_patch(struct _mdi *mdi, unsigned short patchid);
int GetSampleRate() { return _WM_SampleRate; }
struct _sample * load_gus_pat(const char *filename);
private:
void FreePatches(void);
};
const char * WildMidi_GetString (unsigned short int info);
class Renderer
{
Instruments *instruments;
signed int WM_MasterVolume = 948;
unsigned int WM_MixerOptions = 0;
public:
WildMidi_Renderer();
~WildMidi_Renderer();
Renderer(Instruments *instr, unsigned mixOpt = 0);
~Renderer();
void ShortEvent(int status, int parm1, int parm2);
void LongEvent(const unsigned char *data, int len);
void ComputeOutput(float *buffer, int len);
void LoadInstrument(int bank, int percussion, int instr);
int GetVoiceCount();
void SetOption(int opt, int set);
int SetOption(int opt, int set);
void SetMasterVolume(unsigned char master_volume);
midi * NewMidi();
private:
void *handle;
void AdjustNoteVolumes(struct _mdi *mdi, unsigned char ch, struct _note *nte);
void AdjustChannelVolumes(struct _mdi *mdi, unsigned char ch);
void do_note_on(struct _mdi *mdi, struct _event_data *data);
void do_aftertouch(struct _mdi *mdi, struct _event_data *data);
void do_control_channel_volume(struct _mdi *mdi, struct _event_data *data);
void do_control_channel_balance(struct _mdi *mdi, struct _event_data *data);
void do_control_channel_pan(struct _mdi *mdi, struct _event_data *data);
void do_control_channel_expression(struct _mdi *mdi, struct _event_data *data);
void do_control_channel_controllers_off(struct _mdi *mdi, struct _event_data *data);
void do_pitch(struct _mdi *mdi, struct _event_data *data);
void do_patch(struct _mdi *mdi, struct _event_data *data);
void do_channel_pressure(struct _mdi *mdi, struct _event_data *data);
void do_sysex_roland_drum_track(struct _mdi *mdi, struct _event_data *data);
void do_sysex_gm_reset(struct _mdi *mdi, struct _event_data *data);
void do_sysex_roland_reset(struct _mdi *mdi, struct _event_data *data);
void do_sysex_yamaha_reset(struct _mdi *mdi, struct _event_data *data);
struct _mdi *Init_MDI();
unsigned long int get_inc(struct _mdi *mdi, struct _note *nte);
};
}
#endif /* WILDMIDI_LIB_H */

View file

@ -29,22 +29,26 @@
#include <string.h>
#include <stdarg.h>
#include "wm_error.h"
#include "doomtype.h"
#include "v_text.h"
namespace WildMidi
{
static void def_error_func(const char *wmfmt, va_list args)
{
vprintf(wmfmt, args);
}
void (*wm_error_func)(const char *wmfmt, va_list args) = def_error_func;
void _WM_ERROR_NEW(const char * wmfmt, ...) {
va_list args;
fprintf(stderr, "\r");
va_start(args, wmfmt);
vfprintf(stderr, wmfmt, args);
va_end(args);
fprintf(stderr, "\n");
wm_error_func(wmfmt, args);
}
void _WM_ERROR(const char * func, unsigned int lne, int wmerno,
const char * wmfor, int error) {
static const char *errors[WM_ERR_MAX+1] = {
static const char *const errors[WM_ERR_MAX+1] = {
"No error",
"Unable to obtain memory",
"Unable to stat",
@ -67,20 +71,21 @@ void _WM_ERROR(const char * func, unsigned int lne, int wmerno,
if (wmfor != NULL) {
if (error != 0) {
Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s %s (%s)\n", func,
lne, errors[wmerno], wmfor, strerror(error));
_WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s %s (%s)\n", func, lne,
errors[wmerno], wmfor, strerror(error));
} else {
Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s %s\n", func, lne,
_WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s %s\n", func, lne,
errors[wmerno], wmfor);
}
} else {
if (error != 0) {
Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s (%s)\n", func, lne,
_WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s (%s)\n", func, lne,
errors[wmerno], strerror(error));
} else {
Printf(TEXTCOLOR_RED "libWildMidi(%s:%u): ERROR %s\n", func, lne,
_WM_ERROR_NEW("libWildMidi(%s:%u): ERROR %s\n", func, lne,
errors[wmerno]);
}
}
}
}

View file

@ -27,6 +27,9 @@
#ifndef __WM_ERROR_H
#define __WM_ERROR_H
namespace WildMidi
{
enum {
WM_ERR_NONE = 0,
WM_ERR_MEM,
@ -53,4 +56,5 @@ enum {
extern void _WM_ERROR(const char * func, unsigned int lne, int wmerno,
const char * wmfor, int error);
}
#endif /* __WM_ERROR_H */