| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- #include "audio_resample.h"
- #include "sdkconfig.h"
- #if CONFIG_OUTPUT_SAMPLE_RATE_HZ != 44100
- #include "resampler.h"
- #include "esp_log.h"
- #include <stdlib.h>
- static const char *TAG = "audio_resample";
- /* Taps must be a multiple of 4 (resampler requirement).
- * Rounded down from Kconfig value if needed. */
- #define RESAMPLER_NUM_TAPS (CONFIG_RESAMPLER_TAPS & ~3)
- static Resample *resampler;
- static uint32_t current_input_rate;
- static uint32_t current_output_rate;
- static int current_channels;
- static double fixed_ratio;
- static bool active;
- /* Persistent float conversion buffers (heap-allocated once) */
- static float *float_in;
- static float *float_out;
- static size_t float_in_cap; /* in samples (frames * channels) */
- static size_t float_out_cap;
- static void ensure_float_bufs(size_t in_samples, size_t out_samples) {
- if (in_samples > float_in_cap) {
- free(float_in);
- float_in_cap = in_samples;
- float_in = malloc(float_in_cap * sizeof(float));
- }
- if (out_samples > float_out_cap) {
- free(float_out);
- float_out_cap = out_samples;
- float_out = malloc(float_out_cap * sizeof(float));
- }
- }
- bool audio_resample_init(uint32_t input_rate, uint32_t output_rate,
- int channels) {
- audio_resample_destroy();
- if (input_rate == output_rate) {
- active = false;
- ESP_LOGI(TAG, "No resampling needed (rate=%lu)", (unsigned long)input_rate);
- return true;
- }
- current_input_rate = input_rate;
- current_output_rate = output_rate;
- current_channels = channels;
- fixed_ratio = (double)output_rate / (double)input_rate;
- /* Compute the exact filter count for interpolation-free resampling.
- * For 44100→48000: gcd=300, exact_filters=160.
- * Memory: 161 filters × RESAMPLER_NUM_TAPS × 4 bytes. */
- unsigned long g = input_rate;
- unsigned long b = output_rate;
- while (b) {
- unsigned long t = b;
- b = g % b;
- g = t;
- }
- int exact_filters = (int)(output_rate / g);
- int max_filters = exact_filters;
- if (max_filters > 1024) {
- max_filters = 1024;
- }
- int flags = SUBSAMPLE_INTERPOLATE | INCLUDE_LOWPASS | BLACKMAN_HARRIS;
- resampler = resampleFixedRatioInit(channels, RESAMPLER_NUM_TAPS, max_filters,
- (double)input_rate, (double)output_rate,
- 0, /* 0 = auto lowpass freq */
- flags);
- if (!resampler) {
- ESP_LOGE(TAG, "Failed to allocate resampler");
- return false;
- }
- /* Pre-allocate float buffers for typical frame size (352 + margin) */
- size_t typical_in = 400 * (size_t)channels;
- size_t typical_out = (size_t)(400.0 * fixed_ratio + 16) * (size_t)channels;
- ensure_float_bufs(typical_in, typical_out);
- if (!float_in || !float_out) {
- ESP_LOGE(TAG, "Failed to allocate conversion buffers");
- audio_resample_destroy();
- return false;
- }
- active = true;
- ESP_LOGI(TAG, "Resampler: %lu -> %lu Hz, taps=%d, filters=%d, interp=%s",
- (unsigned long)input_rate, (unsigned long)output_rate,
- RESAMPLER_NUM_TAPS, resampleGetNumFilters(resampler),
- resampleInterpolationUsed(resampler) ? "ON" : "OFF");
- return true;
- }
- size_t audio_resample_process(const int16_t *in, size_t in_frames, int16_t *out,
- size_t out_capacity) {
- if (!resampler || !active) {
- return 0;
- }
- size_t in_samples = in_frames * (size_t)current_channels;
- size_t out_samples = out_capacity * (size_t)current_channels;
- ensure_float_bufs(in_samples, out_samples);
- /* int16 → float [-1.0, 1.0) */
- for (size_t i = 0; i < in_samples; i++) {
- float_in[i] = (float)in[i] * (1.0f / 32768.0f);
- }
- ResampleResult result =
- resampleProcessInterleaved(resampler, float_in, (int)in_frames, float_out,
- (int)out_capacity, fixed_ratio);
- /* float → int16 with clamp */
- size_t out_total = (size_t)result.output_generated * (size_t)current_channels;
- for (size_t i = 0; i < out_total; i++) {
- float s = float_out[i] * 32768.0f;
- if (s > 32767.0f) {
- s = 32767.0f;
- } else if (s < -32768.0f) {
- s = -32768.0f;
- }
- out[i] = (int16_t)s;
- }
- return result.output_generated;
- }
- bool audio_resample_is_active(void) {
- return active;
- }
- void audio_resample_reset(void) {
- if (!resampler) {
- return;
- }
- resampleReset(resampler);
- }
- void audio_resample_destroy(void) {
- if (resampler) {
- resampleFree(resampler);
- resampler = NULL;
- }
- free(float_in);
- float_in = NULL;
- float_in_cap = 0;
- free(float_out);
- float_out = NULL;
- float_out_cap = 0;
- active = false;
- }
- size_t audio_resample_max_output(size_t in_frames) {
- if (!active || current_input_rate == 0) {
- return in_frames;
- }
- return (size_t)((double)in_frames * fixed_ratio + 2);
- }
- #else /* CONFIG_OUTPUT_SAMPLE_RATE_HZ == 44100 — no resampling needed */
- bool audio_resample_init(uint32_t input_rate, uint32_t output_rate,
- int channels) {
- (void)input_rate;
- (void)output_rate;
- (void)channels;
- return true;
- }
- size_t audio_resample_process(const int16_t *in, size_t in_frames, int16_t *out,
- size_t out_capacity) {
- (void)in;
- (void)out;
- (void)out_capacity;
- return in_frames;
- }
- bool audio_resample_is_active(void) {
- return false;
- }
- void audio_resample_reset(void) {
- }
- void audio_resample_destroy(void) {
- }
- size_t audio_resample_max_output(size_t in_frames) {
- return in_frames;
- }
- #endif
|