4op FM

4 operator FM/PM synthesizer, with freely configurable modulation path. See help file. Tags: fm pm synthesizer matrix 4 operator
Author: Sputnki
License: BSD
Github: sptnk/osc/4op FM.axo

Inlets

frac32 pitch CV

frac32 connect to some envelope to drive operator 1

frac32 connect to some envelope to drive operator 2

frac32 connect to some envelope to drive operator 3

frac32 connect to some envelope to drive operator 4

frac32 connect to the output of matrix row 4 object

Outlets

frac32buffer audio out

int32 connect to the input of matrix row 4 object

Parameters

int32 pitch multiplier for operator 1

int32 pitch multiplier for operator 2

int32 pitch multiplier for operator 3

int32 pitch multiplier for operator 4

frac32.u.map.gain output mix amount for operator 1

frac32.u.map.gain output mix amount for operator 2

frac32.u.map.gain output mix amount for operator 3

frac32.u.map.gain output mix amount for operator 4

bool32.tgl switch between PM (off) and FM mode. Classic DX sounds are achieved with PM mode, while FM is less stable and more quirky

frac32.s.map.pitch base pitch for the oscillator

Declaration
int32_t matrix[4][4] = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}};

int32_t tempv[4] = {0, 0, 0, 0};
int32_t temps = 0;

int sel = 0;

int32_t freq[4] = {0, 0, 0, 0};
uint32_t phase[4] = {0, 0, 0, 0};
uint32_t wave[4] = {0, 0, 0, 0};
uint32_t wave_old[4] = {0, 0, 0, 0};

int32_t envelope[4] = {0, 0, 0, 0};
int32_t envelope_old[4] = {0, 0, 0, 0};
int32_t increment[4] = {0, 0, 0, 0};

uint32_t gain[4] = {0, 0, 0, 0};
Control Rate
// modmatrix handling section
matrix[sel >> 2][sel - ((sel >> 2) << 2)] = inlet_matrix4 << 1;

sel += 1;
if (sel == 16)
  sel = 0;

outlet_matrix4 = sel;

// pitch handling section
MTOFEXTENDED(param_pitch + inlet_pitch, temps);

freq[0] = temps * param_op1p;
freq[1] = temps * param_op2p;
freq[2] = temps * param_op3p;
freq[3] = temps * param_op4p;

increment[0] = (inlet_env1 - envelope_old[0]) >> 4;
increment[1] = (inlet_env2 - envelope_old[1]) >> 4;
increment[2] = (inlet_env3 - envelope_old[2]) >> 4;
increment[3] = (inlet_env4 - envelope_old[3]) >> 4;

for (int i = 0; i < 4; i++)
  envelope[i] = envelope_old[i];

envelope_old[0] = inlet_env1;
envelope_old[1] = inlet_env2;
envelope_old[2] = inlet_env3;
envelope_old[3] = inlet_env4;

gain[0] = param_op1mix;
gain[1] = param_op2mix;
gain[2] = param_op3mix;
gain[3] = param_op4mix;
Audio Rate
// actual fm handling
for (int i = 0; i < 4; i++) {
  phase[i] += freq[i];
  temps = 0;
  if (param_mode) {
    for (int j = 0; j < 4; j++)
      if (i != j)
        phase[i] = ___SMMLA(matrix[i][j] << 3, wave_old[j] << 4, phase[i]);
    SINE2TINTERP((___SMMUL(matrix[i][i] << 3, wave_old[i] << 3) << 3) +
                     phase[i],
                 wave[i]);
  } else {
    for (int j = 0; j < 4; j++)
      temps += ___SMMUL(matrix[i][j] << ((i != j) ? 3 : 1), wave_old[j] << 4);
    SINE2TINTERP((temps << 4) + phase[i], wave[i]);
  }
  wave[i] = ___SMMUL(wave[i], envelope[i]);
  envelope[i] += increment[i];
}

// final output handling
temps = 0;
for (int i = 0; i < 4; i++) {
  wave_old[i] = wave[i];
  temps = ___SMMLA(wave_old[i], gain[i], temps);
}
outlet_out = temps << 1;

Privacy

© 2024 Zrna Research