- Reduced volume, expression, and panning controllers back to 7 bits.

- Added very basic Soundfont support to the internal TiMidity. Things missing:
  filter, LFOs, modulation envelope, chorus, reverb, and modulators. May or
  may not be compatible with TiMidity++'s soundfont extensions.
- Added support for quoted strings to the TiMidity config parser.


SVN r957 (trunk)
This commit is contained in:
Randy Heit 2008-05-09 03:54:06 +00:00
parent d5563fe478
commit e64586d86f
16 changed files with 3396 additions and 785 deletions

View file

@ -1,9 +1,18 @@
May 8, 2008
- Reduced volume, expression, and panning controllers back to 7 bits.
- Added very basic Soundfont support to the internal TiMidity. Things missing:
filter, LFOs, modulation envelope, chorus, reverb, and modulators. May or
may not be compatible with TiMidity++'s soundfont extensions.
May 8, 2008 (Changes by Graf Zahl)
- Changed all thing coordinates that were stored as shorts into fixed_t.
- Separated mapthing2_t into mapthinghexen_t and the internal FMapThing so
that it is easier to add new features in the UDMF map format.
- Added some initial code to read UDMF maps.
May 6, 2008
- Added support for quoted strings to the TiMidity config parser.
May 2, 2008 (Changes by Graf Zahl)
- Split off the slope creation code from p_Setup.cpp into its own file.
- Separated the linedef activation types into a bit mask that allows combination

View file

@ -101,7 +101,7 @@ struct FActionMap
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static long ParseCommandLine (const char *args, int *argc, char **argv);
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes);
static FConsoleCommand *FindNameInHashTable (FConsoleCommand **table, const char *name, size_t namelen);
static FConsoleCommand *ScanChainForName (FConsoleCommand *start, const char *name, size_t namelen, FConsoleCommand **prev);
@ -724,7 +724,7 @@ void AddCommandString (char *cmd, int keynum)
// \c becomes just TEXTCOLOR_ESCAPE
// $<cvar> is replaced by the contents of <cvar>
static long ParseCommandLine (const char *args, int *argc, char **argv)
static long ParseCommandLine (const char *args, int *argc, char **argv, bool no_escapes)
{
int count;
char *buffplace;
@ -758,15 +758,15 @@ static long ParseCommandLine (const char *args, int *argc, char **argv)
do
{
stuff = *args++;
if (stuff == '\\' && *args == '\"')
if (!no_escapes && stuff == '\\' && *args == '\"')
{
stuff = '\"', args++;
}
else if (stuff == '\\' && *args == '\\')
else if (!no_escapes && stuff == '\\' && *args == '\\')
{
args++;
}
else if (stuff == '\\' && *args == 'c')
else if (!no_escapes && stuff == '\\' && *args == 'c')
{
stuff = TEXTCOLOR_ESCAPE, args++;
}
@ -824,11 +824,12 @@ static long ParseCommandLine (const char *args, int *argc, char **argv)
return (long)(buffplace - (char *)0);
}
FCommandLine::FCommandLine (const char *commandline)
FCommandLine::FCommandLine (const char *commandline, bool no_escapes)
{
cmd = commandline;
_argc = -1;
_argv = NULL;
noescapes = no_escapes;
}
FCommandLine::~FCommandLine ()
@ -839,11 +840,20 @@ FCommandLine::~FCommandLine ()
}
}
void FCommandLine::Shift()
{
// Only valid after _argv has been filled.
for (int i = 1; i < _argc; ++i)
{
_argv[i - 1] = _argv[i];
}
}
int FCommandLine::argc ()
{
if (_argc == -1)
{
argsize = ParseCommandLine (cmd, &_argc, NULL);
argsize = ParseCommandLine (cmd, &_argc, NULL, noescapes);
}
return _argc;
}
@ -855,7 +865,7 @@ char *FCommandLine::operator[] (int i)
int count = argc();
_argv = new char *[count + (argsize+sizeof(char*)-1)/sizeof(char*)];
_argv[0] = (char *)_argv + count*sizeof(char *);
ParseCommandLine (cmd, NULL, _argv);
ParseCommandLine (cmd, NULL, _argv, noescapes);
}
return _argv[i];
}

View file

@ -64,17 +64,19 @@ FString BuildString (int argc, char **argv);
class FCommandLine
{
public:
FCommandLine (const char *commandline);
FCommandLine (const char *commandline, bool no_escapes = false);
~FCommandLine ();
int argc ();
char *operator[] (int i);
const char *args () { return cmd; }
void Shift();
private:
const char *cmd;
int _argc;
char **_argv;
long argsize;
bool noescapes;
};
typedef void (*CCmdRun) (FCommandLine &argv, APlayerPawn *instigator, int key);

View file

@ -623,13 +623,13 @@ FString TimidityMIDIDevice::GetStats()
{
dots << TEXTCOLOR_GREEN;
}
if (Renderer->voice[i].envelope_increment == 0)
if (!Renderer->voice[i].eg1.env.bUpdating)
{
dots << "+";
}
else
{
dots << ('0' + Renderer->voice[i].envelope_stage);
dots << ('0' + Renderer->voice[i].eg1.gf1.stage);
}
}
}

85
src/timidity/gf1patch.h Normal file
View file

