frac32buffer in
bool32.rising trig
frac32 pitch
bool32.risingfalling reverse
bool32.risingfalling retrig
frac32buffer o
frac32 attack
frac32 decay
bool32 rec_status
frac32.u.map attack_threshold
frac32.u.map silence_threshold
frac32.s.map decay_threshold
bool32 rec
bool32 play
bool32 retrig
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;
}
static int32_t _wave[LENGTH] __attribute__((section(".sdram")));
wave = &_wave[0];
for (int i = 0; i < LENGTH; i++)
wave[i] = 0;
/**************/
/* 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);
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;