mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
Continue to tweak AirPlay to SPDIF (still drifting)
This commit is contained in:
@@ -61,8 +61,9 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
|
|||||||
// there will always be room at some point
|
// there will always be room at some point
|
||||||
while (len) {
|
while (len) {
|
||||||
LOCK_O;
|
LOCK_O;
|
||||||
|
|
||||||
bytes = min(len, _buf_cont_write(outputbuf));
|
bytes = min(_buf_space(outputbuf), _buf_cont_write(outputbuf));
|
||||||
|
bytes = min(len, bytes);
|
||||||
#if BYTES_PER_FRAME == 4
|
#if BYTES_PER_FRAME == 4
|
||||||
memcpy(outputbuf->writep, data, bytes);
|
memcpy(outputbuf->writep, data, bytes);
|
||||||
#else
|
#else
|
||||||
@@ -127,7 +128,7 @@ static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
|
|||||||
LOG_INFO("BT sink stopped");
|
LOG_INFO("BT sink stopped");
|
||||||
break;
|
break;
|
||||||
case BT_SINK_RATE:
|
case BT_SINK_RATE:
|
||||||
output.next_sample_rate = va_arg(args, u32_t);
|
output.next_sample_rate = output.current_sample_rate = va_arg(args, u32_t);
|
||||||
LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
|
LOG_INFO("Setting BT sample rate %u", output.next_sample_rate);
|
||||||
break;
|
break;
|
||||||
case BT_SINK_VOLUME: {
|
case BT_SINK_VOLUME: {
|
||||||
@@ -151,7 +152,7 @@ static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t play
|
|||||||
|
|
||||||
raop_sync.playtime = playtime;
|
raop_sync.playtime = playtime;
|
||||||
raop_sync.len = len;
|
raop_sync.len = len;
|
||||||
|
|
||||||
sink_data_handler(data, len);
|
sink_data_handler(data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
|
|||||||
LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, now - raop_sync.start_time, error);
|
LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, now - raop_sync.start_time, error);
|
||||||
if (abs(error) < 10 && abs(raop_sync.error) < 10) raop_sync.start = false;
|
if (abs(error) < 10 && abs(raop_sync.error) < 10) raop_sync.start = false;
|
||||||
} else {
|
} else {
|
||||||
// in how many ms will the most recent block play (there is one extra block of FRAME_BLOCK == MAX_SILENCE_FRAMES which is not in queue and not in outputbuf)
|
// in how many ms will the most recent block play
|
||||||
ms = ((u64_t) ((_buf_used(outputbuf) - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 1000) / RAOP_SAMPLE_RATE - (now - output.updated);
|
ms = ((u64_t) ((_buf_used(outputbuf) - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 1000) / RAOP_SAMPLE_RATE - (now - output.updated);
|
||||||
error = (raop_sync.playtime - now) - ms;
|
error = (raop_sync.playtime - now) - ms;
|
||||||
LOG_INFO("head local:%u, remote:%u (delta:%d)", ms, raop_sync.playtime - now, error);
|
LOG_INFO("head local:%u, remote:%u (delta:%d)", ms, raop_sync.playtime - now, error);
|
||||||
@@ -219,7 +220,7 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
|
|||||||
raop_sync.start = true;
|
raop_sync.start = true;
|
||||||
raop_sync.enabled = !strcasestr(output.device, "BT");
|
raop_sync.enabled = !strcasestr(output.device, "BT");
|
||||||
output.external = true;
|
output.external = true;
|
||||||
output.next_sample_rate = RAOP_SAMPLE_RATE;
|
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
|
||||||
output.state = OUTPUT_STOPPED;
|
output.state = OUTPUT_STOPPED;
|
||||||
break;
|
break;
|
||||||
case RAOP_STOP:
|
case RAOP_STOP:
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ sure that using rate_delay would fix that
|
|||||||
|
|
||||||
typedef enum { DAC_ON = 0, DAC_OFF, DAC_POWERDOWN, DAC_VOLUME } dac_cmd_e;
|
typedef enum { DAC_ON = 0, DAC_OFF, DAC_POWERDOWN, DAC_VOLUME } dac_cmd_e;
|
||||||
|
|
||||||
// must have an integer ratio with FRAME_BLOCK
|
// must have an integer ratio with FRAME_BLOCK (see spdif comment)
|
||||||
#define DMA_BUF_LEN 512
|
#define DMA_BUF_LEN 512
|
||||||
#define DMA_BUF_COUNT 12
|
#define DMA_BUF_COUNT 12
|
||||||
|
|
||||||
@@ -116,6 +116,7 @@ static int bytes_per_frame;
|
|||||||
static thread_type thread, stats_thread;
|
static thread_type thread, stats_thread;
|
||||||
static u8_t *obuf;
|
static u8_t *obuf;
|
||||||
static bool spdif;
|
static bool spdif;
|
||||||
|
static size_t dma_buf_frames;
|
||||||
|
|
||||||
DECLARE_ALL_MIN_MAX;
|
DECLARE_ALL_MIN_MAX;
|
||||||
|
|
||||||
@@ -268,27 +269,37 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
|
|||||||
};
|
};
|
||||||
i2s_config.sample_rate = output.current_sample_rate * 2;
|
i2s_config.sample_rate = output.current_sample_rate * 2;
|
||||||
i2s_config.bits_per_sample = 32;
|
i2s_config.bits_per_sample = 32;
|
||||||
|
// Normally counted in frames, but 16 sample are transformed into 32 bits in spdif
|
||||||
|
i2s_config.dma_buf_len = DMA_BUF_LEN / 2;
|
||||||
|
i2s_config.dma_buf_count = DMA_BUF_COUNT * 2;
|
||||||
|
/*
|
||||||
|
In DMA, we have room for (LEN * COUNT) frames of 32 bits samples that
|
||||||
|
we push at sample_rate * 2. Each of these peuso-frames is a single true
|
||||||
|
audio frame. So the real depth is true frames is (LEN * COUNT / 2)
|
||||||
|
*/
|
||||||
|
dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN / 2;
|
||||||
} else {
|
} else {
|
||||||
pin_config = (i2s_pin_config_t) { .bck_io_num = CONFIG_I2S_BCK_IO, .ws_io_num = CONFIG_I2S_WS_IO,
|
pin_config = (i2s_pin_config_t) { .bck_io_num = CONFIG_I2S_BCK_IO, .ws_io_num = CONFIG_I2S_WS_IO,
|
||||||
.data_out_num = CONFIG_I2S_DO_IO, .data_in_num = -1 //Not used
|
.data_out_num = CONFIG_I2S_DO_IO, .data_in_num = -1 //Not used
|
||||||
};
|
};
|
||||||
i2s_config.sample_rate = output.current_sample_rate;
|
i2s_config.sample_rate = output.current_sample_rate;
|
||||||
i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
|
i2s_config.bits_per_sample = bytes_per_frame * 8 / 2;
|
||||||
|
// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
|
||||||
|
i2s_config.dma_buf_len = DMA_BUF_LEN;
|
||||||
|
i2s_config.dma_buf_count = DMA_BUF_COUNT;
|
||||||
|
dma_buf_frames = DMA_BUF_COUNT * DMA_BUF_LEN;
|
||||||
#ifdef TAS575x
|
#ifdef TAS575x
|
||||||
gpio_pad_select_gpio(CONFIG_SPDIF_DO_IO);
|
gpio_pad_select_gpio(CONFIG_SPDIF_DO_IO);
|
||||||
gpio_set_direction(CONFIG_SPDIF_DO_IO, GPIO_MODE_OUTPUT);
|
gpio_set_direction(CONFIG_SPDIF_DO_IO, GPIO_MODE_OUTPUT);
|
||||||
gpio_set_level(CONFIG_SPDIF_DO_IO, 0);
|
gpio_set_level(CONFIG_SPDIF_DO_IO, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
|
i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX;
|
||||||
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
|
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
|
||||||
i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
|
i2s_config.communication_format = I2S_COMM_FORMAT_I2S| I2S_COMM_FORMAT_I2S_MSB;
|
||||||
// in case of overflow, do not replay old buffer
|
// in case of overflow, do not replay old buffer
|
||||||
i2s_config.tx_desc_auto_clear = true;
|
i2s_config.tx_desc_auto_clear = true;
|
||||||
i2s_config.dma_buf_count = DMA_BUF_COUNT;
|
|
||||||
// Counted in frames (but i2s allocates a buffer <= 4092 bytes)
|
|
||||||
i2s_config.dma_buf_len = DMA_BUF_LEN;
|
|
||||||
i2s_config.use_apll = true;
|
i2s_config.use_apll = true;
|
||||||
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
|
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1
|
||||||
|
|
||||||
@@ -391,7 +402,6 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
|
|||||||
return out_frames;
|
return out_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Main output thread
|
* Main output thread
|
||||||
*/
|
*/
|
||||||
@@ -415,7 +425,7 @@ static void *output_thread_i2s() {
|
|||||||
TIME_MEASUREMENT_START(timer_start);
|
TIME_MEASUREMENT_START(timer_start);
|
||||||
|
|
||||||
LOCK;
|
LOCK;
|
||||||
|
|
||||||
// manage led display
|
// manage led display
|
||||||
if (state != output.state) {
|
if (state != output.state) {
|
||||||
LOG_INFO("Output state is %d", output.state);
|
LOG_INFO("Output state is %d", output.state);
|
||||||
@@ -441,11 +451,11 @@ static void *output_thread_i2s() {
|
|||||||
|
|
||||||
output.updated = gettime_ms();
|
output.updated = gettime_ms();
|
||||||
output.frames_played_dmp = output.frames_played;
|
output.frames_played_dmp = output.frames_played;
|
||||||
// try to estimate how much we have consumed from the DMA buffer
|
// try to estimate how much we have consumed from the DMA buffer (calculation is incorrect at the very beginning ...)
|
||||||
output.device_frames = DMA_BUF_COUNT * DMA_BUF_LEN - ((output.updated - fullness) * output.current_sample_rate) / 1000;
|
output.device_frames = dma_buf_frames - ((output.updated - fullness) * output.current_sample_rate) / 1000;
|
||||||
oframes = _output_frames( iframes );
|
oframes = _output_frames( iframes );
|
||||||
output.frames_in_process = oframes;
|
output.frames_in_process = oframes;
|
||||||
|
|
||||||
SET_MIN_MAX_SIZED(oframes,rec,iframes);
|
SET_MIN_MAX_SIZED(oframes,rec,iframes);
|
||||||
SET_MIN_MAX_SIZED(_buf_used(outputbuf),o,outputbuf->size);
|
SET_MIN_MAX_SIZED(_buf_used(outputbuf),o,outputbuf->size);
|
||||||
SET_MIN_MAX_SIZED(_buf_used(streambuf),s,streambuf->size);
|
SET_MIN_MAX_SIZED(_buf_used(streambuf),s,streambuf->size);
|
||||||
@@ -547,6 +557,11 @@ static void *output_thread_i2s_stats() {
|
|||||||
LOG_INFO(" ----------+----------+-----------+-----------+");
|
LOG_INFO(" ----------+----------+-----------+-----------+");
|
||||||
RESET_ALL_MIN_MAX;
|
RESET_ALL_MIN_MAX;
|
||||||
}
|
}
|
||||||
|
LOG_INFO("Heap internal:%zu (min:%zu) external:%zu (min:%zu)",
|
||||||
|
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||||
|
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||||
|
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||||
|
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
|
||||||
usleep(STATS_PERIOD_MS *1000);
|
usleep(STATS_PERIOD_MS *1000);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user