beatslicer

description
Author: Jérôme Burlando
License: BSD
Github: gao/sample/beatslicer.axo

Inlets

frac32buffer in

bool32.rising trig

frac32 pitch

bool32.risingfalling reverse

bool32.risingfalling retrig

Outlets

frac32buffer o

frac32 attack

frac32 decay

bool32 rec_status

Parameters

frac32.u.map attack_threshold

frac32.u.map silence_threshold

frac32.s.map decay_threshold

Displays

bool32 rec

bool32 play

bool32 retrig

Declaration
static const uint32_t LENGTH = 262144; // 5,46133.. seconds
static const uint8_t FRAME_SIZE =
    64; // Frame size (energy is calculated on 1 frame, etc.)
static const uint8_t NB_CHUNK_FRAME =
    4; // 16 samples for k-rate call, then 4 calls for a 64 samples frame
static const uint8_t INITTIMER =
    128; // Nb frames while attack or silence detection are desactived
static const uint8_t SCALE =
    6; // Scale for the 3 parameters and for calibration outputs
static const uint32_t HANINC =
    16777216; // Increment of the half hanning window use for fadein and fadeout
              // : MAX_UINT / (2*FRAME_SIZE) / 2
static const uint32_t HANMID =
    2147483648; // Index of the middle of the hanning window
static const uint32_t LASTHANPHASE = HANMID + 127 * HANINC;
static const uint32_t LOG2_10 = 404035620; // 10 / log2(10) in fixed point Q27

uint64_t playpos = 0;      // Play position
int8_t reverse = 0;        // Reverse play mode
uint8_t ntrig = 0;         // trig variable
uint8_t play = 0;          // if true, play the wave
uint32_t fadeoutphase = 0; // fadeout phase for hanning window in retrig mode
uint32_t f0;               // pitch input (no modulation)
uint32_t wi = 0;           // Wave current index
uint32_t wave_end = 0;     // wave end index value
int32_t *wave;             // wave table
uint32_t rms = 0;          // root mean square value of the current frame
uint16_t timer = 0;     // A simple timer, for mask consecutive attack detection
uint8_t crosscheck = 0; // true <==> zero crossing check activate
uint32_t zerocrosspoint = 0; // zero crossing index in wave table
int32_t
    twoframe[2 * FRAME_SIZE]; // Buffer used for temporary continuous recording
uint8_t tfi = 0;              // Two frame buffer index
uint8_t curchunk = 0;         // Current chunk number
uint8_t rec = 0;              // Record status : 1 => recording on
int32_t peakenvelope = 0;     // Current envelope value expressed in decibel
int32_t diff = 0;             // For attackstep calibration output
int32_t attackthreshold =
    0;                  // [user] Decibel absolute threshold for attack test
int32_t attackstep = 0; // [user] Threshold for attack detection on difference
                        // of previous peakenvelope and current peakenvelope
int32_t silencethreshold = 0; // [user] Decibel threshold for silence test
uint32_t radius = 130685889; // (uint32_t) (exp(-1.33333333f / 1000) * (1<<27));

// For display a value each second : if(!calib_cpt) LogTextMessage("val : %d",
// val);

/* Take a frac32 value and return the equivalent value in logarithmic scale
 * From axoloti "log" object.
 */
int32_t logscale(int32_t in) {
  Float_t f;
  f.f = in;
  int32_t r1 = ((f.parts.exponent & 0x7F) - 18) << 24;
  int32_t r3 = logt[f.parts.mantissa >> 15] << 10;
  return r1 + r3;
}

/* Convert rms power value to decibel unit */
int32_t rmstodb(int32_t rms) {

  int32_t y = logscale(rms); // 16 + 8 * log2(x)
  return ___SMMUL(
      y - 16,
      LOG2_10); // decibel = 10 * log10(power / power_ref)
                // <=> 10 * log2(x) / log2(10) with power_ref == 1 (1 << 27)
}

/*  Apply an half Hanning window on the begin of the recorded sample (2 frames)
 *  Hanning is full range (input in range 0..MAX_UINT).
 *  Start on the begin of the hanning window and finish on the middle the
 * hanning window.
 */
void insert_twoframe(void) {
  uint32_t phase = 0;
  int8_t tfitmp = tfi;
  int32_t r;
  for (int i = 0; i < (FRAME_SIZE * 2); i++) {
    HANNING2TINTERP(phase, r)
    /* "<< 5" to complete ___SMMUL operation (fixed point choice contraint)
     * ">> 4" => just a copy of the hanning window axoloti object. Why not >> 5
     * ? I don't understand.
     */
    wave[wi++] = ___SMMUL(twoframe[tfitmp++], r >> 4) << 5;
    tfitmp %= FRAME_SIZE * 2;
    phase += HANINC;
  }
}

/*  Apply an half Hanning window on the end of the recorded sample (2 frames)
 *  Hanning is full range (input in range 0..MAX_UINT).
 *  Start on the middle of the hanning window and finish on the end the hanning
 * window.
 */
void fadeout(void) {
  /* MAX_INT / 2 => hanning window is full range (input in range 0..MAX_UINT).
   *  Start on the middle of the hanning window. */
  uint32_t phase = HANMID;
  int32_t r;
  for (int i = wave_end - (2 * FRAME_SIZE); i < wave_end; i++) {
    HANNING2TINTERP(phase, r)
    wave[i] = ___SMMUL(wave[i], r >> 4) << 5; // see "insert_twoframe"
    phase += HANINC;
  }
}

