Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a345622
mic: initialize output buffers and resample across clock domains
sshane Sep 19, 2026
a6b4d3a
mic: improve resampling response and smooth clock tracking
sshane Sep 19, 2026
7758182
mic: make interpolation arithmetic types explicit
sshane Sep 19, 2026
84ed1b2
mic: document hackathon investigation with plots, audio reports and t…
sshane Sep 19, 2026
18c7e9d
mic: document action-camera repeat of phone music and tone tests
sshane Sep 19, 2026
692a1fd
mic: document external headphone capture through 22 kHz
sshane Sep 19, 2026
8391446
mic: compare same music through external AirPods and iPhone
sshane Sep 19, 2026
5e747d0
mic: compare same music through external AirPods and iPhone
sshane Sep 19, 2026
15c3d29
mic: document stationary headphone tone and music repeat
sshane Sep 19, 2026
e07dc33
mic: add iPhone EQ and harmonic listening experiments
sshane Sep 19, 2026
cf297cb
mic: add logged-quality baseline and reference EQ comparisons
sshane Sep 19, 2026
dee7de0
mic: put measured EQ variants on the main listening report
sshane Sep 19, 2026
7ac12bd
mic: measure halfway EQ hiss and add mild denoising audition
sshane Sep 19, 2026
7f7390b
mic: simplify demo controls and complete listening legends
sshane Sep 19, 2026
d0a47d8
mic: add blind compression audition of the preferred processing
sshane Sep 19, 2026
14dd850
mic: add a labeled processed PCM reference to the blind audition
sshane Sep 19, 2026
7ba279b
mic: compare current logging, native rate, processed PCM and proposed…
sshane Sep 19, 2026
ec645f7
mic: add labeled 48 kHz processed AAC 32 kbps comparison
sshane Sep 19, 2026
9d2b45e
mic: add labeled processed AAC 48 kbps comparison
sshane Sep 19, 2026
eacd1ed
mic: audition 32 kHz AAC at 32 kbps
sshane Sep 19, 2026
7707b03
mic: keep 48 kHz and document bitrate preference
sshane Sep 19, 2026
3e2ad56
mic: document selected 48 kHz 48 kbps compromise
sshane Sep 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 83 additions & 14 deletions board/stm32h7/sound.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,21 @@
#define SOUND_TX_BUF_SIZE (SOUND_RX_BUF_SIZE/2U)
#define MIC_RX_BUF_SIZE 512U
#define MIC_TX_BUF_SIZE (MIC_RX_BUF_SIZE * 2U)
#define MIC_RING_SAMPLES (MIC_RX_BUF_SIZE * 2U)
#define MIC_PHASE_ONE 1048576U // Q20 sample position
#define MIC_PHASE_MASK ((MIC_RING_SAMPLES * MIC_PHASE_ONE) - 1U)
#define MIC_TARGET_SAMPLES (MIC_RX_BUF_SIZE + (MIC_RX_BUF_SIZE / 2U))
__attribute__((section(".sram4"))) static uint16_t sound_rx_buf[2][SOUND_RX_BUF_SIZE];
__attribute__((section(".sram4"))) static uint16_t sound_tx_buf[2][SOUND_TX_BUF_SIZE];
__attribute__((section(".sram4"))) static uint32_t mic_rx_buf[2][MIC_RX_BUF_SIZE];
__attribute__((section(".sram4"))) static volatile uint32_t mic_rx_buf[2][MIC_RX_BUF_SIZE];
__attribute__((section(".sram4"))) static uint16_t mic_tx_buf[2][MIC_TX_BUF_SIZE];

#define SOUND_IDLE_TIMEOUT 4U
#define MIC_SKIP_BUFFERS 2U // Skip first 2 buffers (1024 samples = ~21ms at 48kHz)
static uint8_t sound_idle_count;
static uint8_t mic_idle_count;
static uint8_t mic_buffer_count;
static volatile uint8_t mic_buffer_count;
static volatile bool mic_resampler_ready;
uint16_t sound_output_level;

