gzdoom/src/timidity/timidity.h
Randy Heit f9574a98fd Fix Timidity's DLS instrument loading:
- Envelope data needed to be converted to SF2 values.
- Fine tuning was ignored which made pretty much every instrument off tune.
- Despite this, it still sounds like shit compared to FMOD or Microsoft's
  wavetable synth. There are lots of missing notes and some instruments
  are still off tune. I'm not sure it's worth trying to salvage it. It'd
  probably be better to scrap it, since Timidity is very much oriented
  toward GF1 patches, which it handles perfectly fine.
2016-01-13 17:25:24 -06:00

686 lines
16 KiB
C++

/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef TIMIDITY_H
#define TIMIDITY_H
#include "doomtype.h"
#include "pathexpander.h"
class FileReader;
namespace Timidity
{
/*
config.h
*/
/* Acoustic Grand Piano seems to be the usual default instrument. */
#define DEFAULT_PROGRAM 0
/* 9 here is MIDI channel 10, which is the standard percussion channel.
Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too.
On the other hand, some files know that 16 is not a drum channel and
try to play music on it. This is now a runtime option, so this isn't
a critical choice anymore. */
#define DEFAULT_DRUMCHANNELS (1<<9)
/*#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))*/
#define MAXCHAN 16
#define MAXNOTE 128
/* 1000 here will give a control ratio of 22:1 with 22 kHz output.
Higher CONTROLS_PER_SECOND values allow more accurate rendering
of envelopes and tremolo. The cost is CPU time. */
#define CONTROLS_PER_SECOND 1000
/* A scalar applied to the final mix to try and approximate the
volume level of FMOD's built-in MIDI player. */
#define FINAL_MIX_SCALE 0.5
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of
a sample is 1048576 samples (2 megabytes in memory). The GUS gets
by with just 9 bits and a little help from its friends...
"The GUS does not SUCK!!!" -- a happy user :) */
#define FRACTION_BITS 12
/* For some reason the sample volume is always set to maximum in all
patch files. Define this for a crude adjustment that may help
equalize instrument volumes. */
//#define ADJUST_SAMPLE_VOLUMES
/* The number of samples to use for ramping out a dying note. Affects
click removal. */
#define MAX_DIE_TIME 20
/**************************************************************************/
/* Anything below this shouldn't need to be changed unless you're porting
to a new machine with other than 32-bit, big-endian words. */
/**************************************************************************/
/* change FRACTION_BITS above, not these */
#define INTEGER_BITS (32 - FRACTION_BITS)
#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS)
#define FRACTION_MASK (~ INTEGER_MASK)
#define MAX_SAMPLE_SIZE (1 << INTEGER_BITS)
/* This is enforced by some computations that must fit in an int */
#define MAX_CONTROL_RATIO 255
#define MAX_AMPLIFICATION 800
/* The TiMiditiy configuration file */
#define CONFIG_FILE "timidity.cfg"
typedef float sample_t;
typedef float final_volume_t;
#define FINAL_VOLUME(v) (v)
#define FSCALE(a,b) ((a) * (float)(1<<(b)))
#define FSCALENEG(a,b) ((a) * (1.0L / (float)(1<<(b))))
/* Vibrato and tremolo Choices of the Day */
#define SWEEP_TUNING 38
#define VIBRATO_AMPLITUDE_TUNING 1.0
#define VIBRATO_RATE_TUNING 38
#define TREMOLO_AMPLITUDE_TUNING 1.0
#define TREMOLO_RATE_TUNING 38
#define SWEEP_SHIFT 16
#define RATE_SHIFT 5
#define VIBRATO_SAMPLE_INCREMENTS 32
#ifndef PI
#define PI 3.14159265358979323846
#endif
#if defined(__GNUC__) && !defined(__clang__) && (defined(__i386__) || defined(__x86_64__))
// [RH] MinGW's pow() function is terribly slow compared to VC8's
// (I suppose because it's using an old version from MSVCRT.DLL).
// On an Opteron running x86-64 Linux, this also ended up being about
// 100 cycles faster than libm's pow(), which is why I'm using this
// for GCC in general and not just for MinGW.
// [CE] Clang doesn't yet support some inline ASM operations so I disabled it for that instance
extern __inline__ double pow_x87_inline(double x,double y)
{
double result;
if (y == 0)
{
return 1;
}
if (x == 0)
{
if (y > 0)
{
return 0;
}
else
{
union { double fp; long long ip; } infinity;
infinity.ip = 0x7FF0000000000000ll;
return infinity.fp;
}
}
__asm__ (
"fyl2x\n\t"
"fld %%st(0)\n\t"
"frndint\n\t"
"fxch\n\t"
"fsub %%st(1),%%st(0)\n\t"
"f2xm1\n\t"
"fld1\n\t"
"faddp\n\t"
"fxch\n\t"
"fld1\n\t"
"fscale\n\t"
"fstp %%st(1)\n\t"
"fmulp\n\t"
: "=t" (result)
: "0" (x), "u" (y)
: "st(1)", "st(7)" );
return result;
}
#define pow pow_x87_inline
#endif
/*
common.h
*/
extern void *safe_malloc(size_t count);
/*
controls.h
*/
enum
{
CMSG_INFO,
CMSG_WARNING,
CMSG_ERROR
};
enum
{
VERB_NORMAL,
VERB_VERBOSE,
VERB_NOISY,
VERB_DEBUG
};
void cmsg(int type, int verbosity_level, const char *fmt, ...);
/*
instrum.h
*/
enum
{
PATCH_16 = (1<<0),
PATCH_UNSIGNED = (1<<1),
PATCH_LOOPEN = (1<<2),
PATCH_BIDIR = (1<<3),
PATCH_BACKWARD = (1<<4),
PATCH_SUSTAIN = (1<<5),
PATCH_NO_SRELEASE = (1<<6),
PATCH_FAST_REL = (1<<7),
};
struct Sample
{
SDWORD
loop_start, loop_end, data_length,
sample_rate;
float
low_freq, high_freq, root_freq;
union
{
struct
{
BYTE rate[6], offset[6];
} gf1;
struct
{
short delay_vol;
short attack_vol;
short hold_vol;
short decay_vol;
short sustain_vol;
short release_vol;
} sf2;
} envelope;
sample_t *data;
SDWORD
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
BYTE
tremolo_depth, vibrato_depth,
low_vel, high_vel,
type;
WORD
modes;
SWORD
panning;
WORD
scale_factor, key_group;
SWORD
scale_note;
bool
self_nonexclusive;
float
left_offset, right_offset;
// SF2 stuff
SWORD tune;
SBYTE velocity;
float initial_attenuation;
};
void convert_sample_data(Sample *sample, const void *data);
void free_instruments();
/* Magic file words */
#define ID_RIFF MAKE_ID('R','I','F','F')
#define ID_LIST MAKE_ID('L','I','S','T')
#define ID_INFO MAKE_ID('I','N','F','O')
#define ID_sfbk MAKE_ID('s','f','b','k')
#define ID_sdta MAKE_ID('s','d','t','a')
#define ID_pdta MAKE_ID('p','d','t','a')
#define ID_ifil MAKE_ID('i','f','i','l')
#define ID_iver MAKE_ID('i','v','e','r')
#define ID_irom MAKE_ID('i','r','o','m')
#define ID_smpl MAKE_ID('s','m','p','l')
#define ID_sm24 MAKE_ID('s','m','2','4')
#define ID_phdr MAKE_ID('p','h','d','r')
#define ID_pbag MAKE_ID('p','b','a','g')
#define ID_pmod MAKE_ID('p','m','o','d')
#define ID_pgen MAKE_ID('p','g','e','n')
#define ID_inst MAKE_ID('i','n','s','t')
#define ID_ibag MAKE_ID('i','b','a','g')
#define ID_imod MAKE_ID('i','m','o','d')
#define ID_igen MAKE_ID('i','g','e','n')
#define ID_shdr MAKE_ID('s','h','d','r')
/* Instrument definitions */
enum
{
INST_GUS,
INST_DLS,
INST_SF2
};
struct Instrument
{
Instrument();
~Instrument();
int samples;
Sample *sample;
};
struct ToneBankElement
{
ToneBankElement() :
note(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0)
{}
FString name;
int note, pan, fontbank, fontpreset, fontnote;
SBYTE strip_loop, strip_envelope, strip_tail;
};
/* A hack to delay instrument loading until after reading the entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1))
enum
{
MAXPROG = 128,
MAXBANK = 128
};
struct ToneBank
{
ToneBank();
~ToneBank();
ToneBankElement *tone;
Instrument *instrument[MAXPROG];
};
#define SPECIAL_PROGRAM -1
/*
instrum_font.cpp
*/
class FontFile
{
public:
FontFile(FString filename);
virtual ~FontFile();
FString Filename;
FontFile *Next;
virtual Instrument *LoadInstrument(struct Renderer *song, int drum, int bank, int program) = 0;
virtual Instrument *LoadInstrumentOrder(struct Renderer *song, int order, int drum, int bank, int program) = 0;
virtual void SetOrder(int order, int drum, int bank, int program) = 0;
virtual void SetAllOrders(int order) = 0;
};
void font_freeall();
FontFile *font_find(const char *filename);
void font_add(const char *filename, int load_order);
void font_remove(const char *filename);
void font_order(int order, int bank, int preset, int keynote);
Instrument *load_instrument_font(struct Renderer *song, const char *font, int drum, int bank, int instrument);
Instrument *load_instrument_font_order(struct Renderer *song, int order, int drum, int bank, int instrument);
FontFile *ReadDLS(const char *filename, FileReader *f);
/*
mix.h
*/
extern void mix_voice(struct Renderer *song, float *buf, struct Voice *v, int c);
extern int recompute_envelope(struct Voice *v);
extern void apply_envelope_to_amp(struct Voice *v);
/*
playmidi.h
*/
/* Midi events */
enum
{
ME_NOTEOFF = 0x80,
ME_NOTEON = 0x90,
ME_KEYPRESSURE = 0xA0,
ME_CONTROLCHANGE = 0xB0,
ME_PROGRAM = 0xC0,
ME_CHANNELPRESSURE = 0xD0,
ME_PITCHWHEEL = 0xE0
};
/* Controllers */
enum
{
CTRL_BANK_SELECT = 0,
CTRL_DATA_ENTRY = 6,
CTRL_VOLUME = 7,
CTRL_PAN = 10,
CTRL_EXPRESSION = 11,
CTRL_SUSTAIN = 64,
CTRL_HARMONICCONTENT = 71,
CTRL_RELEASETIME = 72,
CTRL_ATTACKTIME = 73,
CTRL_BRIGHTNESS = 74,
CTRL_REVERBERATION = 91,
CTRL_CHORUSDEPTH = 93,
CTRL_NRPN_LSB = 98,
CTRL_NRPN_MSB = 99,
CTRL_RPN_LSB = 100,
CTRL_RPN_MSB = 101,
CTRL_ALL_SOUNDS_OFF = 120,
CTRL_RESET_CONTROLLERS = 121,
CTRL_ALL_NOTES_OFF = 123
};
/* RPNs */
enum
{
RPN_PITCH_SENS = 0x0000,
RPN_FINE_TUNING = 0x0001,
RPN_COARSE_TUNING = 0x0002,
RPN_RESET = 0x3fff
};
struct Channel
{
int
bank, program, sustain, pitchbend,
mono, /* one note only on this channel */
pitchsens;
BYTE
volume, expression;
SBYTE
panning;
WORD
rpn, nrpn;
bool
nrpn_mode;
float
pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
};
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
struct MinEnvelope
{
BYTE stage;
BYTE bUpdating;
};
struct GF1Envelope : public MinEnvelope
{
int volume, target, increment;
int rate[6], offset[6];
void Init(struct Renderer *song, Voice *v);
bool Update(struct Voice *v);
bool Recompute(struct Voice *v);
void ApplyToAmp(struct Voice *v);
void Release(struct Voice *v);
};
struct SF2Envelope : public MinEnvelope
{
float volume;
float DelayTime; // timecents
float AttackTime; // timecents
float HoldTime; // timecents
float DecayTime; // timecents
float SustainLevel; // -0.1%
float ReleaseTime; // timecents
float SampleRate;
int HoldStart;
float RateMul;
float RateMul_cB;
void Init(struct Renderer *song, Voice *v);
bool Update(struct Voice *v);
void ApplyToAmp(struct Voice *v);
void Release(struct Voice *v);
};
struct Envelope
{
union
{
MinEnvelope env;
GF1Envelope gf1;
SF2Envelope sf2;
};
BYTE Type;
void Init(struct Renderer *song, struct Voice *v);
bool Update(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.Update(v);
return sf2.Update(v);
}
void ApplyToAmp(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.ApplyToAmp(v);
return sf2.ApplyToAmp(v);
}
void Release(struct Voice *v)
{
if (Type == INST_GUS)
return gf1.Release(v);
return sf2.Release(v);
}
};
struct Voice
{
BYTE
status, channel, note, velocity;
Sample *sample;
float
orig_frequency, frequency;
int
sample_offset, sample_increment,
tremolo_sweep, tremolo_sweep_position,
tremolo_phase, tremolo_phase_increment,
vibrato_sweep, vibrato_sweep_position;
Envelope eg1, eg2;
final_volume_t left_mix, right_mix;
float
attenuation, left_offset, right_offset;
float
tremolo_volume;
int
vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
control_counter;
int
sample_count;
};
/* Voice status options: */
enum
{
VOICE_RUNNING = (1<<0),
VOICE_SUSTAINING = (1<<1),
VOICE_RELEASING = (1<<2),
VOICE_STOPPING = (1<<3),
VOICE_LPE = (1<<4),
NOTE_SUSTAIN = (1<<5),
};
/* Envelope stages: */
enum
{
GF1_ATTACK,
GF1_HOLD,
GF1_DECAY,
GF1_RELEASE,
GF1_RELEASEB,
GF1_RELEASEC
};
enum
{
SF2_DELAY,
SF2_ATTACK,
SF2_HOLD,
SF2_DECAY,
SF2_SUSTAIN,
SF2_RELEASE,
SF2_FINISHED
};
#define ISDRUMCHANNEL(c) ((drumchannels & (1<<(c))))
/*
resample.h
*/
extern sample_t *resample_voice(struct Renderer *song, Voice *v, int *countptr);
extern void pre_resample(struct Renderer *song, Sample *sp);
/*
tables.h
*/
const double log_of_2 = 0.69314718055994529;
#define sine(x) (sin((2*PI/1024.0) * (x)))
#define note_to_freq(x) (float(8175.7989473096690661233836992789 * pow(2.0, (x) / 12.0)))
#define freq_to_note(x) (log((x) / 8175.7989473096690661233836992789) * (12.0 / log_of_2))
#define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation
/*
timidity.h
*/
struct DLS_Data;
int LoadConfig(const char *filename);
int LoadDMXGUS();
extern int LoadConfig();
extern void FreeAll();
extern PathExpander pathExpander;
extern ToneBank *tonebank[MAXBANK];
extern ToneBank *drumset[MAXBANK];
struct Renderer
{
float rate;
DLS_Data *patches;
Instrument *default_instrument;
int default_program;
int resample_buffer_size;
sample_t *resample_buffer;
Channel channel[16];
Voice *voice;
int control_ratio, amp_with_poly;
int drumchannels;
int adjust_panning_immediately;
int voices;
int lost_notes, cut_notes;
Renderer(float sample_rate, const char *args);
~Renderer();
void HandleEvent(int status, int parm1, int parm2);
void HandleLongMessage(const BYTE *data, int len);
void HandleController(int chan, int ctrl, int val);
void ComputeOutput(float *buffer, int num_samples);
void MarkInstrument(int bank, int percussion, int instr);
void Reset();
int load_missing_instruments();
int set_default_instrument(const char *name);
int convert_tremolo_sweep(BYTE sweep);
int convert_vibrato_sweep(BYTE sweep, int vib_control_ratio);
int convert_tremolo_rate(BYTE rate);
int convert_vibrato_rate(BYTE rate);
void recompute_freq(int voice);
void recompute_amp(Voice *v);
void recompute_pan(Channel *chan);
void kill_key_group(int voice);
float calculate_scaled_frequency(Sample *sample, int note);
void start_note(int chan, int note, int vel);
bool start_region(int chan, int note, int vel, Sample *sp, float freq);
void note_on(int chan, int note, int vel);
void note_off(int chan, int note, int vel);
void all_notes_off(int chan);
void all_sounds_off(int chan);
void adjust_pressure(int chan, int note, int amount);
void adjust_panning(int chan);
void drop_sustain(int chan);
void adjust_pitchbend(int chan);
void adjust_volume(int chan);
void reset_voices();
void reset_controllers(int chan);
void reset_midi();
int allocate_voice();
void kill_note(int voice);
void finish_note(int voice);
void DataEntryCoarseRPN(int chan, int rpn, int val);
void DataEntryFineRPN(int chan, int rpn, int val);
void DataEntryCoarseNRPN(int chan, int nrpn, int val);
void DataEntryFineNRPN(int chan, int nrpn, int val);
static void compute_pan(double panning, int type, float &left_offset, float &right_offset);
};
}
#endif