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 void dx7_eg_init_constants() { float duration = Data.dx7_voice_eg_rate_rise_duration[99] * (Data.dx7_voice_eg_rate_rise_percent[99] - Data.dx7_voice_eg_rate_rise_percent[0]); this.dx7_eg_max_slew = (int)Math.Round((float)Inline.INT_TO_FP(99) / (duration * this.sample_rate)); this.nugget_rate = this.sample_rate / (float)Constants.HEXTER_NUGGET_SIZE; this.ramp_duration = (UInt32)Math.Round(this.sample_rate * 0.006f); /* 6ms ramp */ }
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_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_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; } }
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); }