sine sync

Dual sine oscillator. Slave osc (o2) is hardsynced to master osc (o1). Both oscillators can be phase-reset. Bandlimited (blep)
Author: Sputnki
License: BSD
Github: sptnk/osc/sine sync.axo

Inlets

frac32 master osc pitch

frac32 slave osc pitch

bool32.rising hard reset both oscillators (non bandlimited)

Outlets

frac32buffer o1

frac32buffer o2

Parameters

frac32.s.map.pitch master osc pitch

frac32.s.map.pitch slave osc pitch

Declaration
uint32_t p1 = 0;
uint32_t p2 = 0;

bool rtrig = 0;

static const int blepvoices = 4;
int16_t *oscp[blepvoices];
int32_t vgain[blepvoices];
uint32_t nextvoice = 0;
Init
for (int j = 0; j < blepvoices; j++) {
  oscp[j] = &blept[BLEPSIZE - 1];
  vgain[j] = 0;
}
Control Rate
int32_t f1;
int32_t f2;
MTOFEXTENDED(inlet_master + param_master, f1);
MTOFEXTENDED(inlet_slave + param_slave, f2);

int16_t *lastblep = &blept[BLEPSIZE - 1];

if (inlet_reset && !rtrig) {
  rtrig = 1;
  p1 = 0;
  p2 = 0;
} else if (!inlet_reset) {
  rtrig = 0;
}

/*
 *
 *                          /|
 *                         / |
 * 				     /  sum1
 *                     /     |
 *                   /_______|                         d1
 *                 /|        |			a =   -----------
 *               /  |        |                  	 sum1 + d1
 *             /    |        |
 *           /     d1        |
 *         /        |        |
 * 	    /          |	    |
 *     /______a_____|__1-a___|
 */
Audio Rate
// here i do some geometry, trying to avoid irregular phase resets
// p1 and p2 are unsigned 32 bit int variables.. You can see from simplicity
// them as going from 0 (0x00000000) to 1 (0xFFFFFFFF)
uint32_t sum1 = p1 + f1;       // integrating frequency in phasor 1
uint32_t sum2 = p2 + f2;       // integrating frequency in phasor 2
uint32_t d1 = 0xFFFFFFFF - p1; // distance between p1 (not integrated) and 1
uint32_t sum1_d1 = sum1 + d1;  // distance between p1 (integrated) and 0 . This
                              // is a positive number and makes sense only if the
                              // phasor has reset (by overflowing the variable)
int32_t a =
    134217728.f *
    ((float)d1 / (float)sum1_d1); // this is a variable that goes from 0 to 2^27
                                  // (i'm confortable working with Q27) and tells
                                  // you where the reset exactly happens
int32_t a_compl = (1 << 27) - a;               // 1-a
uint32_t d2 = ___SMMUL(a_compl << 4, f2 << 1); // see the triangle

int32_t ampl;
SINE2TINTERP(sum2 - d2,
             ampl); // i'll use sine2t not interpolated.. trying to save some
                    // energies. If it does not work i'll switch to SINE2TINTERP

if (sum1 < p1) // some blitwork has to be dnoe
{
  p2 = d2;
  nextvoice = (nextvoice + 1) & (blepvoices - 1);
  oscp[nextvoice] = &blept[a_compl >> 21];
  vgain[nextvoice] = ampl >> 18;
} else {
  p2 = sum2;
}

p1 = sum1;

int32_t r1;
int32_t r2;
SINE2TINTERP(p1, r1);
SINE2TINTERP(p2, r2);

int32_t sum = 0;
for (int i = 0; i < blepvoices; i++) { // sample
  int16_t *t = oscp[i];
  sum += (16384 - (*t)) * vgain[i]; // t points to the blep table
  t += 64;

  if (t >= lastblep)
    t = lastblep;

  oscp[i] = t;
}

outlet_o1 = r1 >> 4;
outlet_o2 = (r2 >> 4) + sum;

Privacy

© 2024 Zrna Research