mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-15 16:51:31 +00:00
- 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:
parent
e64586d86f
commit
d4563767ee
7 changed files with 156 additions and 64 deletions
|
@ -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:
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue