- Added a new cvar: midi_timiditylike. This re-enables TiMidity handling of

GUS patch flags, envelopes, and volume levels, while trying to be closer
  to TiMidity++ than original TiMidity.
- Renamed timidity_config and timidity_voices to midi_config and midi_voices
  respectively.


SVN r959 (trunk)
This commit is contained in:
Randy Heit 2008-05-10 01:35:50 +00:00
parent e64586d86f
commit d4563767ee
7 changed files with 156 additions and 64 deletions

View file

@ -1,3 +1,10 @@
May 9, 2008
- Added a new cvar: midi_timiditylike. This re-enables TiMidity handling of
GUS patch flags, envelopes, and volume levels, while trying to be closer
to TiMidity++ than original TiMidity.
- Renamed timidity_config and timidity_voices to midi_config and midi_voices
respectively.
May 8, 2008
- Reduced volume, expression, and panning controllers back to 7 bits.
- Added very basic Soundfont support to the internal TiMidity. Things missing:

View file

@ -362,53 +362,58 @@ fail:
if (header.Description[j] != 0)
break;
}
if (j == DESC_SIZE)
/* Strip any loops and envelopes we're permitted to */
/* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */
if ((strip_loop == 1) &&
(sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
{
/* Strip any loops and envelopes we're permitted to */
/* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */
if ((strip_loop == 1) &&
(sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
if (j == DESC_SIZE)
{
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD);
}
sp->modes |= PATCH_T_NO_LOOP;
}
if (strip_envelope == 1)
if (strip_envelope == 1)
{
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
/* [RH] The envelope isn't really removed, but this is the way the standard
* Gravis patches get that effect: All rates at maximum, and all offsets at
* a constant level.
*/
if (j == DESC_SIZE)
{
cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
/* [RH] The envelope isn't really removed, but this is the way the standard
* Gravis patches get that effect: All rates at maximum, and all offsets at
* a constant level.
*/
for (j = 1; j < ENVELOPES; ++j)
int k;
for (k = 1; k < ENVELOPES; ++k)
{ /* Find highest offset. */
if (patch_data.EnvelopeOffset[j] > patch_data.EnvelopeOffset[0])
if (patch_data.EnvelopeOffset[k] > patch_data.EnvelopeOffset[0])
{
patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[j];
patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[k];
}
}
for (j = 0; j < ENVELOPES; ++j)
for (k = 0; k < ENVELOPES; ++k)
{
patch_data.EnvelopeRate[j] = 63;
patch_data.EnvelopeOffset[j] = patch_data.EnvelopeOffset[0];
patch_data.EnvelopeRate[k] = 63;
patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0];
}
}
sp->modes |= PATCH_T_NO_ENVELOPE;
}
#if 0
else if (strip_envelope != 0)
{
/* Have to make a guess. */
if (!(sp->modes & (PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
{
/* No loop? Then what's there to sustain? No envelope needed either... */
sp->modes &= ~(PATCH_SUSTAIN | PATCH_NO_SRELEASE);
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - No loop, removing sustain and envelope\n");
}
else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[RELEASEC] >= 100)
else if (memcmp(patch_data.EnvelopeRate, "??????", 6) == 0 || patch_data.EnvelopeOffset[GF1_RELEASEC] >= 100)
{
/* Envelope rates all maxed out? Envelope end at a high "offset"?
That's a weird envelope. Take it out. */
sp->modes &= ~PATCH_NO_SRELEASE;
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - Weirdness, removing envelope\n");
}
else if (!(sp->modes & PATCH_SUSTAIN))
@ -417,11 +422,14 @@ fail:
justified, but patches without sustain usually don't need the
envelope either... at least the Gravis ones. They're mostly
drums. I think. */
sp->modes &= ~PATCH_NO_SRELEASE;
sp->modes |= PATCH_T_NO_ENVELOPE;
cmsg(CMSG_INFO, VERB_DEBUG, " - No sustain, removing envelope\n");
}
}
#endif
if (!(sp->modes & PATCH_NO_SRELEASE))
{ // TiMidity thinks that this is an envelope enable flag.
sp->modes |= PATCH_T_NO_ENVELOPE;
}
for (j = 0; j < 6; j++)
{
@ -468,10 +476,10 @@ fail:
}
else
{
#if defined(ADJUST_SAMPLE_VOLUMES)
/* Try to determine a volume scaling factor for the sample.
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option. */
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option.
(This is ignored unless midi_timiditylike is turned on.) */
int i;
sample_t maxamp = 0, a;
sample_t *tmp;
@ -483,9 +491,6 @@ fail:
}
sp->volume = 1 / maxamp;
cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f\n", sp->volume);
#else
sp->volume = 1;
#endif
}
/* Then fractional samples */

View file

@ -27,6 +27,9 @@
#include "timidity.h"
#include "templates.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
@ -75,7 +78,16 @@ void GF1Envelope::Init(Renderer *song, Voice *v)
void GF1Envelope::Release(Voice *v)
{
if (!(v->sample->modes & PATCH_NO_SRELEASE) || (v->sample->modes & PATCH_FAST_REL))
if (midi_timiditylike)
{
if (!(v->sample->modes & PATCH_T_NO_ENVELOPE))
{
stage = GF1_RELEASE;
Recompute(v);
}
// else ... loop was already turned off by the caller
}
else 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;
@ -96,22 +108,31 @@ void GF1Envelope::Release(Voice *v)
/* Returns 1 if envelope runs out */
bool GF1Envelope::Recompute(Voice *v)
{
int oldstage;
int newstage;
oldstage = stage;
newstage = stage;
if (oldstage > GF1_RELEASEC)
if (newstage > GF1_RELEASEC)
{
/* Envelope ran out. */
/* play sampled release */
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
increment = 0;
bUpdating = false;
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
if (midi_timiditylike)
{ /* kill the voice */
v->status |= VOICE_STOPPING;
return 1;
}
else
{ /* play sampled release */
}
return 0;
}
if (oldstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) && (v->sample->modes & PATCH_SUSTAIN))
if (newstage == GF1_RELEASE && !(v->status & VOICE_RELEASING) &&
((!midi_timiditylike && (v->sample->modes & PATCH_SUSTAIN)) ||
(midi_timiditylike && !(v->sample->modes & PATCH_T_NO_ENVELOPE))))
{
v->status |= VOICE_SUSTAINING;
/* Freeze envelope until note turns off. Trumpets want this. */
@ -120,14 +141,14 @@ bool GF1Envelope::Recompute(Voice *v)
}
else
{
stage = oldstage + 1;
stage = newstage + 1;
if (volume == offset[oldstage])
if (volume == offset[newstage])
{
return Recompute(v);
}
target = offset[oldstage];
increment = rate[oldstage];
target = offset[newstage];
increment = rate[newstage];
if (target < volume)
increment = -increment;
}
@ -137,6 +158,10 @@ bool GF1Envelope::Recompute(Voice *v)
bool GF1Envelope::Update(Voice *v)
{
if (midi_timiditylike && (v->sample->modes & PATCH_T_NO_ENVELOPE))
{
return 0;
}
volume += increment;
if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target)))
{
@ -152,14 +177,39 @@ bool GF1Envelope::Update(Voice *v)
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;
double final_amp;
if (midi_timiditylike)
{
final_amp = v->sample->volume * FINAL_MIX_TIMIDITY_SCALE;
if (v->tremolo_phase_increment != 0)
{
env_vol *= v->tremolo_volume;
}
if (!(v->sample->modes & PATCH_T_NO_ENVELOPE))
{
if (stage > GF1_ATTACK)
{
env_vol *= pow(2.0, volume * (6.0 / (1 << 30)) - 6.0);
}
else
{
env_vol *= volume / float(1 << 30);
}
}
}
env_vol *= volume / float(1 << 30);
env_vol = calc_gf1_amp(env_vol) * final_amp;
else
{
final_amp = 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);
}
env_vol *= final_amp;
v->left_mix = float(env_vol * v->left_offset);
v->right_mix = float(env_vol * v->right_offset);
}

