// MEMS mic RMS -> dBFS tests. // // These prove the arithmetic: that a full-scale block reads ~0 dBFS, that // silence reads the -120 floor rather than -inf or NaN (which would poison // the JSON payload the backend receives), and that the level rises // monotonically with amplitude. They prove nothing about whether the // right-shift-by-8 matches this specific module's real bit alignment — // that needs a mic. #include "../main/mems_level.h" #include "test_util.h" #include #include #define N 256 void test_mems_level(void) { SUITE("mems_level"); // A 24-bit sample sits left-justified in the 32-bit slot, so the raw // slot value for full scale is 2^23 << 8. const int32_t full_scale_slot = (int32_t)(8388607 << 8); // 2^23 - 1, shifted up // --- full scale reads ~0 dBFS ---------------------------------------- { int32_t buf[N]; for (size_t i = 0; i < N; i++) buf[i] = full_scale_slot; double rms = mems_level_rms(buf, N); CHECK_NEAR(rms, 8388607.0, 1.0, "full-scale slots recover the 24-bit magnitude"); double dbfs = mems_level_dbfs(rms); CHECK_NEAR(dbfs, 0.0, 0.01, "full-scale input is ~0 dBFS (got %.4f)", dbfs); CHECK(dbfs <= 0.0, "dBFS never exceeds 0 for an in-range input"); } // --- silence reads the floor, not -inf or NaN ------------------------ { int32_t buf[N]; for (size_t i = 0; i < N; i++) buf[i] = 0; double rms = mems_level_rms(buf, N); CHECK(rms == 0.0, "an all-zero block has zero RMS"); double dbfs = mems_level_dbfs(rms); CHECK(dbfs == MEMS_DBFS_FLOOR, "silence clamps to the -120 floor (got %.4f)", dbfs); CHECK(!isinf(dbfs), "silence must not be -inf"); CHECK(!isnan(dbfs), "silence must not be NaN"); // Sub-LSB dither in the padding bits still counts as silence // because the >>8 discards it. int32_t buf2[N]; for (size_t i = 0; i < N; i++) buf2[i] = (int32_t)(i % 256); // padding bits only double dbfs2 = mems_level_dbfs(mems_level_rms(buf2, N)); CHECK(dbfs2 == MEMS_DBFS_FLOOR, "sub-LSB noise stays at the floor (got %.4f)", dbfs2); } // --- halving amplitude drops the level by ~6 dB ---------------------- { int32_t loud[N], quiet[N]; for (size_t i = 0; i < N; i++) { loud[i] = (int32_t)(4194304 << 8); // 2^22, i.e. -6 dBFS quiet[i] = (int32_t)(2097152 << 8); // 2^21, i.e. -12 dBFS } double d_loud = mems_level_dbfs(mems_level_rms(loud, N)); double d_quiet = mems_level_dbfs(mems_level_rms(quiet, N)); CHECK_NEAR(d_loud, -6.0206, 0.001, "2^22 is -6 dBFS (got %.4f)", d_loud); CHECK_NEAR(d_quiet, -12.0412, 0.001, "2^21 is -12 dBFS (got %.4f)", d_quiet); CHECK_NEAR(d_loud - d_quiet, 6.0206, 0.001, "halving amplitude costs ~6 dB"); } // --- negative samples contribute the same energy as positive --------- { int32_t pos[N], neg[N], alt[N]; for (size_t i = 0; i < N; i++) { pos[i] = (int32_t)(1000000 << 8); neg[i] = (int32_t)(-(1000000 << 8)); alt[i] = (i % 2) ? (int32_t)(1000000 << 8) : (int32_t)(-(1000000 << 8)); } double rp = mems_level_rms(pos, N); double rn = mems_level_rms(neg, N); double ra = mems_level_rms(alt, N); CHECK_NEAR(rp, 1000000.0, 1.0, "positive DC block RMS"); CHECK_NEAR(rn, 1000000.0, 1.0, "negative DC block has the same RMS (sign-independent)"); CHECK_NEAR(ra, 1000000.0, 1.0, "an alternating square wave has the same RMS"); } // --- level rises monotonically with amplitude ------------------------ { double prev = -1000.0; for (int shift = 4; shift <= 23; shift++) { int32_t buf[N]; int32_t mag = (int32_t)1 << shift; for (size_t i = 0; i < N; i++) buf[i] = mag << 8; double dbfs = mems_level_dbfs(mems_level_rms(buf, N)); CHECK(dbfs > prev, "level rises with amplitude at 2^%d (%.4f <= %.4f)", shift, dbfs, prev); CHECK(dbfs >= MEMS_DBFS_FLOOR && dbfs <= 0.0, "level stays inside [%.1f, 0] at 2^%d (got %.4f)", MEMS_DBFS_FLOOR, shift, dbfs); CHECK(!isnan(dbfs) && !isinf(dbfs), "level is finite at 2^%d", shift); prev = dbfs; } } // --- degenerate inputs ------------------------------------------------ { int32_t buf[1] = { 0 }; CHECK(mems_level_rms(NULL, 8) == 0.0, "NULL sample buffer yields 0 RMS, not a crash"); CHECK(mems_level_rms(buf, 0) == 0.0, "an empty block yields 0 RMS, not a divide by zero"); CHECK(mems_level_dbfs(mems_level_rms(buf, 0)) == MEMS_DBFS_FLOOR, "an empty block reports the floor"); } }