frac32buffer Audio input.
bool32 Set during recording.
bool32.mom Delete last slice recorded on the slices stack.
int32 Absolute attack threshold in deciBels.
int32 [Actual level - peak envelope level], an interval in deciBels.
int32 Absolute silence threshold in deciBels.
objref Slices stack
objref Slices cuepoints table
text Configuration file
bool32 Set during recording.
int32.label Current slice index (to record).
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 uint32_t HANINC =
33554432; // Increment of the half hanning window use for fadein and fadeout
// : MAX_INT / 2 / FRAME_SIZE
static const uint32_t HANMID =
2147483648; // Index of the middle of the hanning window
uint8_t preset = 0; // Used for rising edge logic on reset inlet
uint32_t si = 0; // slice buffer current index
uint32_t sii = 1; // slice index current index
float energy = 0.f; // root mean square value of the current frame
float power_decibel = 0.f; // root mean square value of the current frame in
// decibel (rms reference = 1)
float power_decibel_prev = 0.f; // root mean square value of the current frame
// in decibel (rms reference = 1)
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 detect = 0; // Detection status : 1 => store slice in slicebuffer
float peakenvelope = 0.f; // Current envelope value expressed in decibel
float delta = 0.f; // For attackstep calibration output
float power_db_threshold =
0.f; // [user] Decibel absolute threshold for attack test
float delta_power_db =
0.f; // [user] Threshold for attack detection on difference of previous
// peakenvelope and current peakenvelope
float silence_db_threshold = 0.f; // [user] Decibel threshold for silence test
float timeconstant =
0.f; // [user] Attribute used for computing the decay factor
float radius = 0.f; // The decay factor
uint8_t trig = 0;
uint8_t confmode = 0;
/* Float <-> Fixed Point conversion */
__attribute__((always_inline)) __STATIC_INLINE float q_to_float(int32_t op1,
int q) {
float fop1 = *(float *)(&op1);
__ASM volatile("VCVT.F32.S32 %0, %0, %1" : "+w"(fop1) : "i"(q));
return (fop1);
}
__attribute__((always_inline)) __STATIC_INLINE int32_t float_to_q(float op1,
int q) {
__ASM volatile("VCVT.S32.F32 %0, %0, %1" : "+w"(op1) : "i"(q));
return (*(int32_t *)(&op1));
}
// For display a value each second : if(!calib_cpt) LogTextMessage("val : %d",
// val);
/* Apply an half Hanning window on the begin of the recorded sample (2 frames)
* Hanning is full range (input in range 0..MAX_INT).
* Start on the begin of the hanning window and finish on the middle the
* hanning window.
*/
void insert_twoframe(void) {
int32_t s = 0;
uint32_t phase = 0;
int8_t tfitmp = tfi;
int32_t r;
int8_t i;
for (i = 0; i < FRAME_SIZE; 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.
*/
s = ___SMMUL(twoframe[tfitmp], r >> 4) << 5;
attr_slicesbuffer.array[si] = __SSAT(s, 28) >> attr_slicesbuffer.GAIN;
tfitmp++;
si++;
tfitmp %= FRAME_SIZE * 2;
phase += HANINC;
}
for (i = 0; i < FRAME_SIZE; i++) {
s = twoframe[tfitmp];
attr_slicesbuffer.array[si] = __SSAT(s, 28) >> attr_slicesbuffer.GAIN;
tfitmp++;
si++;
tfitmp %= FRAME_SIZE * 2;
}
}
/* Apply an half Hanning window on the end of the recorded sample (2 frames)
* Hanning is full range (input in range 0..MAX_INT).
* Start on the middle of the hanning window and finish on the end the hanning
* window.
*/
void fadeout(uint32_t cuepoint) {
/* MAX_INT / 2 => hanning window is full range (input in range 0..MAX_INT).
* Start on the middle of the hanning window. */
uint32_t phase = HANMID;
int32_t r;
int32_t s;
for (int i = cuepoint - (2 * FRAME_SIZE); i < cuepoint; i++) {
HANNING2TINTERP(phase, r)
s = ___SMMUL(attr_slicesbuffer.array[i], r >> 4)
<< 5; // see "insert_twoframe"
attr_slicesbuffer.array[i] = __SSAT(s, 28) >> attr_slicesbuffer.GAIN;
phase += (HANINC / 2); // HANINC is a FRAMESIZE increment. Fadeout apply a
// fadeout on 2 frames.
}
/* Store cuepoint */
if (sii < attr_slicesindex.LENGTH) {
attr_slicesindex.array[0] = sii;
attr_slicesindex.array[sii++] = cuepoint;
}
}
void detection() {
float peakenvelope_tmp = peakenvelope;
power_decibel = log(energy * 15625000 + 1) * 4.34294482f;
// 0.015625 = 1/64 for compute the mean of sum of squares (64 samples
// (FRAME_SIZE)) P0 = 0,000000001 is the power ref, then 10*log10(P/P0) <=>
// 10*log10(energy/64 * 1000000000) log10(x) = ln(x)/ln(10), then 10*log10(x)
// = ln(x)*10/ln(10) = ln(x)*4,34294482
delta = power_decibel - peakenvelope_tmp;
if ((power_decibel > power_db_threshold) && (delta > delta_power_db) &&
!(timer > 0)) {
if (confmode)
LogTextMessage("power_db / delta_power_db : %f / %f", power_decibel,
delta);
trig = 1;
if (!(detect)) {
insert_twoframe();
detect = 1;
} else {
fadeout(zerocrosspoint);
}
timer = INITTIMER; // To avoid closed detections
}
peakenvelope_tmp = radius * peakenvelope_tmp;
if (power_decibel > peakenvelope_tmp)
peakenvelope_tmp = power_decibel;
// Silence detection
if (detect && !(timer > 0) && (peakenvelope_tmp < silence_db_threshold)) {
detect = 0;
fadeout(si);
}
peakenvelope = peakenvelope_tmp;
}
attr_conf
radius = exp(((-1000.f / 48000.f) * ((float)(BUFSIZE * NB_CHUNK_FRAME))) /
timeconstant);
power_db_threshold = (float)param_power__db__threshold;
delta_power_db = (float)param_delta__power__db;
silence_db_threshold = (float)param_silence__db__threshold;
/****************/
/* Record logic */
/****************/
float s = 0.f;
float prev_s = 0.f;
static uint32_t zerocrosspoint_tmp = 0;
trig = 0;
if (param_delete__last > 0 && !preset && (sii > 1)) {
preset = 1;
si = 0;
sii = attr_slicesindex.array[0];
attr_slicesindex.array[0] = (sii - 1);
} else if (!(param_delete__last > 0)) {
preset = 0;
}
if (timer > 0)
timer--;
for (int i = 0; i < BUFSIZE; i++) {
s = q_to_float(inlet_in[i], 27);
if (detect) {
if (crosscheck && (prev_s * s < 0)) {
zerocrosspoint_tmp = si;
crosscheck = 0;
}
attr_slicesbuffer.array[si] =
__SSAT(inlet_in[i], 28) >> attr_slicesbuffer.GAIN;
si++;
} else {
twoframe[tfi] = inlet_in[i];
tfi++;
}
energy = s * s;
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;
energy = 0;
}
curchunk++;
curchunk %= NB_CHUNK_FRAME;
/************************/
/* Displays and outputs */
/************************/
disp_record = detect;
disp_current_slice_index = (sii - 1);
outlet_record = detect;