public void dx7_voice_calculate_runtime_parameters(hexter_instance instance) { int i; double freq; dx7_pitch_envelope_prepare(instance); this.amp_mod_lfo_amd_value = Inline.INT_TO_FP(-64); /* force initial setup */ this.amp_mod_lfo_mods_value = Inline.INT_TO_FP(-64); this.amp_mod_env_value = Inline.INT_TO_FP(-64); this.lfo_delay_segment = 0; this.lfo_delay_value = instance.lfo_delay_value[0]; this.lfo_delay_duration = instance.lfo_delay_duration[0]; this.lfo_delay_increment = instance.lfo_delay_increment[0]; this.mods_serial = instance.mods_serial - 1; /* force mod depths update */ dx7_portamento_prepare(instance); freq = dx7_voice_recalculate_frequency(instance); this.volume_value = -1.0f; /* force initial setup */ dx7_voice_recalculate_volume(instance); for (i = 0; i < Constants.MAX_DX7_OPERATORS; i++) { this.op[i].frequency = freq; if (this.osc_key_sync != 0) { this.op[i].phase = 0; } this.op[i].dx7_op_recalculate_increment(instance); this.op[i].dx7_op_envelope_prepare(instance, limit_note(this.key + this.transpose - 24), this.velocity); } }
public double dx7_voice_recalculate_frequency(hexter_instance instance) { double freq; this.last_port_tuning = instance.tuning; instance.fixed_freq_multiplier = instance.tuning / 440.0; freq = this.pitch_eg.value + this.portamento.value + instance.pitch_bend - instance.lfo_value_for_pitch * (this.pitch_mod_depth_pmd * Inline.FP_TO_DOUBLE(this.lfo_delay_value) + this.pitch_mod_depth_mods); this.last_pitch = freq; freq += (double)(limit_note(this.key + this.transpose - 24)); /* -FIX- this maybe could be optimized */ /* a lookup table of 8k values would give ~1.5 cent accuracy, * but then would interpolating that be faster than exp()? */ freq = instance.tuning * Math.Exp((freq - 69.0) * Constants.M_LN2 / 12.0); return(freq); }
public void dx7_pitch_eg_set_phase(hexter_instance instance, int phase) { this.phase = phase; if (phase == 0) { if (this.level[0] == this.level[1] && this.level[1] == this.level[2] && this.level[2] == this.level[3]) { this.mode = dx7_eg_mode.DX7_EG_CONSTANT; this.value = Data.dx7_voice_pitch_level_to_shift[this.level[3]]; } else { this.mode = dx7_eg_mode.DX7_EG_RUNNING; this.dx7_pitch_eg_set_increment(instance, this.rate[phase], this.level[phase]); } } else { if (this.mode != dx7_eg_mode.DX7_EG_CONSTANT) { this.mode = dx7_eg_mode.DX7_EG_RUNNING; this.dx7_pitch_eg_set_increment(instance, this.rate[phase], this.level[phase]); } } }
public void dx7_voice_setup_note(hexter_instance instance) { dx7_voice_set_data(instance); instance.hexter_instance_set_performance_data(); dx7_lfo_set(instance); dx7_voice_calculate_runtime_parameters(instance); }
public void dx7_pitch_eg_set_increment(hexter_instance instance, int new_rate, int new_level) { double duration; /* translate 0-99 level to shift in semitones */ this.target = Data.dx7_voice_pitch_level_to_shift[new_level]; /* -FIX- This is just a quick approximation that I derived from * regression of Godric Wilkie's pitch eg timings. In particular, * it's not accurate for very slow envelopes. */ duration = Math.Exp(((double)new_rate - 70.337897) / -25.580953) * Math.Abs((this.target - this.value) / 96.0); duration *= (double)instance.nugget_rate; this.duration = (int)Math.Round(duration); if (this.duration > 1) { this.increment = (this.target - this.value) / this.duration; } else { this.duration = 1; this.increment = this.target - this.value; } }
/* * dx7_op_eg_set_next_phase * * assumes a DX7_EG_RUNNING envelope */ public void dx7_op_eg_set_next_phase(hexter_instance instance) { switch (this.phase) { case 0: case 1: this.phase++; dx7_op_eg_set_increment(instance, this.rate[this.phase], this.level[this.phase]); if (this.duration == 1 && this.increment == 0) { dx7_op_eg_set_next_phase(instance); } break; case 2: this.mode = dx7_eg_mode.DX7_EG_SUSTAINING; this.increment = 0; this.duration = -1; break; case 3: default: /* shouldn't be anything but 0 to 3 */ this.mode = dx7_eg_mode.DX7_EG_FINISHED; this.increment = 0; this.duration = -1; break; } }
public void dx7_portamento_process(hexter_instance instance) { if (this.segment == 0) { return; } this.value += this.increment; this.duration--; if (this.duration == 1) { this.increment = this.target - this.value; /* correct any rounding error */ } else if (this.duration == 0) { if (--this.segment > 0) { this.dx7_portamento_set_segment(instance); } else { this.value = 0.0; } } }
void dx7_voice_recalculate_volume(hexter_instance instance) { float f; int i; this.last_port_volume = instance.volume; this.last_cc_volume = instance.cc_volume; /* This 41 OL volume cc mapping matches my TX7 fairly well, to within * +/-0.8dB for most of the scale. (It even duplicates the "feature" * of not going completely silent at zero....) */ f = (instance.volume - 20.0f) * 1.328771f + 86.0f; f += (float)instance.cc_volume * 41.0f / 16256.0f; i = (int)Math.Round(f - 0.5f); f -= (float)i; this.volume_target = (Inline.FP_TO_FLOAT(Data.dx7_voice_eg_ol_to_amp_table[128 + i]) + f * Inline.FP_TO_FLOAT(Data.dx7_voice_eg_ol_to_amp_table[128 + i + 1] - Data.dx7_voice_eg_ol_to_amp_table[128 + i])) * 0.110384f / Data.dx7_voice_carrier_count[this.algorithm]; if (this.volume_value < 0.0f) { /* initial setup */ this.volume_value = this.volume_target; this.volume_duration = 0; } else { this.volume_duration = instance.ramp_duration; this.volume_increment = (this.volume_target - this.volume_value) / (float)this.volume_duration; } }
public void dx7_op_recalculate_increment(hexter_instance instance) { double freq; if (this.osc_mode != 0) { /* fixed frequency */ /* pitch envelope does not affect this */ /* -FIX- convert this to a table lookup for speed? */ freq = instance.fixed_freq_multiplier * Math.Exp(Constants.M_LN10 * ((double)(this.coarse & 3) + (double)this.fine / 100.0)); /* -FIX- figure out what to do with detune */ } else { freq = this.frequency; freq += ((double)this.detune - 7.0) / 32.0; /* -FIX- is this correct? */ if (this.coarse != 0) { freq = freq * (double)this.coarse; } else { freq = freq / 2.0; } freq *= (1.0 + ((double)this.fine / 100.0)); } this.phase_increment = (UInt32)Math.Round(freq * (double)Constants.FP_SIZE / (double)instance.sample_rate); }
public void dx7_voice_set_phase(hexter_instance instance, int phase) { int i; for (i = 0; i < Constants.MAX_DX7_OPERATORS; i++) { this.op[i].eg.dx7_op_eg_set_phase(instance, phase); } this.pitch_eg.dx7_pitch_eg_set_phase(instance, phase); }
/* dx7_lfo_set_speed * * called by dx7_lfo_reset() and dx7_lfo_set() to set LFO speed and phase */ public void dx7_lfo_set(hexter_instance instance) { int set_speed = 0; instance.lfo_wave = this.lfo_wave; if (instance.lfo_speed != this.lfo_speed) { instance.lfo_speed = this.lfo_speed; set_speed = 1; } if (this.lfo_key_sync != 0) { set_speed = 1; /* because we need to reset the LFO phase */ } if (set_speed != 0) { instance.dx7_lfo_set_speed(); } if (instance.lfo_delay != this.lfo_delay) { instance.lfo_delay = this.lfo_delay; if (this.lfo_delay > 0) { instance.lfo_delay_value[0] = 0; /* -FIX- Jamie's early approximation, replace when he has more data */ instance.lfo_delay_duration[0] = (UInt32) Math.Round(instance.sample_rate * (0.00175338f * (float)(Math.Pow((float)this.lfo_delay, 3.10454f)) + 169.344f - 168.0f) / 1000.0f); instance.lfo_delay_increment[0] = 0; instance.lfo_delay_value[1] = 0; /* -FIX- Jamie's early approximation, replace when he has more data */ instance.lfo_delay_duration[1] = (UInt32) Math.Round(instance.sample_rate * (0.321877f * (float)(Math.Pow((float)this.lfo_delay, 2.01163)) + 494.201f - 168.0f) / 1000.0f); /* time from note-on until full on */ instance.lfo_delay_duration[1] -= instance.lfo_delay_duration[0]; /* now time from end-of-delay until full */ instance.lfo_delay_increment[1] = Constants.FP_SIZE / (Int32)instance.lfo_delay_duration[1]; instance.lfo_delay_value[2] = Constants.FP_SIZE; instance.lfo_delay_duration[2] = 0; instance.lfo_delay_increment[2] = 0; } else { instance.lfo_delay_value[0] = Constants.FP_SIZE; instance.lfo_delay_duration[0] = 0; instance.lfo_delay_increment[0] = 0; } /* -FIX- The TX7 resets the lfo delay for all playing notes at each * new note on. We're not doing that yet, and I don't really wanna, * 'cause it's stupid.... */ } }
public void dx7_voice_recalculate_freq_and_inc(hexter_instance instance) { double freq = dx7_voice_recalculate_frequency(instance); int i; for (i = 0; i < 6; i++) { this.op[i].frequency = freq; this.op[i].dx7_op_recalculate_increment(instance); } }
/* * dx7_voice_release_note */ public void dx7_voice_release_note(hexter_instance instance) { //DEBUG_MESSAGE(DB_NOTE, " dx7_voice_release_note: turning off voice %p\n", voice); if (_ON) { /* dummy up a release velocity */ this.rvelocity = 64; } dx7_voice_set_release_phase(instance); this.status = dx7_voice_status.DX7_VOICE_RELEASED; }
public void dx7_pitch_eg_process(hexter_instance instance) { if (this.mode != dx7_eg_mode.DX7_EG_RUNNING) { return; } this.value += this.increment; this.duration--; if (this.duration == 1) { this.increment = this.target - this.value; /* correct any rounding error */ } else if (this.duration == 0) { this.dx7_pitch_eg_set_next_phase(instance); } }
/* * dx7_pitch_eg_set_next_phase * * assumes a DX7_EG_RUNNING envelope */ public void dx7_pitch_eg_set_next_phase(hexter_instance instance) { switch (this.phase) { case 0: case 1: this.phase++; dx7_pitch_eg_set_increment(instance, this.rate[this.phase], this.level[this.phase]); break; case 2: this.mode = dx7_eg_mode.DX7_EG_SUSTAINING; break; case 3: default: /* shouldn't be anything but 0 to 3 */ this.mode = dx7_eg_mode.DX7_EG_FINISHED; break; } }
public void dx7_portamento_prepare(hexter_instance instance) { dx7_portamento port = this.portamento; if (instance.portamento_time == 0 || instance.last_key == this.key) { port.segment = 0; port.value = 0.0; } else { /* -FIX- implement portamento time and multi-segment curve */ float t = ((float)Math.Exp((float)(instance.portamento_time - 99) / 15.0f)) * 18.0f; /* not at all related to what a real DX7 does */ port.segment = 1; port.value = (double)(instance.last_key - this.key); port.duration = (int)Math.Round(instance.nugget_rate * t); port.target = 0.0; port.dx7_portamento_set_segment(instance); } }
public void dx7_op_eg_set_phase(hexter_instance instance, int phase) { this.phase = phase; if (phase == 0) { if (this.level[0] == this.level[1] && this.level[1] == this.level[2] && this.level[2] == this.level[3]) { this.mode = dx7_eg_mode.DX7_EG_CONSTANT; this.value = Inline.INT_TO_FP(this.level[3]); this.increment = 0; this.duration = -1; } else { this.mode = dx7_eg_mode.DX7_EG_RUNNING; this.dx7_op_eg_set_increment(instance, this.rate[phase], this.level[phase]); if (this.duration == 1 && this.increment == 0) { this.dx7_op_eg_set_next_phase(instance); } } } else { if (this.mode != dx7_eg_mode.DX7_EG_CONSTANT) { this.mode = dx7_eg_mode.DX7_EG_RUNNING; this.dx7_op_eg_set_increment(instance, this.rate[phase], this.level[phase]); if (this.duration == 1 && this.increment == 0) { this.dx7_op_eg_set_next_phase(instance); } } } }
public void dx7_op_eg_process(hexter_instance instance) { this.value += this.increment; if (--this.duration == 0) { if (this.mode != dx7_eg_mode.DX7_EG_RUNNING) { this.duration = -1; return; } if (this.in_precomp != 0) { this.in_precomp = 0; this.duration = this.postcomp_duration; this.increment = this.postcomp_increment; } else { dx7_op_eg_set_next_phase(instance); } } }
public void dx7_op_eg_set_increment(hexter_instance instance, int new_rate, int new_level) { int current_level = Inline.FP_TO_INT(this.value); int need_compensation; float duration; this.target = Inline.INT_TO_FP(new_level); if (this.value <= this.target) { /* envelope will be rising */ /* DX7 envelopes, when rising from levels <= 31 to levels * >= 32, include a compensation feature to speed the * attack, thereby making it sound more natural. The * behavior of some of the boundary cases is bizarre, and * this has been exploited by some patch programmers (the * "Watergarden" patch found in the original ROM cartridge * is one example). We try to emulate it here: */ if (this.value <= Constants.INT_TO_FP_31) { if (new_level > 31) { /* rise quickly to 31, then continue normally */ need_compensation = 1; duration = Data.dx7_voice_eg_rate_rise_duration[new_rate] * (Data.dx7_voice_eg_rate_rise_percent[new_level] - Data.dx7_voice_eg_rate_rise_percent[current_level]); } else if (new_level - current_level > 9) { /* these seem to take zero time */ need_compensation = 0; duration = 0.0f; } else { /* these are the exploited delays */ need_compensation = 0; /* -FIX- this doesn't make WATER GDN work? */ duration = Data.dx7_voice_eg_rate_rise_duration[new_rate] * (float)(new_level - current_level) / 100.0f; } } else { need_compensation = 0; duration = Data.dx7_voice_eg_rate_rise_duration[new_rate] * (Data.dx7_voice_eg_rate_rise_percent[new_level] - Data.dx7_voice_eg_rate_rise_percent[current_level]); } } else { need_compensation = 0; duration = Data.dx7_voice_eg_rate_decay_duration[new_rate] * (Data.dx7_voice_eg_rate_decay_percent[current_level] - Data.dx7_voice_eg_rate_decay_percent[new_level]); } duration *= instance.sample_rate; this.duration = (int)Math.Round(duration); if (this.duration < 1) { this.duration = 1; } if (need_compensation != 0) { Int32 precomp_duration = (Constants.INT_TO_FP_31 - this.value + instance.dx7_eg_max_slew - 1) / instance.dx7_eg_max_slew; if (precomp_duration >= this.duration) { this.duration = precomp_duration; this.increment = (this.target - this.value) / this.duration; if (this.increment > instance.dx7_eg_max_slew) { this.duration = (this.target - this.value + instance.dx7_eg_max_slew - 1) / instance.dx7_eg_max_slew; this.increment = (this.target - this.value) / this.duration; } this.in_precomp = 0; } else if (precomp_duration < 1) { this.increment = (this.target - this.value) / this.duration; if (this.increment > instance.dx7_eg_max_slew) { this.duration = (this.target - this.value + instance.dx7_eg_max_slew - 1) / instance.dx7_eg_max_slew; this.increment = (this.target - this.value) / this.duration; } this.in_precomp = 0; } else { this.postcomp_duration = this.duration - precomp_duration; this.duration = precomp_duration; this.increment = (Constants.INT_TO_FP_31 - this.value) / precomp_duration; this.postcomp_increment = (this.target - Constants.INT_TO_FP_31) / this.postcomp_duration; if (this.postcomp_increment > instance.dx7_eg_max_slew) { this.postcomp_duration = (this.target - Constants.INT_TO_FP_31 + instance.dx7_eg_max_slew - 1) / instance.dx7_eg_max_slew; this.postcomp_increment = (this.target - Constants.INT_TO_FP_31) / this.postcomp_duration; } this.in_precomp = 1; } } else { this.increment = (this.target - this.value) / this.duration; if (Math.Abs(this.increment) > instance.dx7_eg_max_slew) { this.duration = (Math.Abs(this.target - this.value) + instance.dx7_eg_max_slew - 1) / instance.dx7_eg_max_slew; this.increment = (this.target - this.value) / this.duration; } this.in_precomp = 0; } }
public void dx7_portamento_set_segment(hexter_instance instance) { /* -FIX- implement portamento multi-segment curve */ this.increment = (this.target - this.value) / (double)this.duration; }
public void dx7_voice_set_release_phase(hexter_instance instance) { dx7_voice_set_phase(instance, 3); }
public void dx7_voice_set_data(hexter_instance instance) { byte[] edit_buffer = instance.current_patch_buffer; bool compat059 = (instance.performance_buffer[0] & 0x01) > 0 ? true : false; /* 0.5.9 compatibility */ int i, j; double aux_feedbk; for (i = 0; i < Constants.MAX_DX7_OPERATORS; i++) { byte[] eb_op = edit_buffer; int offset = ((5 - i) * 21); this.op[i].output_level = (byte)(Inline.limit(eb_op[16 + offset], 0, 99)); this.op[i].osc_mode = (byte)(eb_op[17 + offset] & 0x01); this.op[i].coarse = (byte)(eb_op[18 + offset] & 0x1f); this.op[i].fine = (byte)(Inline.limit(eb_op[19 + offset], 0, 99)); this.op[i].detune = (byte)(Inline.limit(eb_op[20 + offset], 0, 14)); this.op[i].level_scaling_bkpoint = (byte)(Inline.limit(eb_op[8 + offset], 0, 99)); this.op[i].level_scaling_l_depth = (byte)(Inline.limit(eb_op[9 + offset], 0, 99)); this.op[i].level_scaling_r_depth = (byte)(Inline.limit(eb_op[10 + offset], 0, 99)); this.op[i].level_scaling_l_curve = (byte)(eb_op[11 + offset] & 0x03); this.op[i].level_scaling_r_curve = (byte)(eb_op[12 + offset] & 0x03); this.op[i].rate_scaling = (byte)(eb_op[13 + offset] & 0x07); this.op[i].amp_mod_sens = (byte)((compat059 ? 0 : eb_op[14 + offset] & 0x03)); this.op[i].velocity_sens = (byte)(eb_op[15 + offset] & 0x07); for (j = 0; j < 4; j++) { this.op[i].eg.base_rate[j] = (byte)(Inline.limit(eb_op[j + offset], 0, 99)); this.op[i].eg.base_level[j] = (byte)(Inline.limit(eb_op[4 + j + offset], 0, 99)); } } for (i = 0; i < 4; i++) { this.pitch_eg.rate[i] = (byte)(Inline.limit(edit_buffer[126 + i], 0, 99)); this.pitch_eg.level[i] = (byte)(Inline.limit(edit_buffer[130 + i], 0, 99)); } this.algorithm = (byte)(edit_buffer[134] & 0x1f); aux_feedbk = (double)(edit_buffer[135] & 0x07) / (2.0 * Constants.M_PI) * 0.18 /* -FIX- feedback_scaling[this.algorithm] */; /* the "99.0" here is because we're also using this multiplier to scale the * eg level from 0-99 to 0-1 */ this.feedback_multiplier = (int)Math.Round(aux_feedbk / 99.0 * Constants.FP_SIZE); this.osc_key_sync = (byte)(edit_buffer[136] & 0x01); this.lfo_speed = (byte)(Inline.limit(edit_buffer[137], 0, 99)); this.lfo_delay = (byte)(Inline.limit(edit_buffer[138], 0, 99)); this.lfo_pmd = (byte)(Inline.limit(edit_buffer[139], 0, 99)); this.lfo_amd = (byte)(Inline.limit(edit_buffer[140], 0, 99)); this.lfo_key_sync = (byte)(edit_buffer[141] & 0x01); this.lfo_wave = (byte)(Inline.limit(edit_buffer[142], 0, 5)); this.lfo_pms = (byte)((compat059 ? 0 : edit_buffer[143] & 0x07)); this.transpose = Inline.limit(edit_buffer[144], 0, 48); }
/* * dx7_voice_render * * generate the actual sound data for this voice */ public void dx7_voice_render(hexter_instance instance, double[] outx, UInt64 sample_count) { UInt64 sample; Int32 i; Int64 i64; Int32 output; if ((this.last_port_volume != instance.volume) || (this.last_cc_volume != instance.cc_volume)) { dx7_voice_recalculate_volume(instance); } for (sample = 0; sample < sample_count; sample++) { /* calculate amplitude modulation amounts */ i = (Int32)(((Int64)(this.amp_mod_lfo_amd_value) * (Int64)(this.lfo_delay_value)) >> Constants.FP_SHIFT); i = this.amp_mod_env_value + (Int32)(((Int64)(i + this.amp_mod_lfo_mods_value) * (Int64)(instance.lfo_buffer[sample])) >> Constants.FP_SHIFT); i64 = (Int64)(i); ampmod[3] = i; ampmod[2] = (Int32)((i64 * Constants.ampmod2) >> Constants.FP_SHIFT); ampmod[1] = (Int32)((i64 * Constants.ampmod1) >> Constants.FP_SHIFT); switch (this.algorithm) { case 0: /* algorithm 1 */ /* This first algorithm is all written out, so you can see how it looks */ /* * output = ( * dx7_op_calculate_carrier(this.op[Constants.OP_3].eg.value - ampmod[this.op[Constants.OP_3].amp_mod_sens], * (Int32)(this.op[Constants.OP_3].phase) + * dx7_op_calculate_modulator(this.op[Constants.OP_4].eg.value - ampmod[this.op[Constants.OP_4].amp_mod_sens], * (Int32)(this.op[Constants.OP_4].phase) + * dx7_op_calculate_modulator(this.op[Constants.OP_5].eg.value - ampmod[this.op[Constants.OP_5].amp_mod_sens], * (Int32)(this.op[Constants.OP_5].phase) + * // -FIX- need to determine if amp mod is included in feedback, or after * dx7_op_calculate_modulator_saving_feedback( * this.op[Constants.OP_6].eg.value - ampmod[this.op[Constants.OP_6].amp_mod_sens], * (Int32)(this.op[Constants.OP_6].phase) + this.feedback)))) + * dx7_op_calculate_carrier(this.op[Constants.OP_1].eg.value - ampmod[this.op[Constants.OP_1].amp_mod_sens], * (Int32)(this.op[Constants.OP_1].phase) + * dx7_op_calculate_modulator(this.op[Constants.OP_2].eg.value - ampmod[this.op[Constants.OP_2].amp_mod_sens], * (Int32)(this.op[Constants.OP_2].phase))) * );*/ output = ( car(Constants.OP_3, mod(Constants.OP_4, mod(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)))) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 1: /* algorithm 2 */ output = ( car(Constants.OP_3, mod(Constants.OP_4, mod(Constants.OP_5, mod(Constants.OP_6, 0)))) + car(Constants.OP_1, mod_sfb(Constants.OP_2, this.feedback)) ); break; case 2: /* algorithm 3 */ output = ( car(Constants.OP_4, mod(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback))) + car(Constants.OP_1, mod(Constants.OP_2, mod(Constants.OP_3, 0))) ); break; case 3: /* algorithm 4 */ output = ( car_sfb(Constants.OP_4, mod(Constants.OP_5, mod(Constants.OP_6, this.feedback))) + car(Constants.OP_1, mod(Constants.OP_2, mod(Constants.OP_3, 0))) ); break; case 4: /* algorithm 5 */ output = ( car(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)) + car(Constants.OP_3, mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 5: /* algorithm 6 */ output = ( car_sfb(Constants.OP_5, mod(Constants.OP_6, this.feedback)) + car(Constants.OP_3, mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 6: /* algorithm 7 */ output = ( car(Constants.OP_3, mod(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)) + mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 7: /* algorithm 8 */ output = ( car(Constants.OP_3, mod(Constants.OP_5, mod(Constants.OP_6, 0)) + mod_sfb(Constants.OP_4, this.feedback)) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 8: /* algorithm 9 */ output = ( car(Constants.OP_3, mod(Constants.OP_5, mod(Constants.OP_6, 0)) + mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod_sfb(Constants.OP_2, this.feedback)) ); break; case 9: /* algorithm 10 */ output = ( car(Constants.OP_4, mod(Constants.OP_6, 0) + mod(Constants.OP_5, 0)) + car(Constants.OP_1, mod(Constants.OP_2, mod_sfb(Constants.OP_3, this.feedback))) ); break; case 10: /* algorithm 11 */ output = ( car(Constants.OP_4, mod_sfb(Constants.OP_6, this.feedback) + mod(Constants.OP_5, 0)) + car(Constants.OP_1, mod(Constants.OP_2, mod(Constants.OP_3, 0))) ); break; case 11: /* algorithm 12 */ output = ( car(Constants.OP_3, mod(Constants.OP_6, 0) + mod(Constants.OP_5, 0) + mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod_sfb(Constants.OP_2, this.feedback)) ); break; case 12: /* algorithm 13 */ output = ( car(Constants.OP_3, mod_sfb(Constants.OP_6, this.feedback) + mod(Constants.OP_5, 0) + mod(Constants.OP_4, 0)) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 13: /* algorithm 14 */ output = ( car(Constants.OP_3, mod(Constants.OP_4, mod_sfb(Constants.OP_6, this.feedback) + mod(Constants.OP_5, 0))) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 14: /* algorithm 15 */ output = ( car(Constants.OP_3, mod(Constants.OP_4, mod(Constants.OP_6, 0) + mod(Constants.OP_5, 0))) + car(Constants.OP_1, mod_sfb(Constants.OP_2, this.feedback)) ); break; case 15: /* algorithm 16 */ output = car(Constants.OP_1, mod(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)) + mod(Constants.OP_3, mod(Constants.OP_4, 0)) + mod(Constants.OP_2, 0)); break; case 16: /* algorithm 17 */ output = car(Constants.OP_1, mod(Constants.OP_5, mod(Constants.OP_6, 0)) + mod(Constants.OP_3, mod(Constants.OP_4, 0)) + mod_sfb(Constants.OP_2, this.feedback)); break; case 17: /* algorithm 18 */ output = car(Constants.OP_1, mod(Constants.OP_4, mod(Constants.OP_5, mod(Constants.OP_6, 0))) + mod_sfb(Constants.OP_3, this.feedback) + mod(Constants.OP_2, 0)); break; case 18: /* algorithm 19 */ i = mod_sfb(Constants.OP_6, this.feedback); output = ( car(Constants.OP_5, i) + car(Constants.OP_4, i) + car(Constants.OP_1, mod(Constants.OP_2, mod(Constants.OP_3, 0))) ); break; case 19: /* algorithm 20 */ i = mod_sfb(Constants.OP_3, this.feedback); output = ( car(Constants.OP_4, mod(Constants.OP_6, 0) + mod(Constants.OP_5, 0)) + car(Constants.OP_2, i) + car(Constants.OP_1, i) ); break; case 20: /* algorithm 21 */ i = mod(Constants.OP_6, 0); output = car(Constants.OP_5, i) + car(Constants.OP_4, i); i = mod_sfb(Constants.OP_3, this.feedback); output += car(Constants.OP_2, i) + car(Constants.OP_1, i); break; case 21: /* algorithm 22 */ i = mod_sfb(Constants.OP_6, this.feedback); output = ( car(Constants.OP_5, i) + car(Constants.OP_4, i) + car(Constants.OP_3, i) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 22: /* algorithm 23 */ i = mod_sfb(Constants.OP_6, this.feedback); output = ( car(Constants.OP_5, i) + car(Constants.OP_4, i) + car(Constants.OP_2, mod(Constants.OP_3, 0)) + car(Constants.OP_1, 0) ); break; case 23: /* algorithm 24 */ i = mod_sfb(Constants.OP_6, this.feedback); output = ( car(Constants.OP_5, i) + car(Constants.OP_4, i) + car(Constants.OP_3, i) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; case 24: /* algorithm 25 */ i = mod_sfb(Constants.OP_6, this.feedback); output = ( car(Constants.OP_5, i) + car(Constants.OP_4, i) + car(Constants.OP_3, 0) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; case 25: /* algorithm 26 */ output = ( car(Constants.OP_4, mod_sfb(Constants.OP_6, this.feedback) + mod(Constants.OP_5, 0)) + car(Constants.OP_2, mod(Constants.OP_3, 0)) + car(Constants.OP_1, 0) ); break; case 26: /* algorithm 27 */ output = ( car(Constants.OP_4, mod(Constants.OP_6, 0) + mod(Constants.OP_5, 0)) + car(Constants.OP_2, mod_sfb(Constants.OP_3, this.feedback)) + car(Constants.OP_1, 0) ); break; case 27: /* algorithm 28 */ output = ( car(Constants.OP_6, 0) + car(Constants.OP_3, mod(Constants.OP_4, mod_sfb(Constants.OP_5, this.feedback))) + car(Constants.OP_1, mod(Constants.OP_2, 0)) ); break; case 28: /* algorithm 29 */ output = ( car(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)) + car(Constants.OP_3, mod(Constants.OP_4, 0)) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; case 29: /* algorithm 30 */ output = ( car(Constants.OP_6, 0) + car(Constants.OP_3, mod(Constants.OP_4, mod_sfb(Constants.OP_5, this.feedback))) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; case 30: /* algorithm 31 */ output = ( car(Constants.OP_5, mod_sfb(Constants.OP_6, this.feedback)) + car(Constants.OP_4, 0) + car(Constants.OP_3, 0) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; case 31: /* algorithm 32 */ default: /* just in case */ output = ( car_sfb(Constants.OP_6, this.feedback) + car(Constants.OP_5, 0) + car(Constants.OP_4, 0) + car(Constants.OP_3, 0) + car(Constants.OP_2, 0) + car(Constants.OP_1, 0) ); break; } /* this.volume_value contains a scaling factor for the number of carriers */ /* mix voice output into output buffer */ outx[sample] += (((double)output) * Constants.FP_TO_FLOAT_DOUBLE) * ((double)this.volume_value); /* update runtime parameters for next sample */ this.op[Constants.OP_6].phase += this.op[Constants.OP_6].phase_increment; this.op[Constants.OP_5].phase += this.op[Constants.OP_5].phase_increment; this.op[Constants.OP_4].phase += this.op[Constants.OP_4].phase_increment; this.op[Constants.OP_3].phase += this.op[Constants.OP_3].phase_increment; this.op[Constants.OP_2].phase += this.op[Constants.OP_2].phase_increment; this.op[Constants.OP_1].phase += this.op[Constants.OP_1].phase_increment; this.op[Constants.OP_6].eg.dx7_op_eg_process(instance); this.op[Constants.OP_5].eg.dx7_op_eg_process(instance); this.op[Constants.OP_4].eg.dx7_op_eg_process(instance); this.op[Constants.OP_3].eg.dx7_op_eg_process(instance); this.op[Constants.OP_2].eg.dx7_op_eg_process(instance); this.op[Constants.OP_1].eg.dx7_op_eg_process(instance); if (this.amp_mod_env_duration != 0) { this.amp_mod_env_value += this.amp_mod_env_increment; this.amp_mod_env_duration--; } if (this.amp_mod_lfo_mods_duration != 0) { this.amp_mod_lfo_mods_value += this.amp_mod_lfo_mods_increment; this.amp_mod_lfo_mods_duration--; } if (this.amp_mod_lfo_amd_duration != 0) { this.amp_mod_lfo_amd_value += this.amp_mod_lfo_amd_increment; this.amp_mod_lfo_amd_duration--; } if (this.lfo_delay_duration != 0) { this.lfo_delay_value += this.lfo_delay_increment; if (--this.lfo_delay_duration == 0) { i = ++this.lfo_delay_segment; this.lfo_delay_duration = instance.lfo_delay_duration[i]; this.lfo_delay_value = instance.lfo_delay_value[i]; this.lfo_delay_increment = instance.lfo_delay_increment[i]; } } if (this.volume_duration != 0) { this.volume_value += this.volume_increment; this.volume_duration--; } } }
public void dx7_voice_update_mod_depths(hexter_instance instance) { byte kp = instance.key_pressure[this.key]; byte cp = instance.channel_pressure; float pressure; float pdepth, adepth, mdepth, edepth; /* add the channel and key pressures together in a way that 'feels' good */ if (kp > cp) { pressure = (float)kp / 127.0f; pressure += (1.0f - pressure) * ((float)cp / 127.0f); } else { pressure = (float)cp / 127.0f; pressure += (1.0f - pressure) * ((float)kp / 127.0f); } /* calculate modulation depths */ pdepth = (float)this.lfo_pmd / 99.0f; this.pitch_mod_depth_pmd = (double)Data.dx7_voice_pms_to_semitones[this.lfo_pms] * (double)pdepth; // -FIX- this could be optimized: // -FIX- this just adds everything together -- maybe it should limit the result, or // combine the various mods like update_pressure() does pdepth = (((instance.mod_wheel_assign & 0x01) != 0) ? // -FIX- this assumes that mod_wheel_sensitivity, etc. scale linearly => verify (float)instance.mod_wheel_sensitivity / 15.0f * instance.mod_wheel : 0.0f) + (((instance.foot_assign & 0x01) != 0) ? (float)instance.foot_sensitivity / 15.0f * instance.foot : 0.0f) + (((instance.pressure_assign & 0x01) != 0) ? (float)instance.pressure_sensitivity / 15.0f * pressure : 0.0f) + (((instance.breath_assign & 0x01) != 0) ? (float)instance.breath_sensitivity / 15.0f * instance.breath : 0.0f); this.pitch_mod_depth_mods = (double)Data.dx7_voice_pms_to_semitones[this.lfo_pms] * (double)pdepth; // -FIX- these are total guesses at how to combine/limit the amp mods: adepth = Data.dx7_voice_amd_to_ol_adjustment[this.lfo_amd]; // -FIX- this could be optimized: mdepth = (((instance.mod_wheel_assign & 0x02) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.mod_wheel_sensitivity] * instance.mod_wheel : 0.0f) + (((instance.foot_assign & 0x02) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.foot_sensitivity] * instance.foot : 0.0f) + (((instance.pressure_assign & 0x02) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.pressure_sensitivity] * pressure : 0.0f) + (((instance.breath_assign & 0x02) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.breath_sensitivity] * instance.breath : 0.0f); edepth = // -FIX- this could be optimized: (((instance.mod_wheel_assign & 0x04) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.mod_wheel_sensitivity] * (1.0f - instance.mod_wheel) : 0.0f) + (((instance.foot_assign & 0x04) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.foot_sensitivity] * (1.0f - instance.foot) : 0.0f) + (((instance.pressure_assign & 0x04) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.pressure_sensitivity] * (1.0f - pressure) : 0.0f) + (((instance.breath_assign & 0x04) != 0) ? Data.dx7_voice_mss_to_ol_adjustment[instance.breath_sensitivity] * (1.0f - instance.breath) : 0.0f); /* full-scale amp mod for adepth and edepth should be 52.75 and * their sum _must_ be limited to less than 128, or bad things will happen! */ if (adepth > 127.5f) { adepth = 127.5f; } if (adepth + mdepth > 127.5f) { mdepth = 127.5f - adepth; } if (adepth + mdepth + edepth > 127.5f) { edepth = 127.5f - (adepth + mdepth); } this.amp_mod_lfo_amd_target = Inline.FLOAT_TO_FP(adepth); if (this.amp_mod_lfo_amd_value <= Inline.INT_TO_FP(-64)) { this.amp_mod_lfo_amd_value = this.amp_mod_lfo_amd_target; this.amp_mod_lfo_amd_increment = 0; this.amp_mod_lfo_amd_duration = 0; } else { this.amp_mod_lfo_amd_duration = instance.ramp_duration; this.amp_mod_lfo_amd_increment = (this.amp_mod_lfo_amd_target - this.amp_mod_lfo_amd_value) / (Int32)this.amp_mod_lfo_amd_duration; } this.amp_mod_lfo_mods_target = Inline.FLOAT_TO_FP(mdepth); if (this.amp_mod_lfo_mods_value <= Inline.INT_TO_FP(-64)) { this.amp_mod_lfo_mods_value = this.amp_mod_lfo_mods_target; this.amp_mod_lfo_mods_increment = 0; this.amp_mod_lfo_mods_duration = 0; } else { this.amp_mod_lfo_mods_duration = instance.ramp_duration; this.amp_mod_lfo_mods_increment = (this.amp_mod_lfo_mods_target - this.amp_mod_lfo_mods_value) / (Int32)this.amp_mod_lfo_mods_duration; } this.amp_mod_env_target = Inline.FLOAT_TO_FP(edepth); if (this.amp_mod_env_value <= Inline.INT_TO_FP(-64)) { this.amp_mod_env_value = this.amp_mod_env_target; this.amp_mod_env_increment = 0; this.amp_mod_env_duration = 0; } else { this.amp_mod_env_duration = instance.ramp_duration; this.amp_mod_env_increment = (this.amp_mod_env_target - this.amp_mod_env_value) / (Int32)this.amp_mod_env_duration; } }
/* * dx7_voice_note_off */ public void dx7_voice_note_off(hexter_instance instance, byte key, byte rvelocity) { //DEBUG_MESSAGE(DB_NOTE, " dx7_voice_note_off: called for voice %p, key %d\n", voice, key); /* save release velocity */ this.rvelocity = rvelocity; if (instance.monophonic != 0) { /* monophonic mode */ if (instance.held_keys[0] >= 0) { /* still some keys held */ if (this.key != instance.held_keys[0]) { /* most-recently-played key has changed */ this.key = (byte)instance.held_keys[0]; //DEBUG_MESSAGE(DB_NOTE, " note-off in monophonic section: changing pitch to %d\n", this.key); this.mods_serial = instance.mods_serial - 1; /* -FIX- dx7_portamento_prepare(instance, voice); */ dx7_voice_recalculate_freq_and_inc(instance); /* if mono mode is 'both', re-trigger EGs */ if (instance.monophonic == Constants.DSSP_MONO_MODE_BOTH && !_RELEASED) { dx7_voice_set_phase(instance, 0); } } } else { /* no keys still held */ if (instance.HEXTER_INSTANCE_SUSTAINED) { /* no more keys in list, but we're sustained */ //DEBUG_MESSAGE(DB_NOTE, " note-off in monophonic section: sustained with no held keys\n"); if (!_RELEASED) { this.status = dx7_voice_status.DX7_VOICE_SUSTAINED; } } else { /* not sustained */ /* no more keys in list, so turn off note */ //DEBUG_MESSAGE(DB_NOTE, " note-off in monophonic section: turning off voice %p\n", voice); dx7_voice_set_release_phase(instance); this.status = dx7_voice_status.DX7_VOICE_RELEASED; } } } else { /* polyphonic mode */ if (instance.HEXTER_INSTANCE_SUSTAINED) { if (!_RELEASED) { this.status = dx7_voice_status.DX7_VOICE_SUSTAINED; } } else { /* not sustained */ dx7_voice_set_release_phase(instance); this.status = dx7_voice_status.DX7_VOICE_RELEASED; } } }
public void dx7_voice_note_on(hexter_instance instance, byte key, byte velocity) { int i; this.key = key; this.velocity = velocity; if (!(instance.monophonic != 0) || !(_ON || _SUSTAINED)) { /* brand-new voice, or monophonic voice in release phase; set * everything up */ //DEBUG_MESSAGE(DB_NOTE, " dx7_voice_note_on in polyphonic/new section: key %d, mono %d, old status %d\n", key, instance.monophonic, voice.status); dx7_voice_setup_note(instance); } else { /* synth is monophonic, and we're modifying a playing voice */ //DEBUG_MESSAGE(DB_NOTE, " dx7_voice_note_on in monophonic section: old key %d => new key %d\n", instance.held_keys[0], key); /* retrigger LFO if needed */ dx7_lfo_set(instance); /* set new pitch */ this.mods_serial = instance.mods_serial - 1; /* -FIX- dx7_portamento_prepare(instance, voice); */ dx7_voice_recalculate_freq_and_inc(instance); /* if in 'on' or 'both' modes, and key has changed, then re-trigger EGs */ if ((instance.monophonic == Constants.DSSP_MONO_MODE_ON || instance.monophonic == Constants.DSSP_MONO_MODE_BOTH) && (instance.held_keys[0] < 0 || instance.held_keys[0] != key)) { dx7_voice_set_phase(instance, 0); } /* all other variables stay what they are */ } instance.last_key = key; if (instance.monophonic != 0) { /* add new key to the list of held keys */ /* check if new key is already in the list; if so, move it to the * top of the list, otherwise shift the other keys down and add it * to the top of the list. */ // DEBUG_MESSAGE(DB_NOTE, " note-on key list before: %d %d %d %d %d %d %d %d\n", instance.held_keys[0], instance.held_keys[1], instance.held_keys[2], instance.held_keys[3], instance.held_keys[4], instance.held_keys[5], instance.held_keys[6], instance.held_keys[7]); for (i = 0; i < 7; i++) { if (instance.held_keys[i] == key) { break; } } for (; i > 0; i--) { instance.held_keys[i] = instance.held_keys[i - 1]; } instance.held_keys[0] = key; // DEBUG_MESSAGE(DB_NOTE, " note-on key list after: %d %d %d %d %d %d %d %d\n", instance.held_keys[0], instance.held_keys[1], instance.held_keys[2], instance.held_keys[3], instance.held_keys[4], instance.held_keys[5], instance.held_keys[6], instance.held_keys[7]); } if (!_PLAYING) { dx7_voice_start_voice(); } else if (!_ON) { /* must be DX7_VOICE_SUSTAINED or DX7_VOICE_RELEASED */ this.status = dx7_voice_status.DX7_VOICE_ON; } }
public void dx7_op_envelope_prepare(hexter_instance instance, int transposed_note, int velocity) { int scaled_output_level, i, rate_bump; float vel_adj; scaled_output_level = this.output_level; /* things that affect breakpoint calculations: transpose, ? */ /* things that don't affect breakpoint calculations: pitch envelope, ? */ if ((transposed_note < this.level_scaling_bkpoint + 21) && (this.level_scaling_l_depth != 0)) { /* On the original DX7/TX7, keyboard level scaling calculations * group the keyboard into groups of three keys. This can be quite * noticeable on patches with extreme scaling depths, so I've tried * to replicate it here (the steps between levels may not occur at * exactly the keys). If you'd prefer smother scaling, define * SMOOTH_KEYBOARD_LEVEL_SCALING. */ //#ifndef SMOOTH_KEYBOARD_LEVEL_SCALING i = this.level_scaling_bkpoint - (((transposed_note + 2) / 3) * 3) + 21; //#else // i = this.level_scaling_bkpoint - transposed_note + 21; //#endif switch (this.level_scaling_l_curve) { case 0: /* -LIN */ scaled_output_level -= (int)((float)i / 45.0f * (float)this.level_scaling_l_depth); break; case 1: /* -EXP */ scaled_output_level -= (int)(Math.Exp((float)(i - 72) / 13.5f) * (float)this.level_scaling_l_depth); break; case 2: /* +EXP */ scaled_output_level += (int)(Math.Exp((float)(i - 72) / 13.5f) * (float)this.level_scaling_l_depth); break; case 3: /* +LIN */ scaled_output_level += (int)((float)i / 45.0f * (float)this.level_scaling_l_depth); break; } if (scaled_output_level < 0) { scaled_output_level = 0; } if (scaled_output_level > 99) { scaled_output_level = 99; } } else if ((transposed_note > this.level_scaling_bkpoint + 21) && (this.level_scaling_r_depth != 0)) { //#ifndef SMOOTH_KEYBOARD_LEVEL_SCALING i = (((transposed_note + 2) / 3) * 3) - this.level_scaling_bkpoint - 21; //#else // i = transposed_note - op.level_scaling_bkpoint - 21; //#endif switch (this.level_scaling_r_curve) { case 0: /* -LIN */ scaled_output_level -= (int)((float)i / 45.0f * (float)this.level_scaling_r_depth); break; case 1: /* -EXP */ scaled_output_level -= (int)(Math.Exp((float)(i - 72) / 13.5f) * (float)this.level_scaling_r_depth); break; case 2: /* +EXP */ scaled_output_level += (int)(Math.Exp((float)(i - 72) / 13.5f) * (float)this.level_scaling_r_depth); break; case 3: /* +LIN */ scaled_output_level += (int)((float)i / 45.0f * (float)this.level_scaling_r_depth); break; } if (scaled_output_level < 0) { scaled_output_level = 0; } if (scaled_output_level > 99) { scaled_output_level = 99; } } vel_adj = Data.dx7_voice_velocity_ol_adjustment[velocity] * (float)this.velocity_sens; /* DEBUG_MESSAGE(DB_NOTE, " dx7_op_envelope_prepare: s_o_l=%d, vel_adj=%f\n", scaled_output_level, vel_adj); */ /* -FIX- This calculation comes from Pinkston/Harrington; the original "* 6.0" scaling factor * was close to what my TX7 does, but tended to not bump the rate as much, so I changed it * to "* 6.5" which seems a little closer, but it's still not spot-on. */ /* Things which affect this calculation: transpose, ? */ /* rate_bump = lrintf((float)op.rate_scaling * (float)(transposed_note - 21) / (126.0f - 21.0f) * 127.0f / 128.0f * 6.0f - 0.5f); */ rate_bump = (int)Math.Round(((float)this.rate_scaling * (float)(transposed_note - 21) / (126.0f - 21.0f) * 127.0f / 128.0f * 6.5f - 0.5f)); /* -FIX- just a hunch: try it again with "* 6.0f" but also "(120.0f - 21.0f)" instead of "(126.0f - 21.0f)": */ /* rate_bump = lrintf((float)op.rate_scaling * (float)(transposed_note - 21) / (120.0f - 21.0f) * 127.0f / 128.0f * 6.0f - 0.5f); */ for (i = 0; i < 4; i++) { float level = (float)this.eg.base_level[i]; /* -FIX- is this scaling of eg.base_level values to og.level values correct, i.e. does a softer * velocity shorten the time, since the rate stays the same? */ level = level * (float)scaled_output_level / 99.0f + vel_adj; if (level < 0.0f) { level = 0.0f; } else if (level > 99.0f) { level = 99.0f; } this.eg.level[i] = (byte)Math.Round(level); this.eg.rate[i] = (byte)(this.eg.base_rate[i] + rate_bump); if (this.eg.rate[i] > 99) { this.eg.rate[i] = 99; } } this.eg.value = Inline.INT_TO_FP(this.eg.level[3]); this.eg.dx7_op_eg_set_phase(instance, 0); }
public void dx7_pitch_envelope_prepare(hexter_instance instance) { this.pitch_eg.value = Data.dx7_voice_pitch_level_to_shift[this.pitch_eg.level[3]]; this.pitch_eg.dx7_pitch_eg_set_phase(instance, 0); }