View file

@ -27,6 +27,9 @@
#include <math.h>
#include "timidity.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
@ -161,7 +164,16 @@ void Renderer::recompute_amp(Voice *v)
if (v->sample->type == INST_GUS)
{
v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
if (midi_timiditylike)
{
v->attenuation = float(timidityxx_perceived_vol(v->velocity / 127.0) *
timidityxx_perceived_vol(chanvol / 127.0) *
timidityxx_perceived_vol(chanexpr / 127.0));
}
else
{
v->attenuation = (vol_table[(chanvol * chanexpr) / 127] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
}
}
else
{
@ -190,7 +202,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ
}
else
{
if (type == INST_GUS)
if (type == INST_GUS && !midi_timiditylike)
{
/* Original amp equation looks like this:
* calc_gf1_amp(atten + offset)
@ -209,8 +221,12 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ
}
else
{
left_offset = (float)db_to_amp(-20 * log10(sqrt(1 - pan)));
right_offset = (float)db_to_amp(-20 * log10(sqrt(pan)));
/* I have no idea what equation, if any, will reproduce the sc_pan_table
* that TiMidity++ uses, so midi_timiditylike gets the same Equal Power
* Panning as SF2/DLS.
*/
left_offset = (float)sqrt(1 - pan);
right_offset = (float)sqrt(pan);
}
}
}
@ -548,7 +564,7 @@ void Renderer::finish_note(int i)
v->status &= ~VOICE_SUSTAINING;
v->status |= VOICE_RELEASING;
if (!(v->sample->modes & PATCH_NO_SRELEASE))
if (!(v->sample->modes & PATCH_NO_SRELEASE) || midi_timiditylike)
{
v->status &= ~VOICE_LPE; /* sampled release */
}

View file

@ -26,6 +26,9 @@
#include <malloc.h>
#include "timidity.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity
{
@ -484,7 +487,7 @@ static sample_t *rs_vib_bidir(sample_t *resample_buffer, float rate, Voice *vp,
sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
{
int ofs;
BYTE modes;
WORD modes;
if (vp->sample->sample_rate == 0)
{
@ -519,7 +522,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
if (vp->vibrato_control_ratio)
{
if (vp->status & VOICE_LPE)
if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP))
{
if (modes & PATCH_BIDIR)
return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr);
@ -533,7 +536,7 @@ sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
}
else
{
if (vp->status & VOICE_LPE)
if (vp->status & VOICE_LPE && !(midi_timiditylike && vp->sample->modes & PATCH_T_NO_LOOP))
{
if (modes & PATCH_BIDIR)
return rs_bidir(song->resample_buffer, vp, *countptr);

View file

@ -32,8 +32,9 @@
#include "i_system.h"
#include "files.h"
CVAR(String, timidity_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, timidity_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(String, midi_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, midi_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, midi_timiditylike, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
namespace Timidity
{
@ -551,7 +552,7 @@ int LoadConfig(const char *filename)
int LoadConfig()
{
return LoadConfig(timidity_config);
return LoadConfig(midi_config);
}
Renderer::Renderer(float sample_rate)
@ -573,7 +574,7 @@ Renderer::Renderer(float sample_rate)
if (def_instr_name.IsNotEmpty())
set_default_instrument(def_instr_name);
voices = clamp<int>(timidity_voices, 16, 256);
voices = clamp<int>(midi_voices, 16, 256);
voice = new Voice[voices];
drumchannels = DEFAULT_DRUMCHANNELS;
}

View file

@ -53,7 +53,11 @@ config.h
/* 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.5f
#define FINAL_MIX_SCALE 0.5
/* This value is used instead when midi_timiditylike is turned on,
because TiMidity++ is louder than a GUS. */
#define FINAL_MIX_TIMIDITY_SCALE 0.3
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
@ -214,7 +218,10 @@ enum
PATCH_BACKWARD = (1<<4),
PATCH_SUSTAIN = (1<<5),
PATCH_NO_SRELEASE = (1<<6),
PATCH_FAST_REL = (1<<7)
PATCH_FAST_REL = (1<<7),
PATCH_T_NO_ENVELOPE = (1<<8),
PATCH_T_NO_LOOP = (1<<9)
};
struct Sample
@ -249,7 +256,9 @@ struct Sample
BYTE
tremolo_depth, vibrato_depth,
low_vel, high_vel,
modes, type;
type;
WORD
modes;
SWORD
panning;
WORD
@ -608,6 +617,7 @@ const double log_of_2 = 0.69314718055994529;
#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
#define timidityxx_perceived_vol(x) (pow((x), 1.66096404744))
/*
timidity.h