@ -0,0 +1,85 @@
/* GF1 Patch definition: */
enum
{
HEADER_SIZE = 12,
ID_SIZE = 10,
DESC_SIZE = 60,
RESERVED_SIZE = 40,
PATCH_HEADER_RESERVED_SIZE = 36,
LAYER_RESERVED_SIZE = 40,
PATCH_DATA_RESERVED_SIZE = 36,
INST_NAME_SIZE = 16,
ENVELOPES = 6,
MAX_LAYERS = 4
};
#define GF1_HEADER_TEXT "GF1PATCH110"
#ifdef _MSC_VER
#pragma pack(push, 1)
#define GCC_PACKED
#else
#define GCC_PACKED __attribute__((__packed__))
#endif
struct GF1PatchHeader
{
char Header[HEADER_SIZE];
char GravisID[ID_SIZE]; /* Id = "ID#000002" */
char Description[DESC_SIZE];
BYTE Instruments;
BYTE Voices;
BYTE Channels;
WORD WaveForms;
WORD MasterVolume;
DWORD DataSize;
BYTE Reserved[PATCH_HEADER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1InstrumentData
{
WORD Instrument;
char InstrumentName[INST_NAME_SIZE];
int InstrumentSize;
BYTE Layers;
BYTE Reserved[RESERVED_SIZE];
} GCC_PACKED;
struct GF1LayerData
{
BYTE LayerDuplicate;
BYTE Layer;
int LayerSize;
BYTE Samples;
BYTE Reserved[LAYER_RESERVED_SIZE];
} GCC_PACKED;
struct GF1PatchData
{
char WaveName[7];
BYTE Fractions;
int WaveSize;
int StartLoop;
int EndLoop;
WORD SampleRate;
int LowFrequency;
int HighFrequency;
int RootFrequency;
SWORD Tune;
BYTE Balance;
BYTE EnvelopeRate[ENVELOPES];
BYTE EnvelopeOffset[ENVELOPES];
BYTE TremoloSweep;
BYTE TremoloRate;
BYTE TremoloDepth;
BYTE VibratoSweep;
BYTE VibratoRate;
BYTE VibratoDepth;
BYTE Modes;
SWORD ScaleFrequency;
WORD ScaleFactor; /* From 0 to 2048 or 0 to 2 */
BYTE Reserved[PATCH_DATA_RESERVED_SIZE];
} GCC_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef GCC_PACKED

View file

@ -32,16 +32,15 @@
#include "m_swap.h"
#include "files.h"
#include "templates.h"
#include "gf1patch.h"
namespace Timidity
{
extern Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument);
extern int openmode;
Instrument::Instrument()
: type(INST_GUS), samples(0), sample(NULL)
: samples(0), sample(NULL)
{
}
@ -52,7 +51,7 @@ Instrument::~Instrument()
for (i = samples, sp = &(sample[0]); i != 0; i--, sp++)
{
if (sp->data != NULL)
if (sp->type == INST_GUS && sp->data != NULL)
{
free(sp->data);
}
@ -82,28 +81,6 @@ ToneBank::~ToneBank()
}
}
int convert_envelope_rate(Renderer *song, BYTE rate)
{
int r;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio) << 9;
}
int convert_envelope_offset(BYTE offset)
{
/* This is not too good... Can anyone tell me what these values mean?
Are they GUS-style "exponential" volumes? And what does that mean? */
/* 15.15 fixed point */
return offset << (7 + 15);
}
int convert_tremolo_sweep(Renderer *song, BYTE sweep)
{
if (sweep == 0)
@ -268,7 +245,6 @@ failread:
ip->samples = layer_data.Samples;
ip->sample = (Sample *)safe_malloc(sizeof(Sample) * layer_data.Samples);
memset(ip->sample, 0, sizeof(Sample) * layer_data.Samples);
ip->type = INST_GUS;
for (i = 0; i < layer_data.Samples; ++i)
{
if (sizeof(patch_data) != fp->Read(&patch_data, sizeof(patch_data)))
@ -286,22 +262,23 @@ fail:
sp->loop_start = LittleLong(patch_data.StartLoop);
sp->loop_end = LittleLong(patch_data.EndLoop);
sp->sample_rate = LittleShort(patch_data.SampleRate);
sp->low_freq = LittleLong(patch_data.LowFrequency);
sp->high_freq = LittleLong(patch_data.HighFrequency);
sp->root_freq = LittleLong(patch_data.RootFrequency);
sp->low_freq = float(LittleLong(patch_data.LowFrequency));
sp->high_freq = float(LittleLong(patch_data.HighFrequency)) + 0.9999f;
sp->root_freq = float(LittleLong(patch_data.RootFrequency));
sp->high_vel = 127;
sp->velocity = -1;
sp->type = INST_GUS;
// Expand to SF2 range.
if (panning == -1)
{
sp->panning = patch_data.Balance & 0x0F;
sp->panning = (sp->panning << 3) | (sp->panning >> 1);
sp->panning = (patch_data.Balance & 0x0F) * 1000 / 15 - 500;
}
else
{
sp->panning = panning & 0x7f;
sp->panning = (panning & 0x7f) * 1000 / 127 - 500;
}
sp->panning |= sp->panning << 7;
song->compute_pan(sp->panning, sp->left_offset, sp->right_offset);
song->compute_pan((sp->panning + 500) / 1000.0, INST_GUS, sp->left_offset, sp->right_offset);
/* tremolo */
if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0)
@ -353,6 +330,10 @@ fail:
{
sp->scale_factor *= 1024;
}
else if (sp->scale_factor > 2048)
{
sp->scale_factor = 1024;
}
if (sp->scale_factor != 1024)
{
cmsg(CMSG_INFO, VERB_DEBUG, " * Scale: note %d, factor %d\n",
@ -444,9 +425,9 @@ fail:
for (j = 0; j < 6; j++)
{
sp->envelope_rate[j] = convert_envelope_rate(song, patch_data.EnvelopeRate[j]);
sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j];
/* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */
sp->envelope_offset[j] = convert_envelope_offset(clamp<BYTE>(patch_data.EnvelopeOffset[j], 5, 251));
sp->envelope.gf1.offset[j] = clamp<BYTE>(patch_data.EnvelopeOffset[j], 5, 251);
}
/* Then read the sample data */
@ -648,53 +629,62 @@ static int fill_bank(Renderer *song, int dr, int b)
{
if (bank->instrument[i] == MAGIC_LOAD_INSTRUMENT)
{
bank->instrument[i] = NULL;
bank->instrument[i] = load_instrument_dls(song, dr, b, i);
if (bank->instrument[i] != NULL)
{
continue;
}
if (bank->tone[i].name.IsEmpty())
Instrument *ip;
ip = load_instrument_font_order(song, 0, dr, b, i);
if (ip == NULL)
{
cmsg(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_NORMAL,
"No instrument mapped to %s %d, program %d%s\n",
(dr) ? "drum set" : "tone bank", b, i,
(b != 0) ? "" : " - this instrument will not be heard");
if (b != 0)
if (bank->tone[i].fontbank >= 0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (!dr)
{
if (tonebank[0]->instrument[i] != NULL)
{
tonebank[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
ip = load_instrument_font(song, bank->tone[i].name, dr, b, i);
}
else
{
if (drumset[0]->instrument[i] != NULL)
{
drumset[0]->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
}
}
bank->instrument[i] = NULL;
errors++;
}
else if (!(bank->instrument[i] =
load_instrument(song, bank->tone[i].name,
ip = load_instrument(song, bank->tone[i].name,
(dr) ? 1 : 0,
bank->tone[i].pan,
bank->tone[i].amp,
(bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1),
(bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1),
(bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1),
bank->tone[i].strip_tail)))
bank->tone[i].strip_tail);
}
if (ip == NULL)
{
ip = load_instrument_font_order(song, 1, dr, b, i);
}
}
bank->instrument[i] = ip;
if (ip == NULL)
{
if (bank->tone[i].name.IsEmpty())
{
cmsg(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_NORMAL,
"No instrument mapped to %s %d, program %d%s\n",
(dr) ? "drum set" : "tone bank", b, i,
(b != 0) ? "" : " - this instrument will not be heard");
}
else
{
cmsg(CMSG_ERROR, VERB_NORMAL,
"Couldn't load instrument %s (%s %d, program %d)\n",
bank->tone[i].name.GetChars(),
(dr) ? "drum set" : "tone bank", b, i);
}
if (b != 0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (((dr) ? drumset[0] : tonebank[0])->instrument[i] != NULL)
{
((dr) ? drumset[0] : tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT;
}
}
errors++;
}
}

View file

@ -1121,13 +1121,14 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins,
DLS_Region *rgn = &ins->regions[index];
DLS_Wave *wave = &song->patches->waveList[rgn->wlnk->ulTableIndex];
sample->type = INST_DLS;
sample->self_nonexclusive = !!(rgn->header->fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE);
sample->key_group = (SBYTE)rgn->header->usKeyGroup;
sample->low_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usLow));
sample->high_freq = SDWORD(note_to_freq(rgn->header->RangeKey.usHigh));
sample->root_freq = SDWORD(note_to_freq(rgn->wsmp->usUnityNote));
sample->low_vel = rgn->header->RangeVelocity.usLow;
sample->high_vel = rgn->header->RangeVelocity.usHigh;
sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow);
sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh);
sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote);
sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow;
sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh;
sample->modes = wave->format->wBitsPerSample == 8 ? PATCH_UNSIGNED : PATCH_16;
sample->sample_rate = wave->format->dwSamplesPerSec;
@ -1174,25 +1175,12 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins,
printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release);
*/
sample->envelope_offset[ATTACK] = to_offset(255);
sample->envelope_rate[ATTACK] = calc_rate(song, 255, sample->sample_rate, attack);
sample->envelope_offset[HOLD] = to_offset(250);
sample->envelope_rate[HOLD] = calc_rate(song, 5, sample->sample_rate, hold);
sample->envelope_offset[DECAY] = to_offset(sustain);
sample->envelope_rate[DECAY] = calc_rate(song, 255 - sustain, sample->sample_rate, decay);
sample->envelope_offset[RELEASE] = to_offset(0);
sample->envelope_rate[RELEASE] = calc_rate(song, 5 + sustain, sample->sample_rate, release);
sample->envelope_offset[RELEASEB] = to_offset(0);
sample->envelope_rate[RELEASEB] = to_offset(1);
sample->envelope_offset[RELEASEC] = to_offset(0);
sample->envelope_rate[RELEASEC] = to_offset(1);
sample->modes |= PATCH_NO_SRELEASE;
sample->envelope.sf2.decay_vol = 0;
sample->envelope.sf2.attack_vol = (short)attack;
sample->envelope.sf2.hold_vol = (short)hold;
sample->envelope.sf2.decay_vol = (short)decay;
sample->envelope.sf2.release_vol = (short)release;
sample->envelope.sf2.sustain_vol = (short)sustain;
}
sample->data_length <<= FRACTION_BITS;
@ -1236,7 +1224,6 @@ Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrume
}
inst = (Instrument *)safe_malloc(sizeof(Instrument));
inst->type = INST_DLS;
inst->samples = dls_ins->header->cRegions;
inst->sample = (Sample *)safe_malloc(inst->samples * sizeof(Sample));
memset(inst->sample, 0, inst->samples * sizeof(Sample));

View file

@ -0,0 +1,131 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "doomdef.h"
#include "m_swap.h"
#include "templates.h"
#include "timidity.h"
#include "sf2.h"
namespace Timidity
{
FontFile *Fonts;
FontFile *ReadDLS(const char *filename, FileReader *f)
{
return NULL;
}
void font_freeall()
{
FontFile *font, *next;
for (font = Fonts; font != NULL; font = next)
{
next = font->Next;
delete font;
}
Fonts = NULL;
}
FontFile *font_find(const char *filename)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
if (stricmp(filename, font->Filename) == 0)
{
return font;
}
}
return NULL;
}
void font_add(const char *filename, int load_order)
{
FontFile *font;
font = font_find(filename);
if (font != NULL)
{
font->SetAllOrders(load_order);
}
else
{
FileReader *fp = open_filereader(filename, openmode, NULL);
if (fp != NULL)
{
if ((font = ReadSF2(filename, fp)) || (font = ReadDLS(filename, fp)))
{
font->SetAllOrders(load_order);
}
}
}
}
void font_remove(const char *filename)
{
FontFile *font;
font = font_find(filename);
if (font != NULL)
{
// Don't actually remove the font from the list, because instruments
// from it might be loaded using the %font extension.
font->SetAllOrders(255);
}
}
void font_order(int order, int bank, int preset, int keynote)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
font->SetOrder(order, bank, preset, keynote);
}
}
Instrument *load_instrument_font(struct Renderer *song, const char *font, int drum, int bank, int instr)
{
FontFile *fontfile = font_find(font);
if (fontfile != NULL)
{
return fontfile->LoadInstrument(song, drum, bank, instr);
}
return NULL;
}
Instrument *load_instrument_font_order(struct Renderer *song, int order, int drum, int bank, int instr)
{
for (FontFile *font = Fonts; font != NULL; font = font->Next)
{
Instrument *ip = font->LoadInstrument(song, drum, bank, instr);
if (ip != NULL)
{
return ip;
}
}
return NULL;
}
FontFile::FontFile(FString filename)
: Filename(filename)
{
Next = Fonts;
Fonts = this;
}
FontFile::~FontFile()
{
for (FontFile **probe = &Fonts; *probe != NULL; probe = &(*probe)->Next)
{
if (*probe == this)
{
*probe = Next;
break;
}
}
}
}

