Skip to content

Commit

Permalink
Fix SSG envelope control and document available values
Browse files Browse the repository at this point in the history
Still not sure what the two additional values do, so
documenting them with a bit of caution.
  • Loading branch information
YuriSizov committed Nov 13, 2024
1 parent 2961a4d commit 5414f34
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 25 deletions.
40 changes: 39 additions & 1 deletion doc_classes/SiOPMOperatorParams.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
Release rate of the pulse.
</member>
<member name="ssg_envelope_control" type="int" setter="set_ssg_envelope_control" getter="get_ssg_envelope_control">
Type of the SSG envelope control. This features uses the ADSR envelope to create a wave shape as the final envelope. The exact nature of the effect depends on this value.
Type of the SSG envelope control. This features uses the ADSR envelope to create a wave shape as the final envelope. Must be one of the [enum SSGEnvelopeControl] values, or another value in the valid range.
</member>
<member name="sustain_level" type="int" setter="set_sustain_level" getter="get_sustain_level">
Sustain level of the pulse.
Expand All @@ -72,4 +72,42 @@
Total level of the pulse.
</member>
</members>
<constants>
<constant name="SSG_DISABLED" value="0" enum="SSGEnvelopeControl">
SSG envelope control is disabled. Values [code]0[/code] through [code]7[/code] all mean that.
</constant>
<constant name="SSG_REPEAT_TO_ZERO" value="8" enum="SSGEnvelopeControl">
Repeat the ADSR envelope upon the volume reaching zero.
</constant>
<constant name="SSG_IGNORE" value="9" enum="SSGEnvelopeControl">
SSG envelope control is enabled, but the ADSR envelope is executed as is.
</constant>
<constant name="SSG_REPEAT_SHUTTLE" value="10" enum="SSGEnvelopeControl">
Repeat the ADSR envelope forward and backward, reversing the direction when the volume reaches zero or maximum.
</constant>
<constant name="SSG_ONCE_HOLD_HIGH" value="11" enum="SSGEnvelopeControl">
Execute the ADSR envelope as is, then stay at the maximum volume.
</constant>
<constant name="SSG_REPEAT_TO_MAX" value="12" enum="SSGEnvelopeControl">
Same as [constant SSG_REPEAT_TO_ZERO], but the direction is inverted.
</constant>
<constant name="SSG_INVERSE" value="13" enum="SSGEnvelopeControl">
Same as [constant SSG_IGNORE], but the direction is inverted.
</constant>
<constant name="SSG_REPEAT_SHUTTLE_INVERSE" value="14" enum="SSGEnvelopeControl">
Same as [constant SSG_REPEAT_SHUTTLE], but the direction is inverted.
</constant>
<constant name="SSG_ONCE_HOLD_LOW" value="15" enum="SSGEnvelopeControl">
Same as [constant SSG_ONCE_HOLD_HIGH], but the direction is inverted.
</constant>
<constant name="SSG_CONSTANT_HIGH" value="16" enum="SSGEnvelopeControl">
Custom mode that holds at the maximum volume throughout.
</constant>
<constant name="SSG_CONSTANT_LOW" value="17" enum="SSGEnvelopeControl">
Custom mode that holds at the zero volume throughout.
</constant>
<constant name="SSG_MAX" value="18" enum="SSGEnvelopeControl">
Total number of available SSG envelope control modes.
</constant>
</constants>
</class>
37 changes: 22 additions & 15 deletions src/chip/channels/siopm_operator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const int SiOPMOperator::_eg_next_state_table[2][EG_MAX] = {
void SiOPMOperator::set_attack_rate(int p_value) {
_attack_rate = p_value & 63;

if (_ssg_type == 8 || _ssg_type == 12) {
if (_ssg_type == SiOPMOperatorParams::SSG_REPEAT_TO_ZERO || _ssg_type == SiOPMOperatorParams::SSG_REPEAT_TO_MAX) {
_eg_ssgec_attack_rate = (_attack_rate >= 56) ? 1 : 0;
} else {
_eg_ssgec_attack_rate = (_attack_rate >= 60) ? 1 : 0;
Expand Down Expand Up @@ -152,15 +152,15 @@ void SiOPMOperator::set_mute(bool p_mute) {
}

void SiOPMOperator::set_ssg_type(int p_value) {
if (p_value > 7) {
if (p_value >= SiOPMOperatorParams::SSG_REPEAT_TO_ZERO) {
_eg_state_table_index = 1;
_ssg_type = p_value;
if (_ssg_type > 17) {
_ssg_type = 9;
if (_ssg_type >= SiOPMOperatorParams::SSG_MAX) {
_ssg_type = SiOPMOperatorParams::SSG_IGNORE;
}
} else {
_eg_state_table_index = 0;
_ssg_type = 0;
_ssg_type = SiOPMOperatorParams::SSG_DISABLED;
}
}

Expand Down Expand Up @@ -303,7 +303,7 @@ void SiOPMOperator::_shift_eg_state(EGState p_state) {
_eg_state = EG_ATTACK;
_eg_level_table = make_vector<int>(_table->eg_level_tables[0]);

int index = (_attack_rate != 0) ? (_attack_rate + _eg_key_scale_rate) : 96;
const int index = (_attack_rate != 0) ? (_attack_rate + _eg_key_scale_rate) : 96;
_eg_increment_table = make_vector<int>(_table->eg_increment_tables_attack[_table->eg_table_selector[index]]);
_eg_timer_step = _table->eg_timer_steps[index];
break;
Expand All @@ -315,15 +315,16 @@ void SiOPMOperator::_shift_eg_state(EGState p_state) {
if (_eg_sustain_level) {
_eg_state = EG_DECAY;

if (_ssg_type != 0) {
if (_ssg_type > SiOPMOperatorParams::SSG_REPEAT_TO_ZERO) {
_eg_level = 0;

_eg_state_shift_level = _eg_sustain_level >> 2;
if (_eg_state_shift_level > SiOPMRefTable::ENV_BOTTOM_SSGEC) {
_eg_state_shift_level = SiOPMRefTable::ENV_BOTTOM_SSGEC;
}

int level_index = _table->eg_ssg_table_index[_ssg_type - 8][_eg_ssgec_attack_rate][_eg_ssgec_state];
const int normalized_ssg_type = _ssg_type - SiOPMOperatorParams::SSG_REPEAT_TO_ZERO;
const int level_index = _table->eg_ssg_table_index[normalized_ssg_type][_eg_ssgec_attack_rate][_eg_ssgec_state];
_eg_level_table = make_vector<int>(_table->eg_level_tables[level_index]);
} else {
_eg_level = 0;
Expand All @@ -342,19 +343,20 @@ void SiOPMOperator::_shift_eg_state(EGState p_state) {
case EG_SUSTAIN: {
_eg_state = EG_SUSTAIN;

if (_ssg_type != 0) {
if (_ssg_type >= SiOPMOperatorParams::SSG_REPEAT_TO_ZERO) {
_eg_level = _eg_sustain_level >> 2;
_eg_state_shift_level = SiOPMRefTable::ENV_BOTTOM_SSGEC;

int level_index = _table->eg_ssg_table_index[_ssg_type - 8][_eg_ssgec_attack_rate][_eg_ssgec_state];
const int normalized_ssg_type = _ssg_type - SiOPMOperatorParams::SSG_REPEAT_TO_ZERO;
const int level_index = _table->eg_ssg_table_index[normalized_ssg_type][_eg_ssgec_attack_rate][_eg_ssgec_state];
_eg_level_table = make_vector<int>(_table->eg_level_tables[level_index]);
} else {
_eg_level = _eg_sustain_level;
_eg_state_shift_level = SiOPMRefTable::ENV_BOTTOM;
_eg_level_table = make_vector<int>(_table->eg_level_tables[0]);
}

int index = (_sustain_rate != 0) ? (_sustain_rate + _eg_key_scale_rate) : 96;
const int index = (_sustain_rate != 0) ? (_sustain_rate + _eg_key_scale_rate) : 96;
_eg_increment_table = make_vector<int>(_table->eg_increment_tables[_table->eg_table_selector[index]]);
_eg_timer_step = _table->eg_timer_steps[index];
} break;
Expand All @@ -363,9 +365,14 @@ void SiOPMOperator::_shift_eg_state(EGState p_state) {
if (_eg_level < SiOPMRefTable::ENV_BOTTOM) {
_eg_state = EG_RELEASE;
_eg_state_shift_level = SiOPMRefTable::ENV_BOTTOM;
_eg_level_table = make_vector<int>(_table->eg_level_tables[_ssg_type != 0 ? 1 : 0]);

int index = _release_rate + _eg_key_scale_rate;
if (_ssg_type >= SiOPMOperatorParams::SSG_REPEAT_TO_ZERO) {
_eg_level_table = make_vector<int>(_table->eg_level_tables[1]);
} else {
_eg_level_table = make_vector<int>(_table->eg_level_tables[0]);
}

const int index = _release_rate + _eg_key_scale_rate;
_eg_increment_table = make_vector<int>(_table->eg_increment_tables[_table->eg_table_selector[index]]);
_eg_timer_step = _table->eg_timer_steps[index];
break;
Expand Down Expand Up @@ -462,7 +469,7 @@ void SiOPMOperator::set_operator_params(const Ref<SiOPMOperatorParams> &p_params
_pitch_index_shift = p_params->get_detune2();

_mute = p_params->is_mute() ? SiOPMRefTable::ENV_BOTTOM : 0;
_ssg_type = p_params->get_ssg_envelope_control();
set_ssg_type(p_params->get_ssg_envelope_control());
_envelope_reset_on_attack = p_params->is_envelope_reset_on_attack();

if (p_params->get_fixed_pitch() > 0) {
Expand Down Expand Up @@ -498,7 +505,7 @@ void SiOPMOperator::get_operator_params(const Ref<SiOPMOperatorParams> &r_params
r_params->set_detune2(get_ptss_detune());
r_params->set_amplitude_modulation_shift(get_amplitude_modulation_shift());

r_params->set_ssg_envelope_control(_ssg_type);
r_params->set_ssg_envelope_control(get_ssg_type());
r_params->set_envelope_reset_on_attack(is_envelope_reset_on_attack());

r_params->set_initial_phase(get_key_on_phase());
Expand Down
25 changes: 24 additions & 1 deletion src/chip/siopm_operator_params.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ void SiOPMOperatorParams::set_multiple(int p_value) {
fine_multiple = (p_value == 0 ? 64 : (p_value << 7));
}

void SiOPMOperatorParams::set_ssg_envelope_control(int p_value) {
if (p_value >= SSGEnvelopeControl::SSG_MAX) {
ssg_envelope_control = SSGEnvelopeControl::SSG_IGNORE;
} else if (p_value < SSGEnvelopeControl::SSG_REPEAT_TO_ZERO) {
ssg_envelope_control = SSGEnvelopeControl::SSG_DISABLED;
} else {
ssg_envelope_control = p_value;
}
}

void SiOPMOperatorParams::initialize() {
pulse_generator_type = SiONPulseGeneratorType::PULSE_SINE;
pitch_table_type = SiONPitchTableType::PITCH_TABLE_OPM;
Expand All @@ -45,7 +55,7 @@ void SiOPMOperatorParams::initialize() {
fixed_pitch = 0;

mute = false;
ssg_envelope_control = 0;
ssg_envelope_control = SSG_DISABLED;
frequency_modulation_level = 5;
envelope_reset_on_attack = false;
}
Expand Down Expand Up @@ -183,6 +193,19 @@ void SiOPMOperatorParams::_bind_methods() {
ClassDB::add_property("SiOPMOperatorParams", PropertyInfo(Variant::INT, "ssg_envelope_control"), "set_ssg_envelope_control", "get_ssg_envelope_control");
ClassDB::add_property("SiOPMOperatorParams", PropertyInfo(Variant::INT, "frequency_modulation_level"), "set_frequency_modulation_level", "get_frequency_modulation_level");
ClassDB::add_property("SiOPMOperatorParams", PropertyInfo(Variant::BOOL, "envelope_reset_on_attack"), "set_envelope_reset_on_attack", "is_envelope_reset_on_attack");

BIND_ENUM_CONSTANT(SSG_DISABLED);
BIND_ENUM_CONSTANT(SSG_REPEAT_TO_ZERO);
BIND_ENUM_CONSTANT(SSG_IGNORE);
BIND_ENUM_CONSTANT(SSG_REPEAT_SHUTTLE);
BIND_ENUM_CONSTANT(SSG_ONCE_HOLD_HIGH);
BIND_ENUM_CONSTANT(SSG_REPEAT_TO_MAX);
BIND_ENUM_CONSTANT(SSG_INVERSE);
BIND_ENUM_CONSTANT(SSG_REPEAT_SHUTTLE_INVERSE);
BIND_ENUM_CONSTANT(SSG_ONCE_HOLD_LOW);
BIND_ENUM_CONSTANT(SSG_CONSTANT_HIGH);
BIND_ENUM_CONSTANT(SSG_CONSTANT_LOW);
BIND_ENUM_CONSTANT(SSG_MAX);
}

SiOPMOperatorParams::SiOPMOperatorParams() {
Expand Down
28 changes: 27 additions & 1 deletion src/chip/siopm_operator_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,30 @@ class SiOPMOperatorParams : public RefCounted {
friend class SiOPMChannelParams;
friend class TranslatorUtil;

public:
// Explanations sourced from https://mml-guide.readthedocs.io/pmd/ssgeg/.
enum SSGEnvelopeControl {
SSG_DISABLED = 0, // Values 0-7 mean it's disabled.

SSG_REPEAT_TO_ZERO = 8, // Repeats the ADSR envelope upon the volume reaching 0.
SSG_IGNORE = 9, // As if SSG-EG was disabled, though it is enabled.
SSG_REPEAT_SHUTTLE = 10, // Repeats the ADSR envelope forward and backward, reversing the direction when the volume reaches zero or maximum.
SSG_ONCE_HOLD_HIGH = 11, // Uses the ADSR envelope, then stays at the maximum volume.

// Same as above 4, but inverted (volume 0 is maximum, and vice versa).
SSG_REPEAT_TO_MAX = 12,
SSG_INVERSE = 13,
SSG_REPEAT_SHUTTLE_INVERSE = 14,
SSG_ONCE_HOLD_LOW = 15,

// These are not standard, not exactly sure about their purpose.
SSG_CONSTANT_HIGH = 16,
SSG_CONSTANT_LOW = 17,

SSG_MAX
};

private:
// Pulse generator type [0,511]
int pulse_generator_type = SiONPulseGeneratorType::PULSE_SINE;
// Pitch table type [0,7]
Expand Down Expand Up @@ -113,7 +137,7 @@ class SiOPMOperatorParams : public RefCounted {
bool is_mute() const { return mute; }
void set_mute(bool p_mute) { mute = p_mute; }
int get_ssg_envelope_control() const { return ssg_envelope_control; }
void set_ssg_envelope_control(int p_value) { ssg_envelope_control = p_value; }
void set_ssg_envelope_control(int p_value);
int get_frequency_modulation_level() const { return frequency_modulation_level; }
void set_frequency_modulation_level(int p_value) { frequency_modulation_level = p_value; }
bool is_envelope_reset_on_attack() const { return envelope_reset_on_attack; }
Expand All @@ -126,4 +150,6 @@ class SiOPMOperatorParams : public RefCounted {
~SiOPMOperatorParams() {}
};

VARIANT_ENUM_CAST(SiOPMOperatorParams::SSGEnvelopeControl);

#endif // SIOPM_OPERATOR_PARAMS_H
15 changes: 8 additions & 7 deletions src/chip/siopm_ref_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,13 @@ class SiOPMRefTable {
/*16*/ { 1,1, 1,1, 1,1, 1,1 }, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
/*17*/ { 0,0, 0,0, 0,0, 0,0 } /* infinity rates for attack and decay(s) */
};
// EG table selector.
int eg_table_selector[128]; // 128 = 64 rates + 32 ks-rates + 32 dummies for dr,sr=0
// EG timer step.
int eg_timer_steps[128]; // 128 = 64 rates + 32 ks-rates + 32 dummies for dr,sr=0
// EG table selector. 128 = 64 rates + 32 ks-rates + 32 dummies for dr,sr=0
int eg_table_selector[128];
// EG timer step. 128 = 64 rates + 32 ks-rates + 32 dummies for dr,sr=0
int eg_timer_steps[128];
// EG table to calculate EG level tables.
int eg_level_tables[7][1 << ENV_BITS];
// EG table for SSG-type to EG level tables index.
// EG table for SSG-type to EG level tables index. 10 = 8 standard + 2 extra.
int eg_ssg_table_index[10][2][3] = {
// [w/ ar], [w/o ar]
{ {3,3,3}, {1,3,3} }, // ssgec=8
Expand All @@ -199,8 +199,9 @@ class SiOPMRefTable {
{ {2,5,5}, {2,5,5} }, // ssgec=13
{ {1,2,1}, {2,1,2} }, // ssgec=14
{ {1,6,6}, {2,6,6} }, // ssgec=15
{ {1,1,1}, {1,1,1} }, // ssgec=8+
{ {2,2,2}, {2,2,2} } // ssgec=12+

{ {1,1,1}, {1,1,1} }, // ssgec=16
{ {2,2,2}, {2,2,2} } // ssgec=17
};
// EG sustain level table from 15 to 1024.
int eg_sustain_level_table[16];
Expand Down

0 comments on commit 5414f34

Please sign in to comment.