feat(VoiceRecorder): native 44.1kHz recording, band-pass filter, amp mute, 3x boost
- Keep 44.1kHz native sample rate to avoid linear resampler alias noise - MIC_BOOST 300%% (3x) with clipping, after filtering per user request - Low-pass 0.25 (~2.5kHz) to remove MCLK high-pitch hiss - High-pass 0.985 (~100Hz) to remove DC/rumble/pop - Chain: raw -> high-pass -> low-pass -> boost - Disable FM8002E speaker amp GPIO1 during recording (active-low) to eliminate pop/hiss, re-enable after - Add esp_driver_gpio requirement for gpio_set_level - Previous UI improvements (date+duration list, green highlight, volume slider, full-screen layout) included in history
This commit is contained in:
@@ -2,5 +2,5 @@ file(GLOB_RECURSE SOURCE_FILES Source/*.c)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCE_FILES}
|
||||
REQUIRES TactilitySDK
|
||||
REQUIRES TactilitySDK esp_driver_gpio
|
||||
)
|
||||
|
||||
@@ -20,18 +20,22 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#define TAG "VoiceRecorder"
|
||||
|
||||
#define SAMPLE_RATE 16000
|
||||
#define SAMPLE_RATE 44100
|
||||
#define BITS_PER_SAMPLE 16
|
||||
#define CHUNK_SAMPLES 1024
|
||||
#define CHUNK_BYTES (CHUNK_SAMPLES * (BITS_PER_SAMPLE / 8)) // 2048 bytes
|
||||
#define AUDIO_BUF_SIZE 8192
|
||||
|
||||
// Software mic boost (digital gain) to compensate quiet MEMS mic even at 100% hw gain (24dB)
|
||||
// 200 = 2x, 300 = 3x – clipping protection via int16 limits
|
||||
#define MIC_BOOST_PERCENT 250
|
||||
// 3x boost + low-pass + high-pass (band-pass for voice)
|
||||
#define MIC_BOOST_PERCENT 300
|
||||
#define ENABLE_LOWPASS 1
|
||||
#define LOWPASS_ALPHA 0.25f // ~2.5kHz cutoff, reduces high-pitch MCLK hiss
|
||||
#define ENABLE_HIGHPASS 1
|
||||
#define HIGHPASS_ALPHA 0.985f // ~100Hz cutoff, removes DC/rumble/pop
|
||||
|
||||
// ─── Scroll animation flags ───
|
||||
// LVGL label scroll animation (LV_LABEL_LONG_SCROLL_*) causes continuous
|
||||
@@ -264,10 +268,15 @@ static void record_task(void* arg) {
|
||||
|
||||
error_t err = audio_stream_open_input(ctx->stream_dev, &in_cfg, &ctx->input_handle);
|
||||
if (err == ERROR_NONE) {
|
||||
// Ensure mic is unmuted and at max gain (ES8311 0..24dB)
|
||||
// Disable speaker amp (GPIO1 active-low) to avoid FM8002E pop/hiss during recording
|
||||
// Board es3c35p/es3c28p: amp_enable on GPIO1 low = enabled
|
||||
gpio_set_level(GPIO_NUM_1, 1);
|
||||
// Also mute output codec path
|
||||
audio_stream_set_mute(ctx->stream_dev, AUDIO_CODEC_DIR_OUTPUT, true);
|
||||
// Ensure mic is unmuted and at max gain (42dB HW max)
|
||||
audio_stream_set_mute(ctx->stream_dev, AUDIO_CODEC_DIR_INPUT, false);
|
||||
audio_stream_set_volume(ctx->stream_dev, AUDIO_CODEC_DIR_INPUT, 100.0f);
|
||||
ESP_LOGI(TAG, "Mic unmuted, gain 100%%");
|
||||
ESP_LOGI(TAG, "Amp disabled (GPIO1 HIGH), mic unmuted 42dB");
|
||||
}
|
||||
if (err != ERROR_NONE) {
|
||||
ESP_LOGE(TAG, "audio_stream_open_input failed: %d", err);
|
||||
@@ -289,22 +298,45 @@ static void record_task(void* arg) {
|
||||
uint32_t start_time = xTaskGetTickCount();
|
||||
uint32_t last_ui_sec = 0;
|
||||
bool first_ui = true;
|
||||
#if ENABLE_LOWPASS
|
||||
float lp_prev = 0.0f;
|
||||
#endif
|
||||
#if ENABLE_HIGHPASS
|
||||
float hp_x_prev = 0.0f;
|
||||
float hp_y_prev = 0.0f;
|
||||
#endif
|
||||
|
||||
while (ctx->state == STATE_RECORDING) {
|
||||
size_t bytes_read = 0;
|
||||
err = audio_stream_read(ctx->input_handle, ctx->audio_buf, AUDIO_BUF_SIZE, &bytes_read, pdMS_TO_TICKS(200));
|
||||
if (err == ERROR_NONE && bytes_read > 0) {
|
||||
// Software boost: 16-bit mono, apply MIC_BOOST_PERCENT with clipping
|
||||
#if MIC_BOOST_PERCENT != 100
|
||||
int16_t* samples = (int16_t*)ctx->audio_buf;
|
||||
size_t sample_cnt = bytes_read / sizeof(int16_t);
|
||||
for (size_t i = 0; i < sample_cnt; i++) {
|
||||
int32_t boosted = (int32_t)samples[i] * MIC_BOOST_PERCENT / 100;
|
||||
float in = (float)samples[i];
|
||||
#if ENABLE_HIGHPASS
|
||||
// 1st-order high-pass ~100Hz to remove DC, rumble, pop
|
||||
float hp_out = HIGHPASS_ALPHA * (hp_y_prev + in - hp_x_prev);
|
||||
hp_x_prev = in;
|
||||
hp_y_prev = hp_out;
|
||||
in = hp_out;
|
||||
#endif
|
||||
#if ENABLE_LOWPASS
|
||||
float lp_out = LOWPASS_ALPHA * in + (1.0f - LOWPASS_ALPHA) * lp_prev;
|
||||
lp_prev = lp_out;
|
||||
in = lp_out;
|
||||
#endif
|
||||
#if MIC_BOOST_PERCENT != 100
|
||||
int32_t boosted = (int32_t)(in * MIC_BOOST_PERCENT / 100);
|
||||
if (boosted > 32767) boosted = 32767;
|
||||
if (boosted < -32768) boosted = -32768;
|
||||
samples[i] = (int16_t)boosted;
|
||||
}
|
||||
#else
|
||||
if (in > 32767) in = 32767;
|
||||
if (in < -32768) in = -32768;
|
||||
samples[i] = (int16_t)in;
|
||||
#endif
|
||||
}
|
||||
size_t written = fwrite(ctx->audio_buf, 1, bytes_read, f);
|
||||
if (written != bytes_read) {
|
||||
ESP_LOGE(TAG, "SD write error: wrote %d of %d", (int)written, (int)bytes_read);
|
||||
@@ -335,6 +367,10 @@ static void record_task(void* arg) {
|
||||
|
||||
audio_stream_close(ctx->input_handle);
|
||||
ctx->input_handle = NULL;
|
||||
// Re-enable speaker amp and unmute output after recording
|
||||
audio_stream_set_mute(ctx->stream_dev, AUDIO_CODEC_DIR_OUTPUT, false);
|
||||
gpio_set_level(GPIO_NUM_1, 0);
|
||||
ESP_LOGI(TAG, "Recording done, amp re-enabled (GPIO1 LOW)");
|
||||
|
||||
fseek(f, 0, SEEK_SET);
|
||||
write_wav_header(f, SAMPLE_RATE, BITS_PER_SAMPLE, 1, total_written);
|
||||
|
||||
Reference in New Issue
Block a user