1533
src/timidity/instrum_sf2.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -31,68 +31,117 @@
namespace Timidity
{
/* Returns 1 if envelope runs out */
int recompute_envelope(Voice *v)
static int convert_envelope_rate(Renderer *song, BYTE rate)
{
int stage;
int r;
stage = v->envelope_stage;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
if (stage >= ENVELOPES)
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio) << 9;
}
void Envelope::Init(Renderer *song, Voice *v)
{
Type = v->sample->type;
env.bUpdating = true;
if (Type == INST_GUS)
{
gf1.Init(song, v);
gf1.ApplyToAmp(v);
}
else
{
sf2.Init(song, v);
sf2.ApplyToAmp(v);
}
}
void GF1Envelope::Init(Renderer *song, Voice *v)
{
/* Ramp up from 0 */
stage = 0;
volume = 0;
for (int i = 0; i < 6; ++i)
{
offset[i] = v->sample->envelope.gf1.offset[i] << (7 + 15);
rate[i] = convert_envelope_rate(song, v->sample->envelope.gf1.rate[i]);
}
Recompute(v);
}
void GF1Envelope::Release(Voice *v)
{
if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL))
{
/* ramp out to minimum volume with rate from final release stage */
stage = GF1_RELEASEC+1;
target = 0;
increment = -rate[GF1_RELEASEC];
}
else if (v->sample->modes & PATCH_SUSTAIN)
{
if (stage < GF1_RELEASE)
{
stage = GF1_RELEASE;
}
Recompute(v);
}
bUpdating = true;
}
/* Returns 1 if envelope runs out */
bool GF1Envelope::Recompute(Voice *v)
{
int oldstage;
oldstage = stage;
if (oldstage > GF1_RELEASEC)
{
/* Envelope ran out. */
/* play sampled release */
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
v->envelope_increment = 0;
increment = 0;
bUpdating = false;
return 0;
}
if (stage == RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN))
if (oldstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN))
{
v->status |= VOICE_SUSTAINING;
/* Freeze envelope until note turns off. Trumpets want this. */
v->envelope_increment = 0;
increment = 0;
bUpdating = false;
}
else
{
v->envelope_stage = stage + 1;
stage = oldstage + 1;
if (v->envelope_volume == v->sample->envelope_offset[stage])
if (volume == offset[oldstage])
{
return recompute_envelope(v);
return Recompute(v);
}
v->envelope_target = v->sample->envelope_offset[stage];
v->envelope_increment = v->sample->envelope_rate[stage];
if (v->envelope_target < v->envelope_volume)
v->envelope_increment = -v->envelope_increment;
target = offset[oldstage];
increment = rate[oldstage];
if (target < volume)
increment = -increment;
}
return 0;
}
void apply_envelope_to_amp(Voice *v)
bool GF1Envelope::Update(Voice *v)
{
float env_vol = v->attenuation;
float final_amp = v->sample->volume * FINAL_MIX_SCALE;
if (v->tremolo_phase_increment != 0)
volume += increment;
if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target)))
{
env_vol *= v->tremolo_volume;
}
env_vol *= v->envelope_volume / float(1 << 30);
// Note: The pan offsets are negative.
v->left_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->left_offset) * final_amp);
v->right_mix = MAX(0.f, (float)calc_gf1_amp(env_vol + v->right_offset) * final_amp);
}
static int update_envelope(Voice *v)
{
v->envelope_volume += v->envelope_increment;
if (((v->envelope_increment < 0) && (v->envelope_volume <= v->envelope_target)) ||
((v->envelope_increment > 0) && (v->envelope_volume >= v->envelope_target)))
{
v->envelope_volume = v->envelope_target;
if (recompute_envelope(v))
volume = target;
if (Recompute(v))
{
return 1;
}
@ -100,6 +149,214 @@ static int update_envelope(Voice *v)
return 0;
}
void GF1Envelope::ApplyToAmp(Voice *v)
{
double env_vol = v->attenuation;
double final_amp = v->sample->volume * FINAL_MIX_SCALE;
if (v->tremolo_phase_increment != 0)
{ // [RH] FIXME: This is wrong. Tremolo should offset the
// envelope volume, not scale it.
env_vol *= v->tremolo_volume;
}
env_vol *= volume / float(1 << 30);
env_vol = calc_gf1_amp(env_vol) * final_amp;
v->left_mix = float(env_vol * v->left_offset);
v->right_mix = float(env_vol * v->right_offset);
}
void SF2Envelope::Init(Renderer *song, Voice *v)
{
stage = 0;
volume = 0;
DelayTime = v->sample->envelope.sf2.delay_vol;
AttackTime = v->sample->envelope.sf2.attack_vol;
HoldTime = v->sample->envelope.sf2.hold_vol;
DecayTime = v->sample->envelope.sf2.decay_vol;
SustainLevel = v->sample->envelope.sf2.sustain_vol;
ReleaseTime = v->sample->envelope.sf2.release_vol;
SampleRate = song->rate;
HoldStart = 0;
RateMul = song->control_ratio / song->rate;
RateMul_cB = RateMul * 960;
bUpdating = true;
}
void SF2Envelope::Release(Voice *v)
{
if (stage == SF2_ATTACK)
{
// The attack stage does not use an attenuation in cB like all the rest.
volume = log10(volume) * -200;
}
stage = SF2_RELEASE;
bUpdating = true;
}
static double timecent_to_sec(float timecent)
{
if (timecent == -32768)
return 0;
return pow(2.0, timecent / 1200.0);
}
static double calc_rate(double ratemul, double sec)
{
if (sec < 0.006)
sec = 0.006;
return ratemul / sec;
}
static void shutoff_voice(Voice *v)
{
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING | VOICE_STOPPING;
}
static bool check_release(double RateMul, double sec)
{
double rate = calc_rate(960 * RateMul, sec);
// Is release rate very fast? If so, don't do the release, but do
// the voice off ramp instead.
return (rate < 960/20);
}
/* Returns 1 if envelope runs out */
bool SF2Envelope::Update(Voice *v)
{
double sec;
double newvolume;
switch (stage)
{
case SF2_DELAY:
if (v->sample_count >= timecent_to_sec(DelayTime) * SampleRate)
{
stage = SF2_ATTACK;
return Update(v);
}
return 0;
case SF2_ATTACK:
sec = timecent_to_sec(AttackTime);
if (sec <= 0)
{ // instantaneous attack
newvolume = 1;
}
else
{
newvolume = volume + calc_rate(RateMul, sec);
}
if (newvolume >= 1)
{
volume = 0;
HoldStart = v->sample_count;
if (HoldTime <= -32768)
{ // hold time is 0, so skip right to decay
stage = SF2_DECAY;
}
else
{
stage = SF2_HOLD;
}
return Update(v);
}
break;
case SF2_HOLD:
if (v->sample_count - HoldStart >= timecent_to_sec(HoldTime) * SampleRate)
{
stage = SF2_DECAY;
return Update(v);
}
return 0;
case SF2_DECAY:
sec = timecent_to_sec(DecayTime);
if (sec <= 0)
{ // instantaneous decay
newvolume = SustainLevel;
}
else
{
newvolume = volume + calc_rate(RateMul_cB, sec);
}
if (newvolume >= SustainLevel)
{
newvolume = SustainLevel;
stage = SF2_SUSTAIN;
bUpdating = false;
if (!(v->status & VOICE_RELEASING))
{
v->status |= VOICE_SUSTAINING;
}
}
break;
case SF2_SUSTAIN:
// Stay here until released.
return 0;
case SF2_RELEASE:
sec = timecent_to_sec(ReleaseTime);
if (sec <= 0)
{ // instantaneous release
newvolume = 1000;
}
else
{
newvolume = volume + calc_rate(RateMul_cB, sec);
}
if (newvolume >= 960)
{
stage = SF2_FINISHED;
shutoff_voice(v);
bUpdating = false;
return 1;
}
break;
case SF2_FINISHED:
return 1;
}
volume = (float)newvolume;
return 0;
}
/* EMU 8k/10k don't follow spec in regards to volume attenuation.
* This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
* By the standard this should be -200.0. */
#define FLUID_ATTEN_POWER_FACTOR (-531.509)
#define atten2amp(x) pow(10.0, (x) / FLUID_ATTEN_POWER_FACTOR)
void SF2Envelope::ApplyToAmp(Voice *v)
{
double amp;
if (stage == SF2_DELAY)
{
v->left_mix = 0;
v->right_mix = 0;
return;
}
else if (stage == SF2_ATTACK)
{
amp = atten2amp(v->attenuation) * volume;
}
else
{
amp = atten2amp(v->attenuation) * cb_to_amp(volume);
}
amp *= FINAL_MIX_SCALE * 0.5;
v->left_mix = float(amp * v->left_offset);
v->right_mix = float(amp * v->right_offset);
}
void apply_envelope_to_amp(Voice *v)
{
v->eg1.ApplyToAmp(v);
}
static void update_tremolo(Voice *v)
{
int depth = v->sample->tremolo_depth << 7;
@ -136,7 +393,7 @@ static void update_tremolo(Voice *v)
/* Returns 1 if the note died */
static int update_signal(Voice *v)
{
if (v->envelope_increment != 0 && update_envelope(v))
if (v->eg1.env.bUpdating && v->eg1.Update(v))
{
return 1;
}
@ -347,7 +604,7 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
/* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */
if (v->left_offset == 0) // All the way to the left
if (v->right_mix == 0) // All the way to the left
{
left = v->left_mix;
li = -(left/c);
@ -362,7 +619,7 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
lp += 2;
}
}
else if (v->right_offset == 0) // All the way to the right
else if (v->left_mix == 0) // All the way to the right
{
right = v->right_mix;
ri = -(right/c);
@ -441,7 +698,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
if (v->right_mix == 0) // All the way to the left
{
if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0)
{
mix_single_left_signal(song->control_ratio, sp, buf, v, count);
}
@ -452,7 +709,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
else if (v->left_mix == 0) // All the way to the right
{
if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0)
{
mix_single_right_signal(song->control_ratio, sp, buf, v, count);
}
@ -463,7 +720,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
}
else // Somewhere in the middle
{
if (v->envelope_increment || v->tremolo_phase_increment)
if (v->eg1.env.bUpdating || v->tremolo_phase_increment)
{
mix_mystery_signal(song->control_ratio, sp, buf, v, count);
}
@ -472,6 +729,7 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
mix_mystery(song->control_ratio, sp, buf, v, count);
}
}
v->sample_count += count;
}
}

View file

@ -31,8 +31,6 @@
namespace Timidity
{
static const double log_of_2 = 0.69314718055994529;
void Renderer::reset_voices()
{
for (int i = 0; i < voices; i++)
@ -44,8 +42,8 @@ void Renderer::reset_voices()
/* Process the Reset All Controllers event */
void Renderer::reset_controllers(int c)
{
channel[c].volume = (100 << 7) | 100;
channel[c].expression = 0x3fff;
channel[c].volume = 100;
channel[c].expression = 127;
channel[c].sustain = 0;
channel[c].pitchbend = 0x2000;
channel[c].pitchfactor = 0; /* to be computed */
@ -68,55 +66,6 @@ void Renderer::reset_midi()
reset_voices();
}
void Renderer::select_sample(int v, Instrument *ip, int vel)
{
double f, cdiff, diff;
int s, i;
Sample *sp, *closest;
s = ip->samples;
sp = ip->sample;
if (s == 1)
{
voice[v].sample = sp;
return;
}
f = voice[v].orig_frequency;
for (i = 0; i < s; i++)
{
if (sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f)
{
voice[v].sample = sp;
return;
}
sp++;
}
/*
No suitable sample found! We'll select the sample whose root
frequency is closest to the one we want. (Actually we should
probably convert the low, high, and root frequencies to MIDI note
values and compare those.) */
cdiff = 1e10;
closest = sp = ip->sample;
for (i = 0; i < s; i++)
{
diff = fabs(sp->root_freq - f);
if (diff < cdiff)
{
cdiff = diff;
closest = sp;
}
sp++;
}
voice[v].sample = closest;
return;
}
void Renderer::recompute_freq(int v)
{
Channel *ch = &channel[voice[v].channel];
@ -210,37 +159,59 @@ void Renderer::recompute_amp(Voice *v)
int chanvol = chan->volume;
int chanexpr = chan->expression;
v->attenuation = (vol_table[(chanvol * chanexpr) / 2113407] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
}
void Renderer::compute_pan(int panning, float &left_offset, float &right_offset)
{
// Round the left- and right-most positions to their extremes, since
// most songs only do coarse panning.
if (panning < 128)
if (v->sample->type == INST_GUS)
{
panning = 0;
}
else if (panning > 127*128)
{
panning = 32767;
}
if (panning == 0)
{
left_offset = 0;
right_offset = (float)-HUGE_VAL;
}
else if (panning == 32767)
{
left_offset = (float)-HUGE_VAL;
right_offset = 0;
v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
}
else
{
double pan = panning * (1 / 32767.0);
right_offset = (float)(log(pan) * (1 / (log_of_2 * 32)));
left_offset = (float)(log(1 - pan) * (1 / (log_of_2 * 32)));
// Implicit modulators from SF2 spec
double velatten, cc7atten, cc11atten;
velatten = log10(127.0 / v->velocity);
cc7atten = log10(127.0 / chanvol);
cc11atten = log10(127.0 / chanexpr);
v->attenuation = float(400 * (velatten + cc7atten + cc11atten)) + v->sample->initial_attenuation;
}
}
// Pan must be in the range [0,1]
void Renderer::compute_pan(double pan, int type, float &left_offset, float &right_offset)
{
if (pan <= 0)
{
left_offset = 1;
right_offset = 0;
}
else if (pan >= 127/128.0)
{
left_offset = 0;
right_offset = 1;
}
else
{
if (type == INST_GUS)
{
/* Original amp equation looks like this:
* calc_gf1_amp(atten + offset)
* which expands to:
* 2^(16*(atten + offset) - 16)
* Keeping in mind that 2^(x + y) == 2^x * 2^y, we can
* rewrite this to avoid doing two pows in GF1Envelope::ApplyToAmp():
* 2^(16*atten + 16*offset - 16)
* 2^(16*atten - 16 + 16 * offset + 16 - 16)
* 2^(16*atten - 16) * 2^(16*offset + 16 - 16)
* 2^(16*atten - 16) * 2^(16*(offset + 1) - 16)
* calc_gf1_amp(atten) * calc_gf1_amp(offset + 1)
*/
right_offset = (float)calc_gf1_amp((log(pan) * (1 / (log_of_2 * 32))) + 1);
left_offset = (float)calc_gf1_amp((log(1 - pan) * (1 / (log_of_2 * 32))) + 1);
}
else
{
left_offset = (float)db_to_amp(-20 * log10(sqrt(1 - pan)));
right_offset = (float)db_to_amp(-20 * log10(sqrt(pan)));
}
}
}
@ -264,17 +235,112 @@ void Renderer::kill_key_group(int i)
float Renderer::calculate_scaled_frequency(Sample *sp, int note)
{
double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note;
double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note + sp->tune * 0.01;
return (float)note_to_freq(scalednote);
}
void Renderer::start_note(int chan, int note, int vel, int i)
bool Renderer::start_region(int chan, int note, int vel, Sample *sp, float f)
{
int voicenum;
Voice *v;
voicenum = allocate_voice();
if (voicenum < 0)
{
return false;
}
v = &voice[voicenum];
v->sample = sp;
if (sp->type == INST_GUS)
{
v->orig_frequency = f;
}
else
{
if (sp->scale_factor != 1024)
{
v->orig_frequency = calculate_scaled_frequency(sp, note);
}
else if (sp->tune != 0)
{
v->orig_frequency = note_to_freq(note + sp->tune * 0.01);
}
else
{
v->orig_frequency = note_to_freq(note);
}
}
v->status = VOICE_RUNNING;
v->channel = chan;
v->note = note;
v->velocity = vel;
v->sample_offset = 0;
v->sample_increment = 0; /* make sure it isn't negative */
v->sample_count = 0;
v->tremolo_phase = 0;
v->tremolo_phase_increment = v->sample->tremolo_phase_increment;
v->tremolo_sweep = v->sample->tremolo_sweep_increment;
v->tremolo_sweep_position = 0;
v->vibrato_sweep = v->sample->vibrato_sweep_increment;
v->vibrato_sweep_position = 0;
v->vibrato_control_ratio = v->sample->vibrato_control_ratio;
v->vibrato_control_counter = v->vibrato_phase = 0;
kill_key_group(voicenum);
memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment));
if (sp->type == INST_SF2)
{
// Channel pan is added to instrument pan.
double pan;
if (channel[chan].panning == NO_PANNING)
{
pan = (sp->panning + 500) / 1000.0;
}
else
{
pan = channel[chan].panning / 128.0 + sp->panning / 1000.0;
}
compute_pan(pan, sp->type, v->left_offset, v->right_offset);
}
else if (channel[chan].panning != NO_PANNING)
{
compute_pan(channel[chan].panning / 128.0, sp->type, v->left_offset, v->right_offset);
}
else
{
v->left_offset = v->sample->left_offset;
v->right_offset = v->sample->right_offset;
}
recompute_freq(voicenum);
recompute_amp(v);
v->control_counter = 0;
v->eg1.Init(this, v);
if (v->sample->modes & PATCH_LOOPEN)
{
v->status |= VOICE_LPE;
}
return true;
}
void Renderer::start_note(int chan, int note, int vel)
{
Instrument *ip;
Sample *sp;
int bank = channel[chan].bank;
int prog = channel[chan].program;
Voice *v = &voice[i];
int i;
float f;
note &= 0x7f;
if (ISDRUMCHANNEL(chan))
{
if (NULL == drumset[bank] || NULL == (ip = drumset[bank]->instrument[note]))
@ -282,7 +348,12 @@ void Renderer::start_note(int chan, int note, int vel, int i)
if (!(ip = drumset[0]->instrument[note]))
return; /* No instrument? Then we can't play. */
}
if (ip->type == INST_GUS && ip->samples != 1)
assert(ip != MAGIC_LOAD_INSTRUMENT);
if (ip == MAGIC_LOAD_INSTRUMENT)
{
return;
}
if (ip->samples != 1 && ip->sample->type == INST_GUS)
{
cmsg(CMSG_WARNING, VERB_VERBOSE,
"Strange: percussion instrument with %d samples!", ip->samples);
@ -299,62 +370,85 @@ void Renderer::start_note(int chan, int note, int vel, int i)
if (NULL == (ip = tonebank[0]->instrument[prog]))
return; /* No instrument? Then we can't play. */
}
}
if (ip->sample->scale_factor != 1024)
assert(ip != MAGIC_LOAD_INSTRUMENT);
if (ip == MAGIC_LOAD_INSTRUMENT)
{
v->orig_frequency = calculate_scaled_frequency(ip->sample, note & 0x7F);
return;
}
}
if (NULL == ip->sample || ip->samples == 0)
return; /* No samples? Then nothing to play. */
// For GF1 patches, scaling is based solely on the first
// waveform in this layer.
if (ip->sample->type == INST_GUS && ip->sample->scale_factor != 1024)
{
f = calculate_scaled_frequency(ip->sample, note);
}
else
{
v->orig_frequency = note_to_freq(note & 0x7F);
f = note_to_freq(note);
}
select_sample(i, ip, vel);
v->status = VOICE_RUNNING;
v->channel = chan;
v->note = note;
v->velocity = vel;
v->sample_offset = 0;
v->sample_increment = 0; /* make sure it isn't negative */
v->tremolo_phase = 0;
v->tremolo_phase_increment = voice[i].sample->tremolo_phase_increment;
v->tremolo_sweep = voice[i].sample->tremolo_sweep_increment;
v->tremolo_sweep_position = 0;
v->vibrato_sweep = voice[i].sample->vibrato_sweep_increment;
v->vibrato_sweep_position = 0;
v->vibrato_control_ratio = voice[i].sample->vibrato_control_ratio;
v->vibrato_control_counter = voice[i].vibrato_phase = 0;
kill_key_group(i);
memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment));
if (channel[chan].panning != NO_PANNING)
if (ip->sample->type == INST_GUS)
{
v->left_offset = channel[chan].left_offset;
v->right_offset = channel[chan].right_offset;
/* We're more lenient with matching ranges for GUS patches, since the
* official Gravis ones don't cover the full range of possible
* frequencies for every instrument.
*/
if (ip->samples == 1)
{ // If there's only one sample, definitely play it.
start_region(chan, note, vel, ip->sample, f);
}
for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp)
{
// GUS patches don't have velocity ranges, so no need to compare against them.
if (sp->low_freq <= f && sp->high_freq >= f)
{
if (i > 1 && (sp + 1)->low_freq <= f && (sp + 1)->high_freq >= f)
{ /* If there is a range of contiguous regions that match our
* desired frequency, the last one in that block is used.
*/
continue;
}
start_region(chan, note, vel, sp, f);
break;
}
}
if (i == 0)
{ /* Found nothing. Try again, but look for the one with the closest root frequency.
* As per the suggestion in the original TiMidity function, this search uses
* note values rather than raw frequencies.
*/
double cdiff = 1e10;
double want_note = freq_to_note(f);
Sample *closest = sp = ip->sample;
for (i = ip->samples; i != 0; --i, ++sp)
{
double diff = fabs(freq_to_note(sp->root_freq) - want_note);
if (diff < cdiff)
{
cdiff = diff;
closest = sp;
}
}
start_region(chan, note, vel, closest, f);
}
}
else
{
v->left_offset = v->sample->left_offset;
v->right_offset = v->sample->right_offset;
}
recompute_freq(i);
recompute_amp(v);
/* Ramp up from 0 */
v->envelope_stage = ATTACK;
v->envelope_volume = 0;
v->control_counter = 0;
recompute_envelope(v);
apply_envelope_to_amp(v);
if (v->sample->modes & PATCH_LOOPEN)
for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp)
{
v->status |= VOICE_LPE;
if ((sp->low_vel <= vel && sp->high_vel >= vel &&
sp->low_freq <= f && sp->high_freq >= f))
{
if (!start_region(chan, note, vel, sp, f))
{ // Ran out of voices
break;
}
}
}
}
}
@ -369,47 +463,22 @@ void Renderer::kill_note(int i)
}
}
/* Only one instance of a note can be playing on a single channel. */
void Renderer::note_on(int chan, int note, int vel)
int Renderer::allocate_voice()
{
if (vel == 0)
{
note_off(chan, note, 0);
return;
}
int i, lowest;
float lv, v;
int i = voices, lowest = -1;
float lv = 1e10, v;
while (i--)
for (i = 0; i < voices; ++i)
{
if (!(voice[i].status & VOICE_RUNNING))
{
lowest = i; /* Can't get a lower volume than silence */
return i; /* Can't get a lower volume than silence */
}
else if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono))
{
if (channel[chan].mono)
{
kill_note(i);
}
else
{
finish_note(i);
}
}
}
if (lowest != -1)
{
/* Found a free voice. */
start_note(chan, note, vel, lowest);
return;
}
/* Look for the decaying note with the lowest volume */
if (lowest == -1)
{
lowest = -1;
lv = 1e10;
i = voices;
while (i--)
{
@ -423,9 +492,8 @@ void Renderer::note_on(int chan, int note, int vel)
}
}
}
}
if (lowest != -1)
if (lowest >= 0)
{
/* This can still cause a click, but if we had a free voice to
spare for ramping down this note, we wouldn't need to kill it
@ -434,12 +502,41 @@ void Renderer::note_on(int chan, int note, int vel)
cut_notes++;
voice[lowest].status = 0;
start_note(chan, note, vel, lowest);
}
else
{
lost_notes++;
}
return lowest;
}
void Renderer::note_on(int chan, int note, int vel)
{
if (vel == 0)
{
note_off(chan, note, 0);
return;
}
int i = voices;
/* Only one instance of a note can be playing on a single channel. */
while (i--)
{
if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono))
{
if (channel[chan].mono)
{
kill_note(i);
}
else
{
finish_note(i);
}
}
}
start_note(chan, note, vel);
}
void Renderer::finish_note(int i)
@ -455,23 +552,8 @@ void Renderer::finish_note(int i)
{
v->status &= ~VOICE_LPE; /* sampled release */
}
if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL))
{
/* ramp out to minimum volume with rate from final release stage */
v->envelope_stage = RELEASEC;
recompute_envelope(v);
// Get rate from the final release ramp, but force the target to 0.
v->envelope_target = 0;
v->envelope_increment = -v->sample->envelope_rate[RELEASEC];
}
else if (v->sample->modes & PATCH_SUSTAIN)
{
if (v->envelope_stage < RELEASE)
{
v->envelope_stage = RELEASE;
}
recompute_envelope(v);
}
v->eg1.Release(v);
v->eg2.Release(v);
}
}
@ -554,15 +636,19 @@ void Renderer::adjust_pressure(int chan, int note, int amount)
void Renderer::adjust_panning(int chan)
{
Channel *chanp = &channel[chan];
compute_pan(chanp->panning, chanp->left_offset, chanp->right_offset);
int i = voices;
while (i--)
{
if ((voice[i].channel == chan) && (voice[i].status & VOICE_RUNNING))
Voice *v = &voice[i];
if ((v->channel == chan) && (v->status & VOICE_RUNNING))
{
voice[i].left_offset = chanp->left_offset;
voice[i].right_offset = chanp->right_offset;
apply_envelope_to_amp(&voice[i]);
double pan = chanp->panning / 128.0;
if (v->sample->type == INST_SF2)
{ // Add instrument pan to channel pan.
pan += v->sample->panning / 500.0;
}
compute_pan(pan, v->sample->type, v->left_offset, v->right_offset);
apply_envelope_to_amp(v);
}
}
}
@ -674,32 +760,17 @@ void Renderer::HandleController(int chan, int ctrl, int val)
break;
case CTRL_VOLUME:
channel[chan].volume = (channel[chan].volume & 0x007F) | (val << 7);
adjust_volume(chan);
break;
case CTRL_VOLUME+32:
channel[chan].volume = (channel[chan].volume & 0x3F80) | (val);
channel[chan].volume = val;
adjust_volume(chan);
break;
case CTRL_EXPRESSION:
channel[chan].expression = (channel[chan].expression & 0x007F) | (val << 7);
adjust_volume(chan);
break;
case CTRL_EXPRESSION+32:
channel[chan].expression = (channel[chan].expression & 0x3F80) | (val);
channel[chan].expression = val;
adjust_volume(chan);
break;
case CTRL_PAN:
channel[chan].panning = (channel[chan].panning & 0x007F) | (val << 7);
adjust_panning(chan);
break;
case CTRL_PAN+32:
channel[chan].panning = (channel[chan].panning & 0x3F80) | (val);
channel[chan].panning = val;
adjust_panning(chan);
break;

