complexDualSVF

dual state-variable filter with lots of different configuration options. read the desciption of the controls to know what they do or just hit "rnd" till you got a nice preset. Editing of the config controls is directly send to the preset position in the internal array. amount of table-presets can be set with the "presets" control. for quick-setting the filter, you can use the "rnd" button at the bottom, which will randomise all the config settings. table of presets can be saved by sending a gate-high to the save input table of presets can be loaded by sending a gate-low to the save input Remember, changing a setting directly overwrites the internal table, but doesn't overwrite the saved table unless you actually save it (connect a button to "save" input and hit it while in play). To load presets from a file, send a trigger to the "load" input. Using an or-combiner, you can send an automatic pulse when you start a patch, auto-loading the selected file.
Author: Remco van der Most
License: BSD
Github: sss/filter/complexDualSVF.axo

Inlets

frac32buffer filter input

frac32buffer in2

frac32 center

frac32 width

frac32 reso1

frac32 reso2

charptr32 filename

int32 preset

bool32 save table

bool32 load table

Outlets

frac32buffer filter output

Parameters

int32.mini mute or sends input 1 to filter 1 positively or inverted

int32.mini mute or sends input 2 to filter 1 positively or inverted

int32.mini use (inverted) lowpass of filter 1

int32.mini use (inverted) highpass of filter 1

int32.mini use (inverted) bandpass of filter 1

int32.mini use (inverted) notch of filter 1

int32.mini mix input 1 to filter 1 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mix input 2 to filter 1 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mute or sends input 1 to filter 2 input positively or inverted

int32.mini mute or sends input 2 to filter 2 input positively or inverted

int32.mini mute or sends mixed output of filter1 to filter 2 input positively or inverted

int32.mini mute or sends lowpass output of filter 1 to filter 2 input positively or inverted

int32.mini mute or sends highpass output of filter 1 to filter 2 input positively or inverted

int32.mini mute or sends bandpass output of filter 1 to filter 2 input positively or inverted

int32.mini mute or sends notch output of filter 1 to filter 2 input positively or inverted

int32.mini use (inverted) lowpass of filter 2

int32.mini use (inverted) highpass of filter 2

int32.mini use (inverted) bandpass of filter 2

int32.mini use (inverted) notch of filter 2

int32.mini mix input of filter 2 to filter 1 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mix input 1 to filter 2 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mix input 2 to filter 2 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mix the mix of filter 1 to filter 1 output (when opposite values are used for input/output mix, filter response is inverted for that channel)

int32.mini mute or sends mixed output of filter 1 to the module's output positively or inverted

int32.mini mute or sends mixed output of filter 2 to the module's output positively or inverted

bool32.mom randomises all the configuration controls

int32 read/adjust preset in table

frac32.s.map.pitch sets the center frequency of the filter. Both filters' cutoff will be at either side of this frequency, depending on the "width" knob

frac32.s.map.pitch sets the offset frequency for both filters from center position (filter 1 responds inversely from filter 2)

frac32.u.map.filterq resonance amount of filter 1

frac32.u.map.filterq resonance amount of filter 2

bool32.tgl link cutoff frequency of filter 2 to filter 1

bool32.tgl sets filter 2 to on (when off, only filter 1 is used)

int32.hradio below controls are for selecting the inputs for filter 1

int32.hradio below controls are for selecting filter-mode outputs of filter 1 used for the "filter 1 mix"

int32.hradio below controls are for selecting the adding/subtracting the inputs from the filter 1 output to the "filter 1 mix" which can create inverted filter responses

int32.hradio below controls are for selecting the inputs for filter 2

int32.hradio below controls are for selecting filter-mode outputs of filter 2 used for the "filter 2 mix"

int32.hradio below controls are for selecting the adding/subtracting the inputs from the filter 2 output to the "filter 2 mix" which can create inverted filter responses

int32.hradio below controls are for selecting the adding/subtracting the "filter mixes" to the module's output

int32.hradio below controls are for quick-setting the configuration. either by randomisation or saved settings

Attributes

combo presets

Declaration
static const uint32_t LENGTHPOW = (attr_presets);
static const uint32_t LENGTH = (1 << attr_presets);
static const uint32_t LENGTHMASK = ((1 << attr_presets) - 1);
int32_t *array;
int ltrig;
int lltrig;
int strig;

int32_t filter[8];
int32_t freq[2];
int32_t Damp[2];
int i;
int rtrig;

// config algo
int32_t config(int32_t pitch, int32_t reso, int Inst) {
  int32_t damp = (0x80 << 24) - (reso << 4);
  damp = ___SMMUL(damp, damp);
  Damp[Inst] = damp;
  int32_t alpha;
  MTOFEXTENDED(pitch, alpha);
  SINE2TINTERP(alpha, freq[Inst]);
}

