mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-01 16:41:09 +00:00
Merge branch 'wildmidi_cleanup'
This commit is contained in:
commit
a562d69ec3
20 changed files with 585 additions and 456 deletions
|
@ -8,6 +8,10 @@
|
|||
#include "instrum.h"
|
||||
#include "sf2.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <strings.h>
|
||||
#define stricmp strcasecmp
|
||||
#endif
|
||||
|
||||
namespace Timidity
|
||||
{
|
||||
|
|
|
@ -1182,9 +1182,6 @@ int Mixer::recompute_envelope(int v)
|
|||
/* Envelope ran out. */
|
||||
void Mixer::voice_ran_out(int v)
|
||||
{
|
||||
/* Already displayed as dead */
|
||||
int died = (player->voice[v].status == VOICE_DIE);
|
||||
|
||||
player->free_voice(v);
|
||||
}
|
||||
|
||||
|
@ -1624,4 +1621,4 @@ int Mixer::recompute_modulation_envelope(int v)
|
|||
return modenv_next_stage(v);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ Player::Player(Instruments *instr)
|
|||
temper_adj = 0;
|
||||
current_play_tempo = 500000;
|
||||
opt_realtime_playing = 0;
|
||||
check_eot_flag;
|
||||
check_eot_flag = 0;
|
||||
playmidi_seek_flag = 0;
|
||||
opt_pure_intonation = 0;
|
||||
current_freq_table = 0;
|
||||
|
@ -6040,7 +6040,6 @@ static const struct ctl_chg_types {
|
|||
|
||||
int Player::convert_midi_control_change(int chn, int type, int val, MidiEvent *ev_ret)
|
||||
{
|
||||
int etype = -1;
|
||||
for (auto &t : ctl_chg_list)
|
||||
{
|
||||
if (t.mtype == type)
|
||||
|
|
|
@ -40,6 +40,7 @@ struct timidity_file
|
|||
class SoundFontReaderInterface
|
||||
{
|
||||
public:
|
||||
virtual ~SoundFontReaderInterface() {}
|
||||
virtual struct timidity_file* open_timidityplus_file(const char* fn) = 0;
|
||||
virtual void timidityplus_add_path(const char* path) = 0;
|
||||
};
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include "i_musicinterns.h"
|
||||
#include "doomerrors.h"
|
||||
#include "i_soundfont.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
@ -55,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);
|
||||
|
@ -92,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 --------------------------------------------------------------------
|
||||
|
@ -116,25 +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())
|
||||
if (instruments) delete instruments;
|
||||
instruments = nullptr;
|
||||
CurrentConfig = "";
|
||||
|
||||
auto reader = sfmanager.OpenSoundFont(args, SF_GUS);
|
||||
if (reader == nullptr)
|
||||
{
|
||||
WildMidi_Shutdown();
|
||||
CurrentConfig = "";
|
||||
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
|
||||
{
|
||||
|
|
|
@ -90,6 +90,8 @@ public:
|
|||
|
||||
void Timidity_Shutdown();
|
||||
void TimidityPP_Shutdown();
|
||||
void WildMidi_Shutdown ();
|
||||
|
||||
|
||||
// Base class for software synthesizer MIDI output devices ------------------
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -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;
|
||||
|
@ -90,5 +89,7 @@ struct _patch {
|
|||
#ifndef M_LN2
|
||||
#define M_LN2 0.69314718055994530942
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif /* __COMMON_H */
|
||||
|
|
|
@ -36,48 +36,27 @@
|
|||
#include <errno.h>
|
||||
#include <memory>
|
||||
|
||||
#include "files.h"
|
||||
#include "wm_error.h"
|
||||
#include "file_io.h"
|
||||
#include "i_soundfont.h"
|
||||
#include "wildmidi_file.h"
|
||||
|
||||
std::unique_ptr<FSoundFontReader> wm_sfreader;
|
||||
static FString config_name;
|
||||
|
||||
bool _WM_InitReader(const char *config_file)
|
||||
namespace WildMidi
|
||||
{
|
||||
auto reader = sfmanager.OpenSoundFont(config_file, SF_GUS);
|
||||
if (reader == nullptr)
|
||||
{
|
||||
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_LOAD, config_file, errno);
|
||||
return false; // No sound font could be opened.
|
||||
}
|
||||
wm_sfreader.reset(reader);
|
||||
config_name = config_file;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char *_WM_BufferFile(const char *filename, unsigned long int *size)
|
||||
static const int WM_MAXFILESIZE = 0x1fffffff;
|
||||
|
||||
unsigned char *_WM_BufferFile(SoundFontReaderInterface *reader, const char *filename, unsigned long int *size, std::string *fullname)
|
||||
{
|
||||
FileReader fp;
|
||||
auto fp = reader->open_wildmidi_file(filename);
|
||||
|
||||
if (filename == nullptr)
|
||||
{
|
||||
fp = wm_sfreader->OpenMainConfigFile();
|
||||
filename = config_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
fp = wm_sfreader->OpenFile(filename);
|
||||
}
|
||||
|
||||
if (!fp.isOpen())
|
||||
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)
|
||||
{
|
||||
|
@ -94,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);
|
||||
if (fullname)* fullname = fp->filename;
|
||||
fp->close();
|
||||
data[fsize] = 0;
|
||||
*size = (long)fsize;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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) {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
146
src/sound/wildmidi/wildmidi_file.h
Normal file
146
src/sound/wildmidi/wildmidi_file.h
Normal 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
|
||||
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <strings.h>
|
||||
#endif
|
||||
|
@ -44,8 +45,9 @@
|
|||
#include "reverb.h"
|
||||
#include "gus_pat.h"
|
||||
#include "wildmidi_lib.h"
|
||||
#include "files.h"
|
||||
#include "i_soundfont.h"
|
||||
|
||||
namespace WildMidi
|
||||
{
|
||||
|
||||
#define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\')
|
||||
#ifdef _WIN32
|
||||
|
@ -64,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;
|
||||
|
@ -175,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 */
|
||||
|
@ -191,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;
|
||||
}
|
||||
|
||||
|
@ -219,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);
|
||||
|
@ -237,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,
|
||||
|
@ -267,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,
|
||||
|
@ -295,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,
|
||||
|
@ -329,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,
|
||||
|
@ -545,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) {
|
||||
|
@ -650,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;
|
||||
|
@ -661,23 +622,15 @@ 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;
|
||||
|
||||
FileReader fr;
|
||||
if (main)
|
||||
{
|
||||
if (!_WM_InitReader(config_file)) return -1; // unable to open this as a config file.
|
||||
config_parm = nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
FString bp = wm_sfreader->basePath();
|
||||
if (config_parm == nullptr) config_file = bp.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, '/');
|
||||
|
@ -692,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;
|
||||
}
|
||||
|
@ -723,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;
|
||||
|
@ -732,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;
|
||||
|
@ -748,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;
|
||||
|
@ -761,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);
|
||||
|
@ -775,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);
|
||||
|
@ -795,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);
|
||||
|
@ -808,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);
|
||||
|
@ -822,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);
|
||||
|
@ -847,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);
|
||||
|
@ -872,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);
|
||||
|
@ -894,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);
|
||||
|
@ -929,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);
|
||||
|
@ -967,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);
|
||||
|
@ -997,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);
|
||||
|
@ -1020,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);
|
||||
|
@ -1034,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);
|
||||
|
@ -1048,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);
|
||||
|
@ -1194,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;
|
||||
|
@ -1202,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;
|
||||
}
|
||||
|
||||
|
@ -1314,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) {
|
||||
|
@ -1338,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;
|
||||
|
||||
|
@ -1353,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;
|
||||
|
@ -1371,12 +1323,18 @@ 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) {
|
||||
Instruments::~Instruments()
|
||||
{
|
||||
FreePatches();
|
||||
delete sfreader;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1403,7 +1361,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;
|
||||
|
@ -1459,7 +1418,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 {
|
||||
|
@ -1469,8 +1429,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);
|
||||
|
@ -1547,7 +1507,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;
|
||||
|
@ -1564,11 +1525,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;
|
||||
|
@ -1593,7 +1555,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;
|
||||
}
|
||||
|
@ -1657,10 +1619,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;
|
||||
|
||||
|
@ -1675,10 +1638,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1701,37 +1664,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,
|
||||
|
@ -1871,8 +1835,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;
|
||||
|
||||
|
@ -1884,7 +1848,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,
|
||||
|
@ -1920,17 +1884,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;
|
||||
|
||||
|
@ -1939,17 +1905,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;
|
||||
|
||||
|
@ -1974,8 +1941,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);
|
||||
|
@ -1985,16 +1952,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;
|
||||
}
|
||||
|
@ -2011,22 +1979,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;
|
||||
|
@ -2034,7 +2004,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;
|
||||
|
@ -2046,12 +2016,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) {
|
||||
|
@ -2252,7 +2222,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;
|
||||
|
@ -2493,122 +2463,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(const char * config_file, unsigned short int rate,
|
||||
unsigned short int options) {
|
||||
if (WM_Initialized) {
|
||||
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_ALR_INIT, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (config_file == NULL) {
|
||||
_WM_ERROR(__FUNCTION__, __LINE__, WM_ERR_INVALID_ARG,
|
||||
"(NULL config file pointer)", 0);
|
||||
return -1;
|
||||
}
|
||||
WM_InitPatches();
|
||||
if (WM_LoadConfig(config_file, 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);
|
||||
|
@ -2631,7 +2511,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);
|
||||
|
@ -2640,42 +2520,21 @@ 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();
|
||||
instruments = instr;
|
||||
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;
|
||||
|
@ -2738,7 +2597,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
|
||||
|
@ -2795,7 +2654,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;
|
||||
|
@ -2810,12 +2669,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)
|
||||
|
@ -2825,7 +2684,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)];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
||||
WM_GS_VERSION = 0x0001,
|
||||
};
|
||||
|
||||
#define 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;
|
||||
|
@ -52,36 +52,90 @@ struct _WM_Info {
|
|||
};
|
||||
|
||||
typedef void midi;
|
||||
|
||||
WM_SYMBOL const char * WildMidi_GetString (unsigned short int info);
|
||||
WM_SYMBOL int WildMidi_Init (const char * config_file, 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;
|
||||
}
|
||||
~Instruments();
|
||||
|
||||
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 */
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue