| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- #include "audio_output.h"
- #include "rtsp_server.h"
- #include "audio_resample.h"
- #include "dac.h"
- #include "led.h"
- #include "driver/i2s_std.h"
- #include "driver/gpio.h"
- #include "esp_check.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "audio_receiver.h"
- #include <inttypes.h>
- #include <stdlib.h>
- // SIDE NOTE; providing power from GPIO pins is capped ~20mA.
- #if CONFIG_I2S_GND_IO >= 0
- #define I2S_GND_PIN CONFIG_I2S_GND_IO
- #endif
- #if CONFIG_I2S_VCC_IO >= 0
- #define I2S_VCC_PIN CONFIG_I2S_VCC_IO
- #endif
- // DACXSMT引脚必须拉高电平才能在外部DAC上启用输出
- #define DAC_XSMT_PIN 19
- #define TAG "audio_output"
- #define I2S_SCK_PIN CONFIG_I2S_SCK_IO
- #define I2S_BCK_PIN CONFIG_I2S_BCK_IO
- #define I2S_LRCK_PIN CONFIG_I2S_WS_IO
- #define I2S_DOUT_PIN CONFIG_I2S_DO_IO
- #define OUTPUT_RATE CONFIG_OUTPUT_SAMPLE_RATE_HZ
- #define FRAME_SAMPLES 352
- // DMA ring-buffer configuration. Total DMA latency (in samples) is
- // I2S_DMA_DESC_NUM × I2S_DMA_FRAME_NUM
- // which at OUTPUT_RATE gives the hardware pipeline delay in µs.
- // Keep these in sync with the i2s_chan_config_t initialisation below.
- #define I2S_DMA_DESC_NUM 8
- #define I2S_DMA_FRAME_NUM 256
- /* Max output frames after resampling one input frame */
- #define MAX_RESAMPLE_FRAMES \
- ((size_t)((FRAME_SAMPLES + 2) * ((double)OUTPUT_RATE / 44100) + 16))
- #if CONFIG_FREERTOS_UNICORE
- #define PLAYBACK_CORE 0
- #else
- #define PLAYBACK_CORE 1
- #endif
- static i2s_chan_handle_t tx_handle = NULL;
- static TaskHandle_t playback_task_handle = NULL;
- static volatile bool playback_running = false;
- static volatile bool flush_requested = false;
- static SemaphoreHandle_t s_audio_mutex = NULL;
- static volatile int source_rate = 44100;
- static volatile bool resample_reinit_needed = false;
- static volatile audio_channel_mode_t channel_mode = AUDIO_CHANNEL_STEREO;
- static void apply_volume(int16_t *buf, size_t n) {
- #ifndef CONFIG_DAC_CONTROLS_VOLUME
- int32_t vol = airplay_get_volume_q15();
- for (size_t i = 0; i < n; i++) {
- buf[i] = (int16_t)(((int32_t)buf[i] * vol) >> 15);
- }
- #endif
- }
- // Apply the selected channel mode to an interleaved stereo buffer (L,R,...).
- // LEFT/RIGHT route the chosen source channel to BOTH outputs so the selected
- // track is heard from both speakers; STEREO leaves the buffer untouched.
- static void apply_channel_mode(int16_t *buf, size_t frames) {
- audio_channel_mode_t mode = channel_mode;
- if (mode == AUDIO_CHANNEL_STEREO) {
- return;
- }
- size_t src = (mode == AUDIO_CHANNEL_RIGHT) ? 1 : 0;
- for (size_t i = 0; i < frames; i++) {
- int16_t s = buf[i * 2 + src];
- buf[i * 2] = s;
- buf[i * 2 + 1] = s;
- }
- }
- static void playback_task(void *arg) {
- int16_t *pcm = malloc((size_t)(FRAME_SAMPLES + 1) * 2 * sizeof(int16_t));
- int16_t *silence = calloc((size_t)FRAME_SAMPLES * 2, sizeof(int16_t));
- int16_t *resample_buf = malloc(MAX_RESAMPLE_FRAMES * 2 * sizeof(int16_t));
- if (!pcm || !silence || !resample_buf) {
- ESP_LOGE(TAG, "Failed to allocate buffers");
- free(pcm);
- free(silence);
- playback_task_handle = NULL;
- free(resample_buf);
- vTaskDelete(NULL);
- return;
- }
- size_t written;
- while (playback_running) {
- if (resample_reinit_needed) {
- resample_reinit_needed = false;
- audio_resample_init((uint32_t)source_rate, OUTPUT_RATE, 2);
- }
- if (flush_requested) {
- flush_requested = false;
- audio_resample_reset();
- i2s_channel_disable(tx_handle);
- i2s_channel_enable(tx_handle);
- }
- size_t samples = audio_receiver_read(pcm, FRAME_SAMPLES + 1);
- if (samples > 0) {
- if (s_audio_mutex) xSemaphoreTakeRecursive(s_audio_mutex, portMAX_DELAY);
- ESP_LOGD(TAG, "Read %u samples from receiver", (unsigned int)samples);
- int16_t *play_buf = pcm;
- size_t play_samples = samples;
- if (audio_resample_is_active()) {
- play_samples = audio_resample_process(pcm, samples, resample_buf,
- MAX_RESAMPLE_FRAMES);
- play_buf = resample_buf;
- }
- ESP_LOGD(TAG, "Resampled to %u samples", (unsigned int)play_samples);
- apply_volume(play_buf, play_samples * 2);
- apply_channel_mode(play_buf, play_samples);
- led_audio_feed(play_buf, play_samples);
- i2s_channel_write(tx_handle, play_buf, play_samples * 4, &written,
- portMAX_DELAY);
- if (s_audio_mutex) xSemaphoreGiveRecursive(s_audio_mutex);
- ESP_LOGD(TAG, "I2S write: %u bytes written", (unsigned int)written);
- taskYIELD();
- } else {
- // ESP_LOGW(TAG, "Receiver underflow - playing silence");
- led_audio_feed(silence, FRAME_SAMPLES);
- i2s_channel_write(tx_handle, silence, (size_t)FRAME_SAMPLES * 4, &written,
- pdMS_TO_TICKS(10));
- vTaskDelay(1);
- }
- }
- free(pcm);
- free(silence);
- playback_task_handle = NULL;
- vTaskDelete(NULL);
- }
- esp_err_t audio_output_init(void) {
- if (s_audio_mutex == NULL) {
- s_audio_mutex = xSemaphoreCreateRecursiveMutex();
- }
- i2s_chan_config_t chan_cfg =
- I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
- chan_cfg.dma_desc_num = I2S_DMA_DESC_NUM;
- chan_cfg.dma_frame_num = I2S_DMA_FRAME_NUM;
- ESP_RETURN_ON_ERROR(i2s_new_channel(&chan_cfg, &tx_handle, NULL), TAG,
- "channel create failed");
- i2s_std_config_t std_cfg = {
- .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(OUTPUT_RATE),
- .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
- I2S_SLOT_MODE_STEREO),
- .gpio_cfg =
- {
- .mclk = I2S_SCK_PIN,
- .bclk = I2S_BCK_PIN,
- .ws = I2S_LRCK_PIN,
- .dout = I2S_DOUT_PIN,
- .din = I2S_GPIO_UNUSED,
- },
- };
- #ifdef I2S_GND_PIN
- gpio_reset_pin(I2S_GND_PIN);
- gpio_set_direction(I2S_GND_PIN, GPIO_MODE_OUTPUT);
- gpio_set_level(I2S_GND_PIN, 0);
- #endif
- #ifdef I2S_VCC_PIN
- gpio_reset_pin(I2S_VCC_PIN);
- gpio_set_direction(I2S_VCC_PIN, GPIO_MODE_OUTPUT);
- gpio_set_level(I2S_VCC_PIN, 1);
- #endif
- ESP_RETURN_ON_ERROR(i2s_channel_init_std_mode(tx_handle, &std_cfg), TAG,
- "std mode init failed");
- ESP_RETURN_ON_ERROR(i2s_channel_enable(tx_handle), TAG,
- "channel enable failed");
- ESP_LOGI(TAG, "I2S initialized: Rate=%u, DMA_Desc=%d, DMA_Frame=%d",
- (unsigned int)OUTPUT_RATE, I2S_DMA_DESC_NUM, I2S_DMA_FRAME_NUM);
- // DAC XSMT: pull high to enable external DAC output
- #ifdef DAC_XSMT_PIN
- gpio_reset_pin(DAC_XSMT_PIN);
- gpio_set_direction(DAC_XSMT_PIN, GPIO_MODE_OUTPUT);
- gpio_set_level(DAC_XSMT_PIN, 1);
- ESP_LOGI(TAG, "DAC XSMT pin %d set high", DAC_XSMT_PIN);
- #endif
- // MCLK/BCLK/LRCK are now running. Some codecs need this edge to finish their
- // clock setup; amplifiers that manage power from board RTSP events can ignore
- // the hook.
- dac_on_i2s_started();
- audio_resample_init(44100, OUTPUT_RATE, 2);
- return ESP_OK;
- }
- void audio_output_start(void) {
- if (playback_task_handle != NULL) {
- return; // already running
- }
- playback_running = true;
- xTaskCreatePinnedToCore(playback_task, "audio_play", 4096, NULL, 7,
- &playback_task_handle, PLAYBACK_CORE);
- }
- void audio_output_stop(void) {
- if (playback_task_handle == NULL) {
- return;
- }
- playback_running = false;
- // Wait for task to exit cleanly
- int timeout = 40;
- while (playback_task_handle != NULL && timeout-- > 0) {
- vTaskDelay(pdMS_TO_TICKS(50));
- }
- if (playback_task_handle != NULL) {
- ESP_LOGW(TAG, "Playback task did not exit within timeout");
- } else {
- ESP_LOGI(TAG, "Playback task stopped");
- }
- }
- esp_err_t audio_output_write(const void *data, size_t bytes, TickType_t wait) {
- size_t written = 0;
- if (s_audio_mutex) xSemaphoreTakeRecursive(s_audio_mutex, portMAX_DELAY);
- esp_err_t err = i2s_channel_write(tx_handle, data, bytes, &written, wait);
- if (s_audio_mutex) xSemaphoreGiveRecursive(s_audio_mutex);
- return err;
- }
- void audio_output_set_sample_rate(uint32_t rate) {
- // Only safe to call when no writer task is actively using I2S
- // (AirPlay playback task must be stopped, BT calls this before
- // the I2S writer task starts consuming data)
- ESP_LOGI(TAG, "Setting sample rate to %" PRIu32 " Hz", rate);
- if (tx_handle == NULL) {
- ESP_LOGW(TAG, "I2S 通道未初始化,跳过采样率时钟配置");
- return;
- }
- i2s_channel_disable(tx_handle);
- i2s_std_clk_config_t clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(rate);
- i2s_channel_reconfig_std_clock(tx_handle, &clk_cfg);
- i2s_channel_enable(tx_handle);
- }
- void audio_output_flush(void) {
- flush_requested = true;
- }
- void audio_output_lock(void) {
- if (s_audio_mutex) xSemaphoreTakeRecursive(s_audio_mutex, portMAX_DELAY);
- }
- void audio_output_unlock(void) {
- if (s_audio_mutex) xSemaphoreGiveRecursive(s_audio_mutex);
- }
- void audio_output_set_source_rate(int rate) {
- if (rate > 0 && rate != source_rate) {
- source_rate = rate;
- resample_reinit_needed = true;
- }
- }
- uint32_t audio_output_get_hardware_latency_us(void) {
- return (
- uint32_t)(((uint64_t)I2S_DMA_DESC_NUM * I2S_DMA_FRAME_NUM * 1000000ULL) /
- OUTPUT_RATE);
- }
- audio_channel_mode_t audio_output_cycle_channel_mode(void) {
- audio_channel_mode_t next;
- switch (channel_mode) {
- case AUDIO_CHANNEL_STEREO:
- next = AUDIO_CHANNEL_LEFT;
- break;
- case AUDIO_CHANNEL_LEFT:
- next = AUDIO_CHANNEL_RIGHT;
- break;
- default:
- next = AUDIO_CHANNEL_STEREO;
- break;
- }
- channel_mode = next;
- ESP_LOGI(TAG, "Channel mode: %s",
- next == AUDIO_CHANNEL_LEFT ? "LEFT only"
- : next == AUDIO_CHANNEL_RIGHT ? "RIGHT only"
- : "STEREO");
- return next;
- }
- audio_channel_mode_t audio_output_get_channel_mode(void) {
- return channel_mode;
- }
|