// filter algo
int32_t SVF(int32_t in, int32_t Freq, int32_t damp, int32_t Inst) {
  filter[Inst * 4 + 2] = __SSAT(filter[Inst * 4 + 2], 28);
  filter[Inst * 4 + 3] = in - (___SMMUL(damp, filter[Inst * 4 + 2]) << 1);
  filter[Inst * 4] =
      filter[Inst * 4] + (___SMMUL(Freq, filter[Inst * 4 + 2]) << 1);
  filter[Inst * 4 + 1] = filter[Inst * 4 + 3] - filter[Inst * 4];
  filter[Inst * 4 + 2] =
      (___SMMUL(Freq, filter[Inst * 4 + 1]) << 1) + filter[Inst * 4 + 2];
}
// 0=LP1
// 1=HP1
// 2=BP1
// 3=NO1
// 4=LP2
// 5=HP2
// 6=BP2
// 7=NO2
int32_t prev;
Init
static int32_t _array[LENGTH * 25] __attribute__((section(".sdram")));
array = &_array[0];

for (i = 0; i < (LENGTH * 25); i++) {
  array[i] = 0;
}

for (i = 0; i < 8; i++) {
  filter[i] = 0;
}
Control Rate
int32_t width = inlet_width + param_width;
config(inlet_center + param_center - (width >> 1),
       __USAT(param_reso1 + inlet_reso1, 27), 0);
config(((width >> 1) + inlet_center + param_center) * param_link +
           width * (1 - param_link),
       __USAT(param_reso2 + inlet_reso2, 27), 1);
int32_t preset = ((inlet_preset + param_preset) & LENGTHMASK) * 25;

if ((inlet_save > 0) && !strig) {
  strig = 1;
  FIL FileObject;
  FRESULT err;
  UINT bytes_written;
  err = f_open(&FileObject, inlet_filename, FA_WRITE | FA_CREATE_ALWAYS);
  if (err != FR_OK) {
    report_fatfs_error(err, "inlet_filename");
    return;
  }
  int rem_sz = sizeof(*array) * LENGTH * 25;
  int offset = 0;
  while (rem_sz > 0) {
    if (rem_sz > sizeof(fbuff)) {
      memcpy((char *)fbuff, (char *)(&array[0]) + offset, sizeof(fbuff));
      err = f_write(&FileObject, fbuff, sizeof(fbuff), &bytes_written);
      rem_sz -= sizeof(fbuff);
      offset += sizeof(fbuff);
    } else {
      memcpy((char *)fbuff, (char *)(&array[0]) + offset, rem_sz);
      err = f_write(&FileObject, fbuff, rem_sz, &bytes_written);
      rem_sz = 0;
    }
  }
  if (err != FR_OK)
    report_fatfs_error(err, "inlet_filename");
  err = f_close(&FileObject);
  if (err != FR_OK)
    report_fatfs_error(err, "inlet_filename");
} else if (!(inlet_save > 0))
  strig = 0;

if ((inlet_load > 0) && !ltrig) {
  ltrig = 1;
  FIL FileObject;
  FRESULT err;
  UINT bytes_read;
  err = f_open(&FileObject, inlet_filename, FA_READ | FA_OPEN_EXISTING);
  if (err != FR_OK) {
    report_fatfs_error(err, inlet_filename);
    return;
  }
  int rem_sz = sizeof(*array) * LENGTH * 25;
  int offset = 0;
  while (rem_sz > 0) {
    if (rem_sz > sizeof(fbuff)) {
      err = f_read(&FileObject, fbuff, sizeof(fbuff), &bytes_read);
      if (bytes_read == 0)
        break;
      memcpy((char *)(&array[0]) + offset, (char *)fbuff, bytes_read);
      rem_sz -= bytes_read;
      offset += bytes_read;
    } else {
      err = f_read(&FileObject, fbuff, rem_sz, &bytes_read);
      memcpy((char *)(&array[0]) + offset, (char *)fbuff, bytes_read);
      rem_sz = 0;
    }
  }
  if (err != FR_OK) {
    report_fatfs_error(err, inlet_filename);
    return;
  };
  err = f_close(&FileObject);
  if (err != FR_OK) {
    report_fatfs_error(err, inlet_filename);
    return;
  };
} else if (!(inlet_load > 0))
  ltrig = 0;

