/* Chassis polysynth framework Copyright 2024 Gordon JC Pearce Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "chassis.hpp" void Chassis::initParameter(uint32_t index, Parameter& parameter) { parameter.ranges.min = 0.0f; parameter.ranges.max = 127.0f; parameter.hints = kParameterIsAutomatable; parameter.ranges.def = 0.0f; switch (index) { case pLfoRate: parameter.name = "LFO Rate"; parameter.symbol = "ch_lforate"; parameter.midiCC = 3; break; case pLfoDelay: parameter.hints = kParameterIsAutomatable; parameter.name = "LFO Delay"; parameter.symbol = "ch_lfodelay"; parameter.midiCC = 9; break; case pVcoRange: parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.name = "Range"; parameter.symbol = "ch_vcorange"; parameter.ranges.max = 2.0f; parameter.midiCC = 12; parameter.enumValues.count = 3; parameter.enumValues.restrictedMode = true; { ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3]; enumValues[0].value = 0.0f; enumValues[0].label = "16'"; enumValues[1].value = 1.0f; enumValues[1].label = "8'"; enumValues[2].value = 2.0f; enumValues[2].label = "4'"; parameter.enumValues.values = enumValues; } break; case pLfoDepth: parameter.hints = kParameterIsAutomatable; parameter.name = "LFO"; parameter.symbol = "ch_lfo"; parameter.midiCC = 13; break; case pPwmDepth: parameter.hints = kParameterIsAutomatable; parameter.name = "PWM"; parameter.symbol = "ch_pwm"; parameter.midiCC = 14; break; /* case paramPWMMode: parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.name = "PWM Mode"; parameter.symbol = "ch_pwmmode"; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; parameter.ranges.def = 1.0f; parameter.midiCC = 15; parameter.enumValues.count = 2; parameter.enumValues.restrictedMode = true; { ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; enumValues[0].value = 0.0f; enumValues[0].label = "LFO"; enumValues[1].value = 1.0f; enumValues[1].label = "MAN"; parameter.enumValues.values = enumValues; } break; */ case pSaw: parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.name = "Saw"; parameter.symbol = "ch_saw"; parameter.ranges.max = 1.0f; parameter.midiCC = 17; break; case pSqr: parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.name = "Square"; parameter.symbol = "ch_sqr"; parameter.ranges.max = 1.0f; parameter.midiCC = 16; break; case pSubLevel: parameter.hints = kParameterIsAutomatable; parameter.name = "Sub Osc"; parameter.symbol = "ch_sub"; parameter.midiCC = 18; break; case pNoiseLevel: parameter.hints = kParameterIsAutomatable; parameter.name = "Noise"; parameter.symbol = "ch_noise"; parameter.midiCC = 19; break; case pHpf: parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.name = "HPF"; parameter.symbol = "ch_hpf"; parameter.ranges.max = 4.0f; parameter.midiCC = 20; break; case pCutoff: parameter.hints = kParameterIsAutomatable; parameter.name = "Freq"; parameter.symbol = "ch_freq"; parameter.midiCC = 74; break; case pRes: parameter.hints = kParameterIsAutomatable; parameter.name = "Res"; parameter.symbol = "ch_reso"; parameter.midiCC = 71; break; /* case paramVCFMode: parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.name = "Polarity"; parameter.symbol = "ch_vcfmode"; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; parameter.ranges.def = 1.0f; parameter.midiCC = 21; parameter.enumValues.count = 2; parameter.enumValues.restrictedMode = true; { ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; enumValues[0].value = 0.0f; enumValues[0].label = "POS"; enumValues[1].value = 1.0f; enumValues[1].label = "INV"; parameter.enumValues.values = enumValues; } */ break; case pEnv: parameter.hints = kParameterIsAutomatable; parameter.name = "Env"; parameter.symbol = "ch_vcfenv"; parameter.midiCC = 22; break; case pLfo: parameter.hints = kParameterIsAutomatable; parameter.name = "LFO"; parameter.symbol = "ch_vcflfo"; parameter.midiCC = 23; break; case pKyb: parameter.hints = kParameterIsAutomatable; parameter.name = "Kybd"; parameter.symbol = "ch_vcfkey"; parameter.midiCC = 24; break; case pAtk: parameter.hints = kParameterIsAutomatable; parameter.name = "Attack"; parameter.symbol = "ch_attack"; parameter.midiCC = 73; break; case pDcy: parameter.hints = kParameterIsAutomatable; parameter.name = "Decay"; parameter.symbol = "ch_decay"; parameter.midiCC = 75; break; case pStn: parameter.hints = kParameterIsAutomatable; parameter.name = "Sustain"; parameter.symbol = "ch_sustain"; parameter.midiCC = 27; break; case pRls: parameter.hints = kParameterIsAutomatable; parameter.name = "Release"; parameter.symbol = "ch_release"; parameter.midiCC = 72; break; /* case paramEnvGate: parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean; parameter.name = "Env-Gate"; parameter.symbol = "ch_envgate"; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; parameter.ranges.def = 1.0f; parameter.midiCC = 25; parameter.enumValues.count = 2; parameter.enumValues.restrictedMode = true; { ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; enumValues[0].value = 0.0f; enumValues[0].label = "ENV"; enumValues[1].value = 1.0f; enumValues[1].label = "GATE"; parameter.enumValues.values = enumValues; } break; */ case pVcaLevel: parameter.hints = kParameterIsAutomatable; parameter.name = "VCA Level"; parameter.symbol = "ch_vcalevel"; parameter.midiCC = 26; break; case pChorus: parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.name = "Chorus Mode"; parameter.symbol = "ch_chorus"; parameter.ranges.max = 2.0f; parameter.midiCC = 12; parameter.enumValues.count = 3; parameter.enumValues.restrictedMode = true; { ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3]; enumValues[0].value = 0.0f; enumValues[0].label = "Off"; enumValues[1].value = 1.0f; enumValues[1].label = "Slow"; enumValues[2].value = 2.0f; enumValues[2].label = "Fast"; parameter.enumValues.values = enumValues; } break; /* case paramModWheel: parameter.hints = kParameterIsAutomatable | kParameterIsHidden; parameter.name = "Mod wheel"; parameter.symbol = "ch_modwheel"; parameter.ranges.min = 0.0f; parameter.ranges.max = 127.0f; parameter.ranges.def = 0.0f; parameter.midiCC = 1; break; */ } // chorus, porta, bend range, key mode still to do } void Chassis::setParameterValue(uint32_t index, float value) { // should be trapped by host, but let's be safe if (value < 0.0f) value = 0.0f; if (value > 127.0f) value = 127.0f; switch (index) { case pLfoRate: patchRam.lfoRate = value; break; case pLfoDelay: patchRam.lfoDelay = value; break; case pLfoDepth: patchRam.vcoLfo = value; break; case pPwmDepth: patchRam.pwmLfo = value / 1.27; break; case pSubLevel: patchRam.sub = value; break; case pNoiseLevel: patchRam.noise = value; break; case pCutoff: patchRam.vcfFreq = value; break; case pRes: patchRam.vcfReso = value; break; case pEnv: patchRam.vcfEnv = value; break; case pLfo: patchRam.vcfLfo = value; break; case pKyb: patchRam.vcfKey = value; break; case pVcaLevel: patchRam.vca = value; break; case pAtk: patchRam.env_a = value; break; case pDcy: patchRam.env_d = value; break; case pStn: patchRam.env_s = value; break; case pRls: patchRam.env_r = value; break; // switch 1 params case pVcoRange: // bits 0-2 of switch 1 // doesn't look great in Carla because of odd behaviour with small integer knobs patchRam.switch1 &= 0xf8; patchRam.switch1 |= (1 << (int)(value - 1)); break; case pSqr: // bit 3 of switch 1 patchRam.switch1 &= 0xf7; patchRam.switch1 |= (value >= 0.5) << 3; break; case pSaw: // bit 4 of switch 1 patchRam.switch1 &= 0xef; patchRam.switch1 |= (value >= 0.5) << 4; break; case pChorus: patchRam.switch1 &= 0x9f; // 60, 40, 00 switch ((int)value) { case 0: patchRam.switch1 |= 0x60; break; case 1: patchRam.switch1 |= 0x40; break; case 2: patchRam.switch1 |= 0x00; break; } break; /* // switch 2 params case paramPWMMode: // bit 0 of switch 2 patchRam.switch2 &= 0xfe; patchRam.switch2 |= (value >= 0.5); break; case paramVCFMode: // bit 1 of switch 2 patchRam.switch2 &= 0xfd; patchRam.switch2 |= (value >= 0.5) << 1; break; case paramEnvGate: patchRam.switch2 &= 0xfb; patchRam.switch2 |= (value >= 0.5) << 2; break; */ case pHpf: // bits 3-4 of switch 2 // doesn't look great in Carla because of odd behaviour with small integer knobs if (value > 3) value = 3; patchRam.switch2 &= 0xe7; patchRam.switch2 |= (3 - (int)value) << 3; break; /* case paramModWheel: s.ff64 = (int)value << 1; break; */ } } float Chassis::getParameterValue(uint32_t index) const { switch (index) { case pLfoRate: return patchRam.lfoRate; break; case pLfoDelay: return patchRam.lfoDelay; break; case pVcoRange: // FIXME this needs to be better generally switch (patchRam.switch1 & 0x07) { case 1: return 0; break; case 4: return 2; break; default: return 1; } break; case pLfoDepth: return patchRam.vcoLfo; break; case pPwmDepth: return patchRam.pwmLfo * 1.27f; break; /* case paramPWMMode: return (patchRam.switch2 & 0x01) != 0; break; */ case pSaw: return (patchRam.switch1 & 0x10) != 0; break; case pSqr: return (patchRam.switch1 & 0x08) != 0; case pSubLevel: return patchRam.sub; break; case pNoiseLevel: return patchRam.noise; break; case pHpf: return 3 - ((patchRam.switch2 & 0x18) >> 3); break; case pCutoff: return patchRam.vcfFreq; break; case pRes: return patchRam.vcfReso; break; case pEnv: return patchRam.vcfEnv; break; case pLfo: return patchRam.vcfLfo; break; case pKyb: return patchRam.vcfKey; break; /* case paramVCFMode: return (patchRam.switch2 & 0x02) != 0; break; */ case pAtk: return patchRam.env_a; break; case pDcy: return patchRam.env_d; break; case pStn: return patchRam.env_s; break; case pRls: return patchRam.env_r; break; /* case paramEnvGate: return (patchRam.switch2 & 0x04) != 0; */ case pVcaLevel: return patchRam.vca; break; case pChorus: // FIXME this needs to be better generally switch (patchRam.switch1 & 0x60) { case 0x40: return 1; case 0x00: return 2; default: return 0; } break; } return 0; }