granular player 2 stereo

Granular sample player, second version. This one is slightly resource cheaper and allows for a denser grain distribution. Playback scaling is different from granular player 1 (see parameter info) and the maximum grain number is now 64
Author: Sputnki
License: BSD
Github: sptnk/table/granular player 2 stereo.axo

Inlets

frac32 modulation inlet for attack parameter

frac32 modulation inlet for decay parameter

frac32 modulation inlet for density parameter

frac32 modulation inlet for playback parameter

frac32 modulation inlet for pos parameter

bool32 hard retrig for a new grain

Outlets

frac32buffer audio out left

frac32buffer audio out right

int32 number of active grains (use for debug)

Parameters

frac32.u.map how many grains per second are played (not in natural units)

frac32.u.map position in the table from where to read audio

frac32.s.map.ratio playback speed for grains.0=stop; +16 = normal speed; -16 reverse playback

int32 useful in case many grains are played

frac32.s.map.klineartime.exp set the attack length for a grain

frac32.s.map.klineartime.exp set the decay length for a grain

Attributes

objref name of the table object to granulize

spinner maximum number of allocated grains

Declaration
bool grain_active[attr_grains];
uint32_t grain_amp[attr_grains];
uint32_t grain_phase[attr_grains];
bool grain_pan[attr_grains];

bool lr = 0;

uint32_t global_phase;
uint32_t global_phase_old;

uint32_t grain_num = 0;

uint32_t temp_32;

uint32_t freq;

uint32_t global_attack;
uint32_t global_decay;

uint32_t pitchmul =
    66000 *
    ((1 << 27) / attr_table.LENGTH); // this coefficient should be adjusted
                                     // (const*48000*2^27 / LENGTH)

bool rtrig;
Control Rate
bool dostuff = 0;

if (inlet_reset && !rtrig) {
  global_phase_old = 0;
  global_phase = 0;
  dostuff = 1;
  rtrig = 1;
} else {
  if (!inlet_reset)
    rtrig = 0;

  global_phase += param_density + inlet_density << 3;
  if (global_phase < global_phase_old) // time to activate another grain
    dostuff = 1;
  global_phase_old = global_phase;
}

MTOF(-param_attack - inlet_attack, global_attack);
MTOF(-param_decay - inlet_decay, global_decay);

freq = ___SMMUL(param_playback + inlet_playback >> 9, pitchmul);

if (dostuff) {
  lr = !lr;
  grain_num++;
  if (grain_num >= attr_grains)
    grain_num = 0;

  grain_amp[grain_num] = 0;
  grain_pan[grain_num] = lr;
  grain_active[grain_num] = 1;
  grain_phase[grain_num] = param_pos + inlet_pos;
}

int32_t accum[BUFSIZE][2];

for (int j = 0; j < BUFSIZE; j++) {
  accum[j][0] = 0;
  accum[j][1] = 0;
}

int voicealloc = 0;
for (int i = 0; i < attr_grains; i++) {

  if (grain_active[i]) // if in attack phase (active)
  {
    temp_32 = grain_amp[i] + (global_attack >> 1);
    if (temp_32 > grain_amp[i])
      grain_amp[i] = temp_32;
    else
      grain_active[i] = 0;
    voicealloc++;
  } else if (grain_amp[i]) // not active but in decay phase
  {
    temp_32 = grain_amp[i] - (global_decay >> 1);
    if (temp_32 < grain_amp[i])
      grain_amp[i] = temp_32;
    else
      grain_amp[i] = 0;
    voicealloc++;
  }

  for (int j = 0; j < BUFSIZE; j++) {
    if (grain_amp[i]) // if in attack phase (active)
    {
      accum[j][grain_pan[i]] +=
          ___SMMUL(attr_table.array[__USAT(grain_phase[i], 27) >>
                                    (27 - attr_table.LENGTHPOW)]
                       << attr_table.GAIN,
                   grain_amp[i] >> 1) >>
          param_gainreduction;
      grain_phase[i] += freq;
    }
  }
}
outlet_alloc = voicealloc;
for (int j = 0; j < BUFSIZE; j++) {
  outlet_l[j] = accum[j][0];
  outlet_r[j] = accum[j][1];
}

Privacy

© 2024 Zrna Research