paraOpt

Paraphonic Osc Bank inspired by existing string machine waveforms
Author: Smashed Transistors (Smashed Transistors)
License: LGPL
Github: tiar/oscBnk/paraOpt.axo

Inlets

None

Outlets

frac32buffer.bipolar out

Parameters

frac32.u.map octDbl

frac32.s.map.kdecaytime.exp Attack

frac32.s.map.kdecaytime.exp Release

int32 Max Key

int32 Min Key

Declaration
float dp[128]; // delta phase

float p[128];
float coef1[128]; // for gain compensation in DPW algo
float gates[128];
float g[128];
float env[128];
float z[BUFSIZE];
float z1;

float octDbl;

const float l = 0.0f;
const float r = 0.25f;

const float a = (l - 1) / r;
const float K = -0.5f * (l + 1) * r;
const float b = 1.0f + K;
const float A = 0.5 * (l - 1) / r;
const float C = -((1.0f / 3) * A * r * r * r + 0.5 * r * r - 0.5f * K + K * r);
const float C_K = C - K;

int syncRefresh = 0;
int minKey;
Init
// _____________________________________________________________________
int i, j;
for (i = 0; i < 128; i++) {
  p[i] = gates[i] = g[i] = env[i] = 0.0f;
}
z1 = 0.0f;
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
dp[0] = 1.7032914407591056E-4f;
dp[1] = 1.804574420422344E-4f;
dp[2] = 1.9118799994622893E-4f;
dp[3] = 2.0255663002739644E-4f;
dp[4] = 2.1460127403181634E-4f;
dp[5] = 2.2736212983919527E-4f;
dp[6] = 2.4088178561955362E-4f;
dp[7] = 2.5520536196728464E-4f;
dp[8] = 2.703806624869435E-4f;
dp[9] = 2.8645833333333333E-4f;
dp[10] = 3.0349203223833975E-4f;
dp[11] = 3.2153860758862255E-4f;
for (i = 12; i <= 127; i++) {
  dp[i] = 2 * dp[i - 12];
  coef1[i] = ((1 << 25) / 127.0f) / (dp[i]);
}
Control Rate
minKey = param_Min_space_Key < param_Max_space_Key ? param_Min_space_Key
                                                   : param_Max_space_Key;
int maxKey = param_Min_space_Key >= param_Max_space_Key ? param_Min_space_Key
                                                        : param_Max_space_Key;
float attack = 1.0f - param_Attack / ((float)((1UL << 31) - 1));
float release = 1.0f - param_Release / ((float)((1UL << 31) - 1));
octDbl = arm::q_to_float(param_octDbl, 27);
// _____________________________________________________________________
// g: gates levels taking account of doubling
// resync (lock) of phases to avoid drift caused by calc inaccuracies
float pRef = p[minKey + syncRefresh];
g[minKey + syncRefresh] =
    (gates[minKey + syncRefresh]) * coef1[minKey + syncRefresh];
for (int i = minKey + syncRefresh + 12; i <= maxKey; i += 12) {
  g[i] = (gates[i] + octDbl * gates[i - 12]) * coef1[i];
  pRef *= 2;
  if (pRef >= 1)
    pRef -= 1;
  p[i] = pRef;
}
syncRefresh++;
if (syncRefresh >= 12)
  syncRefresh = 0;

// DPW anti ali
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
// lower key generator resets the output buffer
{
  int i = minKey;
  float _env = env[i];
  env[i] += (g[i] > env[i] ? attack : release) * (g[i] - env[i]);
  float denv = (env[i] - _env) * (1.0f / BUFSIZE);
  float _dp = dp[i];
  float _p = p[i];
  for (int j = 0; j < BUFSIZE; j++) {
    _p += _dp;
    _p -= (_p > 1);
    z[j] = (K * _p + C_K + (_p < r) * (K + _p * (1 + A * _p))) * (_env += denv);
  }
  p[i] = _p;
}
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
// higher key generators are added to the output buffer
for (int i = minKey + 1; i <= maxKey; i++) {
  float _env = env[i];
  env[i] += (g[i] > env[i] ? attack : release) * (g[i] - env[i]);
  float denv = (env[i] - _env) * (1.0f / BUFSIZE);
  float _dp = dp[i];
  float _p = p[i];
  for (int j = 0; j < BUFSIZE; j++) {
    _p += _dp;
    _p -= (_p > 1);
    z[j] +=
        (K * _p + C_K + (_p < r) * (K + _p * (1 + A * _p))) * (_env += denv);
  }
  p[i] = _p;
}
// _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
for (int j = 0; j < BUFSIZE; j++) {
  outlet_out[j] = (int32_t)(z[j] - z1); // differentiation of the output
  z1 = z[j];
}
Midi Handler
if (status == MIDI_NOTE_ON + attr_midichannel) {
  // gates[data1 & 0x7F] = data2 * coef1[data1];
  int j = data1 & 0x7F;
  if (j >= minKey || data2 == 0) {
    gates[j] = data2 == 0 ? 0 : 100;
    // update doubling
    if (j >= 12)
      g[j] = (gates[j] + octDbl * gates[j - 12]) * coef1[j];
    if (j < 128 - 12)
      g[j + 12] = (gates[j + 12] + octDbl * gates[j]) * coef1[j];
  }
} else if (status == MIDI_NOTE_OFF + attr_midichannel) {
  int j = data1 & 0x7F;
  gates[j] = 0;
  // update doubling
  if (j >= 12)
    g[j] = (gates[j] + octDbl * gates[j - 12]) * coef1[j];
  if (j < 128 - 12)
    g[j + 12] = (gates[j + 12] + octDbl * gates[j]) * coef1[j];
} else if ((status == attr_midichannel + MIDI_CONTROL_CHANGE) &&
           (data1 == MIDI_C_ALL_NOTES_OFF)) {
  for (int i = 0; i < 128; i++)
    gates[data1 & 0x7F] = 0;
}

Privacy

© 2024 Zrna Research