- change buffer sizes of medians

- change syncing to be more predictable using I2S_EVENT_TX_DONE
  o also increase DMA length so i2s won't eat up so much processing time
  o ensure at least one chunk is in DMA buffer
This commit is contained in:
Carlos
2021-05-03 06:39:30 +02:00
Unverified
parent e331d7afa5
commit 917aa7ab26
2 changed files with 306 additions and 158 deletions

View File

@@ -87,7 +87,7 @@ void codec_header_message_free(codec_header_message_t *msg);
typedef struct wire_chunk_message { typedef struct wire_chunk_message {
tv_t timestamp; tv_t timestamp;
uint32_t size; size_t size;
char *payload; char *payload;
} wire_chunk_message_t; } wire_chunk_message_t;

View File

@@ -50,7 +50,7 @@
#include <sys/time.h> #include <sys/time.h>
#define CONFIG_USE_WIFI_PROVISIONING 1 #define CONFIG_USE_WIFI_PROVISIONING 0
#define COLLECT_RUNTIME_STATS 0 #define COLLECT_RUNTIME_STATS 0
// @ 48kHz, 2ch, 16bit audio data and 24ms wirechunks (hardcoded for now) we expect 0.024 * 2 * 16/8 * 48000 = 4608 Bytes // @ 48kHz, 2ch, 16bit audio data and 24ms wirechunks (hardcoded for now) we expect 0.024 * 2 * 16/8 * 48000 = 4608 Bytes
@@ -100,7 +100,8 @@ TaskHandle_t syncTaskHandle = NULL;
#define CONFIG_USE_SNTP 0 #define CONFIG_USE_SNTP 0
#define DAC_OUT_BUFFER_TIME_US 0 #define DAC_OUT_BUFFER_TIME_US 0//3500 // determined this by comparing a 180bpm metronome signal on a scope which is played by esp32 and ubuntu client
// not sure why I need this though... And why it is so high. I'd expect something in the µs range??!
static const char *TAG = "SC"; static const char *TAG = "SC";
@@ -111,7 +112,7 @@ char *codecString = NULL;
// configMAX_PRIORITIES - 1 // configMAX_PRIORITIES - 1
// TODO: what are the best values here? // TODO: what are the best values here?
#define SYNC_TASK_PRIORITY 8//configMAX_PRIORITIES - 2 #define SYNC_TASK_PRIORITY 7//configMAX_PRIORITIES - 2
#define SYNC_TASK_CORE_ID tskNO_AFFINITY//1//tskNO_AFFINITY #define SYNC_TASK_CORE_ID tskNO_AFFINITY//1//tskNO_AFFINITY
#define TIMESTAMP_TASK_PRIORITY 6 #define TIMESTAMP_TASK_PRIORITY 6
@@ -154,8 +155,8 @@ SemaphoreHandle_t timer0_syncSampleSemaphoreHandle = NULL;
SemaphoreHandle_t latencyBufSemaphoreHandle = NULL; SemaphoreHandle_t latencyBufSemaphoreHandle = NULL;
#define MEDIAN_FILTER_LONG_BUF_LEN 599 #define MEDIAN_FILTER_LONG_BUF_LEN 199//299
#define MEDIAN_FILTER_MINI_BUF_LEN 199 #define MEDIAN_FILTER_MINI_BUF_LEN 59//199
#define MEDIAN_FILTER_SHORT_BUF_LEN 19 #define MEDIAN_FILTER_SHORT_BUF_LEN 19
uint8_t latencyBufCnt = 0; uint8_t latencyBufCnt = 0;
@@ -176,17 +177,13 @@ static int64_t latencyToServer = 0;
// shortBuffer_.setSize(100); // shortBuffer_.setSize(100);
// miniBuffer_.setSize(20); // miniBuffer_.setSize(20);
#define SHORT_BUFFER_LEN 99 #define SHORT_BUFFER_LEN 59//99
int64_t short_buffer[SHORT_BUFFER_LEN]; int64_t short_buffer[SHORT_BUFFER_LEN];
int64_t short_buffer_median[SHORT_BUFFER_LEN]; int64_t short_buffer_median[SHORT_BUFFER_LEN];
int short_buffer_cnt = 0;
int short_buffer_full = 0;
#define MINI_BUFFER_LEN 19 #define MINI_BUFFER_LEN 19
int64_t mini_buffer[MINI_BUFFER_LEN]; int64_t mini_buffer[MINI_BUFFER_LEN];
int64_t mini_buffer_median[MINI_BUFFER_LEN]; int64_t mini_buffer_median[MINI_BUFFER_LEN];
int mini_buffer_cnt = 0;
int mini_buffer_full = 0;
static sMedianFilter_t shortMedianFilter; static sMedianFilter_t shortMedianFilter;
static sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN]; static sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN];
@@ -201,7 +198,7 @@ uint8_t muteCH[4] = {0};
audio_board_handle_t board_handle; audio_board_handle_t board_handle;
/* Constants that aren't configurable in menuconfig */ /* Constants that aren't configurable in menuconfig */
#define HOST "192.168.1.6" #define HOST "192.168.8.1"
#define PORT 1704 #define PORT 1704
#define BUFF_LEN 10000 #define BUFF_LEN 10000
unsigned int addr; unsigned int addr;
@@ -218,8 +215,8 @@ static char buff[BUFF_LEN];
//static audio_element_handle_t snapcast_stream; //static audio_element_handle_t snapcast_stream;
static char mac_address[18]; static char mac_address[18];
#define MY_SSID "..." #define MY_SSID "OpenWrt"
#define MY_WPA2_PSK "..." #define MY_WPA2_PSK ""
static EventGroupHandle_t s_wifi_event_group; static EventGroupHandle_t s_wifi_event_group;
@@ -293,7 +290,7 @@ static void get_device_service_name(char *service_name, size_t max)
* *
*/ */
static void wifi_init_sta(void) { static void wifi_init_sta(void) {
/* Start Wi-Fi in station mode */ // Start Wi-Fi in station mode
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_start()); ESP_ERROR_CHECK(esp_wifi_start());
@@ -423,11 +420,23 @@ void wifi_init(void) {
* *
*/ */
void wifi_init(void) { void wifi_init(void) {
// Register our event handler for Wi-Fi, IP and Provisioning related events
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
// Initialize Wi-Fi including netif with default config
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
// wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
// ESP_ERROR_CHECK(esp_wifi_init(&cfg));
// ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
// ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
wifi_config_t wifi_config = { wifi_config_t wifi_config = {
.sta = { .sta = {
@@ -590,7 +599,7 @@ int8_t latency_buffer_full(void) {
} }
if (xSemaphoreTake( latencyBufSemaphoreHandle, 0) == pdFALSE) { if (xSemaphoreTake( latencyBufSemaphoreHandle, 0) == pdFALSE) {
//ESP_LOGW(TAG, "latency_buffer_full: can't take semaphore"); ESP_LOGW(TAG, "latency_buffer_full: can't take semaphore");
return -1; return -1;
} }
@@ -931,6 +940,8 @@ static void snapcast_sync_task(void *pvParameters) {
int64_t avg = 0; int64_t avg = 0;
int64_t latencyInitialSync = 0; int64_t latencyInitialSync = 0;
int dir = 0; int dir = 0;
i2s_event_t i2sEvent;
uint32_t i2sDmaBufferCnt = 0;
ESP_LOGI(TAG, "started sync task"); ESP_LOGI(TAG, "started sync task");
@@ -940,8 +951,6 @@ static void snapcast_sync_task(void *pvParameters) {
adjust_apll(0); adjust_apll(0);
mini_buffer_cnt = 0;
mini_buffer_full = 0;
miniMedianFilter.numNodes = MINI_BUFFER_LEN; miniMedianFilter.numNodes = MINI_BUFFER_LEN;
miniMedianFilter.medianBuffer = miniMedianBuffer; miniMedianFilter.medianBuffer = miniMedianBuffer;
if (MEDIANFILTER_Init(&miniMedianFilter) ) { if (MEDIANFILTER_Init(&miniMedianFilter) ) {
@@ -950,8 +959,6 @@ static void snapcast_sync_task(void *pvParameters) {
return; return;
} }
short_buffer_cnt = 0;
short_buffer_full = 0;
shortMedianFilter.numNodes = SHORT_BUFFER_LEN; shortMedianFilter.numNodes = SHORT_BUFFER_LEN;
shortMedianFilter.medianBuffer = shortMedianBuffer; shortMedianFilter.medianBuffer = shortMedianBuffer;
if (MEDIANFILTER_Init(&shortMedianFilter) ) { if (MEDIANFILTER_Init(&shortMedianFilter) ) {
@@ -964,37 +971,63 @@ static void snapcast_sync_task(void *pvParameters) {
if (chnk == NULL) { if (chnk == NULL) {
// ESP_LOGE(TAG, "msg waiting pcm %d ts %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle)); // ESP_LOGE(TAG, "msg waiting pcm %d ts %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle));
ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(2000) ); if ((initialSync == 0) && (i2sDmaBufferCnt == 0)) {
if( ret == pdPASS ) { ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(2000) );
//ESP_LOGW(TAG, "pcm chunks waiting %d", uxQueueMessagesWaiting(pcmChunkQueueHandle)); if( ret != pdFAIL ) {
// ESP_LOGW(TAG, "got first pcm chunk");
}
}
else {
ret = xQueueReceive(i2s_event_queue, &i2sEvent, pdMS_TO_TICKS(24) );
if( ret != pdFAIL ) {
if (i2sEvent.type == I2S_EVENT_TX_DONE) {
// ESP_LOGI(TAG, "I2S_EVENT_TX_DONE, %u", i2sDmaBufferCnt);
if (i2sDmaBufferCnt > 0) {
i2sDmaBufferCnt--;
if ((initialSync == 0) && (i2sDmaBufferCnt == 0)) {
i2s_stop(i2s_cfg.i2s_port);
continue;
}
}
if ((i2sDmaBufferCnt % 2) == 0) {
ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(10) );
if( ret != pdFAIL ) {
// ESP_LOGW(TAG, "got next pcm chunk");
}
else {
ESP_LOGW(TAG, "couldn't get pcm chunk %d %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle));
continue;
}
}
else {
// ESP_LOGW(TAG, "continue");
continue;
}
}
else {
ESP_LOGW(TAG, "i2s unexpected event");
continue;
}
}
else {
ESP_LOGW(TAG, "no i2s events");
continue;
}
} }
} }
else { else {
// ESP_LOGW(TAG, "already retrieved chunk needs service");
ret = pdPASS; ret = pdPASS;
} }
if( ret == pdPASS ) { if( ret != pdFAIL ) {
//// // wait for early time syncs to be ready
// int tmp = latency_buffer_full();
// if ( tmp <= 0 ) {
// if (tmp < 0) {
// vTaskDelay(1);
//
// continue;
// }
//
// // free chunk so we can get next one
// free(chnk->payload);
// free(chnk);
// chnk = NULL;
//
//// ESP_LOGW(TAG, "diff buffer not full");
//
// vTaskDelay( pdMS_TO_TICKS(100) );
//
// continue;
// }
// if (initialSync == 0) // using this, latency on initial sync is stored and serverNow calculation is based solely on this value until next hard sync // if (initialSync == 0) // using this, latency on initial sync is stored and serverNow calculation is based solely on this value until next hard sync
if (1) // always use newest, median filtered serverNow if (1) // always use newest, median filtered serverNow
{ {
@@ -1007,9 +1040,11 @@ static void snapcast_sync_task(void *pvParameters) {
else { else {
ESP_LOGW(TAG, "couldn't get server now"); ESP_LOGW(TAG, "couldn't get server now");
free(chnk->payload); if (chnk != NULL) {
free(chnk); free(chnk->payload);
chnk = NULL; free(chnk);
chnk = NULL;
}
vTaskDelay( pdMS_TO_TICKS(100) ); vTaskDelay( pdMS_TO_TICKS(100) );
@@ -1027,6 +1062,46 @@ static void snapcast_sync_task(void *pvParameters) {
(int64_t)taskCfg->outputBufferDacTime_us; (int64_t)taskCfg->outputBufferDacTime_us;
} }
// wait for early time syncs to be ready
int tmp = latency_buffer_full();
if ( tmp <= 0 ) {
if (tmp < 0) {
vTaskDelay(1);
continue;
}
if (age >= 0) {
// free chunk so we can get next one
free(chnk->payload);
free(chnk);
chnk = NULL;
}
// ESP_LOGW(TAG, "diff buffer not full");
vTaskDelay( pdMS_TO_TICKS(10) );
continue;
}
if (chnk != NULL) {
p_payload = chnk->payload;
size = chnk->size;
}
if ((initialSync == 0) && (i2sDmaBufferCnt > 0)) {
ESP_LOGW(TAG, "waiting for i2s to empty %u", i2sDmaBufferCnt);
if (chnk != NULL) {
free(chnk->payload);
free(chnk);
chnk = NULL;
}
continue;
}
if (age < 0) { // get initial sync using hardware timer if (age < 0) { // get initial sync using hardware timer
if (initialSync == 0) { if (initialSync == 0) {
if (MEDIANFILTER_Init(&shortMedianFilter) ) { if (MEDIANFILTER_Init(&shortMedianFilter) ) {
@@ -1037,17 +1112,72 @@ static void snapcast_sync_task(void *pvParameters) {
adjust_apll(0); // reset to normal playback speed adjust_apll(0); // reset to normal playback speed
p_payload = chnk->payload; // i2s_stop(i2s_cfg.i2s_port);
size = chnk->size; // i2s_zero_dma_buffer(i2s_cfg.i2s_port);
// p_payload = chnk->payload;
// size = chnk->size;
i2s_stop(i2s_cfg.i2s_port); i2s_stop(i2s_cfg.i2s_port);
// i2s_zero_dma_buffer(i2s_cfg.i2s_port);
i2sDmaBufferCnt = 0;
size_t writtenBytes; size_t writtenBytes;
int err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, 0); int err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, pdMS_TO_TICKS(2000));
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "I2S write error"); ESP_LOGE(TAG, "I2S write error");
} }
i2sDmaBufferCnt += 2;
// ESP_LOGE(TAG, "I2S written 1 %u / %u", writtenBytes, size);
size -= writtenBytes; size -= writtenBytes;
//p_payload += writtenBytes; // TODO: produces heap error??? //p_payload += writtenBytes; // TODO: produces heap error???
if (size == 0) {
// ESP_LOGI(TAG, "I2S can take more");
if (chnk != NULL) {
free(chnk->payload);
free(chnk);
chnk = NULL;
}
ret = xQueueReceive(pcmChunkQueueHandle, &chnk, portMAX_DELAY);
if( ret != pdFAIL ) {
p_payload = chnk->payload;
size = chnk->size;
err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, pdMS_TO_TICKS(2000));
if (err != ESP_OK) {
ESP_LOGE(TAG, "I2S write error");
}
// ESP_LOGE(TAG, "I2S written 2 %u / %u", writtenBytes, size);
i2sDmaBufferCnt += 2;
size -= writtenBytes;
if (size != 0) {
ESP_LOGE(TAG, "I2S check DMA buffer sizes, 1 chunk should fit in two DMA buffers! %u, %u", size, writtenBytes);
}
if (chnk != NULL) {
free(chnk->payload);
free(chnk);
chnk = NULL;
}
//p_payload += writtenBytes; // TODO: produces heap error???
}
else {
ESP_LOGW(TAG, "I2S writing more not possible, couldn't get pcm chunk");
continue;
}
}
//tg0_timer1_start((-age * 10) - alarmValSub)); // alarm a little earlier to account for context switch duration from freeRTOS, timer with 100ns ticks //tg0_timer1_start((-age * 10) - alarmValSub)); // alarm a little earlier to account for context switch duration from freeRTOS, timer with 100ns ticks
tg0_timer1_start(-age - alarmValSub); // alarm a little earlier to account for context switch duration from freeRTOS, timer with 1µs ticks tg0_timer1_start(-age - alarmValSub); // alarm a little earlier to account for context switch duration from freeRTOS, timer with 1µs ticks
@@ -1070,50 +1200,56 @@ static void snapcast_sync_task(void *pvParameters) {
// TODO: try to get better initial sync using alarmValSub to alarm early, // TODO: try to get better initial sync using alarmValSub to alarm early,
// doesn't work with current style of loading i2s buffer early. // doesn't work with current style of loading i2s buffer early.
// if (((age < -11LL) || (age > 0)) && (initialSync == 0)) { // if (((age < -11LL) || (age > 0)) && (initialSync == 0)) {
// if (age < -11LL) { // if (age < -11LL) {
// alarmValSub--; // alarmValSub--;
// } // }
// else { // else {
// alarmValSub++; // alarmValSub++;
// } // }
// //
// // free chunk so we can get next one // // free chunk so we can get next one
// if (chnk != NULL) {
// free(chnk->payload); // free(chnk->payload);
// free(chnk); // free(chnk);
// chnk = NULL; // chnk = NULL;
//
// struct timeval t;
// get_diff_to_server(&t);
// ESP_LOGI(TAG, "no hard sync, age %lldus, %lldus", age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec));
//
// continue;
// }
// else if (initialSync == 0) {
// // i2s_start(i2s_cfg.i2s_port);
//
// ESP_LOGW(TAG, "start");
// } // }
// //
get_diff_to_server(&latencyInitialSync); // store current latency for later use // int64_t t;
// get_diff_to_server(&t);
// ESP_LOGI(TAG, "no hard sync, age %lldus, %lldus", age, t);
//
// vTaskDelay(pdMS_TO_TICKS((writtenBytes / 4) / 48) + 1); // wait until i2s dma is empty
//
// continue;
// }
// else if (initialSync == 0) {
//// i2s_start(i2s_cfg.i2s_port);
//
// ESP_LOGW(TAG, "start");
// }
// get_diff_to_server(&latencyInitialSync); // store current latency for later use
initialSync = 1; initialSync = 1;
ESP_LOGI(TAG, "initial sync %lldus", age);
// ESP_LOGW(TAG, "chunk %d %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle));
continue;
} }
} }
else if ((age > 0) && (initialSync == 0)) { else if ((age > 0) && (initialSync == 0)) {
free(chnk->payload); if (chnk != NULL) {
free(chnk); free(chnk->payload);
chnk = NULL; free(chnk);
chnk = NULL;
}
int64_t t; int64_t t;
get_diff_to_server(&t); get_diff_to_server(&t);
ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, t); ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, t);
if (short_buffer_full) {
short_buffer_cnt = 0;
short_buffer_full = 0;
}
dir = 0; dir = 0;
initialSync = 0; initialSync = 0;
@@ -1123,95 +1259,98 @@ static void snapcast_sync_task(void *pvParameters) {
} }
if (initialSync == 1) { if (initialSync == 1) {
p_payload = chnk->payload; const uint8_t enableControlLoop = 1;
size = chnk->size; const int64_t age_expect = 48000;
const int64_t maxOffset = 100;
const int64_t maxOffset_dropSample = 1000;
size_t writtenBytes; avg = MEDIANFILTER_Insert(&shortMedianFilter, age + age_expect);
int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, portMAX_DELAY); //avg = age + age_expect;
if (err != ESP_OK) {
ESP_LOGE(TAG, "I2S write error");
}
short_buffer_full = 1;
// short_buffer_cnt++;
// if (short_buffer_cnt >= SHORT_BUFFER_LEN ) {
// short_buffer_full = 1;
// }
avg = MEDIANFILTER_Insert(&shortMedianFilter, age);
// resync hard if we are off too far // resync hard if we are off too far
if ((avg < -1 * chunkDuration_us / 8) || (avg > 1 * chunkDuration_us / 8) || (initialSync == 0)) { if ((avg < -1 * chunkDuration_us / 4) || (avg > 1 * chunkDuration_us / 4) || (initialSync == 0)) {
free(chnk->payload); if (chnk != NULL) {
free(chnk); free(chnk->payload);
chnk = NULL; free(chnk);
chnk = NULL;
}
int64_t t; int64_t t;
get_diff_to_server(&t); get_diff_to_server(&t);
ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus", age, t); ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus, %lldus", age, avg, t);
short_buffer_cnt = 0;
short_buffer_full = 0;
initialSync = 0; initialSync = 0;
alarmValSub = 0; alarmValSub = 0;
continue; continue;
} }
}
size_t writtenBytes;
int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, pdMS_TO_TICKS(100));
if (err != ESP_OK) {
ESP_LOGE(TAG, "I2S write error");
}
const int64_t age_expect = 0; if (writtenBytes != size) {
const int64_t maxOffset = 300; ESP_LOGE(TAG, "written too less %u %u", size, writtenBytes);
const int64_t maxOffset_dropSample = 1000; }
// NOT 100% SURE ABOUT THE FOLLOWING CONTROL LOOP, PROBABLY BETTER WAYS TO DO IT, STILL TESTING
if (initialSync == 1) i2sDmaBufferCnt += 2;
{ // got initial sync, decrease / increase playback speed if abs(age) > maxOffset
// NOT 100% SURE ABOUT THE FOLLOWING CONTROL LOOP, PROBABLY BETTER WAYS TO DO IT, STILL TESTING
int samples = 1; int samples = 1;
int sampleSize = 4; int sampleSize = 4;
int ageDiff = 0; int ageDiff = 0;
if (short_buffer_full) { // TODO: not sure how to do frame correction with current implementation
if (avg < (age_expect - maxOffset)) { // which relies heavily on putting full chunks into I2S DMA buffer
// and tracking the buffer's fill state by counting events. Adding
// or deleting samples will confuse the algorithm...
if (enableControlLoop == 1) {
if (avg < -maxOffset) {
dir = -1; dir = -1;
if (avg < (age_expect - maxOffset_dropSample) ) { // //if (avg < (age_expect - maxOffset_dropSample) ) {
ageDiff = (int)(age_expect - avg); // if (avg < -maxOffset_dropSample) {
samples = ageDiff / (sampleDuration_ns / 1000); // //ageDiff = (int)(age_expect - avg);
if (samples > 4) { // ageDiff = (int)avg;
samples = 4; // samples = ageDiff / (sampleDuration_ns / 1000);
} // if (samples > 4) {
// samples = 4;
// too young add samples // }
size_t writtenBytes; //
int err = i2s_write(I2S_NUM_0, p_payload, samples * sampleSize, &writtenBytes, portMAX_DELAY); // // too young add samples
if (err != ESP_OK) { // size_t writtenBytes;
ESP_LOGE(TAG, "I2S write error"); //
} // int err = i2s_write(I2S_NUM_0, p_payload, samples * sampleSize, &writtenBytes, portMAX_DELAY);
// if (err != ESP_OK) {
ESP_LOGI(TAG, "insert %d samples", samples); // ESP_LOGE(TAG, "I2S write error");
} // }
//
// ESP_LOGI(TAG, "insert %d samples", samples);
// }
} }
else if ((avg >= (age_expect - maxOffset)) && (avg <= (age_expect + maxOffset))) { else if ((avg >= -maxOffset) && (avg <= maxOffset)) {
dir = 0; dir = 0;
} }
else if (avg > (age_expect + maxOffset)) { else if (avg > maxOffset) {
dir = 1; dir = 1;
if (avg > (age_expect + maxOffset_dropSample)) { // //if (avg > (age_expect + maxOffset_dropSample)) {
ageDiff = (int)(avg - age_expect); // if (avg > maxOffset_dropSample) {
samples = ageDiff / (sampleDuration_ns / 1000); // //ageDiff = (int)(avg - age_expect);
if (samples > 4) { // ageDiff = (int)avg;
samples = 4; // samples = ageDiff / (sampleDuration_ns / 1000);
} // if (samples > 4) {
// samples = 4;
// drop samples // }
p_payload += samples * sampleSize; //
size -= samples * sampleSize; // // drop samples
// p_payload += samples * sampleSize;
ESP_LOGI(TAG, "drop %d samples", samples); // size -= samples * sampleSize;
} //
// ESP_LOGI(TAG, "drop %d samples", samples);
// }
} }
adjust_apll(dir); adjust_apll(dir);
@@ -1220,11 +1359,14 @@ static void snapcast_sync_task(void *pvParameters) {
int64_t t; int64_t t;
get_diff_to_server(&t); get_diff_to_server(&t);
ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus %lldus", dir, age, avg, t, latencyInitialSync); ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus", dir, age, avg, t);
// ESP_LOGW(TAG, "chunk %d %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle));
free(chnk->payload); if (chnk != NULL) {
free(chnk); free(chnk->payload);
chnk = NULL; free(chnk);
chnk = NULL;
}
} }
else { else {
int64_t t; int64_t t;
@@ -1236,9 +1378,6 @@ static void snapcast_sync_task(void *pvParameters) {
reset_latency_buffer(); // ensure correct latencies, if there is no stream received from server. reset_latency_buffer(); // ensure correct latencies, if there is no stream received from server.
// latency will be shorter if no wirechunks have to be serviced and decoded // latency will be shorter if no wirechunks have to be serviced and decoded
short_buffer_cnt = 0;
short_buffer_full = 0;
dir = 0; dir = 0;
initialSync = 0; initialSync = 0;
@@ -1747,16 +1886,19 @@ static void http_get_task(void *pvParameters) {
else { else {
// if mini latency buffer is full, we stop initial flooding with time messages // if mini latency buffer is full, we stop initial flooding with time messages
if (latencyBuffFull == false) { if (latencyBuffFull == false) {
if (xSemaphoreTake( latencyBufSemaphoreHandle, portMAX_DELAY ) == pdTRUE) { if (xSemaphoreTake( latencyBufSemaphoreHandle, pdMS_TO_TICKS(1) ) == pdTRUE) {
latencyBuffFull = true; latencyBuffFull = true;
xSemaphoreGive( latencyBufSemaphoreHandle ); xSemaphoreGive( latencyBufSemaphoreHandle );
} }
else {
ESP_LOGW(TAG, "Couldn't set latencyBuffFull = true");
}
} }
} }
} }
if (xSemaphoreTake( latencyBufSemaphoreHandle, portMAX_DELAY ) == pdTRUE) { if (xSemaphoreTake( latencyBufSemaphoreHandle, pdMS_TO_TICKS(1) ) == pdTRUE) {
// TODO: find better way to check if Median Filter is full // TODO: find better way to check if Median Filter is full
// Count how much latencies we have stored so far // Count how much latencies we have stored so far
// latencyBufCnt++; // latencyBufCnt++;
@@ -1774,7 +1916,7 @@ static void http_get_task(void *pvParameters) {
xSemaphoreGive( latencyBufSemaphoreHandle ); xSemaphoreGive( latencyBufSemaphoreHandle );
} }
else { else {
ESP_LOGW(TAG, "can't take latencyBufSemaphoreHandle"); ESP_LOGW(TAG, "couldn't set latencyToServer = medianValue");
} }
// store current time // store current time
@@ -1944,6 +2086,7 @@ int flac_decoder_write_cb(audio_element_handle_t el, char *buf, int len, TickTyp
// receivedByteCnt += len; // receivedByteCnt += len;
// ESP_LOGI(TAG, "flac_decoder_write_cb: len %d, cnt %lld, received total: %lld", len, flacCnt, receivedByteCnt); // ESP_LOGI(TAG, "flac_decoder_write_cb: len %d, cnt %lld, received total: %lld", len, flacCnt, receivedByteCnt);
// ESP_LOGI(TAG, "flac_decoder_write_cb: flac cnt %lld",flacCnt); // ESP_LOGI(TAG, "flac_decoder_write_cb: flac cnt %lld",flacCnt);
// ESP_LOGI(TAG, "flac_decoder_write_cb: len %d", len);
if (xQueueReceive( timestampQueueHandle, &timestamp, wait_time ) == pdPASS) { if (xQueueReceive( timestampQueueHandle, &timestamp, wait_time ) == pdPASS) {
pcm_chunk_message = (wire_chunk_message_t *)heap_caps_malloc(sizeof(wire_chunk_message_t), MALLOC_CAP_SPIRAM); pcm_chunk_message = (wire_chunk_message_t *)heap_caps_malloc(sizeof(wire_chunk_message_t), MALLOC_CAP_SPIRAM);
@@ -1965,6 +2108,9 @@ int flac_decoder_write_cb(audio_element_handle_t el, char *buf, int len, TickTyp
if (xQueueSendToBack( pcmChunkQueueHandle, &pcm_chunk_message, pdMS_TO_TICKS(1000)) != pdTRUE) { if (xQueueSendToBack( pcmChunkQueueHandle, &pcm_chunk_message, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGW(TAG, "wirechunk_to_pcm_timestamp_task: send: pcmChunkQueue full, messages waiting %d", uxQueueMessagesWaiting(pcmChunkQueueHandle)); ESP_LOGW(TAG, "wirechunk_to_pcm_timestamp_task: send: pcmChunkQueue full, messages waiting %d", uxQueueMessagesWaiting(pcmChunkQueueHandle));
} }
else {
//ESP_LOGI(TAG, "flac_decoder_write_cb: pcm_chunk_message->size %u", pcm_chunk_message->size);
}
} }
} }
} }
@@ -2001,8 +2147,8 @@ void setup_dsp_i2s(uint32_t sample_rate, i2s_port_t i2sNum)
.bits_per_sample = BITS_PER_SAMPLE, .bits_per_sample = BITS_PER_SAMPLE,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels
.communication_format = I2S_COMM_FORMAT_STAND_I2S, .communication_format = I2S_COMM_FORMAT_STAND_I2S,
.dma_buf_count = 34, .dma_buf_count = 5,//32 + 0,//34,
.dma_buf_len = 16, .dma_buf_len = 576,//16,//16,
.intr_alloc_flags = 1, //Default interrupt priority .intr_alloc_flags = 1, //Default interrupt priority
.use_apll = true, .use_apll = true,
.fixed_mclk = 0, .fixed_mclk = 0,
@@ -2028,6 +2174,8 @@ void app_main(void) {
esp_err_t ret; esp_err_t ret;
uint8_t base_mac[6]; uint8_t base_mac[6];
//ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init(); ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
/* NVS partition was truncated /* NVS partition was truncated
@@ -2110,7 +2258,7 @@ void app_main(void) {
ESP_LOGI(TAG, "Start audio_pipelines"); ESP_LOGI(TAG, "Start audio_pipelines");
audio_pipeline_run(flacDecodePipeline); audio_pipeline_run(flacDecodePipeline);
i2s_start(i2s_cfg.i2s_port); // i2s_start(i2s_cfg.i2s_port);
// i2s_stop(i2s_cfg.i2s_port); // i2s_stop(i2s_cfg.i2s_port);
// i2s_zero_dma_buffer(i2s_cfg.i2s_port); // i2s_zero_dma_buffer(i2s_cfg.i2s_port);