audio_resample.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #include "audio_resample.h"
  2. #include "sdkconfig.h"
  3. #if CONFIG_OUTPUT_SAMPLE_RATE_HZ != 44100
  4. #include "resampler.h"
  5. #include "esp_log.h"
  6. #include <stdlib.h>
  7. static const char *TAG = "audio_resample";
  8. /* Taps must be a multiple of 4 (resampler requirement).
  9. * Rounded down from Kconfig value if needed. */
  10. #define RESAMPLER_NUM_TAPS (CONFIG_RESAMPLER_TAPS & ~3)
  11. static Resample *resampler;
  12. static uint32_t current_input_rate;
  13. static uint32_t current_output_rate;
  14. static int current_channels;
  15. static double fixed_ratio;
  16. static bool active;
  17. /* Persistent float conversion buffers (heap-allocated once) */
  18. static float *float_in;
  19. static float *float_out;
  20. static size_t float_in_cap; /* in samples (frames * channels) */
  21. static size_t float_out_cap;
  22. static void ensure_float_bufs(size_t in_samples, size_t out_samples) {
  23. if (in_samples > float_in_cap) {
  24. free(float_in);
  25. float_in_cap = in_samples;
  26. float_in = malloc(float_in_cap * sizeof(float));
  27. }
  28. if (out_samples > float_out_cap) {
  29. free(float_out);
  30. float_out_cap = out_samples;
  31. float_out = malloc(float_out_cap * sizeof(float));
  32. }
  33. }
  34. bool audio_resample_init(uint32_t input_rate, uint32_t output_rate,
  35. int channels) {
  36. audio_resample_destroy();
  37. if (input_rate == output_rate) {
  38. active = false;
  39. ESP_LOGI(TAG, "No resampling needed (rate=%lu)", (unsigned long)input_rate);
  40. return true;
  41. }
  42. current_input_rate = input_rate;
  43. current_output_rate = output_rate;
  44. current_channels = channels;
  45. fixed_ratio = (double)output_rate / (double)input_rate;
  46. /* Compute the exact filter count for interpolation-free resampling.
  47. * For 44100→48000: gcd=300, exact_filters=160.
  48. * Memory: 161 filters × RESAMPLER_NUM_TAPS × 4 bytes. */
  49. unsigned long g = input_rate;
  50. unsigned long b = output_rate;
  51. while (b) {
  52. unsigned long t = b;
  53. b = g % b;
  54. g = t;
  55. }
  56. int exact_filters = (int)(output_rate / g);
  57. int max_filters = exact_filters;
  58. if (max_filters > 1024) {
  59. max_filters = 1024;
  60. }
  61. int flags = SUBSAMPLE_INTERPOLATE | INCLUDE_LOWPASS | BLACKMAN_HARRIS;
  62. resampler = resampleFixedRatioInit(channels, RESAMPLER_NUM_TAPS, max_filters,
  63. (double)input_rate, (double)output_rate,
  64. 0, /* 0 = auto lowpass freq */
  65. flags);
  66. if (!resampler) {
  67. ESP_LOGE(TAG, "Failed to allocate resampler");
  68. return false;
  69. }
  70. /* Pre-allocate float buffers for typical frame size (352 + margin) */
  71. size_t typical_in = 400 * (size_t)channels;
  72. size_t typical_out = (size_t)(400.0 * fixed_ratio + 16) * (size_t)channels;
  73. ensure_float_bufs(typical_in, typical_out);
  74. if (!float_in || !float_out) {
  75. ESP_LOGE(TAG, "Failed to allocate conversion buffers");
  76. audio_resample_destroy();
  77. return false;
  78. }
  79. active = true;
  80. ESP_LOGI(TAG, "Resampler: %lu -> %lu Hz, taps=%d, filters=%d, interp=%s",
  81. (unsigned long)input_rate, (unsigned long)output_rate,
  82. RESAMPLER_NUM_TAPS, resampleGetNumFilters(resampler),
  83. resampleInterpolationUsed(resampler) ? "ON" : "OFF");
  84. return true;
  85. }
  86. size_t audio_resample_process(const int16_t *in, size_t in_frames, int16_t *out,
  87. size_t out_capacity) {
  88. if (!resampler || !active) {
  89. return 0;
  90. }
  91. size_t in_samples = in_frames * (size_t)current_channels;
  92. size_t out_samples = out_capacity * (size_t)current_channels;
  93. ensure_float_bufs(in_samples, out_samples);
  94. /* int16 → float [-1.0, 1.0) */
  95. for (size_t i = 0; i < in_samples; i++) {
  96. float_in[i] = (float)in[i] * (1.0f / 32768.0f);
  97. }
  98. ResampleResult result =
  99. resampleProcessInterleaved(resampler, float_in, (int)in_frames, float_out,
  100. (int)out_capacity, fixed_ratio);
  101. /* float → int16 with clamp */
  102. size_t out_total = (size_t)result.output_generated * (size_t)current_channels;
  103. for (size_t i = 0; i < out_total; i++) {
  104. float s = float_out[i] * 32768.0f;
  105. if (s > 32767.0f) {
  106. s = 32767.0f;
  107. } else if (s < -32768.0f) {
  108. s = -32768.0f;
  109. }
  110. out[i] = (int16_t)s;
  111. }
  112. return result.output_generated;
  113. }
  114. bool audio_resample_is_active(void) {
  115. return active;
  116. }
  117. void audio_resample_reset(void) {
  118. if (!resampler) {
  119. return;
  120. }
  121. resampleReset(resampler);
  122. }
  123. void audio_resample_destroy(void) {
  124. if (resampler) {
  125. resampleFree(resampler);
  126. resampler = NULL;
  127. }
  128. free(float_in);
  129. float_in = NULL;
  130. float_in_cap = 0;
  131. free(float_out);
  132. float_out = NULL;
  133. float_out_cap = 0;
  134. active = false;
  135. }
  136. size_t audio_resample_max_output(size_t in_frames) {
  137. if (!active || current_input_rate == 0) {
  138. return in_frames;
  139. }
  140. return (size_t)((double)in_frames * fixed_ratio + 2);
  141. }
  142. #else /* CONFIG_OUTPUT_SAMPLE_RATE_HZ == 44100 — no resampling needed */
  143. bool audio_resample_init(uint32_t input_rate, uint32_t output_rate,
  144. int channels) {
  145. (void)input_rate;
  146. (void)output_rate;
  147. (void)channels;
  148. return true;
  149. }
  150. size_t audio_resample_process(const int16_t *in, size_t in_frames, int16_t *out,
  151. size_t out_capacity) {
  152. (void)in;
  153. (void)out;
  154. (void)out_capacity;
  155. return in_frames;
  156. }
  157. bool audio_resample_is_active(void) {
  158. return false;
  159. }
  160. void audio_resample_reset(void) {
  161. }
  162. void audio_resample_destroy(void) {
  163. }
  164. size_t audio_resample_max_output(size_t in_frames) {
  165. return in_frames;
  166. }
  167. #endif