void sound_tick(void) {
Expand All @@ -31,28 +36,87 @@ void sound_tick(void) {
if (mic_idle_count == 0U) {
register_clear_bits(&DFSDM1_Channel0->CHCFGR1, DFSDM_CHCFGR1_DFSDMEN);
mic_buffer_count = 0U;
mic_resampler_ready = false;
(void)memset(mic_tx_buf, 0, sizeof(mic_tx_buf));
}
}
}

// Recording processing
// Count complete input buffers before reading settled microphone samples.
static void DMA1_Stream0_IRQ_Handler(void) {
DMA1->LIFCR |= 0x7DU; // clear flags
DMA1->LIFCR |= 0x7DU;
if (mic_buffer_count < (MIC_SKIP_BUFFERS + 2U)) {
mic_buffer_count++;
}
}

// Drive recording output from the I2S clock. DFSDM uses an independent clock,
// so interpolate from its circular DMA buffer and gently correct the read rate.
static void BDMA_Channel1_IRQ_Handler(void) {
BDMA->IFCR |= BDMA_IFCR_CGIF1;
uint8_t tx_buf_idx = (((BDMA_Channel1->CCR & BDMA_CCR_CT) >> BDMA_CCR_CT_Pos) == 1U) ? 0U : 1U;

if (mic_buffer_count < MIC_SKIP_BUFFERS) {
// Send silence during settling
mic_buffer_count++;
if ((mic_idle_count == 0U) || (mic_buffer_count < (MIC_SKIP_BUFFERS + 2U))) {
for (uint16_t i = 0U; i < MIC_TX_BUF_SIZE; i++) {
mic_tx_buf[tx_buf_idx][i] = 0U;
}
mic_resampler_ready = false;
} else {
// process samples
uint8_t buf_idx = (((DMA1_Stream0->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos) == 1U) ? 0U : 1U;
for (uint16_t i=0U; i < MIC_RX_BUF_SIZE; i++) {
mic_tx_buf[tx_buf_idx][2U*i] = ((mic_rx_buf[buf_idx][i] >> 16U) & 0xFFFFU);
mic_tx_buf[tx_buf_idx][(2U*i)+1U] = mic_tx_buf[tx_buf_idx][2U*i];
static uint32_t mic_read_phase;
static int32_t mic_rate_error;
// Read a consistent producer position if DMA switches buffers between reads.
uint32_t target;
uint32_t remaining;
do {
target = (DMA1_Stream0->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos;
remaining = DMA1_Stream0->NDTR;
} while (target != ((DMA1_Stream0->CR & DMA_SxCR_CT) >> DMA_SxCR_CT_Pos));
uint32_t write_phase = (((target * MIC_RX_BUF_SIZE) + MIC_RX_BUF_SIZE - remaining) * MIC_PHASE_ONE) & MIC_PHASE_MASK;
bool fade_in = !mic_resampler_ready;
uint32_t available = ((write_phase - mic_read_phase) & MIC_PHASE_MASK) / MIC_PHASE_ONE;
if (!mic_resampler_ready || (available < (MIC_RX_BUF_SIZE + 32U)) || (available > (MIC_RING_SAMPLES - 32U))) {
mic_read_phase = (write_phase - (MIC_TARGET_SAMPLES * MIC_PHASE_ONE)) & MIC_PHASE_MASK;
fade_in = true;
mic_resampler_ready = true;
mic_rate_error = 0;
}
// Smooth the quantized DMA position before correcting the read rate.
// DC gain remains 1/65536 per sample of buffer error; Q20 limits rate jitter.
uint32_t phase_distance = (write_phase - mic_read_phase) & MIC_PHASE_MASK;
uint32_t target_phase = MIC_TARGET_SAMPLES * MIC_PHASE_ONE;
int32_t phase_error = (int32_t)phase_distance - (int32_t)target_phase;
mic_rate_error += (phase_error - mic_rate_error) / 32;
int32_t signed_step = (int32_t)MIC_PHASE_ONE + (mic_rate_error / 65536);
uint32_t step = (uint32_t)signed_step;
for (uint16_t i = 0U; i < MIC_RX_BUF_SIZE; i++) {
uint32_t index = mic_read_phase / MIC_PHASE_ONE;
int32_t samples[4];
for (uint32_t tap = 0U; tap < 4U; tap++) {
uint32_t address = (index + MIC_RING_SAMPLES + tap - 1U) % MIC_RING_SAMPLES;
samples[tap] = (int16_t)(uint16_t)(mic_rx_buf[address / MIC_RX_BUF_SIZE][address % MIC_RX_BUF_SIZE] >> 16U);
}
// Four-point Lagrange interpolation at nodes -1, 0, 1, 2.
// Coefficients scaled by six remain exact and bounded in int32_t.
int32_t cubic = -samples[0] + (3 * samples[1]) - (3 * samples[2]) + samples[3];
int32_t quadratic = (3 * samples[0]) - (6 * samples[1]) + (3 * samples[2]);
int32_t linear = -(2 * samples[0]) - (3 * samples[1]) + (6 * samples[2]) - samples[3];
uint32_t fraction_bits = mic_read_phase % MIC_PHASE_ONE;
float fraction = (float)fraction_bits / (float)MIC_PHASE_ONE;
float interpolated = (((((float)cubic * fraction) + (float)quadratic) * fraction) + (float)linear) * fraction;
float scaled = (interpolated * (1.0f / 6.0f)) + (float)samples[1];
int32_t sample = (int32_t)scaled;
// Polynomial interpolation can overshoot; saturate instead of wrapping.
if (sample > 32767) {
sample = 32767;
}
if (sample < -32768) {
sample = -32768;
}
if (fade_in) {
sample = (sample * (int32_t)i) / (int32_t)MIC_RX_BUF_SIZE;
}
mic_tx_buf[tx_buf_idx][2U * i] = (uint16_t)sample;
mic_tx_buf[tx_buf_idx][(2U * i) + 1U] = (uint16_t)sample;
mic_read_phase = (mic_read_phase + step) & MIC_PHASE_MASK;
}
}
}
Expand Down Expand Up @@ -159,6 +223,7 @@ static void sound_stop_dac(void) {

void sound_init(void) {
REGISTER_INTERRUPT(BDMA_Channel0_IRQn, BDMA_Channel0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
REGISTER_INTERRUPT(BDMA_Channel1_IRQn, BDMA_Channel1_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)
REGISTER_INTERRUPT(DMA1_Stream0_IRQn, DMA1_Stream0_IRQ_Handler, 128U, FAULT_INTERRUPT_RATE_SOUND_DMA)

// Init DAC and its DMA
Expand Down Expand Up @@ -191,6 +256,9 @@ void sound_init(void) {
register_set(&DMAMUX2_Channel0->CCR, 16U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_B_DMA
register_set_bits(&BDMA_Channel0->CCR, BDMA_CCR_EN);

// SRAM4 is not initialized by startup; transmit silence until capture is ready.
(void)memset(mic_tx_buf, 0, sizeof(mic_tx_buf));

// mic output
register_set(&SAI4_Block_A->CR1, SAI_xCR1_DMAEN | (0b01UL << SAI_xCR1_SYNCEN_Pos) | (0b100UL << SAI_xCR1_DS_Pos) | (0b10UL << SAI_xCR1_MODE_Pos), 0x0FFB3FEFU);
register_set(&SAI4_Block_A->CR2, 0U, 0xFFFBU);
Expand Down Expand Up @@ -218,13 +286,14 @@ void sound_init(void) {
register_set(&BDMA_Channel1->CM0AR, (uint32_t) mic_tx_buf[0], 0xFFFFFFFFU);
register_set(&BDMA_Channel1->CM1AR, (uint32_t) mic_tx_buf[1], 0xFFFFFFFFU);
BDMA_Channel1->CNDTR = MIC_TX_BUF_SIZE;
register_set(&BDMA_Channel1->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) |(0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | (0b1U << BDMA_CCR_DIR_Pos), 0xFFFFU);
register_set(&BDMA_Channel1->CCR, BDMA_CCR_DBM | (0b01UL << BDMA_CCR_MSIZE_Pos) |(0b01UL << BDMA_CCR_PSIZE_Pos) | BDMA_CCR_MINC | BDMA_CCR_CIRC | BDMA_CCR_TCIE | (0b1U << BDMA_CCR_DIR_Pos), 0xFFFFU);
register_set(&DMAMUX2_Channel1->CCR, 15U, DMAMUX_CxCR_DMAREQ_ID_Msk); // SAI4_A_DMA
register_set_bits(&BDMA_Channel1->CCR, BDMA_CCR_EN);

// enable all initted blocks
register_set_bits(&SAI4_Block_A->CR1, SAI_xCR1_SAIEN);
register_set_bits(&SAI4_Block_B->CR1, SAI_xCR1_SAIEN);
NVIC_EnableIRQ(BDMA_Channel0_IRQn);
NVIC_EnableIRQ(BDMA_Channel1_IRQn);
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}
13 changes: 13 additions & 0 deletions docs/mic-investigation/ACTION-CAMERA-REPEAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Action-camera repeat

Repeated the existing music and three-level tone sequence after the user confirmed readiness to film. Each recording was preceded by on-device TTS/countdown. Source files and firmware were unchanged; this is not a new quality fix.

The music capture lasted 58 seconds (45 seconds of music plus margins) and the tone capture lasted 48 seconds. Both had zero input overruns. Music/source envelope correlation was 0.902. The 16–18 and 18–20 kHz music band powers were 0.63 and 1.16 dB above the post-music noise interval. The strongest test level still detected 16–17 kHz but did not robustly detect 18–20 kHz.

This repeats the previous combined phone/placement/microphone finding; it does not isolate the microphone or enclosure. The action camera's own audio was not supplied or analyzed. No further playback or captures were started after these two tests.

![Music source and actual capture](figures/phone-camera-report/music.png)

![Three-level tone capture](figures/phone-camera-report/levels.png)

[Numeric results](metrics/phone-camera-repeat.json)
13 changes: 13 additions & 0 deletions docs/mic-investigation/BLIND-COMPRESSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Blind compression audition

Seven anonymous candidates use the same 45-second halfway-reference-EQ plus light-denoise recording, which the listener preferred at a slightly lower playback volume. Candidate assignments are randomized once and persist across reloads. The answer key is retained only in the local investigation archive while listening is underway.

Each codec output is decoded into the same 48 kHz mono 16-bit PCM playback container, so browser codec handling, filenames and displayed file sizes do not disclose the candidate identity. Playback WAV sizes do not represent proposed log-storage sizes. Integrated loudness is matched within 0.01 LU; decoder delay compensation is checked to within one sample. All candidates retain more than 2 dB measured true-peak headroom. No extra synthesis is used.

The report contains only Candidate A–G labels. Buttons share one player, retain time when switching, and do not autoplay. All seven browser buttons were verified muted, with 45-second duration and no page errors. Controls now initialize before the large embedded audio payload finishes loading. The existing three main comparison buttons and collapsed experiments remain available.

This audition compares encoding quality for one recording. It does not validate production integration, runtime cost, other sounds, or raw-rlog compression savings. Compressed sizes and identities can be revealed after listening.

[Self-contained listening report](reports/clarity-comparison.html) · [Blinded verification](metrics/blind-compression-verification.json)

A labeled “Reference — halfway EQ + light denoise (48 kHz PCM)” button precedes A–G. It reuses the exact preferred processed PCM clip, verified in the browser, and does not disclose the anonymous candidate identities.
41 changes: 41 additions & 0 deletions docs/mic-investigation/CLARITY-COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Offline clarity EQ and harmonic audition

At the user's request, made listening variants from the better iPhone-to-comma-four capture (`phone-music-camera-01`), using the same aligned 45 seconds for every version. No new recording, firmware modification, or reference-song mixing was performed. The user prefers the iPhone as the source for further physical tests.

Variants:

- Original: playback gain only.
- Gentle EQ: -2 dB low shelf at 200 Hz, broad +3 dB peaks at 3.2 and 7.5 kHz. Combined boost is roughly 4 dB in the overlapping region.
- Stronger EQ: -3 dB low shelf at 250 Hz, broad +5/+6 dB peaks at 3.2/7.5 kHz. Combined boost approaches 8 dB.
- Gentle EQ plus synthetic harmonics: the prior brighter version, with an oversampled nonlinear branch shaped mainly to 7–12 kHz. Wet RMS is 28 dB below the EQ signal's whole-clip RMS.
- Gentle EQ plus subtler harmonics: reduces the added layer to 34 dB below dry RMS (6 dB less than the prior option), and derives it from an input band confined mainly to 2.5–4.5 kHz. The EQ bass cleanup is unchanged. This adds invented content; it does not recover the original missing frequencies. FIR processing is centered offline; real-time latency/CPU suitability is unvalidated.

EQ choices are restrained taste experiments based on where the iPhone capture still has musical content, not an inverse response calibrated against the downloaded reference. EQ raises noise along with signal. The harmonic option can alter timbre, add harshness or intermodulation, and is not established as higher fidelity. No denoising is used.

All five encoded WAVs measure approximately -25.70 LUFS with about 3 dB or more true-peak headroom. Matching uses constant gain only; no limiter or compressor. A pure 3 kHz probe verifies that the nonlinear path generates a 9 kHz component. Browser checks verified all five buttons, retained playback position when switching, 45-second durations, and restart, with audio muted during testing.

The report is self-contained, with synchronized switching buttons and embedded audio/plot. User feedback: both EQ versions improved the sound and reduced boomy bass. The prior synthetic version was interesting but airy/shallow and made vocals sound too high-pitched. The subtler option responds to this feedback; its preference is pending. These measurements verify processing and headroom, not general perceptual improvement.

Methods: [RBJ/W3C EQ cookbook](https://www.w3.org/TR/audio-eq-cookbook/) and [FFmpeg exciter documentation](https://ffmpeg.org/ffmpeg-filters.html#aexciter).

![Applied EQ and actual output spectra](figures/clarity-comparison-01/eq-and-spectra.png)

[Embedded listening report](reports/clarity-comparison.html) · [Processing measurements](metrics/clarity-comparison.json)

## Logged-equivalent comparison

Both reports now start with a loudness-matched 16 kHz AAC / 32 kbit/s approximation of logged video audio. It uses the same recording and adds no simulated clicks. Native 48 kHz remains experimental. [Reference-derived EQ, later listening feedback, and updated reports](REFERENCE-EQ.md).

## Unified listening page

The original clarity page now also embeds Gentle EQ — bass retained and both Reference fit variants, matched to its −25.70 LUFS playback level. Their measured true peaks are −3.07, −4.55 and −7.52 dBFS. The reference-fit plot is embedded below the earlier experiment plot.

The user reported clipping-like upper-frequency harshness in the subtler synthesis version and agreed to set synthesis aside. Its measured exported true peak was −3 dBFS; this does not rule out audible processing distortion. Existing synthesis clips remain as archived experiments.

## Halfway fit hiss audition

User strongly liked the halfway reference fit but heard mid/high hiss. Applying its measured FIR to post-music quiet raises 3–6 kHz noise by 8.37 dB and 6–10 kHz by 11.30 dB, versus 0.48 dB at 14–20 kHz, before playback gain. This points toward lower treble as the added hiss source; it does not identify the perceptual source conclusively.

Added a separate halfway + light denoise button, preserving the preferred halfway clip byte-for-byte. Noise profile uses capture 50.48–54 s; validation uses held-out quiet 54–57 s. Bounded spectral suppression, at most 6 dB, is inactive below 2 kHz and fully active above 3.5 kHz. Existing halfway EQ follows. Held-out 6–10 kHz quiet drops 5.80 dB; reduction during music and listener preference are not established. Detail loss and watery artifacts remain possible. No synthesis or new capture. Output measures −25.70 LUFS and −4.55 dBFS true peak; the browser button plays all 45 seconds.

![Hiss and held-out quiet measurements](figures/clarity-comparison-01/halfway-hiss.png)
40 changes: 40 additions & 0 deletions docs/mic-investigation/FOUR-WAY-COMPARISON.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Four-way logging comparison

The report now leads with the four requested labeled versions of the same 45-second iPhone capture:

1. Logged equivalent: 16 kHz mono AAC at 32 kbps, no EQ or denoising. This approximates the current format and does not simulate old firmware clicks.
2. Native 48 kHz capture, no EQ or denoising, AAC at the current 32 kbps setting.
3. Preferred halfway reference EQ + light denoise, 48 kHz PCM.
4. The same processed version encoded as 48 kHz AAC at 64 kbps (the former Candidate C).

Compare 1/2 to isolate sample rate at the same nominal codec bitrate, and 3/4 to isolate proposed compression. Playback loudness is matched to approximately −25.70 LUFS. The new 48 kHz / 32 kbps clip measures −2.85 dBFS true peak, and its codec delay compensation aligns within one sample. Earlier clips remain unchanged. All four browser buttons play for 45 seconds and preserve playback position when switching.

The four explanations appear immediately under Restart. Prior blind comparisons and other experiments remain collapsed. Duplicate embedded audio is referenced internally to avoid inflating the report with identical PCM copies. All audio remains embedded. These are offline comparisons, not deployed production changes.

[Listening report](reports/clarity-comparison.html) · [Measurements](metrics/four-way-comparison.json) · [Verification](metrics/four-way-verification.json)

## Added processed 32 kbps comparison

Button 5 adds the same halfway EQ + light denoise at 48 kHz AAC / 32 kbps, reusing the earlier Candidate G exactly. It measures −25.69 LUFS and −3.94 dBFS true peak. Compare 3/5 for compression or 4/5 for bitrate. Its browser playback and 45-second duration were verified; embedded PCM is aliased to avoid increasing report size.

## Added processed 48 kbps comparison

Button 6 adds halfway EQ + light denoise at 48 kHz AAC / 48 kbps, reusing Candidate D exactly. Output measures −25.70 LUFS and −4.65 dBFS true peak; playback and 45-second duration were verified in the browser. The listener preferred 3/4 and, on repeat listening, described 5 as a bit worse. Preference for 6 is pending.

## 32 kHz AAC at 32 kbps

Added button 7 using the same halfway reference EQ + light denoise PCM source. Downsampled to 32 kHz, encoded as mono AAC at 32 kbps, then decoded to 48 kHz WAV for synchronized browser playback. Compare 5 versus 7 for the sample-rate change at the same bitrate; compare 3 versus 7 for overall encoding loss. No new physical recording or production configuration change.

The 45-second encoded M4A is 188,441 bytes (0.251 MB/min including this container), with measured stream bitrate 32,332 bit/s. Playback is matched to -25.70 LUFS, has -3.42 dBFS true peak and zero measured alignment lag. No listening preference established yet. Metrics and reproduction script accompany the embedded report.

## Listening decision: keep 48 kHz

The listener reported hardly any difference between processed 32 kHz AAC/32 kbps (7) and processed 48 kHz AAC/32 kbps (5), with a possible slight preference for 48 kHz; both were acceptable but below the preferred quality. Keep 48 kHz. Removed button 7, its embedded audio, and its legend from the active report; the experiment files and reproduction script remain archived.

Recommend mono AAC at 64 kbps for the next integration trial based on the listener preferring buttons 3/4 (processed PCM/AAC64) and finding AAC32 worse. Nominal audio payload rises from 240 to 480 kB/min, excluding container overhead. This is a listening-based recommendation for this recording, not a production configuration change.

## Selected compromise: 48 kHz / 48 kbps

The user selected mono AAC at 48 kHz and 48 kbps, superseding the prior 64 kbps recommendation. Button 6 is labeled selected; button 4 remains the 64 kbps comparison. The audio is unchanged. Nominal payload is 360 kB/min, +120 kB/min versus current AAC32, or about +4.5% of the measured 2.674 MB/min qcamera baseline before incremental container overhead.

This is a format selection, not a production deployment. Code inspection confirms micd still captures 16 kHz PCM and VideoWriter still sets AAC32. A native-rate integration also needs to preserve the 50 ms capture blocks and 100 ms SPL analysis window. Rlog storage must be addressed separately: simply tripling PCM sample rate adds 3.84 MB/min before Zstd. The measured EQ includes the phone and room and is not yet a validated general microphone calibration.
Loading
Loading