loudness.tc¶
loudness.tc — microphone loudness / VU meter using the TinyC I2S mic (RX) syscalls.
// loudness.tc — microphone loudness / VU meter using the TinyC I2S mic (RX) syscalls.
//
// Uses an OWN I2S RX channel (a separate I2S port), fully independent of the audio
// plugin — so it works on ANY ESP32 with an I2S mic:
// • raw I2S MEMS mic (INMP441 / ICS-43434 / SPH0645): just wire BCLK / WS / DIN.
// • codec mic (ES8311 / ES7210, e.g. the P4 board): enable the codec's ADC first
// via the i2c* syscalls (or let the audio plugin init it), then point DIN at the
// codec's I2S data line.
//
// Requires firmware ABI >= 7:
// int i2sMicBegin(mclk, bclk, lrclk, din, rate) // 0 = ok, -1 = error (mclk=-1 = none)
// int i2sMicLevel() // RMS amplitude 0..32767 (loudness)
// int i2sMicRead(int arr[], max) // raw int16 mono samples (FFT/custom)
// void i2sMicStop()
//
// This file is for a RAW I2S MEMS mic (INMP441/ICS-43434) — MCLK not needed (-1).
// For the P4 board's ES7210 codec mic, see loudness_p4.tc (needs MCLK + I2C init).
//
// Console: MIC -> prints the current level.
// ── set these to YOUR mic wiring ──
#define MIC_MCLK -1 // -1 = none (raw MEMS mic); a codec needs the MCLK pin
#define MIC_BCLK 15
#define MIC_WS 16
#define MIC_DIN 17
#define MIC_RATE 16000
int micok = 0;
int level = 0; // current RMS (0..32767)
int peak = 0; // meter peak (fast attack, slow decay)
// integer sqrt — gives a more ear-like meter than a linear map (no float needed)
int isqrt(int n) {
if (n <= 0) { return 0; }
int x = n;
int y = (x + 1) / 2;
while (y < x) { x = y; y = (x + n / x) / 2; }
return x;
}
// 0..32767 RMS -> 0..100 %, sqrt-shaped (sqrt(32767) = 181)
int pct_of(int rms) {
int p = (isqrt(rms) * 100) / 181;
if (p > 100) { p = 100; }
return p;
}
void Every100ms() {
if (!micok) { return; }
int v = i2sMicLevel(); // RMS over one ~256-sample block
if (v < 0) { return; } // mic not open
level = v;
if (v > peak) { peak = v; } // fast attack
else { peak = (peak * 9) / 10; } // slow decay
}
void Command(char cmd[]) {
char resp[64];
sprintf(resp, "rms=%d (%d%%) peak=%d", level, pct_of(level), pct_of(peak));
responseCmnd(resp);
}
void WebUI() {
char buf[256];
int p = pct_of(level);
int pk = pct_of(peak);
sprintf(buf, "{s}Loudness{m}%d %% (rms %d){e}", p, level);
webSend(buf);
// a little bar with a peak marker
sprintf(buf, "{s}Level{m}<div style='position:relative;background:#222;width:170px;height:14px;border-radius:7px'><div style='background:#3ddc84;height:14px;width:%d%%;border-radius:7px'></div><div style='position:absolute;top:0;left:%d%%;width:2px;height:14px;background:#e66'></div></div>{e}", p, pk);
webSend(buf);
}
int main() {
micok = (i2sMicBegin(MIC_MCLK, MIC_BCLK, MIC_WS, MIC_DIN, MIC_RATE) == 0);
if (micok) { addLog("loudness: mic RX started"); }
else { addLog("loudness: i2sMicBegin FAILED — check pins / firmware ABI>=7"); }
addCommand("MIC");
return 0;
}