- Fixed: When note_on() is called and another copy of the same note is

already playing on the channel, it should stop it with finish_note(), not
  kill_note(). This can be clearly heard in the final cymbal crashes of
  D_DM2TTL where TiMidity cuts them off because the final cymbals are played
  with a velocity of 1 before the preceding cymbals have finished. (I wonder
  if I should be setting the self_nonexclusive flag for GUS patches to
  disable even this behavior, though, since gf1note.c doesn't turn off
  duplicate notes.)
- Changed envelope handling to hopefully match the GUS player's. The most
  egregious mistake TiMidity makes is to treat bit 6 as an envelope enable
  bit. This is not what it does; every sample has an envelope. Rather, this
  is a "no sampled release" flag. Also, despite fiddling with the
  PATCH_SUSTAIN flag during instrument loading, TiMidity never actually
  used it. Nor did it do anything at all with the PATCH_FAST_REL flag.


SVN r934 (trunk)
This commit is contained in:
Randy Heit 2008-04-24 04:18:49 +00:00
commit 16d18c707a
8 changed files with 174 additions and 141 deletions

View file

@ -37,7 +37,7 @@ void Renderer::reset_voices()
{
for (int i = 0; i < voices; i++)
{
voice[i].status = VOICE_FREE;
voice[i].status = 0;
}
}
@ -254,7 +254,7 @@ void Renderer::kill_key_group(int i)
}
while (j--)
{
if (voice[j].status != VOICE_ON && voice[j].status != VOICE_SUSTAINED) continue;
if ((voice[j].status & VOICE_RUNNING) && !(voice[j].status & (VOICE_RELEASING | VOICE_STOPPING))) continue;
if (i == j) continue;
if (voice[i].channel != voice[j].channel) continue;
if (voice[j].sample->key_group != voice[i].sample->key_group) continue;
@ -273,6 +273,7 @@ void Renderer::start_note(int chan, int note, int vel, int i)
Instrument *ip;
int bank = channel[chan].bank;
int prog = channel[chan].program;
Voice *v = &voice[i];
if (ISDRUMCHANNEL(chan))
{
@ -301,66 +302,71 @@ void Renderer::start_note(int chan, int note, int vel, int i)
}
if (ip->sample->scale_factor != 1024)
{
voice[i].orig_frequency = calculate_scaled_frequency(ip->sample, note & 0x7F);
v->orig_frequency = calculate_scaled_frequency(ip->sample, note & 0x7F);
}
else
{
voice[i].orig_frequency = note_to_freq(note & 0x7F);
v->orig_frequency = note_to_freq(note & 0x7F);
}
select_sample(i, ip, vel);
voice[i].status = VOICE_ON;
voice[i].channel = chan;
voice[i].note = note;
voice[i].velocity = vel;
voice[i].sample_offset = 0;
voice[i].sample_increment = 0; /* make sure it isn't negative */
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 */
voice[i].tremolo_phase = 0;
voice[i].tremolo_phase_increment = voice[i].sample->tremolo_phase_increment;
voice[i].tremolo_sweep = voice[i].sample->tremolo_sweep_increment;
voice[i].tremolo_sweep_position = 0;
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;
voice[i].vibrato_sweep = voice[i].sample->vibrato_sweep_increment;
voice[i].vibrato_sweep_position = 0;
voice[i].vibrato_control_ratio = voice[i].sample->vibrato_control_ratio;
voice[i].vibrato_control_counter = voice[i].vibrato_phase = 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(voice[i].vibrato_sample_increment, 0, sizeof(voice[i].vibrato_sample_increment));
memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment));
if (channel[chan].panning != NO_PANNING)
{
voice[i].left_offset = channel[chan].left_offset;
voice[i].right_offset = channel[chan].right_offset;
v->left_offset = channel[chan].left_offset;
v->right_offset = channel[chan].right_offset;
}
else
{
voice[i].left_offset = voice[i].sample->left_offset;
voice[i].right_offset = voice[i].sample->right_offset;
v->left_offset = v->sample->left_offset;
v->right_offset = v->sample->right_offset;
}
recompute_freq(i);
recompute_amp(&voice[i]);
if (voice[i].sample->modes & PATCH_NO_SRELEASE)
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)
{
/* Ramp up from 0 */
voice[i].envelope_stage = ATTACK;
voice[i].envelope_volume = 0;
voice[i].control_counter = 0;
recompute_envelope(&voice[i]);
v->status |= VOICE_LPE;
}
else
{
voice[i].envelope_increment = 0;
}
apply_envelope_to_amp(&voice[i]);
}
void Renderer::kill_note(int i)
{
voice[i].status = VOICE_DIE;
Voice *v = &voice[i];
if (v->status & VOICE_RUNNING)
{
v->status &= ~VOICE_SUSTAINING;
v->status |= VOICE_RELEASING | VOICE_STOPPING;
}
}
/* Only one instance of a note can be playing on a single channel. */
@ -377,13 +383,20 @@ void Renderer::note_on(int chan, int note, int vel)
while (i--)
{
if (voice[i].status == VOICE_FREE)
if (!(voice[i].status & VOICE_RUNNING))
{
lowest = 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))
{
kill_note(i);
if (channel[chan].mono)
{
kill_note(i);
}
else
{
finish_note(i);
}
}
}
@ -400,7 +413,7 @@ void Renderer::note_on(int chan, int note, int vel)
i = voices;
while (i--)
{
if (voice[i].status != VOICE_ON && voice[i].status != VOICE_DIE)
if ((voice[i].status & VOICE_RELEASING) && !(voice[i].status & VOICE_STOPPING))
{
v = voice[i].attenuation;
if (v < lv)
@ -420,7 +433,7 @@ void Renderer::note_on(int chan, int note, int vel)
we could use a reserve of voices to play dying notes only. */
cut_notes++;
voice[lowest].status = VOICE_FREE;
voice[lowest].status = 0;
start_note(chan, note, vel, lowest);
}
else
@ -431,44 +444,54 @@ void Renderer::note_on(int chan, int note, int vel)
void Renderer::finish_note(int i)
{
if (voice[i].sample->modes & PATCH_NO_SRELEASE)
Voice *v = &voice[i];
if ((v->status & (VOICE_RUNNING | VOICE_RELEASING)) == VOICE_RUNNING)
{
/* We need to get the envelope out of Sustain stage */
voice[i].envelope_stage = RELEASE;
voice[i].status = VOICE_OFF;
recompute_envelope(&voice[i]);
apply_envelope_to_amp(&voice[i]);
}
else
{
/* Set status to OFF so resample_voice() will let this voice out
of its loop, if any. In any case, this voice dies when it
hits the end of its data (ofs >= data_length). */
voice[i].status = VOICE_OFF;
v->status &= ~VOICE_SUSTAINING;
v->status |= VOICE_RELEASING;
if (!(v->sample->modes & PATCH_NO_SRELEASE))
{
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);
}
}
}
void Renderer::note_off(int chan, int note, int vel)
{
int i = voices;
while (i--)
int i;
for (i = voices; i-- > 0; )
{
if (voice[i].status == VOICE_ON &&
voice[i].channel == chan &&
voice[i].note == note)
if ((voice[i].status & VOICE_RUNNING) && !(voice[i].status & (VOICE_RELEASING | VOICE_STOPPING))
&& voice[i].channel == chan && voice[i].note == note)
{
if (channel[chan].sustain)
{
voice[i].status = VOICE_SUSTAINED;
voice[i].status |= NOTE_SUSTAIN;
}
else
{
finish_note(i);
}
if (!voice[i].sample->self_nonexclusive)
{
return;
}
}
}
}
@ -479,11 +502,11 @@ void Renderer::all_notes_off(int chan)
int i = voices;
while (i--)
{
if (voice[i].status == VOICE_ON && voice[i].channel == chan)
if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan)
{
if (channel[chan].sustain)
{
voice[i].status = VOICE_SUSTAINED;
voice[i].status |= NOTE_SUSTAIN;
}
else
{
@ -500,8 +523,8 @@ void Renderer::all_sounds_off(int chan)
while (i--)
{
if (voice[i].channel == chan &&
voice[i].status != VOICE_FREE &&
voice[i].status != VOICE_DIE)
(voice[i].status & VOICE_RUNNING) &&
!(voice[i].status & VOICE_STOPPING))
{
kill_note(i);
}
@ -513,7 +536,7 @@ void Renderer::adjust_pressure(int chan, int note, int amount)
int i = voices;
while (i--)
{
if (voice[i].status == VOICE_ON &&
if ((voice[i].status & VOICE_RUNNING) &&
voice[i].channel == chan &&
voice[i].note == note)
{
@ -535,8 +558,7 @@ void Renderer::adjust_panning(int chan)
int i = voices;
while (i--)
{
if ((voice[i].channel == chan) &&
(voice[i].status == VOICE_ON || voice[i].status == VOICE_SUSTAINED))
if ((voice[i].channel == chan) && (voice[i].status & VOICE_RUNNING))
{
voice[i].left_offset = chanp->left_offset;
voice[i].right_offset = chanp->right_offset;
@ -550,7 +572,7 @@ void Renderer::drop_sustain(int chan)
int i = voices;
while (i--)
{
if (voice[i].status == VOICE_SUSTAINED && voice[i].channel == chan)
if (voice[i].channel == chan && (voice[i].status & NOTE_SUSTAIN))
{
finish_note(i);
}
@ -562,7 +584,7 @@ void Renderer::adjust_pitchbend(int chan)
int i = voices;
while (i--)
{
if (voice[i].status != VOICE_FREE && voice[i].channel == chan)
if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan)
{
recompute_freq(i);
}
@ -574,8 +596,7 @@ void Renderer::adjust_volume(int chan)
int i = voices;
while (i--)
{
if (voice[i].channel == chan &&
(voice[i].status == VOICE_ON || voice[i].status == VOICE_SUSTAINED))
if (voice[i].channel == chan && (voice[i].status & VOICE_RUNNING))
{
recompute_amp(&voice[i]);
apply_envelope_to_amp(&voice[i]);