Files
snapclient/components/lightsnapcast/player.c
Karl Osterseher 530ca590fc - repair issue #5 (static IP can't connect)
- increase DMA buffer size from 1 chunk to 3 chunks settable through source code Macro CHNK_CTRL_CNT

- activate OTA server

Signed-off-by: Karl Osterseher <karli_o@gmx.at>
2022-09-18 14:19:16 +02:00

2530 lines
79 KiB
C

/**
*
*/
#include <stdint.h>
#include <string.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
//#include "lwip/stats.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "soc/rtc.h"
#include "driver/timer.h"
#include "MedianFilter.h"
#include "board_pins_config.h"
#include "player.h"
#include "snapcast.h"
#include "i2s.h" // use custom i2s driver instead of IDF version
#define SYNC_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#define SYNC_TASK_CORE_ID 1 // tskNO_AFFINITY
static const char *TAG = "PLAYER";
/**
* @brief Pre define APLL parameters, save compute time. They are calculated in
* player_setup_i2s() | bits_per_sample | rate | sdm0 | sdm1 | sdm2 | odir
*
* apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div +
* 2) * 2) I2S bit clock is (apll_freq / 16)
*/
static int apll_normal_predefine[6] = {0, 0, 0, 0, 0, 0};
static int apll_corr_predefine[][6] = {{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}};
static SemaphoreHandle_t latencyBufSemaphoreHandle = NULL;
static int8_t latencyBuffFull = 0;
static sMedianFilter_t latencyMedianFilterLong;
static sMedianNode_t latencyMedianLongBuffer[LATENCY_MEDIAN_FILTER_LEN];
static int64_t latencyToServer = 0;
static sMedianFilter_t shortMedianFilter;
static sMedianNode_t shortMedianBuffer[SHORT_BUFFER_LEN];
static int8_t currentDir = 0; //!< current apll direction, see apll_adjust()
static QueueHandle_t pcmChkQHdl = NULL;
//#define PCM_CHNK_QUEUE_LENGTH 50 // TODO: one chunk is hardcoded to 20ms,
// change it to be dynamically adjustable. static StaticQueue_t pcmChunkQueue;
// static uint8_t pcmChunkQueueStorageArea[PCM_CHNK_QUEUE_LENGTH
// * sizeof (pcm_chunk_message_t *)];
static TaskHandle_t syncTaskHandle = NULL;
static QueueHandle_t snapcastSettingQueueHandle = NULL;
static size_t chkInBytes;
static uint32_t i2sDmaBufCnt;
static uint32_t i2sDmaBufMaxLen;
static SemaphoreHandle_t snapcastSettingsMux = NULL;
static snapcastSetting_t currentSnapcastSetting;
static void tg0_timer_init(void);
static void tg0_timer_deinit(void);
static void player_task(void *pvParameters);
/*
#define CONFIG_MASTER_I2S_BCK_PIN 5
#define CONFIG_MASTER_I2S_LRCK_PIN 25
#define CONFIG_MASTER_I2S_DATAOUT_PIN 26
#define CONFIG_SLAVE_I2S_BCK_PIN 26
#define CONFIG_SLAVE_I2S_LRCK_PIN 12
#define CONFIG_SLAVE_I2S_DATAOUT_PIN 5
*/
static esp_err_t player_setup_i2s(i2s_port_t i2sNum,
snapcastSetting_t *setting) {
int chunkInFrames;
int __dmaBufCnt;
int __dmaBufLen;
const int __dmaBufMaxLen = 1024;
int m_scale = 8, fi2s_clk;
chkInBytes =
(setting->chkDur_ms * setting->sr * setting->ch * (setting->bits / 8)) /
1000;
chunkInFrames = chkInBytes / (setting->ch * (setting->bits / 8));
__dmaBufCnt = 1;
__dmaBufLen = chunkInFrames;
while ((__dmaBufLen >= __dmaBufMaxLen) || (__dmaBufCnt <= 1)) {
if ((__dmaBufLen % 2) == 0) {
__dmaBufCnt *= 2;
__dmaBufLen /= 2;
} else {
ESP_LOGE(TAG,
"player_setup_i2s: Can't setup i2s with this configuration");
return -1;
}
}
i2sDmaBufCnt = __dmaBufCnt * CHNK_CTRL_CNT;
i2sDmaBufMaxLen = __dmaBufLen;
fi2s_clk = setting->sr * setting->ch * setting->bits * m_scale;
apll_normal_predefine[0] = setting->bits;
apll_normal_predefine[1] = setting->sr;
if (i2s_apll_calculate_fi2s(
fi2s_clk, setting->bits, &apll_normal_predefine[2],
&apll_normal_predefine[3], &apll_normal_predefine[4],
&apll_normal_predefine[5]) != ESP_OK) {
ESP_LOGE(TAG, "ERROR, fi2s_clk");
}
apll_corr_predefine[0][0] = setting->bits;
apll_corr_predefine[0][1] = setting->sr * 1.001;
if (i2s_apll_calculate_fi2s(
fi2s_clk * 1.001, setting->bits, &apll_corr_predefine[0][2],
&apll_corr_predefine[0][3], &apll_corr_predefine[0][4],
&apll_corr_predefine[0][5]) != ESP_OK) {
ESP_LOGE(TAG, "ERROR, fi2s_clk * 1.001");
}
apll_corr_predefine[1][0] = setting->bits;
apll_corr_predefine[1][1] = setting->sr * 0.999;
if (i2s_apll_calculate_fi2s(
fi2s_clk * 0.999, setting->bits, &apll_corr_predefine[1][2],
&apll_corr_predefine[1][3], &apll_corr_predefine[1][4],
&apll_corr_predefine[1][5]) != ESP_OK) {
ESP_LOGE(TAG, "ERROR, fi2s_clk * 0.999");
}
ESP_LOGI(TAG, "player_setup_i2s: dma_buf_len is %d, dma_buf_count is %d",
i2sDmaBufMaxLen, i2sDmaBufCnt);
i2s_config_t i2s_config0 = {
.mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX
.sample_rate = setting->sr,
.bits_per_sample = setting->bits,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.dma_buf_count = i2sDmaBufCnt,
.dma_buf_len = i2sDmaBufMaxLen,
.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;
get_i2s_pins(i2sNum, &pin_config0);
i2s_custom_driver_uninstall(i2sNum);
i2s_custom_driver_install(i2sNum, &i2s_config0, 0, NULL);
i2s_custom_set_pin(i2sNum, &pin_config0);
return 0;
}
/**
*
*/
static int destroy_pcm_queue(QueueHandle_t *queueHandle) {
int ret = pdPASS;
pcm_chunk_message_t *chnk = NULL;
if (*queueHandle == NULL) {
ESP_LOGW(TAG, "no pcm chunk queue created?");
ret = pdFAIL;
} else {
// free all allocated memory
while (uxQueueMessagesWaiting(*queueHandle)) {
ret = xQueueReceive(*queueHandle, &chnk, pdMS_TO_TICKS(2000));
if (ret != pdFAIL) {
if (chnk != NULL) {
free_pcm_chunk(chnk);
}
}
}
// delete the queue
vQueueDelete(*queueHandle);
*queueHandle = NULL;
ret = pdPASS;
}
return ret;
}
// ensure this is called after http_task was killed!
int deinit_player(void) {
int ret = 0;
// stop the task
if (syncTaskHandle == NULL) {
ESP_LOGW(TAG, "no sync task created?");
} else {
vTaskDelete(syncTaskHandle);
}
if (snapcastSettingsMux != NULL) {
vSemaphoreDelete(snapcastSettingsMux);
snapcastSettingsMux = NULL;
}
ret = destroy_pcm_queue(&pcmChkQHdl);
if (latencyBufSemaphoreHandle == NULL) {
ESP_LOGW(TAG, "no latency buffer semaphore created?");
} else {
vSemaphoreDelete(latencyBufSemaphoreHandle);
latencyBufSemaphoreHandle = NULL;
}
tg0_timer_deinit();
ESP_LOGI(TAG, "deinit player done");
return ret;
}
/**
* call before http task creation!
*/
int init_player(void) {
int ret = 0;
currentSnapcastSetting.buf_ms = 1000;
currentSnapcastSetting.chkDur_ms = 20;
currentSnapcastSetting.codec = NONE;
currentSnapcastSetting.sr = 44100;
currentSnapcastSetting.ch = 2;
currentSnapcastSetting.bits = 16;
currentSnapcastSetting.muted = false;
currentSnapcastSetting.volume = 70;
if (snapcastSettingsMux == NULL) {
snapcastSettingsMux = xSemaphoreCreateMutex();
xSemaphoreGive(snapcastSettingsMux);
}
ret = player_setup_i2s(I2S_NUM_0, &currentSnapcastSetting);
if (ret < 0) {
ESP_LOGE(TAG, "player_setup_i2s failed: %d", ret);
return -1;
}
// create semaphore for time diff buffer to server
if (latencyBufSemaphoreHandle == NULL) {
latencyBufSemaphoreHandle = xSemaphoreCreateMutex();
}
// init diff buff median filter
latencyMedianFilterLong.numNodes = LATENCY_MEDIAN_FILTER_LEN;
latencyMedianFilterLong.medianBuffer = latencyMedianLongBuffer;
reset_latency_buffer();
tg0_timer_init();
if (syncTaskHandle == NULL) {
ESP_LOGI(TAG, "Start player_task");
xTaskCreatePinnedToCore(player_task, "player", 2048 + 512, NULL,
SYNC_TASK_PRIORITY, &syncTaskHandle,
SYNC_TASK_CORE_ID);
}
ESP_LOGI(TAG, "init player done");
return 0;
}
int8_t player_set_snapcast_settings(snapcastSetting_t *setting) {
int8_t ret = pdPASS;
xSemaphoreTake(snapcastSettingsMux, portMAX_DELAY);
memcpy(&currentSnapcastSetting, setting, sizeof(snapcastSetting_t));
xSemaphoreGive(snapcastSettingsMux);
return ret;
}
int8_t player_get_snapcast_settings(snapcastSetting_t *setting) {
int8_t ret = pdPASS;
xSemaphoreTake(snapcastSettingsMux, portMAX_DELAY);
memcpy(setting, &currentSnapcastSetting, sizeof(snapcastSetting_t));
xSemaphoreGive(snapcastSettingsMux);
return ret;
}
int8_t player_latency_insert(int64_t newValue) {
int64_t medianValue;
medianValue = MEDIANFILTER_Insert(&latencyMedianFilterLong, newValue);
if (xSemaphoreTake(latencyBufSemaphoreHandle, pdMS_TO_TICKS(5)) == pdTRUE) {
if (MEDIANFILTER_isFull(&latencyMedianFilterLong)) {
latencyBuffFull = true;
}
latencyToServer = medianValue;
xSemaphoreGive(latencyBufSemaphoreHandle);
} else {
ESP_LOGW(TAG, "couldn't set latencyToServer = medianValue");
}
return 0;
}
/**
*
*/
int8_t player_send_snapcast_setting(snapcastSetting_t *setting) {
int ret;
snapcastSetting_t curSet;
uint8_t settingChanged = 1;
if ((syncTaskHandle == NULL) || (snapcastSettingQueueHandle == NULL)) {
return pdFAIL;
}
ret = player_get_snapcast_settings(&curSet);
if ((curSet.bits != setting->bits) || (curSet.buf_ms != setting->buf_ms) ||
(curSet.ch != setting->ch) || (curSet.chkDur_ms != setting->chkDur_ms) ||
(curSet.codec != setting->codec) || (curSet.muted != setting->muted) ||
(curSet.sr != setting->sr) || (curSet.volume != setting->volume) ||
(curSet.cDacLat_ms != setting->cDacLat_ms)) {
// check if it is only volume / mute related setting, which is handled by
// http_get_task()
if (((curSet.muted != setting->muted) ||
(curSet.volume != setting->volume)) &&
((curSet.bits == setting->bits) && (curSet.buf_ms == setting->buf_ms) &&
(curSet.ch == setting->ch) &&
(curSet.chkDur_ms == setting->chkDur_ms) &&
(curSet.codec == setting->codec) && (curSet.sr == setting->sr) &&
(curSet.cDacLat_ms == setting->cDacLat_ms))) {
// no notify needed, only set changed parameters
ret = player_set_snapcast_settings(setting);
if (ret != pdPASS) {
ESP_LOGE(TAG,
"player_send_snapcast_setting: couldn't change "
"snapcast setting");
}
} else {
ret = xQueueOverwrite(snapcastSettingQueueHandle, &settingChanged);
if (ret != pdPASS) {
ESP_LOGE(TAG,
"player_send_snapcast_setting: couldn't notify "
"snapcast setting");
} else {
// notify successful, so change parameters
ret = player_set_snapcast_settings(setting);
if (ret != pdPASS) {
ESP_LOGE(TAG,
"player_send_snapcast_setting: couldn't "
"change snapcast setting");
}
}
}
}
return pdPASS;
}
/**
*
*/
int8_t reset_latency_buffer(void) {
// init diff buff median filter
if (MEDIANFILTER_Init(&latencyMedianFilterLong) < 0) {
ESP_LOGE(TAG, "reset_diff_buffer: couldn't init median filter long. STOP");
return -2;
}
if (latencyBufSemaphoreHandle == NULL) {
ESP_LOGE(TAG, "reset_diff_buffer: latencyBufSemaphoreHandle == NULL");
return -2;
}
if (xSemaphoreTake(latencyBufSemaphoreHandle, portMAX_DELAY) == pdTRUE) {
latencyBuffFull = false;
latencyToServer = 0;
xSemaphoreGive(latencyBufSemaphoreHandle);
} else {
ESP_LOGW(TAG, "reset_diff_buffer: can't take semaphore");
return -1;
}
return 0;
}
/**
*
*/
int8_t latency_buffer_full(void) {
int8_t tmp;
if (latencyBufSemaphoreHandle == NULL) {
ESP_LOGE(TAG, "latency_buffer_full: latencyBufSemaphoreHandle == NULL");
return -2;
}
if (xSemaphoreTake(latencyBufSemaphoreHandle, 0) == pdFALSE) {
ESP_LOGW(TAG, "latency_buffer_full: can't take semaphore");
return -1;
}
tmp = latencyBuffFull;
xSemaphoreGive(latencyBufSemaphoreHandle);
return tmp;
}
/**
*
*/
int8_t get_diff_to_server(int64_t *tDiff) {
static int64_t lastDiff = 0;
if (latencyBufSemaphoreHandle == NULL) {
ESP_LOGE(TAG, "get_diff_to_server: latencyBufSemaphoreHandle == NULL");
return -2;
}
if (xSemaphoreTake(latencyBufSemaphoreHandle, 0) == pdFALSE) {
*tDiff = lastDiff;
ESP_LOGW(TAG,
"get_diff_to_server: can't take semaphore. Old diff retrieved");
return -1;
}
*tDiff = latencyToServer;
lastDiff = latencyToServer; // store value, so we can return a value if
// semaphore couldn't be taken
xSemaphoreGive(latencyBufSemaphoreHandle);
return 0;
}
/**
*
*/
int8_t server_now(int64_t *sNow) {
struct timeval now;
int64_t 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_LOGW(TAG,
"server_now: can't get current diff to server. Retrieved old one");
}
if (diff == 0) {
// ESP_LOGW(TAG, "server_now: diff to server not initialized yet");
return -1;
}
*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", diff);
// ESP_LOGI(TAG, "serverNow: %lldus", *snow);
return 0;
}
/*
* Timer group0 ISR handler
*
* Note:
* We don't call the timer API here because they are not declared with
* IRAM_ATTR. If we're okay with the timer irq not being serviced while SPI
* flash cache is disabled, we can allocate this interrupt without the
* ESP_INTR_FLAG_IRAM flag and use the normal API.
*/
void IRAM_ATTR timer_group0_isr(void *para) {
timer_spinlock_take(TIMER_GROUP_0);
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// Retrieve the interrupt status and the counter value
// from the timer that reported the interrupt
uint32_t timer_intr = timer_group_get_intr_status_in_isr(TIMER_GROUP_0);
// Clear the interrupt
// and update the alarm time for the timer with without reload
if (timer_intr & TIMER_INTR_T1) {
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_1);
// Notify the task in the task's notification value.
xTaskNotifyFromISR(syncTaskHandle, 0, eNoAction, &xHigherPriorityTaskWoken);
}
timer_spinlock_give(TIMER_GROUP_0);
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
static void tg0_timer_deinit(void) { timer_deinit(TIMER_GROUP_0, TIMER_1); }
/*
*
*/
static void tg0_timer_init(void) {
// Select and initialize basic parameters of the timer
timer_config_t config = {
//.divider = 8, // 100ns ticks
.divider = 80, // 1µs ticks
.counter_dir = TIMER_COUNT_UP,
.counter_en = TIMER_PAUSE,
.alarm_en = TIMER_ALARM_EN,
.auto_reload = TIMER_AUTORELOAD_DIS,
}; // default clock source is APB
timer_init(TIMER_GROUP_0, TIMER_1, &config);
// Configure the alarm value and the interrupt on alarm.
timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, 0);
timer_enable_intr(TIMER_GROUP_0, TIMER_1);
if (timer_isr_register(TIMER_GROUP_0, TIMER_1, timer_group0_isr, NULL,
ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3,
NULL) != ESP_OK) {
ESP_LOGE(TAG, "unable to register timer 1 callback");
}
}
/**
*
*/
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, 0);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_1, alarm_value);
timer_set_alarm(TIMER_GROUP_0, TIMER_1, TIMER_ALARM_EN);
timer_start(TIMER_GROUP_0, TIMER_1);
// ESP_LOGI(TAG, "started age timer");
}
// void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t
// sdm2, uint32_t o_div); apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 +
// sdm0/65536)/((o_div + 2) * 2) xtal == 40MHz on lyrat v4.3 I2S bit_clock =
// rate * (number of channels) * bits_per_sample
void adjust_apll(int8_t direction) {
int sdm0, sdm1, sdm2, o_div;
// only change if necessary
if (currentDir == direction) {
return;
}
if (direction == 1) {
// speed up
sdm0 = apll_corr_predefine[0][2];
sdm1 = apll_corr_predefine[0][3];
sdm2 = apll_corr_predefine[0][4];
o_div = apll_corr_predefine[0][5];
} else if (direction == -1) {
// slow down
sdm0 = apll_corr_predefine[1][2];
sdm1 = apll_corr_predefine[1][3];
sdm2 = apll_corr_predefine[1][4];
o_div = apll_corr_predefine[1][5];
} else {
// reset to normal playback speed
sdm0 = apll_normal_predefine[2];
sdm1 = apll_normal_predefine[3];
sdm2 = apll_normal_predefine[4];
o_div = apll_normal_predefine[5];
direction = 0;
}
rtc_clk_apll_enable(1, sdm0, sdm1, sdm2, o_div);
currentDir = direction;
}
/**
*
*/
int8_t free_pcm_chunk_fragments(pcm_chunk_fragment_t *fragment) {
if (fragment == NULL) {
ESP_LOGE(TAG, "free_pcm_chunk_fragments() parameter Error");
return -1;
}
// free all fragments recursive
if (fragment->nextFragment == NULL) {
if (fragment->payload != NULL) {
free(fragment->payload);
fragment->payload = NULL;
}
free(fragment);
fragment = NULL;
} else {
free_pcm_chunk_fragments(fragment->nextFragment);
}
return 0;
}
/**
*
*/
int8_t free_pcm_chunk(pcm_chunk_message_t *pcmChunk) {
if (pcmChunk == NULL) {
ESP_LOGE(TAG, "free_pcm_chunk() parameter Error");
return -1;
}
free_pcm_chunk_fragments(pcmChunk->fragment);
pcmChunk->fragment = NULL; // was freed in free_pcm_chunk_fragments()
free(pcmChunk);
pcmChunk = NULL;
return 0;
}
int8_t insert_pcm_chunk_IRAM(wire_chunk_message_t *decodedWireChunk,
pcm_chunk_message_t *pcmChunk) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if ((freeMem >= decodedWireChunk->size) &&
(largestFreeBlock >= decodedWireChunk->size)) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
pcmChunk->fragment->payload = (char *)heap_caps_malloc(
decodedWireChunk->size, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
// copy the whole payload to our fragment
memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload,
decodedWireChunk->size);
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = decodedWireChunk->size;
ret = 0;
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
int8_t insert_pcm_chunk_DRAM(wire_chunk_message_t *decodedWireChunk,
pcm_chunk_message_t *pcmChunk) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT);
largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if ((freeMem >= decodedWireChunk->size) &&
(largestFreeBlock >= decodedWireChunk->size)) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(decodedWireChunk->size, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate DRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
// copy the whole payload to our fragment
memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload,
decodedWireChunk->size);
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = decodedWireChunk->size;
ret = 0;
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert chunk
// of size %d, DRAM freemem: %d blocksize %d",
// decodedWireChunk->size, freeMem, largestFreeBlock);
}
return ret;
}
int8_t insert_pcm_chunk_IRAM_fragmented(wire_chunk_message_t *decodedWireChunk,
pcm_chunk_message_t *pcmChunk) {
size_t largestFreeBlock, freeMem;
int ret = -3;
size_t tmpSize;
pcm_chunk_fragment_t *next = NULL;
size_t s;
freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
// ESP_LOGW(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
// just to be sure, normally insert_pcm_chunk_IRAM() would have been called
// previously and this shouldn't be possible now
if (largestFreeBlock >= decodedWireChunk->size) {
ret = insert_pcm_chunk_IRAM(decodedWireChunk, pcmChunk);
} else {
ret = 0;
if (freeMem >= decodedWireChunk->size) {
tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size,
// MALLOC_CAP_32BIT);
pcmChunk->fragment->payload = (char *)heap_caps_malloc(
largestFreeBlock, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for pcm chunk "
"fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
next = pcmChunk->fragment;
s = largestFreeBlock;
// loop until we have all data stored to a fragment
do {
// copy the whole payload to our fragment
memcpy(next->payload, decodedWireChunk->payload, s);
next->size = s;
tmpSize -= s;
decodedWireChunk->payload += s;
// ESP_LOGI (TAG,"%p %d", next->payload,
// next->size);
if (tmpSize > 0) {
next->nextFragment = (pcm_chunk_fragment_t *)heap_caps_calloc(
1, sizeof(pcm_chunk_fragment_t), MALLOC_CAP_8BIT);
if (next->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for next pcm "
"chunk fragment %d %d",
heap_caps_get_free_size(MALLOC_CAP_8BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
// free_pcm_chunk
//(pcmChunk);
ret = -3;
break;
} else {
largestFreeBlock = heap_caps_get_largest_free_block(
MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (largestFreeBlock <= tmpSize) {
s = largestFreeBlock;
} else {
s = tmpSize;
}
next->nextFragment->payload = (char *)heap_caps_malloc(
s, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (next->nextFragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for pcm "
"chunk next fragmented payload");
// free_pcm_chunk
//(pcmChunk);
ret = -3;
break;
} else {
next = next->nextFragment;
}
}
}
} while (tmpSize);
}
}
}
/*
if (ret < 0) {
// freeMem = heap_caps_get_free_size (MALLOC_CAP_8BIT);
// largestFreeBlock = heap_caps_get_largest_free_block
(MALLOC_CAP_8BIT);
// ESP_LOGW(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
// just to be sure, normally insert_pcm_chunk_DRAM() would have been
called previously and this shouldn't be possible now
// if (largestFreeBlock >= decodedWireChunk->size)
// {
// ret = insert_pcm_chunk_DRAM(decodedWireChunk,
pcmChunk);
// }
// else
{
// pcm_chunk_fragment_t *next = NULL;
// size_t s;
ret = 0;
// tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t),
decodedWireChunk->size,
// MALLOC_CAP_32BIT);
pcmChunk->fragment->payload
= (char *)heap_caps_malloc (largestFreeBlock,
MALLOC_CAP_8BIT); if (pcmChunk->fragment->payload == NULL)
{
ESP_LOGE (TAG, "Failed to allocate DRAM memory for
pcm chunk " "fragmented payload");
free_pcm_chunk (pcmChunk);
ret = -2;
}
else
{
next = pcmChunk->fragment;
s = largestFreeBlock;
// loop until we have all data stored to a fragment
do
{
// copy the whole payload to our fragment
memcpy (next->payload,
decodedWireChunk->payload, s); next->size = s; tmpSize -= s;
decodedWireChunk->payload += s;
// ESP_LOGI (TAG,"%p %d", next->payload, next->size);
if (tmpSize > 0)
{
next->nextFragment =
(pcm_chunk_fragment_t *)heap_caps_calloc ( 1, sizeof (pcm_chunk_fragment_t),
MALLOC_CAP_8BIT); if (next->nextFragment == NULL)
{
ESP_LOGE (TAG,
"Failed
to allocate DRAM memory for next pcm " "chunk fragment %d %d",
heap_caps_get_free_size
(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block ( MALLOC_CAP_8BIT));
free_pcm_chunk (pcmChunk);
ret = -3;
break;
}
else
{
largestFreeBlock =
heap_caps_get_largest_free_block (MALLOC_CAP_8BIT); if (largestFreeBlock <=
tmpSize)
{
s = largestFreeBlock;
}
else
{
s = tmpSize;
}
next->nextFragment->payload
= (char
*)heap_caps_malloc (s, MALLOC_CAP_8BIT); if (next->nextFragment->payload ==
NULL)
{
ESP_LOGE (TAG,
"Failed to allocate DRAM memory for pcm "
"chunk next fragmented payload");
free_pcm_chunk
(pcmChunk);
ret = -3;
break;
}
else
{
next =
next->nextFragment;
}
}
}
}
while (tmpSize);
}
}
}
*/
return ret;
}
int8_t insert_pcm_chunk_DRAM_fragmented(wire_chunk_message_t *decodedWireChunk,
pcm_chunk_message_t *pcmChunk) {
size_t largestFreeBlock, freeMem;
int ret = -3;
size_t tmpSize;
pcm_chunk_fragment_t *next = NULL;
size_t s;
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT);
largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
// ESP_LOGW(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
// just to be sure, normally insert_pcm_chunk_IRAM() would have been called
// previously and this shouldn't be possible now
if (largestFreeBlock >= decodedWireChunk->size) {
ret = insert_pcm_chunk_DRAM(decodedWireChunk, pcmChunk);
} else {
ret = 0;
if (freeMem >= decodedWireChunk->size) {
tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size,
// MALLOC_CAP_32BIT);
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(largestFreeBlock, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for pcm chunk "
"fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
next = pcmChunk->fragment;
s = largestFreeBlock;
// loop until we have all data stored to a fragment
do {
// copy the whole payload to our fragment
memcpy(next->payload, decodedWireChunk->payload, s);
next->size = s;
tmpSize -= s;
decodedWireChunk->payload += s;
// ESP_LOGI (TAG,"%p %d", next->payload,
// next->size);
if (tmpSize > 0) {
next->nextFragment = (pcm_chunk_fragment_t *)heap_caps_calloc(
1, sizeof(pcm_chunk_fragment_t), MALLOC_CAP_8BIT);
if (next->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for next pcm "
"chunk fragment %d %d",
heap_caps_get_free_size(MALLOC_CAP_8BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
// free_pcm_chunk
//(pcmChunk);
ret = -3;
break;
} else {
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if (largestFreeBlock <= tmpSize) {
s = largestFreeBlock;
} else {
s = tmpSize;
}
next->nextFragment->payload =
(char *)heap_caps_malloc(s, MALLOC_CAP_8BIT);
if (next->nextFragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate IRAM memory for pcm "
"chunk next fragmented payload");
// free_pcm_chunk
//(pcmChunk);
ret = -3;
break;
} else {
next = next->nextFragment;
}
}
}
} while (tmpSize);
}
}
}
/*
if (ret < 0) {
// freeMem = heap_caps_get_free_size (MALLOC_CAP_8BIT);
// largestFreeBlock = heap_caps_get_largest_free_block
(MALLOC_CAP_8BIT);
// ESP_LOGW(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
// just to be sure, normally insert_pcm_chunk_DRAM() would have been
called previously and this shouldn't be possible now
// if (largestFreeBlock >= decodedWireChunk->size)
// {
// ret = insert_pcm_chunk_DRAM(decodedWireChunk,
pcmChunk);
// }
// else
{
// pcm_chunk_fragment_t *next = NULL;
// size_t s;
ret = 0;
// tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t),
decodedWireChunk->size,
// MALLOC_CAP_32BIT);
pcmChunk->fragment->payload
= (char *)heap_caps_malloc (largestFreeBlock,
MALLOC_CAP_8BIT); if (pcmChunk->fragment->payload == NULL)
{
ESP_LOGE (TAG, "Failed to allocate DRAM memory for
pcm chunk " "fragmented payload");
free_pcm_chunk (pcmChunk);
ret = -2;
}
else
{
next = pcmChunk->fragment;
s = largestFreeBlock;
// loop until we have all data stored to a fragment
do
{
// copy the whole payload to our fragment
memcpy (next->payload,
decodedWireChunk->payload, s); next->size = s; tmpSize -= s;
decodedWireChunk->payload += s;
// ESP_LOGI (TAG,"%p %d", next->payload, next->size);
if (tmpSize > 0)
{
next->nextFragment =
(pcm_chunk_fragment_t *)heap_caps_calloc ( 1, sizeof (pcm_chunk_fragment_t),
MALLOC_CAP_8BIT); if (next->nextFragment == NULL)
{
ESP_LOGE (TAG,
"Failed
to allocate DRAM memory for next pcm " "chunk fragment %d %d",
heap_caps_get_free_size
(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block ( MALLOC_CAP_8BIT));
free_pcm_chunk (pcmChunk);
ret = -3;
break;
}
else
{
largestFreeBlock =
heap_caps_get_largest_free_block (MALLOC_CAP_8BIT); if (largestFreeBlock <=
tmpSize)
{
s = largestFreeBlock;
}
else
{
s = tmpSize;
}
next->nextFragment->payload
= (char
*)heap_caps_malloc (s, MALLOC_CAP_8BIT); if (next->nextFragment->payload ==
NULL)
{
ESP_LOGE (TAG,
"Failed to allocate DRAM memory for pcm "
"chunk next fragmented payload");
free_pcm_chunk
(pcmChunk);
ret = -3;
break;
}
else
{
next =
next->nextFragment;
}
}
}
}
while (tmpSize);
}
}
}
*/
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_caps(pcm_chunk_message_t *pcmChunk,
size_t bytes, uint32_t caps) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(caps);
largestFreeBlock = heap_caps_get_largest_free_block(caps);
if ((freeMem >= bytes) && (largestFreeBlock >= bytes)) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
pcmChunk->fragment->payload = (char *)heap_caps_malloc(bytes, caps);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d", bytes, freeMem,
// largestFreeBlock);
}
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_caps_fragmented(pcm_chunk_message_t *pcmChunk,
size_t bytes, uint32_t caps) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(caps);
largestFreeBlock = heap_caps_get_largest_free_block(caps);
if (freeMem >= bytes) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
if (largestFreeBlock >= bytes) {
pcmChunk->fragment->payload = (char *)heap_caps_malloc(bytes, caps);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
size_t remainingBytes = bytes + (largestFreeBlock % 4);
size_t needBytes = largestFreeBlock - (largestFreeBlock % 4);
pcm_chunk_fragment_t *fragment = pcmChunk->fragment;
pcmChunk->totalSize = 0;
while (remainingBytes) {
fragment->payload = (char *)heap_caps_malloc(needBytes, caps);
if (fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate fragmented IRAM memory for "
"pcm chunk payload %d %d %d %d",
needBytes, remainingBytes, heap_caps_get_free_size(caps),
heap_caps_get_largest_free_block(caps));
// free_pcm_chunk (pcmChunk);
ret = -2;
break;
} else {
fragment->size = needBytes;
remainingBytes -= needBytes;
pcmChunk->totalSize += needBytes;
if (remainingBytes > 0) {
fragment->nextFragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if (fragment->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to fragmented IRAM memory "
"for pcm chunk fragment");
ret = -2;
break;
} else {
fragment = fragment->nextFragment;
largestFreeBlock = heap_caps_get_largest_free_block(caps);
if (largestFreeBlock >= remainingBytes) {
needBytes = remainingBytes;
} else {
needBytes = largestFreeBlock - (largestFreeBlock % 4);
}
}
} else {
ret = 0;
}
}
}
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_IRAM_fragmented(pcm_chunk_message_t *pcmChunk,
size_t bytes) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (freeMem >= bytes) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
if (largestFreeBlock >= bytes) {
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(bytes, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
size_t remainingBytes = bytes + (largestFreeBlock % 4);
size_t needBytes = largestFreeBlock - (largestFreeBlock % 4);
pcm_chunk_fragment_t *fragment = pcmChunk->fragment;
pcmChunk->totalSize = 0;
while (remainingBytes) {
fragment->payload = (char *)heap_caps_malloc(
needBytes, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate fragmented IRAM memory for "
"pcm chunk payload %d %d %d",
needBytes, largestFreeBlock, remainingBytes);
// free_pcm_chunk (pcmChunk);
ret = -2;
break;
} else {
fragment->size = needBytes;
remainingBytes -= needBytes;
pcmChunk->totalSize += needBytes;
if (remainingBytes > 0) {
fragment->nextFragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if (fragment->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to fragmented IRAM memory "
"for pcm chunk fragment");
ret = -2;
break;
} else {
fragment = fragment->nextFragment;
largestFreeBlock = heap_caps_get_largest_free_block(
MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (largestFreeBlock >= remainingBytes) {
needBytes = remainingBytes;
} else {
needBytes = largestFreeBlock - (largestFreeBlock % 4);
}
}
} else {
ret = 0;
}
}
}
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_IRAM(pcm_chunk_message_t *pcmChunk,
size_t bytes) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if ((freeMem >= bytes) && (largestFreeBlock >= bytes)) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(bytes, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_DRAM_fragmented(pcm_chunk_message_t *pcmChunk,
size_t bytes) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT);
largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if (freeMem >= bytes) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
if (largestFreeBlock >= bytes) {
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(bytes, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
size_t remainingBytes = bytes + (largestFreeBlock % 4);
size_t needBytes = largestFreeBlock - (largestFreeBlock % 4);
pcm_chunk_fragment_t *fragment = pcmChunk->fragment;
pcmChunk->totalSize = 0;
while (remainingBytes) {
fragment->payload =
(char *)heap_caps_malloc(needBytes, MALLOC_CAP_8BIT);
if (fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate fragmented IRAM memory for "
"pcm chunk payload %d %d %d",
needBytes, largestFreeBlock, remainingBytes);
// free_pcm_chunk (pcmChunk);
ret = -2;
break;
} else {
fragment->size = needBytes;
remainingBytes -= needBytes;
pcmChunk->totalSize += needBytes;
if (remainingBytes > 0) {
fragment->nextFragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if (fragment->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to fragmented IRAM memory "
"for pcm chunk fragment");
ret = -2;
break;
} else {
fragment = fragment->nextFragment;
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if (largestFreeBlock >= remainingBytes) {
needBytes = remainingBytes;
} else {
needBytes = largestFreeBlock - (largestFreeBlock % 4);
}
}
} else {
ret = 0;
}
}
}
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
/**
*
*/
int8_t allocate_pcm_chunk_memory_DRAM(pcm_chunk_message_t *pcmChunk,
size_t bytes) {
size_t largestFreeBlock, freeMem;
int ret = -3;
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT);
largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if ((freeMem >= bytes) && (largestFreeBlock >= bytes)) {
// ESP_LOGI(
// TAG,
// "32b f %d b %d", freeMem,
// largestFreeBlock);
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(bytes, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate IRAM memory for pcm chunk payload");
// free_pcm_chunk (pcmChunk);
ret = -2;
} else {
pcmChunk->totalSize = bytes;
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = bytes;
ret = 0;
}
} else {
// ESP_LOGE (TAG, "couldn't get memory to insert
// chunk of size %d, IRAM freemem: %d blocksize %d",
// decodedWireChunk->size,
// freeMem, largestFreeBlock);
}
return ret;
}
int8_t allocate_pcm_chunk_memory(pcm_chunk_message_t **pcmChunk, size_t bytes) {
size_t largestFreeBlock, freeMem;
int ret = -3;
*pcmChunk = (pcm_chunk_message_t *)calloc(1, sizeof(pcm_chunk_message_t));
if (*pcmChunk == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for pcm chunk message");
return -2;
}
(*pcmChunk)->fragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if ((*pcmChunk)->fragment == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for pcm chunk fragment");
free_pcm_chunk(*pcmChunk);
return -2;
}
#if CONFIG_USE_PSRAM
(*pcmChunk)->fragment->payload =
(char *)heap_caps_malloc(bytes, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if ((*pcmChunk)->fragment->payload == NULL) {
// ESP_LOGE (TAG,
// "Failed to allocate memory for pcm chunk
// fragment payload");
// free_pcm_chunk (pcmChunk);
// freeMem = heap_caps_get_free_size (MALLOC_CAP_8BIT |
// MALLOC_CAP_SPIRAM);
ret = -2;
} else {
(*pcmChunk)->fragment->nextFragment = NULL;
(*pcmChunk)->fragment->size = bytes;
ret = 0;
}
#else
ret = allocate_pcm_chunk_memory_caps(*pcmChunk, bytes,
MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (ret < 0) {
ret = allocate_pcm_chunk_memory_caps(*pcmChunk, bytes, MALLOC_CAP_8BIT);
if (ret < 0) {
// ret = allocate_pcm_chunk_memory_caps_fragmented
//(*pcmChunk, bytes, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (ret < 0) {
// allocate_pcm_chunk_memory_caps_fragmented (*pcmChunk, bytes,
// MALLOC_CAP_8BIT);
}
}
}
// ret = allocate_pcm_chunk_memory_IRAM (*pcmChunk, bytes);
// if (ret < 0) {
// ret = allocate_pcm_chunk_memory_DRAM (*pcmChunk, bytes);
// if (ret < 0) {
// ret = allocate_pcm_chunk_memory_IRAM_fragmented (*pcmChunk, bytes);
// if (ret < 0) {
// //allocate_pcm_chunk_memory_DRAM_fragmented (*pcmChunk,
// bytes);
// }
// }
// }
#endif
if (ret < 0) {
ESP_LOGE(TAG, "couldn't get memory to insert chunk");
ESP_LOGI(
TAG, "%d, %d, %d, %d, %d", heap_caps_get_free_size(MALLOC_CAP_8BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT),
uxQueueMessagesWaiting(pcmChkQHdl),
heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC),
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC));
free_pcm_chunk(*pcmChunk);
} else {
// ESP_LOGI (TAG, "got memory for pcm chunk %p %p %d", *pcmChunk,
// (*pcmChunk)->fragment->payload, bytes);
}
return ret;
}
int8_t insert_pcm_chunk(pcm_chunk_message_t *pcmChunk) {
if (pcmChunk == NULL) {
ESP_LOGE(TAG, "Parameter Error");
return -1;
}
if (pcmChkQHdl == NULL) {
ESP_LOGW(TAG, "pcm chunk queue not created");
return -2;
}
if (xQueueSendToBack(pcmChkQHdl, &pcmChunk, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGW(TAG, "send: pcmChunkQueue full, messages waiting %d",
uxQueueMessagesWaiting(pcmChkQHdl));
free_pcm_chunk(pcmChunk);
}
return 0;
}
/**
*
*/
int8_t insert_pcm_chunk_backup(wire_chunk_message_t *decodedWireChunk) {
pcm_chunk_message_t *pcmChunk;
size_t tmpSize;
size_t largestFreeBlock, freeMem;
int ret = -3;
// heap_caps_get_free_size(MALLOC_CAP_8BIT);
// heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
// heap_caps_get_free_size(MALLOC_CAP_32BIT);
// heap_caps_get_largest_free_block(MALLOC_CAP_32BIT);
if (decodedWireChunk == NULL) {
ESP_LOGE(TAG, "Parameter Error");
return -1;
}
if (pcmChkQHdl == NULL) {
ESP_LOGW(TAG, "pcm chunk queue not created");
return -2;
}
pcmChunk = (pcm_chunk_message_t *)calloc(1, sizeof(pcm_chunk_message_t));
if (pcmChunk == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for pcm chunk message");
return -2;
}
pcmChunk->fragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if (pcmChunk->fragment == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for pcm chunk fragment");
free_pcm_chunk(pcmChunk);
return -2;
}
// store the timestamp
pcmChunk->timestamp = decodedWireChunk->timestamp;
#if CONFIG_USE_PSRAM
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
pcmChunk->fragment->payload = (char *)heap_caps_malloc(
decodedWireChunk->size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for pcm chunk fragment payload");
free_pcm_chunk(pcmChunk);
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
ret = -2;
} else {
// copy the whole payload to our fragment
memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload,
decodedWireChunk->size);
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = decodedWireChunk->size;
ret = 0;
}
#else
// we got valid memory for pcm_chunk_message_t
// first we try to allocated 32 bit aligned memory for payload
// check available memory first so we can decide if we need to fragment the
// data
freeMem = heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
// if (freeMem >= decodedWireChunk->size)
if ((freeMem >= decodedWireChunk->size) &&
(largestFreeBlock >= decodedWireChunk->size)) {
// largestFreeBlock = heap_caps_get_largest_free_block
// (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC); ESP_LOGW(
// TAG, "32b f %d b %d", freeMem, largestFreeBlock);
if (largestFreeBlock >= decodedWireChunk->size) {
pcmChunk->fragment->payload = (char *)heap_caps_malloc(
decodedWireChunk->size, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm chunk fragment payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
// copy the whole payload to our fragment
memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload,
decodedWireChunk->size);
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = decodedWireChunk->size;
ret = 0;
}
} else {
pcm_chunk_fragment_t *next = NULL;
size_t s;
ret = 0;
tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t), decodedWireChunk->size,
// MALLOC_CAP_32BIT);
pcmChunk->fragment->payload = (char *)heap_caps_malloc(
largestFreeBlock, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm chunk "
"fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
next = pcmChunk->fragment;
s = largestFreeBlock;
ret = 0;
// loop until we have all data stored to a fragment
do {
// copy the whole payload to our fragment
memcpy(next->payload, decodedWireChunk->payload, s);
next->size = s;
tmpSize -= s;
decodedWireChunk->payload += s;
// ESP_LOGI (TAG,"%p %d", next->payload,
// next->size);
if (tmpSize > 0) {
next->nextFragment =
(pcm_chunk_fragment_t *)calloc(1, sizeof(pcm_chunk_fragment_t));
if (next->nextFragment == NULL) {
ESP_LOGE(
TAG,
"Failed to allocate memory for next pcm "
"chunk fragment %d %d",
heap_caps_get_free_size(MALLOC_CAP_32BIT | MALLOC_CAP_EXEC),
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT |
MALLOC_CAP_EXEC));
free_pcm_chunk(pcmChunk);
ret = -3;
break;
} else {
largestFreeBlock = heap_caps_get_largest_free_block(
MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (largestFreeBlock <= tmpSize) {
s = largestFreeBlock;
} else {
s = tmpSize;
}
next->nextFragment->payload = (char *)heap_caps_malloc(
s, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC);
if (next->nextFragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm "
"chunk next fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -3;
break;
} else {
next = next->nextFragment;
}
}
}
} while (tmpSize);
}
}
// ret = 0;
} else {
// we got valid memory for pcm_chunk_message_t
// no 32 bit aligned memory available, try to allocate 8 bit aligned
// memory for payload check available memory first so we can decide if we
// need to fragment the data
freeMem = heap_caps_get_free_size(MALLOC_CAP_8BIT);
largestFreeBlock = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
// if (freeMem >= decodedWireChunk->size)
if ((freeMem >= decodedWireChunk->size) &&
(largestFreeBlock >= decodedWireChunk->size)) {
// largestFreeBlock
// = heap_caps_get_largest_free_block (MALLOC_CAP_8BIT);
// ESP_LOGW(
// TAG,
// "8b f %d b %d", freeMem,
// largestFreeBlock);
if (largestFreeBlock >= decodedWireChunk->size) {
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(decodedWireChunk->size, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm chunk "
"fragment payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
// copy the whole payload to our fragment
memcpy(pcmChunk->fragment->payload, decodedWireChunk->payload,
decodedWireChunk->size);
pcmChunk->fragment->nextFragment = NULL;
pcmChunk->fragment->size = decodedWireChunk->size;
ret = 0;
}
} else {
pcm_chunk_fragment_t *next = NULL;
size_t s;
ret = 0;
tmpSize = decodedWireChunk->size;
// heap_caps_aligned_alloc(sizeof(uint32_t),
// decodedWireChunk->size, MALLOC_CAP_32BIT);
pcmChunk->fragment->payload =
(char *)heap_caps_malloc(largestFreeBlock, MALLOC_CAP_8BIT);
if (pcmChunk->fragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm chunk "
"fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -2;
} else {
next = pcmChunk->fragment;
s = largestFreeBlock;
ret = 0;
// loop until we have all data stored to a fragment
do {
// copy the whole payload to our fragment
memcpy(next->payload, decodedWireChunk->payload, s);
next->size = s;
tmpSize -= s;
decodedWireChunk->payload += s;
if (tmpSize > 0) {
next->nextFragment = (pcm_chunk_fragment_t *)calloc(
1, sizeof(pcm_chunk_fragment_t));
if (next->nextFragment == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for next pcm "
"chunk fragment %d %d",
heap_caps_get_free_size(MALLOC_CAP_8BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
free_pcm_chunk(pcmChunk);
ret = -3;
break;
} else {
largestFreeBlock =
heap_caps_get_largest_free_block(MALLOC_CAP_8BIT);
if (largestFreeBlock <= tmpSize) {
s = largestFreeBlock;
} else {
s = tmpSize;
}
next->nextFragment->payload =
(char *)heap_caps_malloc(s, MALLOC_CAP_8BIT);
if (next->nextFragment->payload == NULL) {
ESP_LOGE(TAG,
"Failed to allocate memory for pcm "
"chunk next fragmented payload");
free_pcm_chunk(pcmChunk);
ret = -3;
break;
} else {
next = next->nextFragment;
}
}
}
} while (tmpSize);
}
}
// ret = 0;
}
}
#endif
if (ret == 0) {
if (xQueueSendToBack(pcmChkQHdl, &pcmChunk, pdMS_TO_TICKS(1000)) !=
pdTRUE) {
ESP_LOGW(TAG, "send: pcmChunkQueue full, messages waiting %d",
uxQueueMessagesWaiting(pcmChkQHdl));
free_pcm_chunk(pcmChunk);
}
} else {
ESP_LOGE(TAG,
"couldn't get memory to insert fragmented chunk of size %d, "
"freemem: %d blocksize %d",
decodedWireChunk->size, freeMem, largestFreeBlock);
}
return ret;
}
/**
*
*/
static void player_task(void *pvParameters) {
pcm_chunk_message_t *chnk = NULL;
int64_t serverNow = 0;
int64_t age;
BaseType_t ret;
int64_t chkDur_us;
char *p_payload = NULL;
size_t size = 0;
uint32_t notifiedValue;
snapcastSetting_t scSet;
uint8_t scSetChgd = 0;
uint64_t timer_val;
const int32_t alarmValSub = 0;
int initialSync = 0;
int64_t avg = 0;
int dir = 0;
int64_t buf_us = 0;
pcm_chunk_fragment_t *fragment = NULL;
size_t written;
bool gotSnapserverConfig = false;
int64_t clientDacLatency_us = 0;
uint8_t sendResyncMSg = 0;
ESP_LOGI(TAG, "started sync task");
// stats_init();
// create message queue to inform task of changed settings
snapcastSettingQueueHandle = xQueueCreate(1, sizeof(uint8_t));
initialSync = 0;
shortMedianFilter.numNodes = SHORT_BUFFER_LEN;
shortMedianFilter.medianBuffer = shortMedianBuffer;
if (MEDIANFILTER_Init(&shortMedianFilter)) {
ESP_LOGE(TAG, "snapcast_sync_task: couldn't init shortMedianFilter. STOP");
return;
}
while (1) {
// ESP_LOGW(
// TAG,
// "32b f %d b %d", heap_caps_get_free_size
//(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block (MALLOC_CAP_8BIT));
// ESP_LOGW (TAG, "stack free: %d",
// uxTaskGetStackHighWaterMark(NULL));
// check if we got changed setting available, if so we need to
// reinitialize
ret = xQueueReceive(snapcastSettingQueueHandle, &scSetChgd, 0);
if (ret == pdTRUE) {
player_get_snapcast_settings(&scSet);
buf_us = (int64_t)(scSet.buf_ms) * 1000LL;
chkDur_us = (int64_t)(scSet.chkDur_ms) * 1000LL;
chkInBytes =
(scSet.chkDur_ms * scSet.sr * scSet.ch * (scSet.bits / 8)) / 1000;
clientDacLatency_us = (int64_t)scSet.cDacLat_ms * 1000LL;
if ((scSet.sr > 0) && (scSet.bits) > 0 && (scSet.ch > 0)) {
i2s_custom_stop(I2S_NUM_0);
ret = player_setup_i2s(I2S_NUM_0, &currentSnapcastSetting);
if (ret < 0) {
ESP_LOGE(TAG, "player_setup_i2s failed: %d", ret);
return;
}
// force adjust_apll() to set playback speed
currentDir = 1;
adjust_apll(0);
i2s_custom_set_clk(I2S_NUM_0, scSet.sr, scSet.bits, scSet.ch);
initialSync = 0;
}
if ((scSet.buf_ms > 0) && (scSet.chkDur_ms > 0)) {
// create snapcast receive buffer
if (pcmChkQHdl != NULL) {
destroy_pcm_queue(&pcmChkQHdl);
}
// round up
int entries = ((float)scSet.buf_ms / (float)scSet.chkDur_ms) + 0.5;
pcmChkQHdl = xQueueCreate(entries, sizeof(pcm_chunk_message_t *));
ESP_LOGI(TAG, "created new queue with %d", entries);
}
ESP_LOGI(TAG,
"snapserver config changed, buffer %dms, chunk %dms, "
"sample rate %d, ch %d, bits %d mute %d latency %d",
scSet.buf_ms, scSet.chkDur_ms, scSet.sr, scSet.ch, scSet.bits,
scSet.muted, scSet.cDacLat_ms);
gotSnapserverConfig = true;
} else if (gotSnapserverConfig == false) {
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
if (chnk == NULL) {
if (pcmChkQHdl != NULL) {
ret = xQueueReceive(pcmChkQHdl, &chnk, pdMS_TO_TICKS(2000));
} else {
// ESP_LOGE (TAG,
// "Couldn't get PCM chunk, pcm queue not
// created");
vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
if (ret != pdFAIL) {
// ESP_LOGW(TAG, "got pcm chunk");
}
} else {
// ESP_LOGW(TAG, "already retrieved chunk needs
// service");
ret = pdPASS;
}
if (ret != pdFAIL) {
if (server_now(&serverNow) >= 0) {
age = serverNow -
((int64_t)chnk->timestamp.sec * 1000000LL +
(int64_t)chnk->timestamp.usec) -
(int64_t)buf_us + (int64_t)clientDacLatency_us;
} else {
// ESP_LOGW(TAG, "couldn't get server now");
if (chnk != NULL) {
free_pcm_chunk(chnk);
chnk = NULL;
}
vTaskDelay(pdMS_TO_TICKS(1));
continue;
}
// 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) {
if (chnk != NULL) {
free_pcm_chunk(chnk);
chnk = NULL;
}
}
// ESP_LOGW(TAG, "diff buffer not full");
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
if (age < 0) { // get initial sync using hardware timer
if (initialSync == 0) {
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 * 10) - alarmValSub));
// // alarm a
// little earlier to account for context switch duration from
// freeRTOS, timer with 100ns ticks
i2s_custom_stop(I2S_NUM_0);
if (MEDIANFILTER_Init(&shortMedianFilter)) {
ESP_LOGE(TAG,
"snapcast_sync_task: couldn't init "
"shortMedianFilter. STOP");
return;
}
adjust_apll(0); // reset to normal playback speed
fragment = chnk->fragment;
p_payload = fragment->payload;
size = fragment->size;
i2s_custom_init_dma_tx_queues(I2S_NUM_0, (uint8_t *)p_payload, size,
&written);
size -= written;
p_payload += written;
// ESP_LOGE(TAG, "wrote %d", written);
if (size == 0) {
if (fragment->nextFragment != NULL) {
fragment = fragment->nextFragment;
p_payload = fragment->payload;
size = fragment->size;
} else {
free_pcm_chunk(chnk);
chnk = NULL;
}
}
// TCP_STATS_DISPLAY();
// IP_STATS_DISPLAY();
// MEM_STATS_DISPLAY();
// LINK_STATS_DISPLAY();
// Wait to be notified of a timer interrupt.
xTaskNotifyWait(pdFALSE, // Don't clear bits on entry.
pdFALSE, // Don't clear bits on exit.
&notifiedValue, // Stores the notified value.
portMAX_DELAY);
// or use simple task delay for this
// vTaskDelay( pdMS_TO_TICKS(-age / 1000) );
i2s_custom_start(I2S_NUM_0);
// 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
// check if we need to write remaining data
if (size != 0) {
do {
written = 0;
if (i2s_custom_write(I2S_NUM_0, p_payload, (size_t)size, &written,
portMAX_DELAY) != ESP_OK) {
ESP_LOGE(TAG, "i2s_playback_task: I2S write error");
}
if (written < size) {
ESP_LOGE(TAG,
"i2s_playback_task: I2S didn't "
"write all data");
}
size -= written;
p_payload += written;
if (size == 0) {
if (fragment->nextFragment != NULL) {
fragment = fragment->nextFragment;
p_payload = fragment->payload;
size = fragment->size;
} else {
free_pcm_chunk(chnk);
chnk = NULL;
break;
}
}
} while (1);
}
initialSync = 1;
sendResyncMSg = 1;
ESP_LOGI(TAG, "initial sync %lldus", age);
continue;
}
} else if ((age > 0) && (initialSync == 0)) {
if (chnk != NULL) {
free_pcm_chunk(chnk);
chnk = NULL;
}
int64_t t;
get_diff_to_server(&t);
wifi_ap_record_t ap;
esp_wifi_sta_get_ap_info(&ap);
// if (sendResyncMSg == 1)
{
sendResyncMSg = 0;
ESP_LOGW(TAG,
"RESYNCING HARD 1: age %lldus, latency %lldus, free %d, "
"largest block %d, %d, rssi: %d",
age, t, heap_caps_get_free_size(MALLOC_CAP_32BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT),
uxQueueMessagesWaiting(pcmChkQHdl), ap.rssi);
}
dir = 0;
initialSync = 0;
continue;
}
const uint8_t enableControlLoop = 1;
const int64_t age_expect =
-chkDur_us *
CHNK_CTRL_CNT; // this value is highly coupled with I2S DMA buffer
// size. DMA buffer has a size of 1 chunk (e.g. 20ms)
// so next chunk we get from queue will be -20ms
const int64_t maxOffset = 25; //µs, softsync 1
const int64_t hardResyncThreshold = 10000; //µs, hard sync
if (initialSync == 1) {
avg = MEDIANFILTER_Insert(&shortMedianFilter, age + (-age_expect));
if (MEDIANFILTER_isFull(&shortMedianFilter) == 0) {
avg = age + (-age_expect);
} else {
// resync hard if we are off too far
// if ((avg < -hardResyncThreshold)
// || (avg > hardResyncThreshold) ||
// (initialSync == 0))
if ((initialSync == 0) || (uxQueueMessagesWaiting(pcmChkQHdl) ==
0)) // only resync if we are getting late.
// hopefully being early will get ok
// through apll speed control
// if ((avg > hardResyncThreshold) || (initialSync == 0))
// // only resync if we are getting late. hopefully being
// early will get ok through apll speed control
{
if (chnk != NULL) {
free_pcm_chunk(chnk);
chnk = NULL;
}
int64_t t;
get_diff_to_server(&t);
wifi_ap_record_t ap;
esp_wifi_sta_get_ap_info(&ap);
ESP_LOGW(TAG,
"RESYNCING HARD 2: age %lldus, latency %lldus, free "
"%d, largest block %d, %d, rssi: %d",
avg, t, heap_caps_get_free_size(MALLOC_CAP_32BIT),
heap_caps_get_largest_free_block(MALLOC_CAP_32BIT),
uxQueueMessagesWaiting(pcmChkQHdl), ap.rssi);
initialSync = 0;
continue;
}
}
if (enableControlLoop == 1) {
if (avg < -maxOffset) { // we are early
dir = -1;
} else if ((avg >= -maxOffset) && (avg <= maxOffset)) {
dir = 0;
} else if (avg > maxOffset) { // we are late
dir = 1;
}
adjust_apll(dir);
}
// clang-format off
// int64_t t;
// get_diff_to_server (&t);
//
// struct timeval now;
// // get current time
// if (gettimeofday (&now, NULL))
// {
// ESP_LOGE (TAG, "Failed to get time of day");
// }
//
// // for getting rssi value
// wifi_ap_record_t ap;
// esp_wifi_sta_get_ap_info(&ap);
//
// ESP_LOGI (TAG, "%ld.%ld, rssi: %d, %d, %lldus, %lldus %lldus,%d, %d, %d, %d, %d", now.tv_sec, now.tv_usec,
// ap.rssi,
// dir, age, avg, t,
// heap_caps_get_free_size
// (MALLOC_CAP_8BIT),
// heap_caps_get_largest_free_block
// (MALLOC_CAP_8BIT),
// uxQueueMessagesWaiting
// (pcmChkQHdl),
// heap_caps_get_free_size
// (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC),
// heap_caps_get_largest_free_block
// (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC));
// ESP_LOGI (TAG, "%d, %lldus ,%d, %d, %d, %d, %d",
// dir, avg,
// heap_caps_get_free_size
// (MALLOC_CAP_8BIT),
// heap_caps_get_largest_free_block
// (MALLOC_CAP_8BIT),
// uxQueueMessagesWaiting
// (pcmChkQHdl),
// heap_caps_get_free_size
// (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC),
// heap_caps_get_largest_free_block
// (MALLOC_CAP_32BIT | MALLOC_CAP_EXEC));
// ESP_LOGI (TAG, "%d, %lldus, %lldus %lldus", dir, age, avg, t);
// ESP_LOGI (TAG, "%d %lldus, %d", dir, avg, uxQueueMessagesWaiting(pcmChkQHdl));
// clang-format on
fragment = chnk->fragment;
p_payload = fragment->payload;
size = fragment->size;
do {
written = 0;
if (i2s_custom_write(I2S_NUM_0, p_payload, (size_t)size, &written,
portMAX_DELAY) != ESP_OK) {
ESP_LOGE(TAG, "i2s_playback_task: I2S write error %d", size);
}
if (written < size) {
ESP_LOGE(TAG, "i2s_playback_task: I2S didn't write all data");
}
size -= written;
p_payload += written;
if (size == 0) {
if (fragment->nextFragment != NULL) {
fragment = fragment->nextFragment;
p_payload = fragment->payload;
size = fragment->size;
// ESP_LOGI (TAG,
// "i2s_playback_task:
// fragmented");
} else {
free_pcm_chunk(chnk);
chnk = NULL;
break;
}
}
} while (1);
}
} else {
int64_t t;
get_diff_to_server(&t);
if (pcmChkQHdl != NULL) {
ESP_LOGE(TAG,
"Couldn't get PCM chunk, recv: messages waiting %d, "
"latency %lldus",
uxQueueMessagesWaiting(pcmChkQHdl), t);
}
dir = 0;
initialSync = 0;
}
}
}