/* TiMidity -- Experimental MIDI to WAVE converter Copyright (C) 1995 Tuukka Toivonen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA mix.c */ #include #include #include #include "timidity.h" #include "templates.h" #include "c_cvars.h" EXTERN_CVAR(Bool, midi_timiditylike) namespace Timidity { static 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; } 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 (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; 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 newstage; newstage = stage; if (newstage > GF1_RELEASEC) { /* Envelope ran out. */ increment = 0; bUpdating = false; v->status &= ~(VOICE_SUSTAINING | VOICE_LPE); v->status |= VOICE_RELEASING; if (midi_timiditylike) { /* kill the voice ... or not */ if (volume <= 0) { v->status |= VOICE_STOPPING; } return 1; } else { /* play sampled release */ } return 0; } 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. */ increment = 0; bUpdating = false; } else { stage = newstage + 1; if (volume == offset[newstage]) { return Recompute(v); } target = offset[newstage]; increment = rate[newstage]; if (target < volume) increment = -increment; } return 0; } 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))) { volume = target; if (Recompute(v)) { return 1; } } return 0; } void GF1Envelope::ApplyToAmp(Voice *v) { double env_vol = v->attenuation; 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); } } } 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); } 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 = 0; 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; if (v->tremolo_sweep != 0) { /* Update sweep position */ v->tremolo_sweep_position += v->tremolo_sweep; if (v->tremolo_sweep_position >= (1 << SWEEP_SHIFT)) { /* Swept to max amplitude */ v->tremolo_sweep = 0; } else { /* Need to adjust depth */ depth *= v->tremolo_sweep_position; depth >>= SWEEP_SHIFT; } } v->tremolo_phase += v->tremolo_phase_increment; v->tremolo_volume = (float) (1.0 - FSCALENEG((sine(v->tremolo_phase >> RATE_SHIFT) + 1.0) * depth * TREMOLO_AMPLITUDE_TUNING, 17)); /* I'm not sure about the +1.0 there -- it makes tremoloed voices' volumes on average the lower the higher the tremolo amplitude. */ } /* Returns 1 if the note died */ static int update_signal(Voice *v) { if (v->eg1.env.bUpdating && v->eg1.Update(v)) { return 1; } if (v->tremolo_phase_increment != 0) { update_tremolo(v); } apply_envelope_to_amp(v); return 0; } static void mix_mystery_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix, right = v->right_mix; int cc; sample_t s; if (!(cc = v->control_counter)) { cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ left = v->left_mix; right = v->right_mix; } while (count) { if (cc < count) { count -= cc; while (cc--) { s = *sp++; lp[0] += left * s; lp[1] += right * s; lp += 2; } cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ left = v->left_mix; right = v->right_mix; } else { v->control_counter = cc - count; while (count--) { s = *sp++; lp[0] += left * s; lp[1] += right * s; lp += 2; } return; } } } static void mix_single_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, float *ampat, int count) { final_volume_t amp; int cc; if (0 == (cc = v->control_counter)) { cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ } amp = *ampat; while (count) { if (cc < count) { count -= cc; while (cc--) { lp[0] += *sp++ * amp; lp += 2; } cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ amp = *ampat; } else { v->control_counter = cc - count; while (count--) { lp[0] += *sp++ * amp; lp += 2; } return; } } } static void mix_single_left_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { mix_single_signal(control_ratio, sp, lp, v, &v->left_mix, count); } static void mix_single_right_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { mix_single_signal(control_ratio, sp, lp + 1, v, &v->right_mix, count); } static void mix_mono_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix; int cc; if (!(cc = v->control_counter)) { cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ left = v->left_mix; } while (count) { if (cc < count) { count -= cc; while (cc--) { *lp++ += *sp++ * left; } cc = control_ratio; if (update_signal(v)) return; /* Envelope ran out */ left = v->left_mix; } else { v->control_counter = cc - count; while (count--) { *lp++ += *sp++ * left; } return; } } } static void mix_mystery(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix, right = v->right_mix; sample_t s; while (count--) { s = *sp++; lp[0] += s * left; lp[1] += s * right; lp += 2; } } static void mix_single(const sample_t *sp, float *lp, final_volume_t amp, int count) { while (count--) { lp[0] += *sp++ * amp; lp += 2; } } static void mix_single_left(const sample_t *sp, float *lp, Voice *v, int count) { mix_single(sp, lp, v->left_mix, count); } static void mix_single_right(const sample_t *sp, float *lp, Voice *v, int count) { mix_single(sp, lp + 1, v->right_mix, count); } static void mix_mono(const sample_t *sp, float *lp, Voice *v, int count) { final_volume_t left = v->left_mix; while (count--) { *lp++ += *sp++ * left; } } /* Ramp a note out in c samples */ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c) { final_volume_t left, right, li, ri; sample_t s = 0; /* silly warning about uninitialized s */ /* Fix by James Caldwell */ if ( c == 0 ) c = 1; /* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */ if (v->right_mix == 0) // All the way to the left { left = v->left_mix; li = -(left/c); if (li == 0) li = -1; while (c--) { left += li; if (left < 0) return; lp[0] += *sp++ * left; lp += 2; } } else if (v->left_mix == 0) // All the way to the right { right = v->right_mix; ri = -(right/c); if (ri == 0) ri = -1; while (c--) { right += ri; if (right < 0) return; s = *sp++; lp[1] += *sp++ * right; lp += 2; } } else // Somewhere in the middle { left = v->left_mix; li = -(left/c); if (li == 0) li = -1; right = v->right_mix; ri = -(right/c); if (ri == 0) ri = -1; right = v->right_mix; ri = -(right/c); while (c--) { left += li; right += ri; if (left < 0) { if (right < 0) { return; } left = 0; } else if (right < 0) { right = 0; } s = *sp++; lp[0] += s * left; lp[1] += s * right; lp += 2; } } } /**************** interface function ******************/ void mix_voice(Renderer *song, float *buf, Voice *v, int c) { int count = c; sample_t *sp; if (c < 0) { return; } if (v->status & VOICE_STOPPING) { if (count >= MAX_DIE_TIME) count = MAX_DIE_TIME; sp = resample_voice(song, v, &count); ramp_out(sp, buf, v, count); v->status = 0; } else { sp = resample_voice(song, v, &count); if (count < 0) { return; } if (v->right_mix == 0) // All the way to the left { if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0) { mix_single_left_signal(song->control_ratio, sp, buf, v, count); } else { mix_single_left(sp, buf, v, count); } } else if (v->left_mix == 0) // All the way to the right { if (v->eg1.env.bUpdating || v->tremolo_phase_increment != 0) { mix_single_right_signal(song->control_ratio, sp, buf, v, count); } else { mix_single_right(sp, buf, v, count); } } else // Somewhere in the middle { if (v->eg1.env.bUpdating || v->tremolo_phase_increment) { mix_mystery_signal(song->control_ratio, sp, buf, v, count); } else { mix_mystery(song->control_ratio, sp, buf, v, count); } } v->sample_count += count; } } }