- 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 May 8, 2008
- Reduced volume, expression, and panning controllers back to 7 bits. - Reduced volume, expression, and panning controllers back to 7 bits.
- Added very basic Soundfont support to the internal TiMidity. Things missing: - Added very basic Soundfont support to the internal TiMidity. Things missing:

View file

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

View file

@ -27,6 +27,9 @@
#include "timidity.h" #include "timidity.h"
#include "templates.h" #include "templates.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity namespace Timidity
{ {
@ -75,7 +78,16 @@ void GF1Envelope::Init(Renderer *song, Voice *v)
void GF1Envelope::Release(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 */ /* ramp out to minimum volume with rate from final release stage */
stage = GF1_RELEASEC+1; stage = GF1_RELEASEC+1;
@ -96,22 +108,31 @@ void GF1Envelope::Release(Voice *v)
/* Returns 1 if envelope runs out */ /* Returns 1 if envelope runs out */
bool GF1Envelope::Recompute(Voice *v) bool GF1Envelope::Recompute(Voice *v)
{ {
int oldstage; int newstage;
oldstage = stage; newstage = stage;
if (oldstage > GF1_RELEASEC) if (newstage > GF1_RELEASEC)
{ {
/* Envelope ran out. */ /* Envelope ran out. */
/* play sampled release */
v->status &= ~(VOICE_SUSTAINING | VOICE_LPE);
v->status |= VOICE_RELEASING;
increment = 0; increment = 0;
bUpdating = false; 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; 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; v->status |= VOICE_SUSTAINING;
/* Freeze envelope until note turns off. Trumpets want this. */ /* Freeze envelope until note turns off. Trumpets want this. */
@ -120,14 +141,14 @@ bool GF1Envelope::Recompute(Voice *v)
} }
else else
{ {
stage = oldstage + 1; stage = newstage + 1;
if (volume == offset[oldstage]) if (volume == offset[newstage])
{ {
return Recompute(v); return Recompute(v);
} }
target = offset[oldstage]; target = offset[newstage];
increment = rate[oldstage]; increment = rate[newstage];
if (target < volume) if (target < volume)
increment = -increment; increment = -increment;
} }
@ -137,6 +158,10 @@ bool GF1Envelope::Recompute(Voice *v)
bool GF1Envelope::Update(Voice *v) bool GF1Envelope::Update(Voice *v)
{ {
if (midi_timiditylike && (v->sample->modes & PATCH_T_NO_ENVELOPE))
{
return 0;
}
volume += increment; volume += increment;
if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target))) if (((increment < 0) && (volume <= target)) || ((increment > 0) && (volume >= target)))
{ {
@ -152,14 +177,39 @@ bool GF1Envelope::Update(Voice *v)
void GF1Envelope::ApplyToAmp(Voice *v) void GF1Envelope::ApplyToAmp(Voice *v)
{ {
double env_vol = v->attenuation; double env_vol = v->attenuation;
double final_amp = v->sample->volume * FINAL_MIX_SCALE; double final_amp;
if (v->tremolo_phase_increment != 0)
{ // [RH] FIXME: This is wrong. Tremolo should offset the if (midi_timiditylike)
// envelope volume, not scale it. {
env_vol *= v->tremolo_volume; 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); else
env_vol = calc_gf1_amp(env_vol) * final_amp; {
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->left_mix = float(env_vol * v->left_offset);
v->right_mix = float(env_vol * v->right_offset); v->right_mix = float(env_vol * v->right_offset);
} }

View file

@ -27,6 +27,9 @@
#include <math.h> #include <math.h>
#include "timidity.h" #include "timidity.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, midi_timiditylike)
namespace Timidity namespace Timidity
{ {
@ -161,7 +164,16 @@ void Renderer::recompute_amp(Voice *v)
if (v->sample->type == INST_GUS) 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 else
{ {
@ -190,7 +202,7 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ
} }
else else
{ {
if (type == INST_GUS) if (type == INST_GUS && !midi_timiditylike)
{ {
/* Original amp equation looks like this: /* Original amp equation looks like this:
* calc_gf1_amp(atten + offset) * calc_gf1_amp(atten + offset)
@ -209,8 +221,12 @@ void Renderer::compute_pan(double pan, int type, float &left_offset, float &righ
} }
else else
{ {
left_offset = (float)db_to_amp(-20 * log10(sqrt(1 - pan))); /* I have no idea what equation, if any, will reproduce the sc_pan_table
right_offset = (float)db_to_amp(-20 * log10(sqrt(pan))); * 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_SUSTAINING;
v->status |= VOICE_RELEASING; 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 */ v->status &= ~VOICE_LPE; /* sampled release */
} }

View file

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

View file

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

View file

@ -53,7 +53,11 @@ config.h
/* A scalar applied to the final mix to try and approximate the /* A scalar applied to the final mix to try and approximate the
volume level of FMOD's built-in MIDI player. */ 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. /* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit This affects tonal accuracy. The entire position counter must fit
@ -214,7 +218,10 @@ enum
PATCH_BACKWARD = (1<<4), PATCH_BACKWARD = (1<<4),
PATCH_SUSTAIN = (1<<5), PATCH_SUSTAIN = (1<<5),
PATCH_NO_SRELEASE = (1<<6), 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 struct Sample
@ -249,7 +256,9 @@ struct Sample
BYTE BYTE
tremolo_depth, vibrato_depth, tremolo_depth, vibrato_depth,
low_vel, high_vel, low_vel, high_vel,
modes, type; type;
WORD
modes;
SWORD SWORD
panning; panning;
WORD 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 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 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 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 timidity.h