diff --git a/main/main.c b/main/main.c index 0a25469..df3001a 100644 --- a/main/main.c +++ b/main/main.c @@ -47,10 +47,6 @@ #include -#define TEST_WIRECHUNK_TO_PCM_PART 1 -#define USE_I2S_STREAM 0 -#define CONFIG_USE_HW_TIMER_FOR_SYNC 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 @@ -59,6 +55,7 @@ #define CHANNELS 2UL #define BITS_PER_SAMPLE 16UL +const char *VERSION_STRING = "0.1.0"; /** * @brief Pre define APLL parameters, save compute time @@ -90,12 +87,7 @@ i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT(); xQueueHandle i2s_event_queue; audio_pipeline_handle_t flacDecodePipeline; -audio_element_handle_t raw_stream_writer_to_decoder, decoder, raw_stream_reader_from_decoder; -#if USE_I2S_STREAM == 1 -audio_pipeline_handle_t playbackPipeline; -audio_element_handle_t raw_stream_writer_to_i2s, i2s_stream_writer; -#endif - +audio_element_handle_t raw_stream_writer_to_decoder, decoder; uint64_t wirechnkCnt = 0; uint64_t pcmchnkCnt = 0; @@ -152,23 +144,42 @@ typedef struct http_task_cfg_s { audio_element_handle_t *p_raw_stream_writer_to_i2s; } http_task_cfg_t; -SemaphoreHandle_t diffBufSemaphoreHandle = NULL; - SemaphoreHandle_t timeSyncSemaphoreHandle = NULL; SemaphoreHandle_t timer0_syncSampleSemaphoreHandle = NULL; -#define DIFF_BUF_LEN 199 -uint8_t diffBufCnt = 0; -static int8_t diffBuffFull = 0; -static struct timeval diffToServer = {0, 0}; // median diff to server in µs -static struct timeval diffBuf[DIFF_BUF_LEN] = {0}; // collected diff's to server -//static struct timeval *medianArray = NULL; // temp median calculation data is stored at this location -static struct timeval medianArray[DIFF_BUF_LEN] = {0}; // temp median calculation data is stored at this location +SemaphoreHandle_t latencyBufSemaphoreHandle = NULL; +#define LATENCY_BUF_LEN 199 +uint8_t latencyBufCnt = 0; +static int8_t latencyBuffFull = 0; static sMedianFilter_t latencyMedianFilter; -static sMedianNode_t latencyMedianBuffer[DIFF_BUF_LEN]; -static int64_t diffToServer_int64_t = 0; +static sMedianNode_t latencyMedianBuffer[LATENCY_BUF_LEN]; +static int64_t latencyToServer = 0; + +//buffer_.setSize(500); +// shortBuffer_.setSize(100); +// miniBuffer_.setSize(20); + +#define SHORT_BUFFER_LEN 99 +int64_t short_buffer[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 +int64_t mini_buffer[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 sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN]; + +static sMedianFilter_t miniMedianFilter; +static sMedianNode_t miniMedianBuffer[MINI_BUFFER_LEN]; + +static int8_t currentDir = 0; //!< current apll direction, see apll_adjust() uint32_t buffer_ms = 400; uint8_t muteCH[4] = {0}; @@ -338,235 +349,35 @@ void find_mdns_service(const char * service_name, const char * proto) { /** * */ -void quick_sort_timeval(struct timeval *a, int left, int right) { - int i = left; - int j = right; - struct timeval temp = a[i]; - - if( left < right ) { - while(i < j) { - while(timercmp(&a[j], &temp, >=) && (i < j)) { - j--; - } - a[i] = a[j]; - - while(timercmp(&a[j], &temp, <=) && (i < j)) { - i++; - } - a[j] = a[i]; - } - a[i] = temp; - - quick_sort_timeval( a, left, i - 1 ); - quick_sort_timeval( a, j + 1, right ); - } -} - -/** - * - */ -void quick_sort_int32(int32_t *a, int left, int right) { - int i = left; - int j = right; - int32_t temp = a[i]; - - if( left < right ) { - while(i < j) { - while(a[j] >= temp && (i < j)) { - j--; - } - a[i] = a[j]; - - while(a[j] <= temp && (i < j)) { - i++; - } - a[j] = a[i]; - } - a[i] = temp; - - quick_sort_int32( a, left, i - 1 ); - quick_sort_int32( a, j + 1, right ); - } -} - -/** - * - */ -void quick_sort_int64(int64_t *a, int left, int right) { - int i = left; - int j = right; - int64_t temp = a[i]; - - if( left < right ) { - while(i < j) { - while(a[j] >= temp && (i < j)) { - j--; - } - a[i] = a[j]; - - while(a[j] <= temp && (i < j)) { - i++; - } - a[j] = a[i]; - } - a[i] = temp; - - quick_sort_int64( a, left, i - 1 ); - quick_sort_int64( a, j + 1, right ); - } -} - - -/** - * - */ -int8_t get_median( const struct timeval *tDiff, size_t n, struct timeval *result ) { - struct timeval median; - - if (tDiff == NULL) { - ESP_LOGE(TAG, "get_median: buffer error"); - - return -1; - } - - if (n == 0) { - median = tDiff[0]; - *result = median; - - return 0; - } - - memcpy( medianArray, tDiff, sizeof(struct timeval) * n ); // TODO: how to avoid this copy? - quick_sort_timeval(medianArray, 0, n); - -// if( (n % 2) == 0 ) { -// // if there is an even number of elements, return mean of the two elements in the middle -// timeradd(&medianArray[n/2], &medianArray[n/2 - 1], &median); -// if ((median.tv_sec / 2) == 0) { -// median.tv_sec = 0; -// median.tv_usec = (suseconds_t)((int64_t)median.tv_sec * 1000000LL / 2) + median.tv_usec / 2; -// } -// else -// { -// median.tv_sec /= 2; -// median.tv_usec /= 2; -// } -// } -// else - { - // else return the element in the middle - median = medianArray[n/2]; - } - - *result = median; - - return 0; -} - -/** - * - */ -int8_t set_diff_to_server( struct timeval *tDiff, size_t len) { - int8_t ret = -1; - struct timeval tmpDiffToServer; - - - ret = get_median(tDiff, len, &tmpDiffToServer); - if (ret < 0) { - ESP_LOGW(TAG, "set_diff_to_server: get median failed"); - } - -// tmpDiffToServer = *tDiff; - - //ESP_LOGI(TAG, "set_diff_to_server: median is %ld.%06ld", tmpDiffToServer.tv_sec, tmpDiffToServer.tv_usec); - - if (len >= (sizeof(diffBuf) / (sizeof(struct timeval)))) { - diffBuffFull = 1; -// ESP_LOGI(TAG, "set_diff_to_server: diffBufFull"); - } - else { - diffBuffFull = 0; - } - - diffToServer = tmpDiffToServer; - - return ret; -} - -// TODO: implement diff buffer using some sort of fifo -// current implementation isn't very good nor user friendly - -int8_t reset_diff_buffer(void) { - if (xSemaphoreTake( diffBufSemaphoreHandle, 1 ) == pdFALSE) { - ESP_LOGW(TAG, "reset_diff_buffer: can't take semaphore"); - - return -1; - } - - memset(diffBuf, 0, sizeof(diffBuf)); - diffBufCnt = 0; - diffBuffFull = false; - +int8_t reset_latency_buffer(void) { // init diff buff median filter - latencyMedianFilter.numNodes = DIFF_BUF_LEN; + latencyMedianFilter.numNodes = LATENCY_BUF_LEN; latencyMedianFilter.medianBuffer = latencyMedianBuffer; if (MEDIANFILTER_Init(&latencyMedianFilter) < 0) { ESP_LOGE(TAG, "reset_diff_buffer: couldn't init median filter. STOP"); - xSemaphoreGive( diffBufSemaphoreHandle ); + return -2; + } + + if (latencyBufSemaphoreHandle == NULL) { + ESP_LOGE(TAG, "reset_diff_buffer: latencyBufSemaphoreHandle == NULL"); return -2; } - xSemaphoreGive( diffBufSemaphoreHandle ); + if (xSemaphoreTake( latencyBufSemaphoreHandle, portMAX_DELAY ) == pdTRUE) { + latencyBufCnt = 0; + latencyBuffFull = false; + latencyToServer = 0; - return 0; -} - -int8_t add_to_diff_buffer(struct timeval tv) { - size_t bufLen; - int64_t medianValue, newValue; - - newValue = ((int64_t)tv.tv_sec * 1000000LL + (int64_t)tv.tv_usec); - medianValue = MEDIANFILTER_Insert(&latencyMedianFilter, newValue); - - if (xSemaphoreTake( diffBufSemaphoreHandle, 1 ) == pdFALSE) { - ESP_LOGW(TAG, "add_to_diff_buffer: can't take semaphore"); + xSemaphoreGive( latencyBufSemaphoreHandle ); + } + else { + ESP_LOGW(TAG, "reset_diff_buffer: can't take semaphore"); return -1; } - diffToServer_int64_t = medianValue; - - diffBufCnt++; - if (diffBufCnt >= DIFF_BUF_LEN) { - diffBuffFull = true; - } - -// ESP_LOGW(TAG, "add_to_diff_buffer"); - - - /* - diffBuf[diffBufCnt++] = tv; - if (diffBufCnt >= DIFF_BUF_LEN) { - diffBuffFull = true; - - diffBufCnt = 0; - } - - if (diffBuffFull == true) { - bufLen = DIFF_BUF_LEN; - } - else { - bufLen = diffBufCnt; - } - - - set_diff_to_server(diffBuf, bufLen); - */ - - xSemaphoreGive( diffBufSemaphoreHandle ); - return 0; } @@ -576,15 +387,21 @@ int8_t add_to_diff_buffer(struct timeval tv) { int8_t diff_buffer_full(void) { int8_t tmp; - if (xSemaphoreTake( diffBufSemaphoreHandle, 0) == pdFALSE) { + if (latencyBufSemaphoreHandle == NULL) { + ESP_LOGE(TAG, "diff_buffer_full: latencyBufSemaphoreHandle == NULL"); + + return -2; + } + + if (xSemaphoreTake( latencyBufSemaphoreHandle, 0) == pdFALSE) { //ESP_LOGW(TAG, "diff_buffer_full: can't take semaphore"); return -1; } - tmp = diffBuffFull; + tmp = latencyBuffFull; - xSemaphoreGive( diffBufSemaphoreHandle ); + xSemaphoreGive( latencyBufSemaphoreHandle ); return tmp; } @@ -592,54 +409,27 @@ int8_t diff_buffer_full(void) { /** * */ -/* -int8_t get_diff_to_server( struct timeval *tDiff ) { - static struct timeval lastDiff = { 0, 0 }; - - if (diffBufSemaphoreHandle == NULL) { - ESP_LOGE(TAG, "get_diff_to_server: diffBufSemaphoreHandle == NULL"); - - return -1; - } - - if (xSemaphoreTake( diffBufSemaphoreHandle, 0 ) == pdFALSE) { - *tDiff = lastDiff; - - //ESP_LOGW(TAG, "get_diff_to_server: can't take semaphore. Old diff retreived"); - - return -2; - } - - *tDiff = diffToServer; - lastDiff = diffToServer; // store value, so we can return a value if semaphore couldn't be taken - - xSemaphoreGive( diffBufSemaphoreHandle ); - - return 0; -} -*/ - int8_t get_diff_to_server( int64_t *tDiff ) { - static int64_t lastDiff = { 0, 0 }; + static int64_t lastDiff = 0; - if (diffBufSemaphoreHandle == NULL) { - ESP_LOGE(TAG, "get_diff_to_server: diffBufSemaphoreHandle == NULL"); - - return -1; - } - - if (xSemaphoreTake( diffBufSemaphoreHandle, 0 ) == pdFALSE) { - *tDiff = lastDiff; - - //ESP_LOGW(TAG, "get_diff_to_server: can't take semaphore. Old diff retreived"); + if (latencyBufSemaphoreHandle == NULL) { + ESP_LOGE(TAG, "get_diff_to_server: latencyBufSemaphoreHandle == NULL"); return -2; } - *tDiff = diffToServer_int64_t; - lastDiff = diffToServer_int64_t; // store value, so we can return a value if semaphore couldn't be taken + if (xSemaphoreTake( latencyBufSemaphoreHandle, 0 ) == pdFALSE) { + *tDiff = lastDiff; - xSemaphoreGive( diffBufSemaphoreHandle ); + //ESP_LOGW(TAG, "get_diff_to_server: can't take semaphore. Old diff retreived"); + + return -1; + } + + *tDiff = latencyToServer; + lastDiff = latencyToServer; // store value, so we can return a value if semaphore couldn't be taken + + xSemaphoreGive( latencyBufSemaphoreHandle ); return 0; } @@ -647,42 +437,9 @@ int8_t get_diff_to_server( int64_t *tDiff ) { /** * */ -/* -int8_t server_now( struct timeval *sNow ) { - struct timeval now; - struct timeval diff; - - // get current time - if (gettimeofday(&now, NULL)) { - ESP_LOGE(TAG, "server_now: Failed to get time of day"); - - return -1; - } - - if (get_diff_to_server(&diff) == -1) { - ESP_LOGE(TAG, "server_now: can't get diff to server"); - - return -1; - } - - if ((diff.tv_sec == 0) && (diff.tv_usec == 0)) { - //ESP_LOGW(TAG, "server_now: diff to server not initialized yet"); - - return -1; - } - - timeradd(&now, &diff, sNow); - -// ESP_LOGI(TAG, "now: %lldus", (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec); -// ESP_LOGI(TAG, "diff: %lldus", (int64_t)diff.tv_sec * 1000000LL + (int64_t)diff.tv_usec); -// ESP_LOGI(TAG, "serverNow: %lldus", (int64_t)sNow->tv_sec * 1000000LL + (int64_t)sNow->tv_usec); - - return 0; -} -*/ int8_t server_now( int64_t *sNow ) { struct timeval now; - int64_t diff, now2; + int64_t diff; // get current time if (gettimeofday(&now, NULL)) { @@ -703,14 +460,11 @@ int8_t server_now( int64_t *sNow ) { return -1; } - now2 = ((int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec); - *sNow = now2 + diff; - - //timeradd(&now, &diff, sNow); + *sNow = ((int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec) + diff; // ESP_LOGI(TAG, "now: %lldus", (int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec); -// ESP_LOGI(TAG, "diff: %lldus", (int64_t)diff.tv_sec * 1000000LL + (int64_t)diff.tv_usec); -// ESP_LOGI(TAG, "serverNow: %lldus", (int64_t)sNow->tv_sec * 1000000LL + (int64_t)sNow->tv_usec); +// ESP_LOGI(TAG, "diff: %lldus", diff); +// ESP_LOGI(TAG, "serverNow: %lldus", *snow); return 0; } @@ -773,9 +527,11 @@ static void tg0_timer_init(void) { timer_isr_register(TIMER_GROUP_0, TIMER_1, timer_group0_isr, NULL, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1, NULL); } +/** + * + */ static void tg0_timer1_start(uint64_t alarm_value) { timer_pause(TIMER_GROUP_0, TIMER_1); - //timer_set_counter_value(TIMER_GROUP_0, TIMER_1, timer_start_value_us); timer_set_counter_value(TIMER_GROUP_0, TIMER_1, 0); timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, alarm_value); timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN); @@ -785,7 +541,7 @@ static void tg0_timer1_start(uint64_t alarm_value) { } - +#if COLLECT_RUNTIME_STATS == 1 #define STATS_TASK_PRIO 3 #define STATS_TICKS pdMS_TO_TICKS(5000) @@ -900,10 +656,9 @@ exit: //Common return path return ret; } - - - - +/** + * + */ static void stats_task(void *arg) { //Print real time stats periodically while (1) { @@ -917,30 +672,7 @@ static void stats_task(void *arg) { } } -//buffer_.setSize(500); -// shortBuffer_.setSize(100); -// miniBuffer_.setSize(20); - -#define SHORT_BUFFER_LEN 99 -int64_t short_buffer[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 -int64_t mini_buffer[MINI_BUFFER_LEN]; -int64_t mini_buffer_median[MINI_BUFFER_LEN]; -int mini_buffer_cnt = 0; -int mini_buffer_full = 0; - -int8_t currentDir = 0; - - -static sMedianFilter_t shortMedianFilter; -static sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN]; - -static sMedianFilter_t miniMedianFilter; -static sMedianNode_t miniMedianBuffer[MINI_BUFFER_LEN]; +#endif // COLLECT_RUNTIME_STATS // void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div); @@ -985,37 +717,26 @@ void adjust_apll(int8_t direction) { currentDir = direction; } - -#if CONFIG_USE_HW_TIMER_FOR_SYNC == 0 /** * */ -// works quite well static void snapcast_sync_task(void *pvParameters) { snapcast_sync_task_cfg_t *taskCfg = (snapcast_sync_task_cfg_t *)pvParameters; wire_chunk_message_t *chnk = NULL; - //struct timeval serverNow = {0, 0}; int64_t serverNow = 0; int64_t age; BaseType_t ret; int64_t chunkDuration_us = 24000; - int64_t chunkDuration_ns = 24000 * 1000; int64_t sampleDuration_ns = (1000000 / 48); // 16bit, 2ch, 48kHz (in nano seconds) char *p_payload = NULL; size_t size = 0; uint32_t notifiedValue; uint64_t timer_val; - const int32_t alarmValSubBase = 500; int32_t alarmValSub = 0; - int bytesWritten = 0; int initialSync = 0; - int sdm0, sdm1, sdm2, o_div; - int64_t avg = 0, avg2 = 0; - //struct timeval latencyInitialSync = {0, 0}; - int64_t latencyInitialSync = {0, 0}; + int64_t avg = 0; + int64_t latencyInitialSync = 0; int dir = 0; - //struct timeval serverNow_saved = {0, 0}; - int64_t serverNow_saved = {0, 0}; ESP_LOGI(TAG, "started sync task"); @@ -1023,17 +744,10 @@ static void snapcast_sync_task(void *pvParameters) { initialSync = 0; - short_buffer_cnt = 0; - memset(short_buffer, 0, sizeof(short_buffer)); - - // use 48kHz sample rate - sdm0 = apll_predefine[5][2]; - sdm1= apll_predefine[5][3]; - sdm2 = apll_predefine[5][4]; - o_div = apll_predefine[5][5]; - - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, o_div); + adjust_apll(0); + mini_buffer_cnt = 0; + mini_buffer_full = 0; miniMedianFilter.numNodes = MINI_BUFFER_LEN; miniMedianFilter.medianBuffer = miniMedianBuffer; if (MEDIANFILTER_Init(&miniMedianFilter) ) { @@ -1042,6 +756,8 @@ static void snapcast_sync_task(void *pvParameters) { return; } + short_buffer_cnt = 0; + short_buffer_full = 0; shortMedianFilter.numNodes = SHORT_BUFFER_LEN; shortMedianFilter.medianBuffer = shortMedianBuffer; if (MEDIANFILTER_Init(&shortMedianFilter) ) { @@ -1085,102 +801,79 @@ static void snapcast_sync_task(void *pvParameters) { continue; } - - //if (server_now(&serverNow) >= 0) { - if (1) { -// if (initialSync == 0) - if (1) - { - if (server_now(&serverNow) >= 0) { - // first chunk needs to be at exact time point -// age = ((int64_t)serverNow.tv_sec * 1000000LL + (int64_t)serverNow.tv_usec) - -// ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - -// (int64_t)taskCfg->buffer_us + -// (int64_t)taskCfg->outputBufferDacTime_us; - age = serverNow - - ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - - (int64_t)taskCfg->buffer_us + - (int64_t)taskCfg->outputBufferDacTime_us; - } - else { - ESP_LOGW(TAG, "couldn't get server now"); - - free(chnk->payload); - free(chnk); - chnk = NULL; - - vTaskDelay( 1 ); - - continue; - } - } - else { -// server_now(&serverNow); -// serverNow_saved = serverNow; - - struct timeval now; - gettimeofday(&now, NULL); - serverNow_saved = ((int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec) + latencyInitialSync; - //timeradd(&now, &latencyInitialSync, &serverNow_saved); - - age = serverNow_saved - +// 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 (server_now(&serverNow) >= 0) { + age = serverNow - ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - (int64_t)taskCfg->buffer_us + (int64_t)taskCfg->outputBufferDacTime_us; } + else { + ESP_LOGW(TAG, "couldn't get server now"); - if (age < 0) { // get initial sync using hardware timer - if (initialSync == 0) { - if (MEDIANFILTER_Init(&shortMedianFilter) ) { - ESP_LOGE(TAG, "snapcast_sync_task: couldn't init shortMedianFilter. STOP"); + free(chnk->payload); + free(chnk); + chnk = NULL; - return; - } + vTaskDelay( 1 ); -#if 1 - p_payload = chnk->payload; - size = chnk->size; + continue; + } + } + else { + struct timeval now; + gettimeofday(&now, NULL); + serverNow = ((int64_t)now.tv_sec * 1000000LL + (int64_t)now.tv_usec) + latencyInitialSync; - #if USE_I2S_STREAM == 1 - // write data to I2S - bytesWritten = 0; - bytesWritten += raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); - if (bytesWritten < size) { - ESP_LOGE(TAG, "i2s raw writer ring buf full"); - } - #else - i2s_stop(i2s_cfg.i2s_port); - size_t writtenBytes; - int err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, 0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "I2S write error"); - } - size -= writtenBytes; - //p_payload += writtenBytes; // TODO: produces heap error??? + age = serverNow - + ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - + (int64_t)taskCfg->buffer_us + + (int64_t)taskCfg->outputBufferDacTime_us; + } - #endif + if (age < 0) { // get initial sync using hardware timer + if (initialSync == 0) { + if (MEDIANFILTER_Init(&shortMedianFilter) ) { + ESP_LOGE(TAG, "snapcast_sync_task: couldn't init shortMedianFilter. STOP"); - //tg0_timer1_start((-age * 10) - (alarmValSubBase + alarmValSub)); // alarm a little earlier to account for context switch duration from freeRTOS - tg0_timer1_start(-age - alarmValSub); // alarm a little earlier to account for context switch duration from freeRTOS + return; + } - // Wait to be notified of an interrupt. - xTaskNotifyWait( pdFALSE, // Don't clear bits on entry. - ULONG_MAX, // Clear all bits on exit. - ¬ifiedValue, // Stores the notified value. - portMAX_DELAY - ); + p_payload = chnk->payload; + size = chnk->size; + i2s_stop(i2s_cfg.i2s_port); + size_t writtenBytes; + int err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, 0); + if (err != ESP_OK) { + ESP_LOGE(TAG, "I2S write error"); + } + size -= writtenBytes; + //p_payload += writtenBytes; // TODO: produces heap error??? - #if USE_I2S_STREAM == 0 - i2s_start(i2s_cfg.i2s_port); - #endif + //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 - // get timer value so we can get the real age - timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &timer_val); - timer_pause(TIMER_GROUP_0, TIMER_1); + // Wait to be notified of an interrupt. + xTaskNotifyWait( pdFALSE, // Don't clear bits on entry. + ULONG_MAX, // Clear all bits on exit. + ¬ifiedValue, // Stores the notified value. + portMAX_DELAY + ); - //age = ((int64_t)timer_val - (-age) * 10) / 10; - age = (int64_t)timer_val - (-age); + i2s_start(i2s_cfg.i2s_port); + // get timer value so we can get the real age + timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &timer_val); + timer_pause(TIMER_GROUP_0, TIMER_1); + + // get actual age after alarm + //age = ((int64_t)timer_val - (-age) * 10) / 10; // timer with 100ns ticks + age = (int64_t)timer_val - (-age); // timer with 1µs ticks + + // TODO: try to get better initial sync using alarmValSub to alarm early, + // doesn't work with current style of loading i2s buffer early. // if (((age < -11LL) || (age > 0)) && (initialSync == 0)) { // if (age < -11LL) { // alarmValSub--; @@ -1205,349 +898,150 @@ static void snapcast_sync_task(void *pvParameters) { // // ESP_LOGW(TAG, "start"); // } -#else - p_payload = chnk->payload; - size = chnk->size; +// + get_diff_to_server(&latencyInitialSync); // store current latency for later use - #if USE_I2S_STREAM == 1 - // write data to I2S - bytesWritten = 0; - bytesWritten += raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); - if (bytesWritten < size) { - ESP_LOGE(TAG, "i2s raw writer ring buf full"); - } - #else - i2s_stop(i2s_cfg.i2s_port); - size_t writtenBytes; - int err = i2s_write(i2s_cfg.i2s_port, p_payload, size, &writtenBytes, 0); - if (err != ESP_OK) { - ESP_LOGE(TAG, "I2S write error"); - } - size -= writtenBytes; - p_payload += writtenBytes; - - #endif - - vTaskDelay( pdMS_TO_TICKS(-age / 1000) ); - - #if USE_I2S_STREAM == 0 - i2s_start(i2s_cfg.i2s_port); - #endif -#endif - // - get_diff_to_server(&latencyInitialSync); - - // i2s_start(i2s_cfg.i2s_port); - initialSync = 1; -// i2s_zero_dma_buffer(i2s_cfg.i2s_port); - -// get_diff_to_server(&latencyInitialSync); - - // p_payload = chnk->payload; - // size = chnk->size; - - // #if USE_I2S_STREAM == 1 - // // write data to I2S - // bytesWritten = raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); - // if (bytesWritten < 0) { - // ESP_LOGE(TAG, "i2s raw writer ring buf timeout"); - // } - // else if (bytesWritten < size) { - // ESP_LOGE(TAG, "i2s raw writer ring buf full"); - // } - // #else - // size_t writtenBytes; - // int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, portMAX_DELAY); - // if (err != ESP_OK) { - // ESP_LOGE(TAG, "I2S write error"); - // } - // #endif - - // ESP_LOGI(TAG, "bytesWritten %d", bytesWritten); - -// ESP_LOGI(TAG, "%lldus, %d, %d, %d %lldus", age, sdm0, sdm1, sdm2, ((int64_t)latencyInitialSync.tv_sec * 1000000LL + (int64_t)latencyInitialSync.tv_usec)); - -// ESP_LOGI(TAG, "initial sync: %lldus", age); - - // continue; - } - else { - //vTaskDelay( pdMS_TO_TICKS(-age / 1000) ); -// ESP_LOGI(TAG, "obuf: %lld, buf: %lld, age:%lld, is:%lld ts:%lld", (int64_t)taskCfg->outputBufferDacTime_us, -// (int64_t)taskCfg->buffer_us , -// age, -// ((int64_t)latencyInitialSync.tv_sec * 1000000LL + (int64_t)latencyInitialSync.tv_usec), -// ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec)); - } + initialSync = 1; } - else if ((age > 0) && (initialSync == 0)) { + } + else if ((age > 0) && (initialSync == 0)) { + free(chnk->payload); + free(chnk); + chnk = NULL; + + int64_t t; + get_diff_to_server(&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; + + initialSync = 0; + alarmValSub = 0; + + continue; + } + + if (initialSync == 1) { + p_payload = chnk->payload; + size = chnk->size; + + size_t writtenBytes; + int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, portMAX_DELAY); + 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 + if ((age < -1 * chunkDuration_us / 8) || (age > 1 * chunkDuration_us / 8) || (initialSync == 0)) { free(chnk->payload); free(chnk); chnk = NULL; - //struct timeval t; int64_t t; get_diff_to_server(&t); - //ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec)); - ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, t); + ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus", age, t); - if (short_buffer_full) { - memset( short_buffer, 0, sizeof(short_buffer) ); - short_buffer_cnt = 0; - short_buffer_full = 0; + if (initialSync == 1) { + adjust_apll(0); // reset to normal playback speed } - - dir = 0; + short_buffer_cnt = 0; + short_buffer_full = 0; initialSync = 0; alarmValSub = 0; continue; } - - - - if (initialSync == 1) { - p_payload = chnk->payload; - size = chnk->size; - - #if USE_I2S_STREAM == 1 - // write data to I2S - bytesWritten = 0; - bytesWritten += raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); - if (bytesWritten < size) { - ESP_LOGE(TAG, "i2s raw writer ring buf full"); - } - #else - size_t writtenBytes; - int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, portMAX_DELAY); - if (err != ESP_OK) { - ESP_LOGE(TAG, "I2S write error"); - } - #endif - -// avg = age + chunkDuration_us; - short_buffer_full = 1; - -// short_buffer_cnt++; -// if (short_buffer_cnt >= SHORT_BUFFER_LEN ) { -// short_buffer_full = 1; -// } - - avg = MEDIANFILTER_Insert(&shortMedianFilter, age); - -/* - int l; - // TODO: - // BADAIX original code uses 3 buffers and median on them to do sample rate adjustment calculations - // so for sure there is a better implementation than the following - short_buffer[short_buffer_cnt++] = age;// + chunkDuration_us;; - if (short_buffer_cnt >= SHORT_BUFFER_LEN ) { - short_buffer_full = 1; - - short_buffer_cnt = 0; - } - - - if (short_buffer_full) { - l = SHORT_BUFFER_LEN; - } - else { - l = short_buffer_cnt; - } - - memcpy( short_buffer_median, short_buffer, sizeof(short_buffer) ); - quick_sort_int64(short_buffer_median, 0, l); - avg = short_buffer_median[l/2]; -*/ - - /* - mini_buffer[mini_buffer_cnt++] = age; - if (mini_buffer_cnt >= MAX_MINI_BUFFER_COUNT ) { - mini_buffer_full = 1; - - mini_buffer_cnt = 0; - } - - if (mini_buffer_full) { - l = MAX_MINI_BUFFER_COUNT; - } - else { - l = mini_buffer_cnt; - } - - memcpy( mini_buffer_median, mini_buffer, sizeof(mini_buffer) ); - quick_sort_int64(mini_buffer_median, 0, l); - avg2 = mini_buffer_median[l/2]; - */ - - - - if ((age < -1 * chunkDuration_us / 8) || (age > 1 * chunkDuration_us / 8) || (initialSync == 0)) { - free(chnk->payload); - free(chnk); - chnk = NULL; - -// struct timeval t; -// get_diff_to_server(&t); -// ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus", age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec)); -// ESP_LOGW(TAG, "RESYNCING HARD %lldus, %lldus", age); - - int64_t t; - get_diff_to_server(&t); - //ESP_LOGW(TAG, "RESYNCING HARD 1 %lldus, %lldus", age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec)); - ESP_LOGW(TAG, "RESYNCING HARD 2 %lldus, %lldus", age, t); - - //reset_diff_buffer(); - - if (initialSync == 1) { -// i2s_stop(i2s_cfg.i2s_port); -// i2s_zero_dma_buffer(i2s_cfg.i2s_port); - - #if USE_I2S_STREAM == 1 - audio_element_reset_input_ringbuf(i2s_stream_writer); - #endif - - // reset to normal playback speed - adjust_apll(0); - -// ESP_LOGW(TAG, "stop"); - } - - memset( short_buffer, 0, sizeof(short_buffer) ); - short_buffer_cnt = 0; - short_buffer_full = 0; - - initialSync = 0; - alarmValSub = 0; - - continue; - } - } - - - const int64_t age_expect = 0; - const int64_t maxOffset = 250; - const int64_t maxOffset_dropSample = 1500; - // NOT 100% SURE ABOUT THE FOLLOWING CONTROL LOOP, PROBABLY BETTER WAYS TO DO IT, STILL TESTING - if (initialSync == 1) - { // got initial sync, decrease / increase playback speed if age != 0 - int samples = 1; - int sampleSize = 4; - int ageDiff = 0; - - if (short_buffer_full) { - //if (1) { - if (avg < (age_expect - maxOffset)) { - dir = -1; - - if (avg < (age_expect - maxOffset_dropSample) ) { - ageDiff = (int)(age_expect - avg); - samples = ageDiff / (sampleDuration_ns / 1000); - if (samples > 4) { - samples = 4; - } - - // too young add samples - // write data to I2S - #if USE_I2S_STREAM == 1 - bytesWritten = 0; - bytesWritten += raw_stream_write(raw_stream_writer_to_i2s, p_payload, samples * sampleSize); - if (bytesWritten < samples * sampleSize) { - ESP_LOGE(TAG, "i2s raw writer ring buf full"); - } - #else - size_t writtenBytes; - int err = i2s_write(I2S_NUM_0, p_payload, samples * sampleSize, &writtenBytes, portMAX_DELAY); - if (err != ESP_OK) { - ESP_LOGE(TAG, "I2S write error"); - } - #endif - -// size += samples; // naughty add samples - -// avg -= (samples * sampleDuration_ns / 1000); - - ESP_LOGI(TAG, "insert %d samples", samples); - } - } - else if ((avg >= (age_expect - maxOffset)) && (avg <= (age_expect + maxOffset))) { - dir = 0; - } - else if (avg > (age_expect + maxOffset)) { - dir = 1; - - if (avg > (age_expect + maxOffset_dropSample)) { - ageDiff = (int)(avg - age_expect); - samples = ageDiff / (sampleDuration_ns / 1000); - if (samples > 4) { - samples = 4; - } - - // drop samples - p_payload += samples * sampleSize; - size -= samples * sampleSize; - -// avg += (samples * sampleDuration_ns / 1000); - - ESP_LOGI(TAG, "drop %d samples", samples); - } - } - - adjust_apll(dir); - } - } - -// struct timeval t; -// get_diff_to_server(&t); -// ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus %lldus", dir, age, avg, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec), -// ((int64_t)latencyInitialSync.tv_sec * 1000000LL + (int64_t)latencyInitialSync.tv_usec)); - - int64_t t; - get_diff_to_server(&t); - ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus %lldus", dir, age, avg, t, latencyInitialSync); } - else { - ESP_LOGW(TAG, "couldn't get server now"); - vTaskDelay( pdMS_TO_TICKS(10) ); - continue; + const int64_t age_expect = 0; + const int64_t maxOffset = 300; + 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) + { // got initial sync, decrease / increase playback speed if abs(age) > maxOffset + int samples = 1; + int sampleSize = 4; + int ageDiff = 0; + + if (short_buffer_full) { + if (avg < (age_expect - maxOffset)) { + dir = -1; + + if (avg < (age_expect - maxOffset_dropSample) ) { + ageDiff = (int)(age_expect - avg); + 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); + if (err != ESP_OK) { + ESP_LOGE(TAG, "I2S write error"); + } + + ESP_LOGI(TAG, "insert %d samples", samples); + } + } + else if ((avg >= (age_expect - maxOffset)) && (avg <= (age_expect + maxOffset))) { + dir = 0; + } + else if (avg > (age_expect + maxOffset)) { + dir = 1; + + if (avg > (age_expect + maxOffset_dropSample)) { + ageDiff = (int)(avg - age_expect); + samples = ageDiff / (sampleDuration_ns / 1000); + if (samples > 4) { + samples = 4; + } + + // drop samples + p_payload += samples * sampleSize; + size -= samples * sampleSize; + + ESP_LOGI(TAG, "drop %d samples", samples); + } + } + + adjust_apll(dir); + } } + int64_t t; + get_diff_to_server(&t); + ESP_LOGI(TAG, "%d: %lldus, %lldus %lldus %lldus", dir, age, avg, t, latencyInitialSync); + free(chnk->payload); free(chnk); chnk = NULL; } else { - ESP_LOGE(TAG, "Couldn't get PCM chunk, recv: messages waiting %d, %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle)); + int64_t t; + get_diff_to_server(&t); + ESP_LOGE(TAG, "Couldn't get PCM chunk, recv: messages waiting %d, %d, latency %lldus", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle), t); -// ESP_LOGI(TAG, "\n1: s %d f %d s %d f %d\n2: s %d f %d s %d f %d\n3: s %d f %d s %d f %d", -// rb_get_size(audio_element_get_input_ringbuf(raw_stream_writer_to_decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_writer_to_decoder)), -// rb_get_size(audio_element_get_output_ringbuf(raw_stream_writer_to_decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(raw_stream_writer_to_decoder)), -// rb_get_size(audio_element_get_input_ringbuf(decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(decoder)), -// rb_get_size(audio_element_get_output_ringbuf(decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(decoder)), -// rb_get_size(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)), -// rb_get_size(audio_element_get_output_ringbuf(raw_stream_reader_from_decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(raw_stream_reader_from_decoder))); + 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 -// // probably the stream stopped, so we need to reset decoder's buffers here -// audio_element_reset_output_ringbuf(raw_stream_writer_to_decoder); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_decoder); -// -// audio_element_reset_output_ringbuf(decoder); -// audio_element_reset_input_ringbuf(decoder); -// -// audio_element_reset_output_ringbuf(raw_stream_writer_to_i2s); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_i2s); -// -// audio_element_reset_output_ringbuf(i2s_stream_writer); -// audio_element_reset_input_ringbuf(i2s_stream_writer); -// -// wirechnkCnt = 0; -// pcmchnkCnt = 0; - - memset( short_buffer, 0, sizeof(short_buffer) ); short_buffer_cnt = 0; short_buffer_full = 0; @@ -1555,372 +1049,9 @@ static void snapcast_sync_task(void *pvParameters) { initialSync = 0; alarmValSub = 0; - - //vTaskDelay( pdMS_TO_TICKS(100) ); } } } -#else -/** - * - */ -static void snapcast_sync_task(void *pvParameters) { - snapcast_sync_task_cfg_t *taskCfg = (snapcast_sync_task_cfg_t *)pvParameters; - wire_chunk_message_t *chnk = NULL; - struct timeval serverNow = {0, 0}; - int64_t age; - BaseType_t ret; - int64_t chunkDuration_us = 24000; - int64_t chunkDuration_ns = 24000 * 1000; - int64_t sampleDuration_ns = (1000000 / 48); // 16bit, 2ch, 48kHz (in nano seconds) - char *p_payload = NULL; - size_t size = 0; - uint32_t notifiedValue; - uint64_t timer_val; - const int32_t alarmValSubBase = 500; - int32_t alarmValSub = 0; - int bytesWritten = 0; - int initialSync = 0; - int sdm0, sdm1, sdm2, o_div; - int64_t avg = 0; - struct timeval latencyInitialSync = {0, 0}; - int64_t age_save = 0; - - ESP_LOGI(TAG, "started sync task"); - - tg0_timer_init(); // initialize sample sync timer - - initialSync = 0; - - short_buffer_cnt = 0; - memset(short_buffer, 0, sizeof(short_buffer)); - - // use 48kHz sample rate - sdm0 = apll_predefine[5][2]; - sdm1= apll_predefine[5][3]; - sdm2 = apll_predefine[5][4]; - o_div = apll_predefine[5][5]; - - rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, o_div); - - while(1) { - if (chnk == NULL) { -// ESP_LOGE(TAG, "msg waiting pcm %d ts %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle)); - - ret = xQueueReceive(pcmChunkQueueHandle, &chnk, pdMS_TO_TICKS(2000) ); - if( ret == pdPASS ) { - //ESP_LOGW(TAG, "pcm chunks waiting %d", uxQueueMessagesWaiting(pcmChunkQueueHandle)); - } - } - else { - ret = pdPASS; - } - - if( ret == pdPASS ) { - // wait for early time syncs to be ready - if ( diff_buffer_full() <= 0 ) { - // free chunk so we can get next one - free(chnk->payload); - free(chnk); - chnk = NULL; - - vTaskDelay(10); - - continue; - } - - - if (server_now(&serverNow) >= 0) { -// if (initialSync == 0) - { - // first chunk needs to be at exact time point - age = ((int64_t)serverNow.tv_sec * 1000000LL + (int64_t)serverNow.tv_usec) - - ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - - (int64_t)taskCfg->buffer_us + - (int64_t)taskCfg->outputBufferDacTime_us; - } -// else { -// // add chunk duration, this ensures i2s buffer always stays ahead for one chunk duration -// // and we got at least this much time to do apll adjustments -// age = ((int64_t)serverNow.tv_sec * 1000000LL + (int64_t)serverNow.tv_usec) - -// ((int64_t)chnk->timestamp.sec * 1000000LL + (int64_t)chnk->timestamp.usec) - -// (int64_t)taskCfg->buffer_us + -// (int64_t)taskCfg->outputBufferDacTime_us + chunkDuration_us; -// } - - age_save = age; - - //ESP_LOGW(TAG, "%lldus", age_save); - - if (age < 0) { // get sync using hardware timer - //tg0_timer1_start((-age * 10) - (alarmValSubBase + alarmValSub)); // alarm a little earlier to account for context switch duration from freeRTOS - tg0_timer1_start(-age - alarmValSub); // alarm a little earlier to account for context switch duration from freeRTOS - - // Wait to be notified of an interrupt. - xTaskNotifyWait( pdFALSE, // Don't clear bits on entry. - ULONG_MAX, // Clear all bits on exit. - ¬ifiedValue, // Stores the notified value. - portMAX_DELAY - ); - - // get timer value so we can get the real age - timer_get_counter_value(TIMER_GROUP_0, TIMER_1, &timer_val); - timer_pause(TIMER_GROUP_0, TIMER_1); - - //age = ((int64_t)timer_val - (-age) * 10) / 10; - age = (int64_t)timer_val - (-age); - -// if (((age < -11LL) || (age > 0)) && (initialSync == 0)) { // test if we got a good initial sync, if not do the following and adjust hardware timer alarm -// if (age < -11LL) { -// alarmValSub--; -// } -// else { -// alarmValSub++; -// } -// -// // free chunk so we can get next one -// free(chnk->payload); -// free(chnk); -// 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"); -// } - - - - initialSync = 1; - -// get_diff_to_server(&latencyInitialSync); -// -// -// p_payload = chnk->payload; -// size = chnk->size; -// -// // write data to I2S -// bytesWritten = raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); -// if (bytesWritten < 0) { -// ESP_LOGE(TAG, "i2s raw writer ring buf timeout"); -// } -// else if (bytesWritten < size) { -// ESP_LOGE(TAG, "i2s raw writer ring buf full"); -// } -// -//// ESP_LOGI(TAG, "bytesWritten %d", bytesWritten); -// -// ESP_LOGI(TAG, "%lldus, %d, %d, %d %lldus", age, sdm0, sdm1, sdm2, ((int64_t)latencyInitialSync.tv_sec * 1000000LL + (int64_t)latencyInitialSync.tv_usec)); -// -// continue; - } - else if (age > chunkDuration_us) { // out of sync, resync hard! - ESP_LOGW(TAG, "resync hard %lld", age); - - - if (initialSync == 1) { - short_buffer_full = 0; - short_buffer_cnt = 0; - memset(short_buffer, 0, sizeof(short_buffer)); - } - - initialSync = 0; - - free(chnk->payload); - free(chnk); - chnk = NULL; - - continue; - } - - if (initialSync == 1) { - // TODO: - // BADAIX original code uses 3 buffers and median on them to do sample rate adjustment calculations - // so for sure there is a better implementation than the following - short_buffer[short_buffer_cnt++] = age_save;// age; - if (short_buffer_cnt >= SHORT_BUFFER_LEN ) { - short_buffer_full = 1; - - short_buffer_cnt = 0; - } - - int l; - if (short_buffer_full) { - l = SHORT_BUFFER_LEN; - } - else { - l = short_buffer_cnt; - } - - memcpy( short_buffer_median, short_buffer, sizeof(short_buffer) ); - quick_sort_int64(short_buffer_median, 0, l); - avg = short_buffer_median[l/2]; - - if ((avg < -1 * chunkDuration_us) || (avg > 1 * chunkDuration_us) || (initialSync == 0)) { - free(chnk->payload); - free(chnk); - chnk = NULL; - - struct timeval t; - get_diff_to_server(&t); - ESP_LOGW(TAG, "RESYNCING HARD %lldus, %lldus", age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec)); -// ESP_LOGW(TAG, "RESYNCING HARD %lldus, %lldus", age); - - //reset_diff_buffer(); - - if (initialSync == 1) { -// i2s_stop(i2s_cfg.i2s_port); -// i2s_zero_dma_buffer(i2s_cfg.i2s_port); - - #if USE_I2S_STREAM == 1 - audio_element_reset_input_ringbuf(i2s_stream_writer); - #endif - - // reset to normal playback speed - adjust_apll(0); - -// ESP_LOGW(TAG, "stop"); - } - - memset( short_buffer, 0, sizeof(short_buffer) ); - short_buffer_cnt = 0; - short_buffer_full = 0; - - initialSync = 0; - alarmValSub = 0; - - continue; - } - } - - p_payload = chnk->payload; - size = chnk->size; - - - const int64_t age_expect = 0; - const int64_t maxOffset = 10; - - // NOT 100% SURE ABOUT THE FOLLOWING CONTROL LOOP, PROBABLY BETTER WAYS TO DO IT, STILL TESTING - if (initialSync == 1) - { // got initial sync, decrease / increase playback speed if age != 0 - int dir = 0; - int samples = 1; - int sampleSize = 4; - int ageDiff = 0; - - if (short_buffer_full) { - if (avg < (age_expect - maxOffset)) { - dir = -1; - -// if (avg < (age_expect - maxOffset - 1000)) { -// ageDiff = (int)(age_expect - avg); -// samples = ageDiff / (sampleDuration_ns / 1000); -// if (samples > 4) { -// samples = 4; -// } -// -// // too young add samples -// // write data to I2S -// #if USE_I2S_STREAM == 1 -// bytesWritten = 0; -// bytesWritten += raw_stream_write(raw_stream_writer_to_i2s, p_payload, samples * sampleSize); -// if (bytesWritten < samples * sampleSize) { -// ESP_LOGE(TAG, "i2s raw writer ring buf full"); -// } -// #else -// size_t writtenBytes; -// int err = i2s_write(I2S_NUM_0, p_payload, samples * sampleSize, &writtenBytes, portMAX_DELAY); -// if (err != ESP_OK) { -// ESP_LOGE(TAG, "I2S write error"); -// } -// #endif -// -//// avg -= (samples * sampleDuration_ns / 1000); -// -// ESP_LOGI(TAG, "insert %d samples", samples); -// } - } - else if ((avg >= (age_expect - maxOffset)) && (avg <= (age_expect + maxOffset))) { - dir = 0; - } - else if (avg > (age_expect + maxOffset)) { - dir = 1; - -// if (avg > (age_expect + maxOffset + 1000)) { -// ageDiff = (int)(avg - age_expect); -// samples = ageDiff / (sampleDuration_ns / 1000); -// if (samples > 4) { -// samples = 4; -// } -// -// // drop samples -// p_payload += samples * sampleSize; -// size -= samples * sampleSize; -// -//// avg += (samples * sampleDuration_ns / 1000); -// -// ESP_LOGI(TAG, "drop %d samples", samples); -// } - } - - adjust_apll(dir); - - //ESP_LOGI(TAG, "dir %d: avg %lldus", dir, avg); - } - - - } - - #if USE_I2S_STREAM == 1 - // write data to I2S - bytesWritten = raw_stream_write(raw_stream_writer_to_i2s, p_payload, size); - if (bytesWritten < 0) { - ESP_LOGE(TAG, "i2s raw writer ring buf timeout"); - } - else if (bytesWritten < size) { - ESP_LOGE(TAG, "i2s raw writer ring buf full"); - } - #else - size_t writtenBytes; - int err = i2s_write(I2S_NUM_0, p_payload, size, &writtenBytes, portMAX_DELAY); - if (err != ESP_OK) { - ESP_LOGE(TAG, "I2S write error"); - } - #endif - - struct timeval t; - get_diff_to_server(&t); - ESP_LOGI(TAG, "%lldus, %lldus, %lldus, %lldus", age_save, avg, age, ((int64_t)t.tv_sec * 1000000LL + (int64_t)t.tv_usec)); - } - else { - ESP_LOGW(TAG, "couldn't get server now"); - - vTaskDelay( pdMS_TO_TICKS(10) ); - - continue; - } - - free(chnk->payload); - free(chnk); - chnk = NULL; - } - else { - ESP_LOGE(TAG, "Couldn't get PCM chunk, recv: messages waiting %d, %d", uxQueueMessagesWaiting(pcmChunkQueueHandle), uxQueueMessagesWaiting(timestampQueueHandle)); - - initialSync = 0; - alarmValSub = 0; - - vTaskDelay( pdMS_TO_TICKS(100) ); - } - } -} -#endif /** * @@ -1949,7 +1080,6 @@ static void http_get_task(void *pvParameters) { const int64_t outputBufferDacTime_us = DAC_OUT_BUFFER_TIME_US; // in ms snapcast_sync_task_cfg_t snapcastTaskCfg; struct timeval lastTimeSync = { 0, 0 }; - wire_chunk_message_t wire_chunk_message_last = {{0,0}, 0, NULL}; esp_timer_handle_t timeSyncMessageTimer; const esp_timer_create_args_t tSyncArgs = { @@ -1958,7 +1088,7 @@ static void http_get_task(void *pvParameters) { }; // create semaphore for time diff buffer to server - diffBufSemaphoreHandle = xSemaphoreCreateMutex(); + latencyBufSemaphoreHandle = xSemaphoreCreateMutex(); // create a timer to send time sync messages every x µs esp_timer_create(&tSyncArgs, &timeSyncMessageTimer); @@ -1975,11 +1105,10 @@ static void http_get_task(void *pvParameters) { ); while(1) { - memset((void *)diffBuf, 0, sizeof(diffBuf)); - diffBufCnt = 0; + latencyBufCnt = 0; // init diff buff median filter - latencyMedianFilter.numNodes = DIFF_BUF_LEN; + latencyMedianFilter.numNodes = LATENCY_BUF_LEN; latencyMedianFilter.medianBuffer = latencyMedianBuffer; if (MEDIANFILTER_Init(&latencyMedianFilter) < 0) { ESP_LOGE(TAG, "reset_diff_buffer: couldn't init median filter. STOP"); @@ -2000,8 +1129,8 @@ static void http_get_task(void *pvParameters) { mdns_init(); mdns_result_t * r = NULL; esp_err_t err = 0; - while ( !r || err ) - { ESP_LOGI(TAG, "Lookup snapcast service on network"); + while ( !r || err ) { + ESP_LOGI(TAG, "Lookup snapcast service on network"); esp_err_t err = mdns_query_ptr("_snapcast", "_tcp", 3000, 20, &r); if(err){ ESP_LOGE(TAG, "Query Failed"); @@ -2014,7 +1143,6 @@ static void http_get_task(void *pvParameters) { // ESP_LOGI(TAG,"Found %08x", r->addr->addr.u_addr.ip4.addr); ESP_LOGI(TAG,"Found %s", inet_ntoa(r->addr->addr.u_addr.ip4.addr)); - servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = r->addr->addr.u_addr.ip4.addr; // inet_addr("192.168.1.158"); servaddr.sin_port = htons(r->port); @@ -2059,7 +1187,7 @@ static void http_get_task(void *pvParameters) { hello_message_t hello_message = { mac_address, "ESP32-Caster", - "0.0.2", + VERSION_STRING, "libsnapcast", "esp32", "xtensa", @@ -2127,8 +1255,6 @@ static void http_get_task(void *pvParameters) { // base_message.sent.sec,base_message.sent.usec, // base_message.received.sec,base_message.received.usec); - //ESP_LOGI(TAG,"Free Heap: %d", xPortGetFreeHeapSize()); - start = buff; size = 0; @@ -2182,17 +1308,6 @@ static void http_get_task(void *pvParameters) { // TODO: maybe restart the whole thing if a new codec header is received while stream session is ongoing raw_stream_write(raw_stream_writer_to_decoder, codec_header_message.payload, size); - -// printf("\r\n"); -// for (int i=0; iaudio_hal, server_settings_message.muted); audio_hal_set_volume(board_handle->audio_hal, server_settings_message.volume); -// #if TEST_WIRECHUNK_TO_PCM_PART == 0 - if (syncTaskHandle == NULL) { - ESP_LOGI(TAG, "Start snapcast_sync_task"); + if (syncTaskHandle == NULL) { + ESP_LOGI(TAG, "Start snapcast_sync_task"); - snapcastTaskCfg.outputBufferDacTime_us = outputBufferDacTime_us; - snapcastTaskCfg.buffer_us = (int64_t)buffer_ms * 1000LL; - xTaskCreatePinnedToCore(snapcast_sync_task, "snapcast_sync_task", 8 * 1024, &snapcastTaskCfg, SYNC_TASK_PRIORITY, &syncTaskHandle, SYNC_TASK_CORE_ID); - } -// #endif + snapcastTaskCfg.outputBufferDacTime_us = outputBufferDacTime_us; + snapcastTaskCfg.buffer_us = (int64_t)buffer_ms * 1000LL; + xTaskCreatePinnedToCore(snapcast_sync_task, "snapcast_sync_task", 8 * 1024, &snapcastTaskCfg, SYNC_TASK_PRIORITY, &syncTaskHandle, SYNC_TASK_CORE_ID); + } break; @@ -2399,9 +1504,10 @@ static void http_get_task(void *pvParameters) { // ESP_LOGI(TAG, "Current latency: %ld.%06ld", tmpDiffToServer.tv_sec, tmpDiffToServer.tv_usec); // following code is storing / initializing / resetting diff to server algorithm - // we collect a number of latencies. Based on these we can get the median of server now + // we collect a number of latencies and apply a median filter. Based on these we can get server now { struct timeval diff; + int64_t medianValue, newValue; // clear diffBuffer if last update is older than a minute timersub(&now, &lastTimeSync, &diff); @@ -2409,11 +1515,27 @@ static void http_get_task(void *pvParameters) { if ( diff.tv_sec > 60 ) { ESP_LOGW(TAG, "Last time sync older than a minute. Clearing time buffer"); - reset_diff_buffer(); + reset_latency_buffer(); } - add_to_diff_buffer(tmpDiffToServer); + newValue = ((int64_t)tmpDiffToServer.tv_sec * 1000000LL + (int64_t)tmpDiffToServer.tv_usec); + medianValue = MEDIANFILTER_Insert(&latencyMedianFilter, newValue); + // TODO: find better way to check if Median Filter is full + // Count how much latencies we have stored so far + latencyBufCnt++; + if (latencyBufCnt >= LATENCY_BUF_LEN) { + latencyBuffFull = true; + } + + if (xSemaphoreTake( latencyBufSemaphoreHandle, 1 ) == pdTRUE) { + latencyToServer = medianValue; + + xSemaphoreGive( latencyBufSemaphoreHandle ); + } + else { + ESP_LOGW(TAG, "can't take latencyBufSemaphoreHandle"); + } // store current time lastTimeSync.tv_sec = now.tv_sec; @@ -2488,8 +1610,9 @@ static void http_get_task(void *pvParameters) { vTaskDelete(syncTaskHandle); syncTaskHandle = NULL; + // TODO: do not just reset queue but free allocated memory too! xQueueReset(timestampQueueHandle); - //xQueueReset(pcmChunkQueueHandle); + xQueueReset(pcmChunkQueueHandle); wirechnkCnt = 0; pcmchnkCnt = 0; @@ -2566,119 +1689,10 @@ void set_time_from_sntp() { */ } - - -/** - * - */ -static void wirechunk_to_pcm_timestamp_task(void *pvParameters) { - wire_chunk_message_t *pcm_chunk_message; - tv_t timestamp; - const int size_expect = (WIRE_CHUNK_DURATION_MS * CHANNELS * (BITS_PER_SAMPLE / 8) * SAMPLE_RATE) / 1000; - int itemsRead = 0; - ringbuf_handle_t raw_stream_reader_from_decoder_rb_handle = audio_element_get_input_ringbuf(raw_stream_reader_from_decoder); - ringbuf_handle_t decoder_rb_handle = audio_element_get_input_ringbuf(decoder); - ringbuf_handle_t raw_stream_writer_to_decoder_rb_handle = audio_element_get_output_ringbuf(raw_stream_writer_to_decoder); - - while (1) { - if (xQueueReceive( timestampQueueHandle, ×tamp, pdMS_TO_TICKS(10000) ) == pdPASS) { -// ESP_LOGI(TAG, "pcm2ts out %d", rb_bytes_filled(raw_stream_writer_to_decoder_rb_handle)); -// ESP_LOGI(TAG, "ts msg: %d", uxQueueMessagesWaiting(timestampQueueHandle)); - - //pcm_chunk_message = (wire_chunk_message_t *)malloc(sizeof(wire_chunk_message_t)); - pcm_chunk_message = (wire_chunk_message_t *)heap_caps_malloc(sizeof(wire_chunk_message_t), MALLOC_CAP_SPIRAM); - if (pcm_chunk_message == NULL) { - ESP_LOGE(TAG, "wirechunk_to_pcm_timestamp_task: Failed to allocate memory for pcm chunk message"); - - continue; - } - - //pcm_chunk_message->payload = (char *)malloc(sizeof(char) * size_expect); - pcm_chunk_message->payload = (char *)heap_caps_malloc(sizeof(char) * size_expect, MALLOC_CAP_SPIRAM); - if (pcm_chunk_message->payload == NULL) { - ESP_LOGE(TAG, "wirechunk_to_pcm_timestamp_task: Failed to allocate memory for pcm chunk payload"); - - free(pcm_chunk_message); - - continue; - } - - pcm_chunk_message->size = size_expect; - pcm_chunk_message->timestamp = timestamp; - -// ESP_LOGI(TAG, "\n1: s %d f %d s %d f %d\n2: s %d f %d s %d f %d\n3: s %d f %d s %d f %d", -// rb_get_size(audio_element_get_input_ringbuf(raw_stream_writer_to_decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_writer_to_decoder)), -// rb_get_size(audio_element_get_output_ringbuf(raw_stream_writer_to_decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(raw_stream_writer_to_decoder)), -// rb_get_size(audio_element_get_input_ringbuf(decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(decoder)), -// rb_get_size(audio_element_get_output_ringbuf(decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(decoder)), -// rb_get_size(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)), rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)), -// rb_get_size(audio_element_get_output_ringbuf(raw_stream_reader_from_decoder)), rb_bytes_filled(audio_element_get_output_ringbuf(raw_stream_reader_from_decoder))); - -// ESP_LOGI(TAG, "raw_stream_reader_from_decoder: s %d f %d chunks %d", rb_get_size(audio_element_get_out_ringbuf(raw_stream_reader_from_decoder)), -// rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)), -// rb_bytes_filled(audio_element_get_input_ringbuf(raw_stream_reader_from_decoder)) / size_expect); - itemsRead = raw_stream_read(raw_stream_reader_from_decoder, pcm_chunk_message->payload, size_expect); - if (itemsRead < size_expect) { -// // probably the stream stopped, so we need to reset decoder's buffers here -// audio_element_reset_output_ringbuf(raw_stream_writer_to_decoder); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_decoder); -// -// audio_element_reset_output_ringbuf(decoder); -// audio_element_reset_input_ringbuf(decoder); -// -// audio_element_reset_output_ringbuf(raw_stream_writer_to_i2s); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_i2s); -// -// audio_element_reset_output_ringbuf(i2s_stream_writer); -// audio_element_reset_input_ringbuf(i2s_stream_writer); -// -// wirechnkCnt = 0; -// pcmchnkCnt = 0; -// -// xQueueReset(timestampQueueHandle); - - ESP_LOGE(TAG, "wirechunk_to_pcm_timestamp_task: read %d, expected %d", itemsRead, size_expect); - } - else { - 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)); - } - } - } - else { -// // probably the stream stopped, so we need to reset decoder's buffers here -// audio_element_reset_output_ringbuf(raw_stream_writer_to_decoder); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_decoder); -// -// audio_element_reset_output_ringbuf(decoder); -// audio_element_reset_input_ringbuf(decoder); -// -// audio_element_reset_output_ringbuf(raw_stream_writer_to_i2s); -// audio_element_reset_input_ringbuf(raw_stream_writer_to_i2s); -// -// audio_element_reset_output_ringbuf(i2s_stream_writer); -// audio_element_reset_input_ringbuf(i2s_stream_writer); -// -// wirechnkCnt = 0; -// pcmchnkCnt = 0; -// -// xQueueReset(timestampQueueHandle); - - ESP_LOGW(TAG, "wirechunk_to_pcm_timestamp_task: failed to get timestamp for it, messages waiting %d, %d", uxQueueMessagesWaiting(timestampQueueHandle), uxQueueMessagesWaiting(pcmChunkQueueHandle)); - - vTaskDelay(pdMS_TO_TICKS(100)); - } - } - - // should never get here ... - return; -} - uint64_t flacCnt = 0; uint64_t receivedByteCnt = 0; int flac_decoder_write_cb(audio_element_handle_t el, char *buf, int len, TickType_t wait_time, void *ctx) { - //ringbuf_handle_t *rb = (ringbuf_handle_t *)ctx; wire_chunk_message_t *pcm_chunk_message; tv_t timestamp; @@ -2687,9 +1701,6 @@ int flac_decoder_write_cb(audio_element_handle_t el, char *buf, int len, TickTyp // 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); - //rb_write(*rb, buf, len, wait_time); - - if (xQueueReceive( timestampQueueHandle, ×tamp, wait_time ) == pdPASS) { pcm_chunk_message = (wire_chunk_message_t *)heap_caps_malloc(sizeof(wire_chunk_message_t), MALLOC_CAP_SPIRAM); if (pcm_chunk_message == NULL) { @@ -2738,37 +1749,21 @@ int flac_decoder_write_cb(audio_element_handle_t el, char *buf, int len, TickTyp #define CONFIG_SLAVE_I2S_LRCK_PIN 12 #define CONFIG_SLAVE_I2S_DATAOUT_PIN 5 -void setup_dsp_i2s(uint32_t sample_rate) +void setup_dsp_i2s(uint32_t sample_rate, i2s_port_t i2sNum) { -// i2s_config_t i2s_config0 = { -// .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX -// .sample_rate = sample_rate, -// .bits_per_sample = BITS_PER_SAMPLE, -// .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels -// .communication_format = I2S_COMM_FORMAT_STAND_I2S, -// .dma_buf_count = 5, -// .dma_buf_len = 1024, -// .intr_alloc_flags = 1, //Default interrupt priority -// .use_apll = true, -// .fixed_mclk = 0, -// .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow -// }; - - i2s_config_t i2s_config0 = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX - .sample_rate = sample_rate, - .bits_per_sample = BITS_PER_SAMPLE, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .dma_buf_count = 34, - .dma_buf_len = 16, -// .dma_buf_count = 2, -// .dma_buf_len = 256, - .intr_alloc_flags = 1, //Default interrupt priority - .use_apll = true, - .fixed_mclk = 0, - .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow - }; + i2s_config_t i2s_config0 = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = sample_rate, + .bits_per_sample = BITS_PER_SAMPLE, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels + .communication_format = I2S_COMM_FORMAT_STAND_I2S, + .dma_buf_count = 34, + .dma_buf_len = 16, + .intr_alloc_flags = 1, //Default interrupt priority + .use_apll = true, + .fixed_mclk = 0, + .tx_desc_auto_clear = true // Auto clear tx descriptor on underflow + }; i2s_pin_config_t pin_config0 = { .bck_io_num = CONFIG_MASTER_I2S_BCK_PIN, @@ -2777,9 +1772,9 @@ void setup_dsp_i2s(uint32_t sample_rate) .data_in_num = -1 //Not used }; - i2s_driver_install(I2S_NUM_0, &i2s_config0, 7, &i2s_event_queue); + i2s_driver_install(i2sNum, &i2s_config0, 7, &i2s_event_queue); // i2s_zero_dma_buffer(I2S_NUM_0); - i2s_set_pin(I2S_NUM_0, &pin_config0); + i2s_set_pin(i2sNum, &pin_config0); } /** @@ -2812,7 +1807,6 @@ void app_main(void) { ESP_LOGI(TAG, "Start codec chip"); board_handle = audio_board_init(); audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START); -#if USE_I2S_STREAM == 0 // audio_hal_codec_i2s_iface_t i2sCfg = { // .mode = AUDIO_HAL_MODE_MASTER, // .fmt = AUDIO_HAL_I2S_NORMAL, @@ -2820,20 +1814,17 @@ void app_main(void) { // .bits = AUDIO_HAL_BIT_LENGTH_16BITS, // }; // audio_hal_codec_iface_config(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, &i2sCfg); - i2s_mclk_gpio_select(I2S_NUM_0, 0); - setup_dsp_i2s(48000); -#endif + i2s_mclk_gpio_select(I2S_NUM_0, GPIO_NUM_0); + setup_dsp_i2s(48000, I2S_NUM_0); ESP_LOGI(TAG, "Create audio pipeline for decoding"); audio_pipeline_cfg_t flac_dec_pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); flacDecodePipeline = audio_pipeline_init(&flac_dec_pipeline_cfg); - //flac_dec_pipeline_cfg.rb_size = 16 * 4096; // TODO: how much is really needed? AUDIO_NULL_CHECK(TAG, flacDecodePipeline, return); ESP_LOGI(TAG, "Create raw stream to write data from snapserver to decoder"); raw_stream_cfg_t raw_1_cfg = RAW_STREAM_CFG_DEFAULT(); raw_1_cfg.type = AUDIO_STREAM_WRITER; -// raw_1_cfg.out_rb_size = 16 * 1024; // TODO: how much is really needed? raw_stream_writer_to_decoder = raw_stream_init(&raw_1_cfg); audio_element_set_output_timeout(raw_stream_writer_to_decoder, pdMS_TO_TICKS(1000)); @@ -2841,79 +1832,17 @@ void app_main(void) { flac_decoder_cfg_t flac_cfg = DEFAULT_FLAC_DECODER_CONFIG(); flac_cfg.task_prio = FLAC_DECODER_PRIORITY; flac_cfg.task_core = FLAC_DECODER_CORE_ID; -// flac_cfg.out_rb_size = 2 * 16 * 1024; // TODO: how much is really needed? decoder = flac_decoder_init(&flac_cfg); - - ESP_LOGI(TAG, "Create raw stream to read data from decoder"); - raw_stream_cfg_t raw_3_cfg = RAW_STREAM_CFG_DEFAULT(); - raw_3_cfg.type = AUDIO_STREAM_READER; - raw_stream_reader_from_decoder = raw_stream_init(&raw_3_cfg); - audio_element_set_input_timeout(raw_stream_reader_from_decoder, pdMS_TO_TICKS(500)); + audio_element_set_write_cb(decoder, flac_decoder_write_cb, NULL); ESP_LOGI(TAG, "Register all elements to audio pipeline"); audio_pipeline_register(flacDecodePipeline, raw_stream_writer_to_decoder, "raw_1"); audio_pipeline_register(flacDecodePipeline, decoder, "decoder"); -#if TEST_WIRECHUNK_TO_PCM_PART == 0 - audio_pipeline_register(flacDecodePipeline, raw_stream_reader_from_decoder, "raw_3"); - ESP_LOGI(TAG, "Link it together [snapclient]-->raw_1-->decoder --> raw_3"); - const char *link_tag[] = {"raw_1", "decoder", "raw_3"}; - audio_pipeline_link(flacDecodePipeline, &link_tag[0], 3); -#else - // TESTING, with write callback, so ripped up pipeline here -// ringbuf_handle_t test; -// test = rb_create(1024, 8); -// audio_element_set_input_ringbuf(raw_stream_reader_from_decoder, test); -// audio_element_set_write_cb(decoder, flac_decoder_write_cb, &test); - audio_element_set_write_cb(decoder, flac_decoder_write_cb, NULL); -// audio_element_set_write_cb(decoder, flac_decoder_write_cb, NULL); ESP_LOGI(TAG, "Link it together [snapclient]-->raw_1-->decoder"); const char *link_tag[] = {"raw_1", "decoder"}; audio_pipeline_link(flacDecodePipeline, &link_tag[0], 2); -#endif - - - - - - -// ESP_LOGI(TAG, "Link it together [snapclient]-->raw_1-->decoder"); -// const char *link_tag[] = {"raw_1", "decoder"}; -// audio_pipeline_link(flacDecodePipeline, &link_tag[0], 2); - - - -#if USE_I2S_STREAM == 1 - ESP_LOGI(TAG, "Create audio pipeline for playback"); - audio_pipeline_cfg_t playback_pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG(); - playbackPipeline = audio_pipeline_init(&playback_pipeline_cfg); - AUDIO_NULL_CHECK(TAG, playbackPipeline, return); - - ESP_LOGI(TAG, "Create raw stream to write data from decoder to i2s"); - raw_stream_cfg_t raw_2_cfg = RAW_STREAM_CFG_DEFAULT(); - raw_2_cfg.type = AUDIO_STREAM_WRITER; - raw_2_cfg.out_rb_size = 256;//2*3072;//1 * 4608;//3840;//2304;//2 * 4608;// TODO: how much is really needed? effect on age calculation? Probably needs dynamically changeable to chunk size??? - raw_stream_writer_to_i2s = raw_stream_init(&raw_2_cfg); - audio_element_set_output_timeout(raw_stream_writer_to_i2s, pdMS_TO_TICKS(1000)); - - ESP_LOGI(TAG, "Create i2s stream to write data to codec chip"); - i2s_cfg.i2s_config.sample_rate = 48000; - i2s_cfg.i2s_config.dma_buf_count = 4; // 2 * 576 = 24ms = 4608 Byte @ 48kHz - i2s_cfg.i2s_config.dma_buf_len = 64;//16;//576; // this is number of frames NOT bytes !!! // TODO: how much is really needed? effect on age calculation? -// i2s_cfg.i2s_config.dma_buf_count = 16; // -// i2s_cfg.i2s_config.dma_buf_len = 16; // this is number of frames NOT bytes !!! // TODO: how much is really needed? effect on age calculation? - i2s_cfg.task_core = I2S_TASK_CORE_ID; - i2s_cfg.task_prio = I2S_TASK_PRIORITY; - i2s_stream_writer = i2s_stream_init(&i2s_cfg); - - audio_pipeline_register(playbackPipeline, raw_stream_writer_to_i2s, "raw_2"); - audio_pipeline_register(playbackPipeline, i2s_stream_writer, "i2s"); - - ESP_LOGI(TAG, "Link it together [sync task]-->raw_2-->i2s_stream-->[codec_chip]"); - const char *link_tag_2[2] = {"raw_2", "i2s"}; - audio_pipeline_link(playbackPipeline, &link_tag_2[0], 2); -#endif ESP_LOGI(TAG, "Set up event listener"); audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG(); @@ -2921,15 +1850,9 @@ void app_main(void) { ESP_LOGI(TAG, "Listening event from all elements of pipelines"); audio_pipeline_set_listener(flacDecodePipeline, evt); -#if USE_I2S_STREAM == 1 - audio_pipeline_set_listener(playbackPipeline, evt); -#endif ESP_LOGI(TAG, "Start audio_pipelines"); audio_pipeline_run(flacDecodePipeline); -#if USE_I2S_STREAM == 1 - audio_pipeline_run(playbackPipeline); -#endif i2s_start(i2s_cfg.i2s_port); // i2s_stop(i2s_cfg.i2s_port); @@ -2945,7 +1868,8 @@ void app_main(void) { set_time_from_sntp(); #else { - // don't use sntp, if server and client are too different, we get overflowing timevals + // don't use sntp, if server and client are too different, we get overflowing timevals, + // insted we sync our clock to the server on reception of codec header struct timeval tv = { .tv_sec = 0, .tv_usec = 0, @@ -2974,10 +1898,6 @@ void app_main(void) { xTaskCreatePinnedToCore(http_get_task, "http_get_task", 2*4096, NULL, HTTP_TASK_PRIORITY, NULL, HTTP_TASK_CORE_ID); -#if TEST_WIRECHUNK_TO_PCM_PART == 0 - xTaskCreatePinnedToCore(wirechunk_to_pcm_timestamp_task, "timestamp_task", 2*4096, NULL, TIMESTAMP_TASK_PRIORITY, NULL, TIMESTAMP_TASK_CORE_ID); -#endif - #if COLLECT_RUNTIME_STATS == 1 xTaskCreatePinnedToCore(stats_task, "stats", 4096, NULL, STATS_TASK_PRIO, NULL, tskNO_AFFINITY); #endif @@ -3008,13 +1928,6 @@ void app_main(void) { music_info.sample_rates, music_info.bits, music_info.channels); } - #if USE_I2S_STREAM == 1 - audio_element_setinfo(i2s_stream_writer, &music_info); - i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels); - #else - - #endif - continue; } else if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) decoder && @@ -3039,42 +1952,18 @@ void app_main(void) { // ESP_LOGW(TAG, "raw_stream_writer_to_i2s unknown event received: %d", (int)msg.data); // } // } - -#if USE_I2S_STREAM == 1 - /* Stop when the last pipeline element (i2s_stream_writer in this case) receives stop event */ - if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer - && msg.cmd == AEL_MSG_CMD_REPORT_STATUS - && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED))) - { - ESP_LOGW(TAG, "[ * ] Stop event received"); - break; - } -#endif } ESP_LOGI(TAG, "Stop audio_pipeline"); audio_pipeline_stop(flacDecodePipeline); audio_pipeline_wait_for_stop(flacDecodePipeline); audio_pipeline_terminate(flacDecodePipeline); -#if USE_I2S_STREAM == 1 - audio_pipeline_stop(playbackPipeline); - audio_pipeline_wait_for_stop(playbackPipeline); - audio_pipeline_terminate(playbackPipeline); -#endif audio_pipeline_unregister(flacDecodePipeline, raw_stream_writer_to_decoder); audio_pipeline_unregister(flacDecodePipeline, decoder); - audio_pipeline_unregister(flacDecodePipeline, raw_stream_reader_from_decoder); -#if USE_I2S_STREAM == 1 - audio_pipeline_unregister(playbackPipeline, raw_stream_writer_to_i2s); - audio_pipeline_unregister(playbackPipeline, i2s_stream_writer); -#endif /* Terminal the pipeline before removing the listener */ audio_pipeline_remove_listener(flacDecodePipeline); -#if USE_I2S_STREAM == 1 - audio_pipeline_remove_listener(playbackPipeline); -#endif /* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */ audio_event_iface_destroy(evt); @@ -3083,13 +1972,6 @@ void app_main(void) { audio_pipeline_deinit(flacDecodePipeline); audio_element_deinit(raw_stream_writer_to_decoder); audio_element_deinit(decoder); - audio_element_deinit(raw_stream_reader_from_decoder); - -#if USE_I2S_STREAM == 1 - audio_pipeline_deinit(playbackPipeline); - audio_element_deinit(raw_stream_writer_to_i2s); - audio_element_deinit(i2s_stream_writer); -#endif // TODO: clean up all created tasks and delete them }