/* 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 playmidi.c -- random stuff in need of rearrangement */ #include #include #include #include #include "timidity.h" #include "c_cvars.h" EXTERN_CVAR(Bool, midi_timiditylike) namespace Timidity { void Renderer::reset_voices() { for (int i = 0; i < voices; i++) { voice[i].status = 0; } } /* Process the Reset All Controllers event */ void Renderer::reset_controllers(int c) { channel[c].volume = 100; channel[c].expression = 127; channel[c].sustain = 0; channel[c].pitchbend = 0x2000; channel[c].pitchfactor = 0; /* to be computed */ channel[c].mono = 0; channel[c].rpn = RPN_RESET; channel[c].nrpn = RPN_RESET; } void Renderer::reset_midi() { for (int i = 0; i < MAXCHAN; i++) { reset_controllers(i); /* The rest of these are unaffected by the Reset All Controllers event */ channel[i].program = default_program; channel[i].panning = NO_PANNING; channel[i].pitchsens = 200; channel[i].bank = 0; /* tone bank or drum set */ } reset_voices(); } void Renderer::recompute_freq(int v) { Channel *ch = &channel[voice[v].channel]; int sign = (voice[v].sample_increment < 0), /* for bidirectional loops */ pb = ch->pitchbend; double a; if (voice[v].sample->sample_rate == 0) { return; } if (voice[v].vibrato_control_ratio != 0) { /* This instrument has vibrato. Invalidate any precomputed sample_increments. */ memset(voice[v].vibrato_sample_increment, 0, sizeof(voice[v].vibrato_sample_increment)); } if (pb == 0x2000 || pb < 0 || pb > 0x3FFF) { voice[v].frequency = voice[v].orig_frequency; } else { pb -= 0x2000; if (ch->pitchfactor == 0) { /* Damn. Somebody bent the pitch. */ ch->pitchfactor = pow(2.f, ((abs(pb) * ch->pitchsens) / (8191.f * 1200.f))); } if (pb < 0) { voice[v].frequency = voice[v].orig_frequency / ch->pitchfactor; } else { voice[v].frequency = voice[v].orig_frequency * ch->pitchfactor; } } a = FSCALE(((double)(voice[v].sample->sample_rate) * voice[v].frequency) / ((double)(voice[v].sample->root_freq) * rate), FRACTION_BITS); if (sign) a = -a; /* need to preserve the loop direction */ voice[v].sample_increment = (int)(a); } static BYTE vol_table[] = { 000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */, 161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */, 177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */, 187 /* 012 */, 188 /* 013 */, 190 /* 014 */, 192 /* 015 */, 193 /* 016 */, 195 /* 017 */, 196 /* 018 */, 197 /* 019 */, 198 /* 020 */, 199 /* 021 */, 201 /* 022 */, 202 /* 023 */, 203 /* 024 */, 203 /* 025 */, 204 /* 026 */, 205 /* 027 */, 206 /* 028 */, 207 /* 029 */, 208 /* 030 */, 208 /* 031 */, 209 /* 032 */, 210 /* 033 */, 211 /* 034 */, 211 /* 035 */, 212 /* 036 */, 213 /* 037 */, 213 /* 038 */, 214 /* 039 */, 214 /* 040 */, 215 /* 041 */, 215 /* 042 */, 216 /* 043 */, 217 /* 044 */, 217 /* 045 */, 218 /* 046 */, 218 /* 047 */, 219 /* 048 */, 219 /* 049 */, 219 /* 050 */, 220 /* 051 */, 220 /* 052 */, 221 /* 053 */, 221 /* 054 */, 222 /* 055 */, 222 /* 056 */, 222 /* 057 */, 223 /* 058 */, 223 /* 059 */, 224 /* 060 */, 224 /* 061 */, 224 /* 062 */, 225 /* 063 */, 225 /* 064 */, 226 /* 065 */, 227 /* 066 */, 228 /* 067 */, 229 /* 068 */, 230 /* 069 */, 231 /* 070 */, 231 /* 071 */, 232 /* 072 */, 233 /* 073 */, 234 /* 074 */, 234 /* 075 */, 235 /* 076 */, 236 /* 077 */, 236 /* 078 */, 237 /* 079 */, 238 /* 080 */, 238 /* 081 */, 239 /* 082 */, 239 /* 083 */, 240 /* 084 */, 241 /* 085 */, 241 /* 086 */, 242 /* 087 */, 242 /* 088 */, 243 /* 089 */, 243 /* 090 */, 244 /* 091 */, 244 /* 092 */, 244 /* 093 */, 245 /* 094 */, 245 /* 095 */, 246 /* 096 */, 246 /* 097 */, 247 /* 098 */, 247 /* 099 */, 247 /* 100 */, 248 /* 101 */, 248 /* 102 */, 249 /* 103 */, 249 /* 104 */, 249 /* 105 */, 250 /* 106 */, 250 /* 107 */, 250 /* 108 */, 251 /* 109 */, 251 /* 110 */, 251 /* 111 */, 252 /* 112 */, 252 /* 113 */, 252 /* 114 */, 253 /* 115 */, 253 /* 116 */, 253 /* 117 */, 254 /* 118 */, 254 /* 119 */, 254 /* 120 */, 254 /* 121 */, 255 /* 122 */, 255 /* 123 */, 255 /* 124 */, 255 /* 125 */, 255 /* 126 */, 255 /* 127 */, }; void Renderer::recompute_amp(Voice *v) { Channel *chan = &channel[v->channel]; int chanvol = chan->volume; int chanexpr = chan->expression; if (v->sample->type == INST_GUS) { 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 { // Implicit modulators from SF2 spec double velatten, cc7atten, cc11atten; velatten = log10(127.0 / v->velocity); cc7atten = log10(127.0 / chanvol); cc11atten = log10(127.0 / chanexpr); v->attenuation = float(400 * (velatten + cc7atten + cc11atten)) + v->sample->initial_attenuation; } } // Pan must be in the range [0,1] void Renderer::compute_pan(double pan, int type, float &left_offset, float &right_offset) { if (pan <= 0) { left_offset = 1; right_offset = 0; } else if (pan >= 127/128.0) { left_offset = 0; right_offset = 1; } else { if (type == INST_GUS && !midi_timiditylike) { /* Original amp equation looks like this: * calc_gf1_amp(atten + offset) * which expands to: * 2^(16*(atten + offset) - 16) * Keeping in mind that 2^(x + y) == 2^x * 2^y, we can * rewrite this to avoid doing two pows in GF1Envelope::ApplyToAmp(): * 2^(16*atten + 16*offset - 16) * 2^(16*atten - 16 + 16 * offset + 16 - 16) * 2^(16*atten - 16) * 2^(16*offset + 16 - 16) * 2^(16*atten - 16) * 2^(16*(offset + 1) - 16) * calc_gf1_amp(atten) * calc_gf1_amp(offset + 1) */ right_offset = (float)calc_gf1_amp((log(pan) * (1 / (log_of_2 * 32))) + 1); left_offset = (float)calc_gf1_amp((log(1 - pan) * (1 / (log_of_2 * 32))) + 1); } else { /* 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); } } } void Renderer::kill_key_group(int i) { int j = voices; if (voice[i].sample->key_group == 0) { return; } while (j--) { 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; kill_note(j); } } float Renderer::calculate_scaled_frequency(Sample *sp, int note) { double scalednote = (note - sp->scale_note) * sp->scale_factor / 1024.0 + sp->scale_note + sp->tune * 0.01; return (float)note_to_freq(scalednote); } bool Renderer::start_region(int chan, int note, int vel, Sample *sp, float f) { int voicenum; Voice *v; voicenum = allocate_voice(); if (voicenum < 0) { return false; } v = &voice[voicenum]; v->sample = sp; if (sp->type == INST_GUS) { v->orig_frequency = f; } else { if (sp->scale_factor != 1024) { v->orig_frequency = calculate_scaled_frequency(sp, note); } else if (sp->tune != 0) { v->orig_frequency = note_to_freq(note + sp->tune * 0.01); } else { v->orig_frequency = note_to_freq(note); } } 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 */ v->sample_count = 0; v->tremolo_phase = 0; v->tremolo_phase_increment = v->sample->tremolo_phase_increment; v->tremolo_sweep = v->sample->tremolo_sweep_increment; v->tremolo_sweep_position = 0; v->vibrato_sweep = v->sample->vibrato_sweep_increment; v->vibrato_sweep_position = 0; v->vibrato_control_ratio = v->sample->vibrato_control_ratio; v->vibrato_control_counter = v->vibrato_phase = 0; kill_key_group(voicenum); memset(v->vibrato_sample_increment, 0, sizeof(v->vibrato_sample_increment)); if (sp->type == INST_SF2) { // Channel pan is added to instrument pan. double pan; if (channel[chan].panning == NO_PANNING) { pan = (sp->panning + 500) / 1000.0; } else { pan = channel[chan].panning / 128.0 + sp->panning / 1000.0; } compute_pan(pan, sp->type, v->left_offset, v->right_offset); } else if (channel[chan].panning != NO_PANNING) { compute_pan(channel[chan].panning / 128.0, sp->type, v->left_offset, v->right_offset); } else { v->left_offset = v->sample->left_offset; v->right_offset = v->sample->right_offset; } recompute_freq(voicenum); recompute_amp(v); v->control_counter = 0; v->eg1.Init(this, v); if (v->sample->modes & PATCH_LOOPEN) { v->status |= VOICE_LPE; } return true; } void Renderer::start_note(int chan, int note, int vel) { Instrument *ip; Sample *sp; int bank = channel[chan].bank; int prog = channel[chan].program; int i; float f; note &= 0x7f; if (ISDRUMCHANNEL(chan)) { if (NULL == drumset[bank] || NULL == (ip = drumset[bank]->instrument[note])) { if (!(ip = drumset[0]->instrument[note])) return; /* No instrument? Then we can't play. */ } assert(ip != MAGIC_LOAD_INSTRUMENT); if (ip == MAGIC_LOAD_INSTRUMENT) { return; } if (ip->samples != 1 && ip->sample->type == INST_GUS) { cmsg(CMSG_WARNING, VERB_VERBOSE, "Strange: percussion instrument with %d samples!", ip->samples); } } else { if (channel[chan].program == SPECIAL_PROGRAM) { ip = default_instrument; } else if (NULL == tonebank[bank] || NULL == (ip = tonebank[bank]->instrument[prog])) { if (NULL == (ip = tonebank[0]->instrument[prog])) return; /* No instrument? Then we can't play. */ } assert(ip != MAGIC_LOAD_INSTRUMENT); if (ip == MAGIC_LOAD_INSTRUMENT) { return; } } if (NULL == ip->sample || ip->samples == 0) return; /* No samples? Then nothing to play. */ // For GF1 patches, scaling is based solely on the first // waveform in this layer. if (ip->sample->type == INST_GUS && ip->sample->scale_factor != 1024) { f = calculate_scaled_frequency(ip->sample, note); } else { f = note_to_freq(note); } if (ip->sample->type == INST_GUS) { /* We're more lenient with matching ranges for GUS patches, since the * official Gravis ones don't cover the full range of possible * frequencies for every instrument. */ if (ip->samples == 1) { // If there's only one sample, definitely play it. start_region(chan, note, vel, ip->sample, f); } for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp) { // GUS patches don't have velocity ranges, so no need to compare against them. if (sp->low_freq <= f && sp->high_freq >= f) { if (i > 1 && (sp + 1)->low_freq <= f && (sp + 1)->high_freq >= f) { /* If there is a range of contiguous regions that match our * desired frequency, the last one in that block is used. */ continue; } start_region(chan, note, vel, sp, f); break; } } if (i == 0) { /* Found nothing. Try again, but look for the one with the closest root frequency. * As per the suggestion in the original TiMidity function, this search uses * note values rather than raw frequencies. */ double cdiff = 1e10; double want_note = freq_to_note(f); Sample *closest = sp = ip->sample; for (i = ip->samples; i != 0; --i, ++sp) { double diff = fabs(freq_to_note(sp->root_freq) - want_note); if (diff < cdiff) { cdiff = diff; closest = sp; } } start_region(chan, note, vel, closest, f); } } else { for (i = ip->samples, sp = ip->sample; i != 0; --i, ++sp) { if ((sp->low_vel <= vel && sp->high_vel >= vel && sp->low_freq <= f && sp->high_freq >= f)) { if (!start_region(chan, note, vel, sp, f)) { // Ran out of voices break; } } } } } void Renderer::kill_note(int i) { Voice *v = &voice[i]; if (v->status & VOICE_RUNNING) { v->status &= ~VOICE_SUSTAINING; v->status |= VOICE_RELEASING | VOICE_STOPPING; } } int Renderer::allocate_voice() { int i, lowest; float lv, v; for (i = 0; i < voices; ++i) { if (!(voice[i].status & VOICE_RUNNING)) { return i; /* Can't get a lower volume than silence */ } } /* Look for the decaying note with the lowest volume */ lowest = -1; lv = 1e10; i = voices; while (i--) { if ((voice[i].status & VOICE_RELEASING) && !(voice[i].status & VOICE_STOPPING)) { v = voice[i].attenuation; if (v < lv) { lv = v; lowest = i; } } } if (lowest >= 0) { /* This can still cause a click, but if we had a free voice to spare for ramping down this note, we wouldn't need to kill it in the first place... Still, this needs to be fixed. Perhaps we could use a reserve of voices to play dying notes only. */ cut_notes++; voice[lowest].status = 0; } else { lost_notes++; } return lowest; } void Renderer::note_on(int chan, int note, int vel) { if (vel == 0) { note_off(chan, note, 0); return; } int i = voices; /* Only one instance of a note can be playing on a single channel. */ while (i--) { if (voice[i].channel == chan && ((voice[i].note == note && !voice[i].sample->self_nonexclusive) || channel[chan].mono)) { if (channel[chan].mono) { kill_note(i); } else { finish_note(i); } } } start_note(chan, note, vel); } void Renderer::finish_note(int i) { Voice *v = &voice[i]; if ((v->status & (VOICE_RUNNING | VOICE_RELEASING)) == VOICE_RUNNING) { v->status &= ~VOICE_SUSTAINING; v->status |= VOICE_RELEASING; if (!(v->sample->modes & PATCH_NO_SRELEASE) || midi_timiditylike) { v->status &= ~VOICE_LPE; /* sampled release */ } v->eg1.Release(v); v->eg2.Release(v); } } void Renderer::note_off(int chan, int note, int vel) { int i; for (i = voices; i-- > 0; ) { 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 |= NOTE_SUSTAIN; } else { finish_note(i); } } } } /* Process the All Notes Off event */ void Renderer::all_notes_off(int chan) { int i = voices; while (i--) { if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan) { if (channel[chan].sustain) { voice[i].status |= NOTE_SUSTAIN; } else { finish_note(i); } } } } /* Process the All Sounds Off event */ void Renderer::all_sounds_off(int chan) { int i = voices; while (i--) { if (voice[i].channel == chan && (voice[i].status & VOICE_RUNNING) && !(voice[i].status & VOICE_STOPPING)) { kill_note(i); } } } void Renderer::adjust_pressure(int chan, int note, int amount) { int i = voices; while (i--) { if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan && voice[i].note == note) { voice[i].velocity = amount; recompute_amp(&voice[i]); apply_envelope_to_amp(&voice[i]); if (!(voice[i].sample->self_nonexclusive)) { return; } } } } void Renderer::adjust_panning(int chan) { Channel *chanp = &channel[chan]; int i = voices; while (i--) { Voice *v = &voice[i]; if ((v->channel == chan) && (v->status & VOICE_RUNNING)) { double pan = chanp->panning / 128.0; if (v->sample->type == INST_SF2) { // Add instrument pan to channel pan. pan += v->sample->panning / 500.0; } compute_pan(pan, v->sample->type, v->left_offset, v->right_offset); apply_envelope_to_amp(v); } } } void Renderer::drop_sustain(int chan) { int i = voices; while (i--) { if (voice[i].channel == chan && (voice[i].status & NOTE_SUSTAIN)) { finish_note(i); } } } void Renderer::adjust_pitchbend(int chan) { int i = voices; while (i--) { if ((voice[i].status & VOICE_RUNNING) && voice[i].channel == chan) { recompute_freq(i); } } } void Renderer::adjust_volume(int chan) { int i = voices; while (i--) { if (voice[i].channel == chan && (voice[i].status & VOICE_RUNNING)) { recompute_amp(&voice[i]); apply_envelope_to_amp(&voice[i]); } } } void Renderer::HandleEvent(int status, int parm1, int parm2) { int command = status & 0xF0; int chan = status & 0x0F; switch (command) { case ME_NOTEON: note_on(chan, parm1, parm2); break; case ME_NOTEOFF: note_off(chan, parm1, parm2); break; case ME_KEYPRESSURE: adjust_pressure(chan, parm1, parm2); break; case ME_CONTROLCHANGE: HandleController(chan, parm1, parm2); break; case ME_PROGRAM: if (ISDRUMCHANNEL(chan)) { /* Change drum set */ channel[chan].bank = parm1; } else { channel[chan].program = parm1; } break; case ME_CHANNELPRESSURE: /* Unimplemented */ break; case ME_PITCHWHEEL: channel[chan].pitchbend = parm1 | (parm2 << 7); channel[chan].pitchfactor = 0; /* Adjust for notes already playing */ adjust_pitchbend(chan); break; } } void Renderer::HandleController(int chan, int ctrl, int val) { switch (ctrl) { /* These should be the SCC-1 tone bank switch commands. I don't know why there are two, or why the latter only allows switching to bank 0. Also, some MIDI files use 0 as some sort of continuous controller. This will cause lots of warnings about undefined tone banks. */ case CTRL_BANK_SELECT: channel[chan].bank = val; break; case CTRL_BANK_SELECT+32: if (val == 0) { channel[chan].bank = 0; } break; case CTRL_VOLUME: channel[chan].volume = val; adjust_volume(chan); break; case CTRL_EXPRESSION: channel[chan].expression = val; adjust_volume(chan); break; case CTRL_PAN: channel[chan].panning = val; adjust_panning(chan); break; case CTRL_SUSTAIN: channel[chan].sustain = val; if (val == 0) { drop_sustain(chan); } break; case CTRL_NRPN_LSB: channel[chan].nrpn = (channel[chan].nrpn & 0x3F80) | (val); channel[chan].nrpn_mode = true; break; case CTRL_NRPN_MSB: channel[chan].nrpn = (channel[chan].nrpn & 0x007F) | (val << 7); channel[chan].nrpn_mode = true; break; case CTRL_RPN_LSB: channel[chan].rpn = (channel[chan].rpn & 0x3F80) | (val); channel[chan].nrpn_mode = false; break; case CTRL_RPN_MSB: channel[chan].rpn = (channel[chan].rpn & 0x007F) | (val << 7); channel[chan].nrpn_mode = false; break; case CTRL_DATA_ENTRY: if (channel[chan].nrpn_mode) { DataEntryCoarseNRPN(chan, channel[chan].nrpn, val); } else { DataEntryCoarseRPN(chan, channel[chan].rpn, val); } break; case CTRL_DATA_ENTRY+32: if (channel[chan].nrpn_mode) { DataEntryFineNRPN(chan, channel[chan].nrpn, val); } else { DataEntryFineRPN(chan, channel[chan].rpn, val); } break; case CTRL_ALL_SOUNDS_OFF: all_sounds_off(chan); break; case CTRL_RESET_CONTROLLERS: reset_controllers(chan); break; case CTRL_ALL_NOTES_OFF: all_notes_off(chan); break; } } void Renderer::DataEntryCoarseRPN(int chan, int rpn, int val) { switch (rpn) { case RPN_PITCH_SENS: channel[chan].pitchsens = (channel[chan].pitchsens % 100) + (val * 100); channel[chan].pitchfactor = 0; break; // TiMidity resets the pitch sensitivity when a song attempts to write to // RPN_RESET. My docs tell me this is just a dummy value that is guaranteed // to not cause future data entry to go anywhere until a new RPN is set. } } void Renderer::DataEntryFineRPN(int chan, int rpn, int val) { switch (rpn) { case RPN_PITCH_SENS: channel[chan].pitchsens = (channel[chan].pitchsens / 100) * 100 + val; channel[chan].pitchfactor = 0; break; } } void Renderer::DataEntryCoarseNRPN(int chan, int nrpn, int val) { } void Renderer::DataEntryFineNRPN(int chan, int nrpn, int val) { } void Renderer::HandleLongMessage(const BYTE *data, int len) { // SysEx handling goes here. } void Renderer::Reset() { lost_notes = cut_notes = 0; reset_midi(); } }