void detection() {
  int32_t peakenvelope_tmp = peakenvelope;
  rms >>= 2; // rms = rms << 5 (to complete last ___SMMLA operation) and >> 7 (/
             // 128) for the mean value
  int32_t decibel =
      rms > 1 ? rmstodb(rms)
              : 0; // rmstodb produce bad value with a very little value of rms
  diff = decibel - peakenvelope_tmp;

  // New slice detection
  if ((decibel > attackthreshold) && (diff > attackstep) && !(timer > 0)) {
    if (!(rec)) {
      wi = 0;
      insert_twoframe();
      rec = 1;
    } else {
      wave_end = zerocrosspoint - 1;
      fadeout();
      rec = 0;
    }
    timer = INITTIMER; // To avoid closed detections
  }
  peakenvelope_tmp = ___SMMUL(radius, peakenvelope_tmp)
                     << 5; // << 5 (to complete ___SMMUL operation
  if (decibel > peakenvelope_tmp)
    peakenvelope_tmp = decibel;

  // Silence detection
  if (rec && !(timer > 0) && (peakenvelope_tmp < silencethreshold)) {
    wave_end = wi;
    fadeout();
    wi = 0;
    rec = 0;
  }
  peakenvelope = peakenvelope_tmp;
}
Init
static int32_t _wave[LENGTH] __attribute__((section(".sdram")));
wave = &_wave[0];
for (int i = 0; i < LENGTH; i++)
  wave[i] = 0;
Control Rate
/**************/
/* Parameters */
/**************/

attackthreshold = param_attack__threshold >> SCALE;
attackstep = param_decay__threshold >> (SCALE - 2);
silencethreshold = param_silence__threshold >> SCALE;

/**************/
/* Play logic */
/**************/

if ((inlet_trig > 0) && !ntrig && !rec && (inlet_retrig || !play)) {
  reverse = inlet_reverse;
  if (inlet_retrig && play) {
    fadeoutphase = HANMID;
  } else {
    MTOFEXTENDED(inlet_pitch, f0);
    if (reverse > 0) {
      playpos = ((uint64_t)wave_end) << 32;
    } else {
      playpos = 0;
    }
  }
  play = 1;
  ntrig = 1;
} else if (!(inlet_trig > 0) && ntrig) {
  ntrig = 0;
}

/****************/
/* Record logic */
/****************/

int32_t s = 0;
int32_t prev_s = 0;
uint32_t zerocrosspoint_tmp = zerocrosspoint;

if (timer > 0)
  timer--;

crosscheck = 1;
for (int i = 0; i < BUFSIZE; i++) {
  s = inlet_in[i];
  if (rec) {
    if (crosscheck && (prev_s * s < 0)) {
      zerocrosspoint_tmp = wi;
      crosscheck = 0;
    }
    wave[wi++] = s;
  } else {
    twoframe[tfi++] = s;
  }
  rms = __USAT(___SMMLA(s, s, rms),
               27); // rms += s * s (must apply "<< 5" in order to exactly have
                    // the sum of square)
  prev_s = s;
}

tfi %= 2 * FRAME_SIZE;
if (curchunk == NB_CHUNK_FRAME - 1) {
  crosscheck = 1;
  detection(); // Attack and decay to zero detection
  zerocrosspoint = zerocrosspoint_tmp;
  rms = 0;
}
curchunk++;
curchunk %= NB_CHUNK_FRAME;

/************************/
/* Displays and outputs */
/************************/
disp_rec = rec;
disp_play = play;
disp_retrig = inlet_retrig;
outlet_rec__status = rec;
outlet_attack = peakenvelope << 2;
outlet_decay = diff << (SCALE - 2);
Audio Rate
int32_t o = 0;

if (play) {
  uint32_t r = ___SMMUL(wave[playpos >> 32], INT32_MAX - (((uint32_t)playpos) >>
                                                          1)); // Interpolation
  o = ___SMMLA(wave[(playpos >> 32) + 1], (((uint32_t)playpos) >> 1),
               r); // Interpolation
  if (fadeoutphase > 0) {
    HANNING2TINTERP(fadeoutphase, r)
    o = ___SMMUL(o, r >> 4) << 5;
    fadeoutphase += HANINC; // 256 sample fadeout to avoid click
    if (fadeoutphase == LASTHANPHASE) {
      fadeoutphase = 0;
      MTOFEXTENDED(inlet_pitch, f0);
      if (reverse > 0) {
        playpos = ((uint64_t)wave_end) << 32;
      } else {
        playpos = 0;
      }
    }
  }
  if (reverse == 0) {
    playpos += ((uint64_t)f0) << 4; // Wave index incrementation
  } else {
    uint64_t f0_tmp = ((uint64_t)f0) << 4;
    if (playpos > f0_tmp) {
      playpos -= f0_tmp; // Wave index decrementation
    } else {
      playpos = 0;
    }
  }
  if ((((playpos >> 32) > wave_end) && (reverse == 0)) ||
      (((playpos >> 32) <= 0) && (reverse > 0))) {
    play = 0;
  }
}
outlet_o = o;

Privacy

© 2024 Zrna Research