if (((!(preset == prev)) || (inlet_load > 0)) && !lltrig) {
  lltrig = 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1to1],
                     array[0 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2to1],
                     array[1 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LP1],
                     array[2 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HP1],
                     array[3 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BP1],
                     array[4 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NO1],
                     array[5 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1mix1],
                     array[6 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2mix1],
                     array[7 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1to2],
                     array[8 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2to2],
                     array[9 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_mixto2],
                     array[10 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LPto2],
                     array[11 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HPto2],
                     array[12 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BPto2],
                     array[13 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NOto2],
                     array[14 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LP2],
                     array[15 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HP2],
                     array[16 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BP2],
                     array[17 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NO2],
                     array[18 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2IN2],
                     array[19 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1mix2],
                     array[20 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2mix2],
                     array[21 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_summix1],
                     array[22 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_outmix1],
                     array[23 + preset], 0xFFFD);
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_outmix2],
                     array[24 + preset], 0xFFFD);
} else if (!((!(preset == prev)) || (inlet_load > 0))) {
  lltrig = 0;
}

array[0 + preset] = param_1to1;
array[1 + preset] = param_2to1;
array[2 + preset] = param_LP1;
array[3 + preset] = param_HP1;
array[4 + preset] = param_BP1;
array[5 + preset] = param_NO1;
array[6 + preset] = param_1mix1;
array[7 + preset] = param_2mix1;
array[8 + preset] = param_1to2;
array[9 + preset] = param_2to2;
array[10 + preset] = param_mixto2;
array[11 + preset] = param_LPto2;
array[12 + preset] = param_HPto2;
array[13 + preset] = param_BPto2;
array[14 + preset] = param_NOto2;
array[15 + preset] = param_LP2;
array[16 + preset] = param_HP2;
array[17 + preset] = param_BP2;
array[18 + preset] = param_NO2;
array[19 + preset] = param_2IN2;
array[20 + preset] = param_1mix2;
array[21 + preset] = param_2mix2;
array[22 + preset] = param_summix1;
array[23 + preset] = param_outmix1;
array[24 + preset] = param_outmix2;

prev = preset;
if ((param_rnd > 0) && !rtrig) {
  rtrig = 1;
  int tmp;
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1to1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2to1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LP1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HP1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BP1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NO1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1mix1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2mix1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1to2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2to2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_mixto2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LPto2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HPto2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BPto2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NOto2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_LP2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_HP2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_BP2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_NO2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2IN2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_1mix2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_2mix2], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_summix1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = tmp > 0 ? tmp : -tmp;
  tmp = tmp - (tmp / 3) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_outmix1], tmp,
                     0xFFFD);
  tmp = (uint32_t)(GenerateRandomNumber());
  tmp = (tmp & 1) * 3 - 1;
  PExParameterChange(&parent->PExch[PARAM_INDEX_attr_legal_name_outmix2], tmp,
                     0xFFFD);
} else if (param_rnd == 0) {
  rtrig = 0;
}
Audio Rate
int32_t IN1;
int32_t IN2;

// input for filter 1
IN1 = inlet_in1 * param_1to1 + inlet_in2 * param_2to1;
// filter1
SVF(IN1, freq[0], Damp[0], 0);
int32_t mix1 = filter[0] * param_LP1 + filter[1] * param_HP1 +
               filter[2] * param_BP1 + filter[3] * param_NO1 +
               inlet_in1 * param_1mix1 + inlet_in2 * param_2mix1;

// drive
int32_t ts = __SSAT(mix1 >> 2, 28);
int32_t tsq31 = ts << 3;
int32_t tsq31p3 = ___SMMUL(tsq31, ___SMMUL(tsq31, tsq31));
mix1 = ts + (ts >> 1) - (tsq31p3);

int32_t mix2 = 0;

// mute filter2
if (param_ON2 > 0) {
  // filter2
  IN2 = inlet_in1 * param_1to2 + inlet_in2 * param_2to2 + mix1 * param_mixto2 +
        filter[0] * param_LPto2 + filter[1] * param_HPto2 +
        filter[2] * param_BPto2 + filter[3] * param_NOto2;
  SVF(IN2, freq[1], Damp[1], 1);

  mix2 = filter[4] * param_LP2 + filter[5] * param_HP2 + filter[6] * param_BP2 +
         filter[7] * param_NO2 + inlet_in1 * param_1mix2 +
         inlet_in2 * param_2mix2 + IN2 * param_2IN2 + mix1 * param_summix1;
}
ts = __SSAT(mix2 >> 2, 28);
tsq31 = ts << 3;
tsq31p3 = ___SMMUL(tsq31, ___SMMUL(tsq31, tsq31));
mix2 = ts + (ts >> 1) - (tsq31p3);

int32_t sum = mix1 * param_outmix1 + mix2 * param_outmix2;
ts = __SSAT(sum >> 2, 28);
tsq31 = ts << 3;
tsq31p3 = ___SMMUL(tsq31, ___SMMUL(tsq31, tsq31));
if (ts < 0) {
  int32_t tm = ts > 0 ? ts : -ts;
  tsq31p3 = ___SMMUL(tm << 3, tsq31) - (ts);
} else {
  tsq31p3 -= (ts >> 1);
}
sum = ts - (tsq31p3);

outlet_out = sum;

Privacy

© 2024 Zrna Research