envelope_1(); envelope_2(); // copy modulation sources in arry and scaled 0-255 ------------------- uint8_t modulation_sources[7]; modulation_sources[MOD_SRC_ENV1] = Env1.value_ >> 8; // max. Value = 255 modulation_sources[MOD_SRC_ENV2] = Env2.value_ >> 8; // max. Value = 255 modulation_sources[MOD_SRC_LFO1] = lfo1_out; // max. Value = 255 modulation_sources[MOD_SRC_LFO2] = lfo2_out; // max. Value = 255 modulation_sources[MOD_SRC_VELO] = midi_velocity << 1; // max. Value = 255 modulation_sources[MOD_SRC_NOTE] = midi_note << 1; // max. Value = 255 modulation_sources[MOD_SRC_NOISE] = rng_state8; // max. Value = 255 // destinations result values in arry --------------------------------- uint16_t modulation_destination[8]; modulation_destination[MOD_DST_VCA]; // VCA_gain modulation_destination[MOD_DST_VCF]; // VCF_cutoff modulation_destination[MOD_DST_RESO]; // resonance modulation_destination[MOD_DST_LFO1]; // LFO1 modulation_destination[MOD_DST_LFO2]; // LFO2 modulation_destination[MOD_DST_OSC_FINE]; // Osc1 fine modulation_destination[MOD_DST_VCA_BAL]; // VAC balance modulation_destination[MOD_DST_Fx_BAL]; // Fx balance // Load initial values of modulation and scale to 0-8192 ------------- uint16_t dst_[8]; dst_[MOD_DST_VCA] = 255; // max value vca_gain dst_[MOD_DST_VCF] = cutoff_value1 << 7; // max value cutoff dst_[MOD_DST_RESO] = resonanc_value1 << 7; // max value resonance dst_[MOD_DST_LFO1] = 8192; // max value lfo1 dst_[MOD_DST_LFO2] = 8192; // max value lfo2 dst_[MOD_DST_OSC_FINE] = 8192; // max value osc1 fine dst_[MOD_DST_VCA_BAL] = 8192; // max value vca balance dst_[MOD_DST_Fx_BAL] = 8192; // max fx balance // calc modulation ---------------------------------------------------- for (uint8_t slot = 0; slot < 6; slot++) { uint8_t source_number = slot_array[slot][0]; // load source.nr uint8_t destination_number = slot_array[slot][1]; // load destination.nr int8_t amount = (slot_array[slot][2]-63); // load amount value (0-127) and make signed -63/+64 uint8_t source_value = modulation_sources[source_number]; // load source.value // target modulation without VCA ---------------------------------- if (destination_number != MOD_DST_VCA) { int16_t modulation_ = dst_[destination_number]; if (source_number == MOD_SRC_LFO1 || source_number == MOD_SRC_LFO2 || source_number == MOD_SRC_NOTE || source_number == MOD_SRC_ENV2) { modulation_ += S8S8Mul(amount, source_value + 128); } else { modulation_ += S8U8Mul(amount, source_value); } dst_[destination_number] = S16ClipU14(modulation_); } // target modulation only VCA (modulation multiplicativ) ---------- else { if (amount < 0) { amount = -amount; source_value = 255 - source_value; } if (amount != 64) { source_value = U8Mix(255, source_value, amount << 2); } dst_[MOD_DST_VCA] = U8U8MulShift8(dst_[MOD_DST_VCA], source_value); } } // Update destinations ------------------------------------------------ // update vca_gain 12Bit modulation_destination[MOD_DST_VCA] = dst_[MOD_DST_VCA] << 4; CV_VCA_Cha1 = modulation_destination[MOD_DST_VCA]; CV_VCA_Cha2 = modulation_destination[MOD_DST_VCA]; // update vcf_cufoff 12Bit modulation_destination[MOD_DST_VCF] = S16ClipU14(dst_[MOD_DST_VCF]) >> 2; CV_VCF_Cha1 = modulation_destination[MOD_DST_VCF]; CV_VCF_Cha2 = modulation_destination[MOD_DST_VCF]; // update resonance 8Bit modulation_destination[MOD_DST_RESO] = S16ClipU14(dst_[MOD_DST_RESO]) >> 6; CV_Reso1 = modulation_destination[MOD_DST_RESO]; CV_Reso2 = modulation_destination[MOD_DST_RESO]; // set Fx values ------------------------------------------------------ CV_delay_time = delay_time1; CV_delay_feedback = delay_feedback; CV_delay_value1 = delay_volume1; CV_delay_value2 = delay_volume2; } //------------------------------------------------------------------------- // calc routine (avr gcc inline assembler) //------------------------------------------------------------------------- static inline uint8_t U8U8MulShift8(uint8_t a, uint8_t b) { uint8_t result; asm( "mul %1, %2" "\n\t" "mov %0, r1" "\n\t" "eor r1, r1" "\n\t" : "=r" (result) : "a" (a), "a" (b) ); return result; } static inline uint16_t U16ShiftRight4(uint16_t a) { uint16_t result; asm( "movw %A0, %A1" "\n\t" "lsr %B0" "\n\t" "ror %A0" "\n\t" "lsr %B0" "\n\t" "ror %A0" "\n\t" "lsr %B0" "\n\t" "ror %A0" "\n\t" "lsr %B0" "\n\t" "ror %A0" "\n\t" : "=r" (result) : "a" (a) ); return result; } static inline uint16_t U16U8MulShift8(uint16_t a, uint8_t b) { uint16_t result; asm( "eor %B0, %B0" "\n\t" "mul %A1, %A2" "\n\t" "mov %A0, r1" "\n\t" "mul %B1, %A2" "\n\t" "add %A0, r0" "\n\t" "adc %B0, r1" "\n\t" "eor r1, r1" "\n\t" : "=&r" (result) : "a" (a), "a" (b) ); return result; } static inline uint16_t U8MixU16(uint8_t a, uint8_t b, uint8_t balance) { uint16_t result; asm( "mul %3, %2" "\n\t" // b * balance "movw %A0, r0" "\n\t" // to sum "com %2" "\n\t" // 255 - balance "mul %1, %2" "\n\t" // a * (255 - balance) "com %2" "\n\t" // reset balance to its previous value "add %A0, r0" "\n\t" // add to sum L "adc %B0, r1" "\n\t" // add to sum H "eor r1, r1" "\n\t" // reset r1 after multiplication : "&=r" (result) : "a" (a), "a" (balance), "a" (b) ); return result; } static inline uint8_t InterpolateSample( //const uint8_t* table, uint16_t phase) { const uint8_t table[], uint16_t phase) { uint8_t result; uint8_t work; asm( "movw r30, %A2" "\n\t" // copy base address to r30:r31 "add r30, %B3" "\n\t" // increment table address by phaseH "adc r31, r1" "\n\t" // just carry "mov %1, %A3" "\n\t" // move phaseL to working register "lpm %0, z+" "\n\t" // load sample[n] "lpm r1, z+" "\n\t" // load sample[n+1] "mul %1, r1" "\n\t" // multiply second sample by phaseL "movw r30, r0" "\n\t" // result to accumulator "com %1" "\n\t" // 255 - phaseL -> phaseL "mul %1, %0" "\n\t" // multiply first sample by phaseL "add r30, r0" "\n\t" // accumulate L "adc r31, r1" "\n\t" // accumulate H "eor r1, r1" "\n\t" // reset r1 after multiplication "mov %0, r31" "\n\t" // use sum H as output : "=r" (result), "=r" (work) : "r" (table), "r" (phase) : "r30", "r31" ); return result; } static inline int16_t S8U8Mul(int8_t a, uint8_t b) { int16_t result; asm( "mulsu %1, %2" "\n\t" "movw %0, r0" "\n\t" "eor r1, r1" "\n\t" : "=r" (result) : "a" (a), "a" (b) ); return result; } static inline int16_t S8S8Mul(int8_t a, int8_t b) { int16_t result; asm( "muls %1, %2" "\n\t" "movw %0, r0" "\n\t" "eor r1, r1" "\n\t" : "=r" (result) : "a" (a), "a" (b) ); return result; } static inline uint8_t U8Mix(uint8_t a, uint8_t b, uint8_t balance) { uint16_t sum; asm( "mul %3, %2" "\n\t" // b * balance "movw %A0, r0" "\n\t" // to sum "com %2" "\n\t" // 255 - balance "mul %1, %2" "\n\t" // a * (255 - balance) "com %2" "\n\t" // reset balance to its previous value "add %A0, r0" "\n\t" // add to sum L "adc %B0, r1" "\n\t" // add to sum H "eor r1, r1" "\n\t" // reset r1 after multiplication : "&=r" (sum) : "a" (a), "a" (balance), "a" (b) ); return sum >> 8; } static inline int16_t S16S8MulShift8(int16_t a, int8_t b) { int16_t result; asm( "eor %B0, %B0" "\n\t" "muls %A2, %B1" "\n\t" "movw %A0, r0" "\n\t" "mulsu %A2, %A1" "\n\t" "eor r0, r0" "\n\t" "sbc %B0, r0" "\n\t" "add %A0, r1" "\n\t" "adc %B0, r0" "\n\t" "eor r1, r1" "\n\t" : "=&r" (result) : "a" (a), "a" (b) ); return result; } static inline int16_t S16ClipU14(int16_t value) { uint8_t msb = value >> 8; if (msb & 0x80) { return 0; } if (msb & 0x40) { return 16383; } return value; } // The code generated by gcc for >> 6 is short but uses a loop. This saves // a couple of cycles. Note that this solution only works for operands with // a 14-bits resolution. static inline uint8_t U14ShiftRight6(uint16_t value) { uint8_t b = value >> 8; uint8_t a = value & 0xff; uint8_t result; asm( "add %1, %1" "\n\t" "adc %2, %2" "\n\t" "add %1, %1" "\n\t" "adc %2, %2" "\n\t" : "=r" (result) : "a" (a), "0" (b) ); return result; } static inline uint16_t U8U8Mul(uint8_t a, uint8_t b) { uint16_t result; asm( "mul %1, %2" "\n\t" "movw %0, r0" "\n\t" "eor r1, r1" "\n\t" : "=r" (result) : "a" (a), "a" (b) ); return result; }