View file

@ -509,6 +509,14 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
/* Need to resample. Use the proper function. */
modes = vp->sample->modes;
if (vp->status & VOICE_LPE)
{
if (vp->sample->loop_end - vp->sample->loop_start < 2)
{ // Loop is too short; turn it off.
vp->status &= ~VOICE_LPE;
}
}
if (vp->vibrato_control_ratio)
{
if (vp->status & VOICE_LPE)

318
src/timidity/sf2.h Normal file
View file

@ -0,0 +1,318 @@
typedef WORD SFGenerator;
struct SFRange
{
BYTE Lo;
BYTE Hi;
};
struct SFPreset
{
char Name[21];
BYTE LoadOrder:7;
BYTE bHasGlobalZone:1;
WORD Program;
WORD Bank;
WORD BagIndex;
/* Don't care about library, genre, and morphology */
};
struct SFBag
{
WORD GenIndex;
// WORD ModIndex; // If I am feeling ambitious, I might add support for modulators some day.
SFRange KeyRange;
SFRange VelRange;
int Target; // Either an instrument or sample index
};
struct SFInst
{
char Name[21];
BYTE Pad:7;
BYTE bHasGlobalZone:1;
WORD BagIndex;
};
struct SFSample
{
float *InMemoryData;
DWORD Start;
DWORD End;
DWORD StartLoop;
DWORD EndLoop;
DWORD SampleRate;
BYTE OriginalPitch;
SBYTE PitchCorrection;
WORD SampleLink;
WORD SampleType;
char Name[21];
};
// Sample type bit fields (all but ROM are mutually exclusive)
enum
{
SFST_Mono = 1,
SFST_Right = 2,
SFST_Left = 4,
SFST_Linked = 8, /* SF2.04 defines this bit but not its function */
SFST_Bad = 16384, /* Used internally */
SFST_ROM = 32768
};
// Generator definitions
struct SFGenList
{
SFGenerator Oper;
union
{
SFRange Range;
SWORD Amount;
WORD uAmount;
};
};
enum
{
GEN_startAddrsOffset,
GEN_endAddrsOffset,
GEN_startloopAddrsOffset,
GEN_endloopAddrsOffset,
GEN_startAddrsCoarseOffset,
GEN_modLfoToPitch,
GEN_vibLfoToPitch,
GEN_modEnvToPitch,
GEN_initialFilterFC,
GEN_initialFilterQ,
GEN_modLfoToFilterFc,
GEN_modEnvToFilterFc,
GEN_endAddrsCoarseOffset,
GEN_modLfoToVolume,
GEN_unused1,
GEN_chorusEffectsSend,
GEN_reverbEffectsSend,
GEN_pan,
GEN_unused2,
GEN_unused3,
GEN_unused4,
GEN_delayModLFO,
GEN_freqModLFO,
GEN_delayVibLFO,
GEN_freqVibLFO,
GEN_delayModEnv,
GEN_attackModEnv,
GEN_holdModEnv,
GEN_decayModEnv,
GEN_sustainModEnv,
GEN_releaseModEnv,
GEN_keynumToModEnvHold,
GEN_keynumToModEnvDecay,
GEN_delayVolEnv,
GEN_attackVolEnv,
GEN_holdVolEnv,
GEN_decayVolEnv,
GEN_sustainVolEnv,
GEN_releaseVolEnv,
GEN_keynumToVolEnvHold,
GEN_keynumToVolEnvDecay,
GEN_instrument,
GEN_reserved1,
GEN_keyRange,
GEN_velRange,
GEN_startloopAddrsCoarseOffset,
GEN_keynum,
GEN_velocity,
GEN_initialAttenuation,
GEN_reserved2,
GEN_endloopAddrsCoarseOffset,
GEN_coarseTune,
GEN_fineTune,
GEN_sampleID,
GEN_sampleModes,
GEN_reserved3,
GEN_scaleTuning,
GEN_exclusiveClass,
GEN_overridingRootKey,
GEN_NumGenerators
};
// Modulator definitions
struct SFModulator
{
WORD Index:7;
WORD CC:1;
WORD Dir:1; /* 0 = min->max, 1 = max->min */
WORD Polarity:1; /* 0 = unipolar, 1 = bipolar */
WORD Type:6;
};
struct SFModList
{
SFModulator SrcOper;
SFGenerator DestOper;
SWORD Amount;
SFModulator AmtSrcOper;
WORD Transform;
};
// Modulator sources when CC is 0
enum
{
SFMod_One = 0, // Psuedo-controller that always has the value 1
SFMod_NoteVelocity = 2,
SFMod_KeyNumber = 3,
SFMod_PolyPressure = 10,
SFMod_ChannelPressure = 13,
SFMod_PitchWheel = 14,
SFMod_PitchSens = 16,
SFMod_Link = 127
};
// Modulator types
enum
{
SFModType_Linear,
SFModType_Concave, // log(fabs(value)/(max value)^2)
SFModType_Convex,
SFModType_Switch
};
// Modulator transforms
enum
{
SFModTrans_Linear = 0,
SFModTrans_Abs = 2
};
// All possible generators in a single structure
struct SFGenComposite
{
union
{
SFRange keyRange; // For normal use
struct // For intermediate percussion use
{
BYTE drumset;
BYTE key;
};
};
SFRange velRange;
union
{
WORD instrument; // At preset level
WORD sampleID; // At instrument level
};
SWORD modLfoToPitch;
SWORD vibLfoToPitch;
SWORD modEnvToPitch;
SWORD initialFilterFc;
SWORD initialFilterQ;
SWORD modLfoToFilterFc;
SWORD modEnvToFilterFc;
SWORD modLfoToVolume;
SWORD chorusEffectsSend;
SWORD reverbEffectsSend;
SWORD pan;
SWORD delayModLFO;
SWORD freqModLFO;
SWORD delayVibLFO;
SWORD freqVibLFO;
SWORD delayModEnv;
SWORD attackModEnv;
SWORD holdModEnv;
SWORD decayModEnv;
SWORD sustainModEnv;
SWORD releaseModEnv;
SWORD keynumToModEnvHold;
SWORD keynumToModEnvDecay;
SWORD delayVolEnv;
SWORD attackVolEnv;
SWORD holdVolEnv;
SWORD decayVolEnv;
SWORD sustainVolEnv;
SWORD releaseVolEnv;
SWORD keynumToVolEnvHold;
SWORD keynumToVolEnvDecay;
SWORD initialAttenuation;
SWORD coarseTune;
SWORD fineTune;
SWORD scaleTuning;
// The following are only for instruments:
SWORD startAddrsOffset, startAddrsCoarseOffset;
SWORD endAddrsOffset, endAddrsCoarseOffset;
SWORD startLoopAddrsOffset, startLoopAddrsCoarseOffset;
SWORD endLoopAddrsOffset, endLoopAddrsCoarseOffset;
SWORD keynum;
SWORD velocity;
WORD sampleModes;
SWORD exclusiveClass;
SWORD overridingRootKey;
};
// Intermediate percussion representation
struct SFPerc
{
SFPreset *Preset;
SFGenComposite Generators;
BYTE LoadOrder;
};
// Container for all parameters from a SoundFont file
struct SFFile : public Timidity::FontFile
{
SFFile(FString filename);
~SFFile();
Timidity::Instrument *LoadInstrument(struct Timidity::Renderer *song, int drum, int bank, int program);
Timidity::Instrument *LoadInstrumentOrder(struct Timidity::Renderer *song, int order, int drum, int bank, int program);
void SetOrder(int order, int drum, int bank, int program);
void SetAllOrders(int order);
bool FinalStructureTest();
void CheckBags();
void CheckZones(int start, int stop, bool instr);
void TranslatePercussions();
void TranslatePercussionPreset(SFPreset *preset);
void TranslatePercussionPresetZone(SFPreset *preset, SFBag *zone);
void SetInstrumentGenerators(SFGenComposite *composite, int start, int stop);
void AddPresetGenerators(SFGenComposite *composite, int start, int stop, SFPreset *preset);
void AddPresetGenerators(SFGenComposite *composite, int start, int stop, bool gen_set[GEN_NumGenerators]);
Timidity::Instrument *LoadPercussion(Timidity::Renderer *song, SFPerc *perc);
Timidity::Instrument *LoadPreset(Timidity::Renderer *song, SFPreset *preset);
void LoadSample(SFSample *sample);
void ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Timidity::Renderer *song, Timidity::Sample *sp);
SFPreset *Presets;
SFBag *PresetBags;
SFGenList *PresetGenerators;
SFInst *Instruments;
SFBag *InstrBags;
SFGenList *InstrGenerators;
SFSample *Samples;
TArray<SFPerc> Percussion;
int MinorVersion;
DWORD SampleDataOffset;
DWORD SampleDataLSBOffset;
DWORD SizeSampleData;
DWORD SizeSampleDataLSB;
int NumPresets;
int NumPresetBags;
int NumPresetGenerators;
int NumInstruments;
int NumInstrBags;
int NumInstrGenerators;
int NumSamples;
};
SFFile *ReadSF2(const char *filename, FileReader *f);

