testing, debugging

Signed-off-by: Karl Osterseher <karli_o@gmx.at>
This commit is contained in:
Karl Osterseher
2024-04-26 09:07:41 +02:00
Unverified
parent bddcaa6017
commit 76fee88d38
8 changed files with 995 additions and 936 deletions

1602
.project

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,7 @@ struct pcm_chunk_fragment {
typedef struct pcmData {
tv_t timestamp;
uint32_t totalSize;
size_t totalSize;
pcm_chunk_fragment_t *fragment;
} pcm_chunk_message_t;

View File

@@ -5,8 +5,7 @@
#include <stddef.h>
#include <stdint.h>
enum message_type
{
enum message_type {
SNAPCAST_MESSAGE_BASE = 0,
SNAPCAST_MESSAGE_CODEC_HEADER = 1,
SNAPCAST_MESSAGE_WIRE_CHUNK = 2,
@@ -16,17 +15,16 @@ enum message_type
SNAPCAST_MESSAGE_STREAM_TAGS = 6,
SNAPCAST_MESSAGE_FIRST = SNAPCAST_MESSAGE_BASE,
SNAPCAST_MESSAGE_LAST = SNAPCAST_MESSAGE_STREAM_TAGS
SNAPCAST_MESSAGE_LAST = SNAPCAST_MESSAGE_STREAM_TAGS,
SNAPCAST_MESSAGE_INVALID
};
typedef struct tv
{
typedef struct tv {
int32_t sec;
int32_t usec;
} tv_t;
typedef struct base_message
{
typedef struct base_message {
uint16_t type;
uint16_t id;
uint16_t refersTo;
@@ -38,10 +36,10 @@ typedef struct base_message
#define BASE_MESSAGE_SIZE 26
#define TIME_MESSAGE_SIZE 8
int base_message_serialize (base_message_t *msg, char *data, uint32_t size);
int base_message_serialize(base_message_t *msg, char *data, uint32_t size);
int base_message_deserialize (base_message_t *msg, const char *data,
uint32_t size);
int base_message_deserialize(base_message_t *msg, const char *data,
uint32_t size);
/* Sample Hello message
{
@@ -57,8 +55,7 @@ int base_message_deserialize (base_message_t *msg, const char *data,
}
*/
typedef struct hello_message
{
typedef struct hello_message {
char *mac;
char *hostname;
char *version;
@@ -70,49 +67,45 @@ typedef struct hello_message
int protocol_version;
} hello_message_t;
char *hello_message_serialize (hello_message_t *msg, size_t *size);
char *hello_message_serialize(hello_message_t *msg, size_t *size);
typedef struct server_settings_message
{
typedef struct server_settings_message {
int32_t buffer_ms;
int32_t latency;
uint32_t volume;
bool muted;
} server_settings_message_t;
int server_settings_message_deserialize (server_settings_message_t *msg,
const char *json_str);
int server_settings_message_deserialize(server_settings_message_t *msg,
const char *json_str);
typedef struct codec_header_message
{
typedef struct codec_header_message {
char *codec;
uint32_t size;
char *payload;
} codec_header_message_t;
int codec_header_message_deserialize (codec_header_message_t *msg,
const char *data, uint32_t size);
void codec_header_message_free (codec_header_message_t *msg);
int codec_header_message_deserialize(codec_header_message_t *msg,
const char *data, uint32_t size);
void codec_header_message_free(codec_header_message_t *msg);
typedef struct wire_chunk_message
{
typedef struct wire_chunk_message {
tv_t timestamp;
size_t size;
char *payload;
} wire_chunk_message_t;
// TODO currently copies, could be made to not copy probably
int wire_chunk_message_deserialize (wire_chunk_message_t *msg,
const char *data, uint32_t size);
void wire_chunk_message_free (wire_chunk_message_t *msg);
int wire_chunk_message_deserialize(wire_chunk_message_t *msg, const char *data,
uint32_t size);
void wire_chunk_message_free(wire_chunk_message_t *msg);
typedef struct time_message
{
typedef struct time_message {
tv_t latency;
} time_message_t;
int time_message_serialize (time_message_t *msg, char *data, uint32_t size);
int time_message_deserialize (time_message_t *msg, const char *data,
uint32_t size);
int time_message_serialize(time_message_t *msg, char *data, uint32_t size);
int time_message_deserialize(time_message_t *msg, const char *data,
uint32_t size);
#endif // __SNAPCAST_H__
#endif // __SNAPCAST_H__

View File

@@ -230,8 +230,6 @@ static esp_err_t player_setup_i2s(i2s_port_t i2sNum,
my_i2s_channel_disable(tx_chan);
i2s_del_channel(tx_chan);
tx_chan = NULL;
// periph_rtc_apll_release();
}
i2s_chan_config_t tx_chan_cfg = {
@@ -257,9 +255,7 @@ static esp_err_t player_setup_i2s(i2s_port_t i2sNum,
I2S_SLOT_MODE_STEREO),
.gpio_cfg =
{
.mclk = pin_config0
.mck_io_num, // some codecs may require mclk signal,
// this example doesn't need it
.mclk = pin_config0.mck_io_num,
.bclk = pin_config0.bck_io_num,
.ws = pin_config0.ws_io_num,
.dout = pin_config0.data_out_num,
@@ -1186,6 +1182,7 @@ static void player_task(void *pvParameters) {
int64_t clientDacLatency_us = 0;
int64_t diff2Server;
int64_t outputBufferDacTime = 0;
static int64_t lastChunkStart = 0;
memset(&scSet, 0, sizeof(snapcastSetting_t));
@@ -1218,14 +1215,16 @@ static void player_task(void *pvParameters) {
buf_us = (int64_t)(__scSet.buf_ms) * 1000LL;
chkDur_us =
(int64_t)__scSet.chkInFrames * (int64_t)1E6 / (int64_t)__scSet.sr;
(int64_t)__scSet.chkInFrames * 1000000LL / (int64_t)__scSet.sr;
// 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
outputBufferDacTime = chkDur_us * CHNK_CTRL_CNT;
clientDacLatency_us = (int64_t)__scSet.cDacLat_ms * 1000;
clientDacLatency_us = (int64_t)__scSet.cDacLat_ms * 1000LL;
// ESP_LOGE(TAG, "### outputBufferDacTime: %lld", outputBufferDacTime);
if ((scSet.sr != __scSet.sr) || (scSet.bits != __scSet.bits) ||
(scSet.ch != __scSet.ch)) {
@@ -1313,7 +1312,14 @@ static void player_task(void *pvParameters) {
}
if (ret != pdFAIL) {
// ESP_LOGW(TAG, "got pcm chunk");
// chkDur_us = (int64_t)scSet.chkInFrames * (int64_t)1E6 /
// (int64_t)scSet.sr;
// chkDur_us = (int64_t)(chnk->fragment->size / (scSet.bits / 8)
// / scSet.ch) * (int64_t)1000000LL / (int64_t)scSet.sr;
// outputBufferDacTime = chkDur_us * CHNK_CTRL_CNT;
// ESP_LOGI(TAG, "got pcm chunk with size %d",
// chnk->fragment->size);
}
} else {
// ESP_LOGW(TAG, "already retrieved chunk needs service");
@@ -1326,7 +1332,7 @@ static void player_task(void *pvParameters) {
(int64_t)chnk->timestamp.usec;
age = serverNow - chunkStart - buf_us + clientDacLatency_us;
#if USE_BIG_DMA_BUFFER
#if USE_BIG_DMA_BUFFER && USE_SAMPLE_INSERTION
savedAge = age;
if (insertedSamplesCounter > 0) {
age -= (((1 + insertedSamplesCounter / i2sDmaBufMaxLen) * chkDur_us /
@@ -1339,13 +1345,30 @@ static void player_task(void *pvParameters) {
}
#endif
// ESP_LOGE(TAG,"age: %lld, serverNow %lld, chunkStart %lld,
// buf_us %lld", age, serverNow, chunkStart, buf_us);
// ESP_LOGE(TAG, "next chunk scheduled in %lld, (%lld, %lld), age %lld",
// chunkStart - lastChunkStart, chunkStart, lastChunkStart, age);
// ESP_LOGE(TAG, "%lld, %d", chunkStart,
// chnk->fragment->size);
lastChunkStart = chunkStart;
// ESP_LOGE(TAG, "age: %lld, serverNow %lld, chunkStart %lld, "
// "buf_us %lld", age, serverNow,
// chunkStart, buf_us);
if (initialSync == 1) {
// on initialSync == 0 (hard sync) we don't have any data in i2s DMA
// buffer so in that case we don't need to add this
age += outputBufferDacTime;
if (chnk->fragment->size > (size_t)4608UL) {
// age += chkDur_us;
// ESP_LOGE(TAG, "age 1 %lld", age);
} else {
// ESP_LOGE(TAG, "age 2 %lld", age);
}
// ESP_LOGE(TAG, "age %lld", age);
}
} else {
// ESP_LOGW(TAG, "couldn't get server now");
@@ -1362,6 +1385,8 @@ static void player_task(void *pvParameters) {
if (age < 0) { // get initial sync using hardware timer
if (initialSync == 0) {
bool dmaFull = false;
MEDIANFILTER_Init(&shortMedianFilter);
MEDIANFILTER_Init(&miniMedianFilter);
@@ -1377,6 +1402,26 @@ static void player_task(void *pvParameters) {
if (chnk == NULL) {
if (pcmChkQHdl != NULL) {
ret = xQueueReceive(pcmChkQHdl, &chnk, portMAX_DELAY);
if (ret != pdFAIL) {
ESP_LOGI(TAG, "got pcm chunk with size %d",
chnk->fragment->size);
int64_t chunkStart =
(int64_t)chnk->timestamp.sec * 1000000LL +
(int64_t)chnk->timestamp.usec;
server_now(&serverNow, &diff2Server);
int64_t tmpAge =
serverNow - chunkStart - buf_us + clientDacLatency_us;
// ESP_LOGE(TAG, "next chunk scheduled in %lld, (%lld, %lld),
// age %lld", chunkStart - lastChunkStart, chunkStart,
// lastChunkStart, tmpAge);
// ESP_LOGE(TAG, "chunk scheduled in %lld",
// tmpAge);
lastChunkStart = chunkStart;
}
}
}
@@ -1386,9 +1431,13 @@ static void player_task(void *pvParameters) {
ESP_ERROR_CHECK(
i2s_channel_preload_data(tx_chan, p_payload, size, &written));
// ESP_LOGE(TAG, "preload %d bytes", written);
ESP_LOGE(TAG, "preload %d bytes", written);
// TODO: do error check if DMA is full here
// check if DMA is full at first try here
if (fragment->size >= (CHNK_CTRL_CNT * scSet.chkInFrames *
(scSet.bits / 8) * scSet.ch)) {
dmaFull = true;
}
size -= written;
p_payload += written;
@@ -1401,9 +1450,15 @@ static void player_task(void *pvParameters) {
} else {
free_pcm_chunk(chnk);
chnk = NULL;
ESP_LOGE(TAG, "free");
}
}
if (dmaFull == true) {
break;
}
tmpCnt--;
}
@@ -1434,7 +1489,9 @@ static void player_task(void *pvParameters) {
ESP_LOGI(TAG, "initial sync age: %lldus, chunk duration: %lldus", age,
chkDur_us);
continue;
if (size == 0) {
continue;
}
}
} else if ((age > 0) && (initialSync == 0)) {
if (chnk != NULL) {
@@ -1497,9 +1554,10 @@ static void player_task(void *pvParameters) {
int msgWaiting = uxQueueMessagesWaiting(pcmChkQHdl);
// resync hard if we are getting very late / early.
// rest gets tuned in through apll speed control
// rest gets tuned in through apll speed control or sample insertion
if ((msgWaiting == 0) || (MEDIANFILTER_isFull(&shortMedianFilter, 0) &&
(abs(shortMedian) > hardResyncThreshold))) {
((shortMedian > hardResyncThreshold) ||
(shortMedian < -hardResyncThreshold)))) {
if (chnk != NULL) {
free_pcm_chunk(chnk);
chnk = NULL;
@@ -1570,11 +1628,11 @@ static void player_task(void *pvParameters) {
msec = usec / 1000;
usec = usec % 1000;
ESP_LOGI(TAG, "%d, %lldus, q %d", dir, avg,
uxQueueMessagesWaiting(pcmChkQHdl));
// ESP_LOGI(TAG, "%d, %lldus, q %d", dir, avg,
// uxQueueMessagesWaiting(pcmChkQHdl));
// ESP_LOGI (TAG, "%d, %lldus, %lldus %llds,
//%lld.%lldms", dir, age, avg, sec, msec, usec);
ESP_LOGI(TAG, "%d, %lldus, %lldus %llds, %lld.%lldms", dir, age, avg,
sec, msec, usec);
// ESP_LOGI(TAG, "%d, %lldus, %lldus, %lldus, q:%d", dir, avg,
// shortMedian, miniMedian,
@@ -1592,9 +1650,13 @@ static void player_task(void *pvParameters) {
dir = 0;
fragment = chnk->fragment;
p_payload = fragment->payload;
size = fragment->size;
if (size == 0) {
fragment = chnk->fragment;
p_payload = fragment->payload;
size = fragment->size;
} else {
// ESP_LOGI(TAG, "remaining");
}
if (p_payload != NULL) {
do {
@@ -1619,11 +1681,14 @@ static void player_task(void *pvParameters) {
if (i2s_channel_write(tx_chan, p_payload, size, &written,
portMAX_DELAY) != ESP_OK) {
ESP_LOGE(TAG, "i2s_playback_task: I2S write error %d", size);
} else {
// ESP_LOGI(TAG, "wrote %d", written);
}
if (written < size) {
ESP_LOGE(TAG, "i2s_playback_task: I2S didn't write all data");
}
size -= written;
p_payload += written;
@@ -1638,6 +1703,7 @@ static void player_task(void *pvParameters) {
free_pcm_chunk(chnk);
chnk = NULL;
dir = 0;
break;
}
}

View File

@@ -1,21 +1,21 @@
dependencies:
espressif/esp-dsp:
component_hash: 46498b4973097cac29cfc6a692abef44375ffb7b00b73b3f354124efc626a154
component_hash: 3e7bbd487f1357a1d4944d0c85966d049501ea281b8a4c7f93f7cfedd5b7f23d
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.4.10
version: 1.4.12
espressif/mdns:
component_hash: 090826401d30f756037f91d91068f470ba8d0ef04fe507d3aa8761305cecd7ca
component_hash: ed10ef031bce505e423c5dbf0fdf2ce7a02df5f9ebffb18df2d6b9852e48817d
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.2.3
version: 1.3.1
idf:
component_hash: null
source:
type: idf
version: 5.1.1
manifest_hash: 9ac14bd8f3a9a37aeee3b53f52afcfb139289cef9e76f4efe8ccbd08a5022f20
manifest_hash: d6b434b4ae9e215619e0dfb1b731739d5799f7570a1fdee428100c9146c752e2
target: esp32
version: 1.0.0

View File

@@ -82,8 +82,8 @@ SemaphoreHandle_t decoderWriteSemaphore = NULL;
const char *VERSION_STRING = "0.0.2";
#define HTTP_TASK_PRIORITY (configMAX_PRIORITIES - 2) // 9
#define HTTP_TASK_CORE_ID 1 // 1 // tskNO_AFFINITY
#define HTTP_TASK_PRIORITY 9
#define HTTP_TASK_CORE_ID tskNO_AFFINITY // 1 // tskNO_AFFINITY
#define OTA_TASK_PRIORITY 6
#define OTA_TASK_CORE_ID tskNO_AFFINITY
@@ -1760,14 +1760,15 @@ static void http_get_task(void *pvParameters) {
int32_t ret = allocate_pcm_chunk_memory(
&new_pcmChunk, pcmChunk.bytes);
// ESP_LOGE
//(TAG, "block size: %ld", pcmChunk.bytes /
//(scSet.ch * (scSet.bits/8)));
scSet.chkInFrames =
FLAC__stream_decoder_get_blocksize(
flacDecoder);
// ESP_LOGI (TAG, "mem %p %p %d",
// flacData->outData,
// flacData->outData->fragment->payload,
// flacData->bytes);
// ESP_LOGE (TAG, "block size: %ld",
// scSet.chkInFrames * scSet.bits / 8 * scSet.ch);
// ESP_LOGI
//(TAG, "new_pcmChunk with size %ld",
// new_pcmChunk->totalSize);
if (ret == 0) {
pcm_chunk_fragment_t *fragment =
@@ -1775,10 +1776,11 @@ static void http_get_task(void *pvParameters) {
uint32_t fragmentCnt = 0;
if (fragment->payload != NULL) {
for (int i = 0;
i < pcmChunk.bytes /
(scSet.ch * (scSet.bits / 8));
i++) {
uint32_t frames =
pcmChunk.bytes /
(scSet.ch * (scSet.bits / 8));
for (int i = 0; i < frames; i++) {
// TODO: for now fragmented payload is not
// supported and the whole chunk is expected
// to be in the first fragment
@@ -1814,8 +1816,39 @@ static void http_get_task(void *pvParameters) {
}
}
// static
// uint64_t
// oldTimestamp =
// 0;
//
// ESP_LOGW(TAG,
// "time step
// %lld",
// (uint64_t)wire_chnk.timestamp.sec
// * 1000000UL +
// (uint64_t)wire_chnk.timestamp.usec
// -
// oldTimestamp);
new_pcmChunk->timestamp = wire_chnk.timestamp;
// int64_t
// chunkStart =
// (int64_t)new_pcmChunk->timestamp.sec
// * 1000000LL +
// (int64_t)new_pcmChunk->timestamp.usec;
// ESP_LOGE(TAG,
// "%lld, %d",
// chunkStart,
// new_pcmChunk->fragment->size);
// oldTimestamp
// =
// (uint64_t)wire_chnk.timestamp.sec
// * 1000000ULL
// +
// (uint64_t)wire_chnk.timestamp.usec;
#if CONFIG_USE_DSP_PROCESSOR
if (new_pcmChunk.fragment->payload) {
dsp_processor_worker(
@@ -2127,7 +2160,7 @@ static void http_get_task(void *pvParameters) {
currentPos += typedMsgLen;
len -= typedMsgLen;
} else {
memcpy(&p_tmp[offset], start, typedMsgLen);
memcpy(&p_tmp[offset], start, len);
offset += len;
@@ -2247,8 +2280,10 @@ static void http_get_task(void *pvParameters) {
return;
}
free(p_tmp);
p_tmp = NULL;
if (p_tmp) {
free(p_tmp);
p_tmp = NULL;
}
// ESP_LOGI(TAG, "done codec header msg");

View File

@@ -1647,23 +1647,6 @@ CONFIG_WEBSOCKET_SERVER_TASK_PRIORITY=5
# CONFIG_WEBSOCKET_SERVER_PINNED is not set
# end of WebSocket Server
#
# DSP Library
#
CONFIG_DSP_OPTIMIZATIONS_SUPPORTED=y
# CONFIG_DSP_ANSI is not set
CONFIG_DSP_OPTIMIZED=y
CONFIG_DSP_OPTIMIZATION=1
# CONFIG_DSP_MAX_FFT_SIZE_512 is not set
# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set
# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set
CONFIG_DSP_MAX_FFT_SIZE_4096=y
# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set
# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set
# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set
CONFIG_DSP_MAX_FFT_SIZE=4096
# end of DSP Library
#
# mDNS
#
@@ -1681,6 +1664,7 @@ CONFIG_MDNS_TIMER_PERIOD_MS=100
# CONFIG_MDNS_NETWORKING_SOCKET is not set
# CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES is not set
# CONFIG_MDNS_ENABLE_DEBUG_PRINTS is not set
CONFIG_MDNS_ENABLE_CONSOLE_CLI=y
# CONFIG_MDNS_RESPOND_REVERSE_QUERIES is not set
CONFIG_MDNS_MULTIPLE_INSTANCE=y
@@ -1692,6 +1676,23 @@ CONFIG_MDNS_PREDEF_NETIF_AP=y
CONFIG_MDNS_PREDEF_NETIF_ETH=y
# end of MDNS Predefined interfaces
# end of mDNS
#
# DSP Library
#
CONFIG_DSP_OPTIMIZATIONS_SUPPORTED=y
# CONFIG_DSP_ANSI is not set
CONFIG_DSP_OPTIMIZED=y
CONFIG_DSP_OPTIMIZATION=1
# CONFIG_DSP_MAX_FFT_SIZE_512 is not set
# CONFIG_DSP_MAX_FFT_SIZE_1024 is not set
# CONFIG_DSP_MAX_FFT_SIZE_2048 is not set
CONFIG_DSP_MAX_FFT_SIZE_4096=y
# CONFIG_DSP_MAX_FFT_SIZE_8192 is not set
# CONFIG_DSP_MAX_FFT_SIZE_16384 is not set
# CONFIG_DSP_MAX_FFT_SIZE_32768 is not set
CONFIG_DSP_MAX_FFT_SIZE=4096
# end of DSP Library
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set

View File

@@ -352,14 +352,14 @@ CONFIG_SNAPCLIENT_NAME="esp-snapclient"
CONFIG_WEB_PORT=8000
# end of HTTP Server Setting
CONFIG_USE_SAMPLE_INSERTION=y
# CONFIG_USE_SAMPLE_INSERTION is not set
# end of Snapclient Configuration
#
# Audio HAL
#
CONFIG_AUDIO_BOARD_CUSTOM=y
# CONFIG_ESP_LYRAT_V4_3_BOARD is not set
# CONFIG_AUDIO_BOARD_CUSTOM is not set
CONFIG_ESP_LYRAT_V4_3_BOARD=y
# CONFIG_ESP_LYRAT_V4_2_BOARD is not set
# CONFIG_ESP_LYRATD_MSC_V2_1_BOARD is not set
# CONFIG_ESP_LYRATD_MSC_V2_2_BOARD is not set
@@ -367,43 +367,6 @@ CONFIG_AUDIO_BOARD_CUSTOM=y
# CONFIG_ESP32_KORVO_DU1906_BOARD is not set
# end of Audio HAL
#
# Custom Audio Board
#
# CONFIG_DAC_PCM51XX is not set
# CONFIG_DAC_PCM5102A is not set
# CONFIG_DAC_MA120 is not set
# CONFIG_DAC_MA120X0 is not set
CONFIG_DAC_ADAU1961=y
# CONFIG_DAC_MAX98357 is not set
#
# DAC I2C control interface
#
CONFIG_DAC_I2C_SDA=12
CONFIG_DAC_I2C_SCL=14
CONFIG_DAC_I2C_ADDR=0x70
# end of DAC I2C control interface
#
# I2S master interface
#
CONFIG_MASTER_I2S_MCLK_PIN=3
CONFIG_MASTER_I2S_BCK_PIN=15
CONFIG_MASTER_I2S_LRCK_PIN=13
CONFIG_MASTER_I2S_DATAOUT_PIN=4
# end of I2S master interface
#
# I2S slave interface
#
CONFIG_SLAVE_I2S_MCLK_PIN=0
CONFIG_SLAVE_I2S_BCK_PIN=26
CONFIG_SLAVE_I2S_LRCK_PIN=12
CONFIG_SLAVE_I2S_DATAOUT_PIN=5
# end of I2S slave interface
# end of Custom Audio Board
#
# ESP32 DSP processor config
#
@@ -1718,6 +1681,7 @@ CONFIG_MDNS_TIMER_PERIOD_MS=100
# CONFIG_MDNS_NETWORKING_SOCKET is not set
# CONFIG_MDNS_SKIP_SUPPRESSING_OWN_QUERIES is not set
# CONFIG_MDNS_ENABLE_DEBUG_PRINTS is not set
CONFIG_MDNS_ENABLE_CONSOLE_CLI=y
# CONFIG_MDNS_RESPOND_REVERSE_QUERIES is not set
CONFIG_MDNS_MULTIPLE_INSTANCE=y