View file

@ -28,6 +28,7 @@
#include "m_alloc.h"
#include "cmdlib.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "i_system.h"
#include "files.h"
@ -42,13 +43,10 @@ ToneBank *tonebank[MAXBANK], *drumset[MAXBANK];
static FString def_instr_name;
int openmode = OM_FILEORLUMP;
#define MAXWORDS 10
static int read_config_file(const char *name, bool ismain)
{
FileReader *fp;
char tmp[1024], *w[MAXWORDS], *cp;
char tmp[1024], *cp;
ToneBank *bank = NULL;
int i, j, k, line = 0, words;
static int rcf_count = 0;
@ -81,17 +79,29 @@ static int read_config_file(const char *name, bool ismain)
while (fp->Gets(tmp, sizeof(tmp)))
{
line++;
w[words = 0] = strtok(tmp, " \t\r\n\240");
if (!w[0]) continue;
FCommandLine w(tmp, true);
words = w.argc();
if (words == 0) continue;
/* Originally the TiMidity++ extensions were prefixed like this */
if (strcmp(w[0], "#extension") == 0)
words = -1;
{
w.Shift();
words--;
}
else if (*w[0] == '#')
{
continue;
}
while (w[words] && *w[words] != '#' && (words < MAXWORDS))
w[++words] = strtok(0, " \t\r\n\240");
for (i = 0; i < words; ++i)
{
if (*w[i] == '#')
{
words = i;
break;
}
}
/*
* TiMidity++ adds a number of extensions to the config file format.
@ -155,19 +165,93 @@ static int read_config_file(const char *name, bool ismain)
*/
Printf("FIXME: Implement \"altassign\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "soundfont") || !strcmp(w[0], "font"))
else if (!strcmp(w[0], "soundfont"))
{
/*
* I can't find any documentation for these, but I guess they're
* an alternative way of loading/unloading instruments.
*
* "soundfont" sf_file "remove"
* "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
* ["reso=" reso] ["amp=" amp]
*/
if (words < 2)
{
Printf("%s: line %d: No soundfont given\n", name, line);
delete fp;
return -2;
}
if (words > 2 && !strcmp(w[2], "remove"))
{
font_remove(w[1]);
}
else
{
int order = 0;
for (i = 2; i < words; ++i)
{
if (!(cp = strchr(w[i], '=')))
{
Printf("%s: line %d: bad soundfont option %s\n", name, line, w[i]);
delete fp;
return -2;
}
}
font_add(w[1], order);
}
}
else if (!strcmp(w[0], "font"))
{
/*
* "font" "exclude" bank preset keynote
* "font" "order" order bank preset keynote
*/
Printf("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]);
int order, drum = -1, bank = -1, instr = -1;
if (words < 3)
{
Printf("%s: line %d: syntax error\n", name, line);
delete fp;
return -2;
}
if (!strcmp(w[1], "exclude"))
{
order = 254;
i = 2;
}
else if (!strcmp(w[1], "order"))
{
order = atoi(w[2]);
i = 3;
}
else
{
Printf("%s: line %d: font subcommand must be 'order' or 'exclude'\n", name, line);
delete fp;
return -2;
}
if (i < words)
{
drum = atoi(w[i++]);
}
if (i < words)
{
bank = atoi(w[i++]);
}
if (i < words)
{
instr = atoi(w[i++]);
}
if (drum != 128)
{
instr = bank;
bank = drum;
drum = 0;
}
else
{
drum = 1;
}
font_order(order, drum, bank, instr);
}
else if (!strcmp(w[0], "progbase"))
{
@ -289,12 +373,34 @@ static int read_config_file(const char *name, bool ismain)
delete fp;
return -2;
}
bank->tone[i].name = w[1];
bank->tone[i].note = bank->tone[i].amp = bank->tone[i].pan =
bank->tone[i].strip_loop = bank->tone[i].strip_envelope =
bank->tone[i].strip_tail = -1;
bank->tone[i].fontbank = bank->tone[i].fontpreset =
bank->tone[i].fontnote = bank->tone[i].strip_loop =
bank->tone[i].strip_envelope = bank->tone[i].strip_tail = -1;
for (j = 2; j<words; j++)
if (!strcmp(w[1], "%font"))
{
bank->tone[i].name = w[2];
bank->tone[i].fontbank = atoi(w[3]);
bank->tone[i].fontpreset = atoi(w[4]);
if (bank->tone[i].fontbank == 128 || (w[5][0] >= '0' && w[5][0] <= '9'))
{
bank->tone[i].fontnote = atoi(w[5]);
j = 6;
}
else
{
j = 5;
}
font_add(w[2], 254);
}
else
{
bank->tone[i].name = w[1];
j = 2;
}
for (; j<words; j++)
{
if (!(cp=strchr(w[j], '=')))
{
@ -397,6 +503,7 @@ static int read_config_file(const char *name, bool ismain)
void FreeAll()
{
free_instruments();
font_freeall();
for (int i = 0; i < MAXBANK; ++i)
{
if (tonebank[i] != NULL)

View file

@ -177,6 +177,7 @@ extern void clear_pathlist();
extern void *safe_malloc(size_t count);
FileReader *open_filereader(const char *name, int open, int *plumpnum);
extern int openmode;
/*
controls.h
@ -204,53 +205,6 @@ void cmsg(int type, int verbosity_level, const char *fmt, ...);
instrum.h
*/
struct Sample
{
SDWORD
loop_start, loop_end, data_length,
sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq;
SDWORD
envelope_rate[6], envelope_offset[6];
float
volume;
sample_t *data;
SDWORD
tremolo_sweep_increment, tremolo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio;
BYTE
tremolo_depth, vibrato_depth,
modes;
WORD
panning, scale_factor;
SWORD
scale_note;
bool
self_nonexclusive;
BYTE
key_group;
float
left_offset, right_offset;
};
void convert_sample_data(Sample *sample, const void *data);
void free_instruments();
/* Patch definition: */
enum
{
HEADER_SIZE = 12,
ID_SIZE = 10,
DESC_SIZE = 60,
RESERVED_SIZE = 40,
PATCH_HEADER_RESERVED_SIZE = 36,
LAYER_RESERVED_SIZE = 40,
PATCH_DATA_RESERVED_SIZE = 36,
INST_NAME_SIZE = 16,
ENVELOPES = 6,
MAX_LAYERS = 4
};
#define GF1_HEADER_TEXT "GF1PATCH110"
enum
{
PATCH_16 = (1<<0),
@ -263,80 +217,90 @@ enum
PATCH_FAST_REL = (1<<7)
};
#ifdef _MSC_VER
#pragma pack(push, 1)
#define GCC_PACKED
#else
#define GCC_PACKED __attribute__((__packed__))
#endif
struct GF1PatchHeader
struct Sample
{
char Header[HEADER_SIZE];
char GravisID[ID_SIZE]; /* Id = "ID#000002" */
char Description[DESC_SIZE];
BYTE Instruments;
BYTE Voices;
BYTE Channels;
WORD WaveForms;
WORD MasterVolume;
DWORD DataSize;
BYTE Reserved[PATCH_HEADER_RESERVED_SIZE];
} GCC_PACKED;
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;
float
volume;
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,
modes, type;
SWORD
panning;
WORD
scale_factor, key_group;
SWORD
scale_note;
bool
self_nonexclusive;
float
left_offset, right_offset;
struct GF1InstrumentData
{
WORD Instrument;
char InstrumentName[INST_NAME_SIZE];
int InstrumentSize;
BYTE Layers;
BYTE Reserved[RESERVED_SIZE];
} GCC_PACKED;
// SF2 stuff
SWORD tune;
SBYTE velocity;
struct GF1LayerData
{
BYTE LayerDuplicate;
BYTE Layer;
int LayerSize;
BYTE Samples;
BYTE Reserved[LAYER_RESERVED_SIZE];
} GCC_PACKED;
float initial_attenuation;
};
struct GF1PatchData
{
char WaveName[7];
BYTE Fractions;
int WaveSize;
int StartLoop;
int EndLoop;
WORD SampleRate;
int LowFrequency;
int HighFrequency;
int RootFrequency;
SWORD Tune;
BYTE Balance;
BYTE EnvelopeRate[ENVELOPES];
BYTE EnvelopeOffset[ENVELOPES];
BYTE TremoloSweep;
BYTE TremoloRate;
BYTE TremoloDepth;
BYTE VibratoSweep;
BYTE VibratoRate;
BYTE VibratoDepth;
BYTE Modes;
SWORD ScaleFrequency;
WORD ScaleFactor; /* From 0 to 2048 or 0 to 2 */
BYTE Reserved[PATCH_DATA_RESERVED_SIZE];
} GCC_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef GCC_PACKED
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_DLS,
INST_SF2
};
struct Instrument
@ -344,7 +308,6 @@ struct Instrument
Instrument();
~Instrument();
int type;
int samples;
Sample *sample;
};
@ -356,11 +319,10 @@ struct ToneBankElement
{}
FString name;
int note, amp, pan, strip_loop, strip_envelope, strip_tail;
int note, amp, pan, fontbank, fontpreset, fontnote, strip_loop, strip_envelope, strip_tail;
};
/* A hack to delay instrument loading until after reading the
entire MIDI file. */
/* A hack to delay instrument loading until after reading the entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1))
enum
@ -381,7 +343,34 @@ struct ToneBank
#define SPECIAL_PROGRAM -1
extern void pcmap(int *b, int *v, int *p, int *drums);
/*
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
@ -446,9 +435,9 @@ struct Channel
bank, program, sustain, pitchbend,
mono, /* one note only on this channel */
pitchsens;
WORD
BYTE
volume, expression;
SWORD
SBYTE
panning;
WORD
rpn, nrpn;
@ -456,13 +445,81 @@ struct Channel
nrpn_mode;
float
pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
float
left_offset, right_offset; /* precomputed panning values */
};
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
struct MinEnvelope
{
int 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
@ -472,11 +529,12 @@ struct Voice
orig_frequency, frequency;
int
sample_offset, sample_increment,
envelope_volume, envelope_target, envelope_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
@ -487,8 +545,10 @@ struct Voice
vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
envelope_stage, control_counter;
control_counter;
int
sample_count;
};
/* Voice status options: */
@ -506,12 +566,23 @@ enum
/* Envelope stages: */
enum
{
ATTACK,
HOLD,
DECAY,
RELEASE,
RELEASEB,
RELEASEC
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))))
@ -527,10 +598,16 @@ 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 calc_vol(x) (pow(2.0,((x)*6.0 - 6.0))) // Physically ideal equation
#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
#define cb_to_amp(x) (pow(10.0, (x) * (1 / -200.0))) // centibels to amp
#define db_to_amp(x) (pow(10.0, (x) * (1 / -20.0))) // decibels to map
/*
timidity.h
@ -582,7 +659,8 @@ struct Renderer
void kill_key_group(int voice);
float calculate_scaled_frequency(Sample *sample, int note);
void start_note(int chan, int note, int vel, int voice);
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);
@ -598,7 +676,7 @@ struct Renderer
void reset_controllers(int chan);
void reset_midi();
void select_sample(int voice, Instrument *instr, int vel);
int allocate_voice();
void kill_note(int voice);
void finish_note(int voice);
@ -608,7 +686,7 @@ struct Renderer
void DataEntryCoarseNRPN(int chan, int nrpn, int val);
void DataEntryFineNRPN(int chan, int nrpn, int val);
static void compute_pan(int panning, float &left_offset, float &right_offset);
static void compute_pan(double panning, int type, float &left_offset, float &right_offset);
};
}

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8,00"
Version="8.00"
Name="zdoom"
ProjectGUID="{8049475B-5C87-46F9-9358-635218A4EF18}"
RootNamespace=" zdoom"
@ -135,112 +135,6 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
>
<Tool
Name="VCPreBuildEventTool"
Description="Checking svnrevision.h..."
CommandLine="$(OutDir)\updaterevision.exe src src/svnrevision.h"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="true"
SuppressStartupBanner="true"
TargetEnvironment="1"
TypeLibraryName=".\Debug/zdoom.tlb"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;jpeg-6b;snes_spc\snes_spc"
PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,USEASM,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH"
MinimalRebuild="true"
RuntimeLibrary="1"
EnableFunctionLevelLinking="true"
ForceConformanceInForLoopScope="true"
PrecompiledHeaderFile=""
AssemblerOutput="0"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
CompileAs="0"
DisableSpecificWarnings="4996"
ForcedIncludeFiles=""
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="gdi32.lib user32.lib comctl32.lib shell32.lib advapi32.lib comdlg32.lib ole32.lib dxguid.lib dsound.lib dinput8.lib strmiids.lib wsock32.lib winmm.lib fmodex_vc.lib setupapi.lib ws2_32.lib"
OutputFile="../zdoomd.exe"
LinkIncremental="2"
SuppressStartupBanner="true"
AdditionalLibraryDirectories=""
IgnoreDefaultLibraryNames="libcmt;msvcrtd;msvcrt"
GenerateDebugInformation="true"
ProgramDatabaseFile=".\Debug/zdoomd.pdb"
SubSystem="2"
StackReserveSize="0"
TerminalServerAware="2"
SetChecksum="false"
TargetMachine="0"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
@ -352,6 +246,112 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
>
<Tool
Name="VCPreBuildEventTool"
Description="Checking svnrevision.h..."
CommandLine="$(OutDir)\updaterevision.exe src src/svnrevision.h"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
PreprocessorDefinitions="_DEBUG"
MkTypLibCompatible="true"
SuppressStartupBanner="true"
TargetEnvironment="1"
TypeLibraryName=".\Debug/zdoom.tlb"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="src\win32;src\sound;src;zlib;src\g_shared;src\g_doom;src\g_raven;src\g_heretic;src\g_hexen;src\g_strife;jpeg-6b;snes_spc\snes_spc"
PreprocessorDefinitions="WIN32,_DEBUG,_WIN32,_WINDOWS,USEASM,_CRTDBG_MAP_ALLOC,HAVE_STRUPR,HAVE_FILELENGTH"
MinimalRebuild="true"
RuntimeLibrary="1"
EnableFunctionLevelLinking="true"
ForceConformanceInForLoopScope="true"
PrecompiledHeaderFile=""
AssemblerOutput="0"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
CompileAs="0"
DisableSpecificWarnings="4996"
ForcedIncludeFiles=""
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalOptions="/MACHINE:I386"
AdditionalDependencies="gdi32.lib user32.lib comctl32.lib shell32.lib advapi32.lib comdlg32.lib ole32.lib dxguid.lib dsound.lib dinput8.lib strmiids.lib wsock32.lib winmm.lib fmodex_vc.lib setupapi.lib ws2_32.lib"
OutputFile="../zdoomd.exe"
LinkIncremental="2"
SuppressStartupBanner="true"
AdditionalLibraryDirectories=""
IgnoreDefaultLibraryNames="libcmt;msvcrtd;msvcrt"
GenerateDebugInformation="true"
ProgramDatabaseFile=".\Debug/zdoomd.pdb"
SubSystem="2"
StackReserveSize="0"
TerminalServerAware="2"
SetChecksum="false"
TargetMachine="0"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(PlatformName)\$(ConfigurationName)"
@ -926,16 +926,6 @@
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Creating $(InputName).h from src/$(InputFileName)"
CommandLine="tools\re2c\re2c --no-generation-date -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
@ -946,6 +936,16 @@
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Creating $(InputName).h from src/$(InputFileName)"
CommandLine="tools\re2c\re2c --no-generation-date -s -o &quot;src/$(InputName).h&quot; &quot;src/$(InputFileName)&quot;&#x0D;&#x0A;"
Outputs="&quot;src/$(InputName).h&quot;"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
@ -1540,16 +1540,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
@ -1561,6 +1551,16 @@
Outputs="$(IntDir)/$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -1586,16 +1586,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
@ -1607,6 +1597,16 @@
Outputs="$(IntDir)/$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -1632,16 +1632,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
@ -1653,6 +1643,16 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -1678,16 +1678,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
@ -1699,6 +1689,16 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -1724,16 +1724,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
@ -1745,6 +1735,16 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Assembling $(InputPath)..."
CommandLine="nasm -g -o &quot;$(IntDir)\$(InputName).obj&quot; -f win32 &quot;$(InputPath)&quot;&#x0D;&#x0A;$(OutDir)\fixrtext &quot;$(IntDir)\$(InputName).obj&quot;&#x0D;&#x0A;"
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -1910,6 +1910,14 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
@ -1920,14 +1928,6 @@
Outputs="$(IntDir)\$(InputName).obj"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
Name="VCCustomBuildTool"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
ExcludedFromBuild="true"
@ -2785,6 +2785,14 @@
AdditionalIncludeDirectories="src\win32;$(NoInherit)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCResourceCompilerTool"
AdditionalIncludeDirectories="src\win32;$(NoInherit)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
@ -2794,14 +2802,6 @@
AdditionalIncludeDirectories="src\win32;$(NoInherit)"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCResourceCompilerTool"
AdditionalIncludeDirectories="src\win32;$(NoInherit)"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
@ -2952,9 +2952,32 @@
Name="Timidity"
>
<File
RelativePath=".\src\timidity\common.cpp"
RelativePath=".\src\timidity\instrum_font.cpp"
>
</File>
<Filter
Name="Docs"
>
<File
RelativePath=".\src\timidity\CHANGES"
>
</File>
<File
RelativePath=".\src\timidity\COPYING"
>
</File>
<File
RelativePath=".\src\timidity\FAQ"
>
</File>
<File
RelativePath=".\src\timidity\README"
>
</File>
</Filter>
<Filter
Name="Headers"
>
<File
RelativePath=".\src\timidity\dls1.h"
>
@ -2963,6 +2986,26 @@
RelativePath=".\src\timidity\dls2.h"
>
</File>
<File
RelativePath=".\src\timidity\gf1patch.h"
>
</File>
<File
RelativePath=".\src\timidity\sf2.h"
>
</File>
<File
RelativePath=".\src\timidity\timidity.h"
>
</File>
</Filter>
<Filter
Name="Source"
>
<File
RelativePath=".\src\timidity\common.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum.cpp"
>
@ -2971,6 +3014,10 @@
RelativePath=".\src\timidity\instrum_dls.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum_sf2.cpp"
>
</File>
<File
RelativePath=".\src\timidity\mix.cpp"
>
@ -3003,29 +3050,6 @@
RelativePath=".\src\timidity\timidity.cpp"
>
</File>
<File
RelativePath=".\src\timidity\timidity.h"
>
</File>
<Filter
Name="Docs"
>
<File
RelativePath=".\src\timidity\CHANGES"
>
</File>
<File
RelativePath=".\src\timidity\COPYING"
>
</File>
<File
RelativePath=".\src\timidity\FAQ"
>
</File>
<File
RelativePath=".\src\timidity\README"
>
</File>
</Filter>
</Filter>
</Filter>
@ -3044,7 +3068,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3052,7 +3076,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3084,7 +3108,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3092,7 +3116,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3121,7 +3145,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3130,7 +3154,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3160,7 +3184,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3168,7 +3192,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3197,7 +3221,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3206,7 +3230,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3237,7 +3261,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3246,7 +3270,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3276,7 +3300,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3284,7 +3308,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3313,7 +3337,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3322,7 +3346,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3353,7 +3377,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3362,7 +3386,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3393,7 +3417,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3402,7 +3426,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3432,7 +3456,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3440,7 +3464,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3468,7 +3492,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3476,7 +3500,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3504,7 +3528,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3512,7 +3536,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3540,7 +3564,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3548,7 +3572,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3578,7 +3602,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3588,7 +3612,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool
@ -3632,7 +3656,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
Name="Release|x64"
ExcludedFromBuild="true"
>
<Tool
@ -3640,7 +3664,7 @@
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
Name="Debug|Win32"
ExcludedFromBuild="true"
>
<Tool