From 5107f995e6b745b0c84cb87ccc883b9c2b0226db Mon Sep 17 00:00:00 2001 From: Sebastien Leclerc Date: Tue, 28 May 2019 17:47:14 -0400 Subject: [PATCH 1/5] Stable bluetooth Fix bluetooth disconnect --- main/Kconfig.projbuild | 7 +++ main/bt_app_core.c | 2 + main/output_bt.c | 109 ++++++++++++++++++++++++++++++++--------- sdkconfig.defaults | 6 +++ 4 files changed, 102 insertions(+), 22 deletions(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 930ba977..3dbac91f 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -147,6 +147,13 @@ menu "Squeezelite-ESP32" default "Squeezelite" help This is the name of the device that the Bluetooth speaker will see when it is connected to. + config A2DP_CONTROL_DELAY_MS + int "Control loop delay. " + depends on BTAUDIO + default 500 + help + Decreasing this will lead to a more responsive BT control, but might lead to noisy log files if debug is enabled. + endmenu endmenu diff --git a/main/bt_app_core.c b/main/bt_app_core.c index e38fd136..8f54b715 100644 --- a/main/bt_app_core.c +++ b/main/bt_app_core.c @@ -26,6 +26,8 @@ static log_level loglevel; static xQueueHandle s_bt_app_task_queue = NULL; static xTaskHandle s_bt_app_task_handle = NULL; + + void bt_set_log_level(log_level level){ loglevel = level; } diff --git a/main/output_bt.c b/main/output_bt.c index ae3f55fe..3e7ed2f8 100644 --- a/main/output_bt.c +++ b/main/output_bt.c @@ -52,6 +52,7 @@ void set_volume(unsigned left, unsigned right) { output.gainR = FIXED_ONE; UNLOCK; } +#define LOG_DEBUG_EVENT(e) LOG_DEBUG("evt: " STR(e)) /* event for handler "bt_av_hdl_stack_up */ enum { @@ -69,7 +70,20 @@ enum { APP_AV_STATE_DISCONNECTING, }; +char * APP_AV_STATE_DESC[] = { + "APP_AV_STATE_IDLE", + "APP_AV_STATE_DISCOVERING", + "APP_AV_STATE_DISCOVERED", + "APP_AV_STATE_UNCONNECTED", + "APP_AV_STATE_CONNECTING", + "APP_AV_STATE_CONNECTED", + "APP_AV_STATE_DISCONNECTING" +}; + + + /* sub states of APP_AV_STATE_CONNECTED */ + enum { APP_AV_MEDIA_STATE_IDLE, APP_AV_MEDIA_STATE_STARTING, @@ -374,12 +388,15 @@ static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) { + switch (event) { case ESP_BT_GAP_DISC_RES_EVT: { + LOG_DEBUG_EVENT(ESP_BT_GAP_DISC_RES_EVT); filter_inquiry_scan_result(param); break; } case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { + LOG_DEBUG_EVENT(ESP_BT_GAP_DISC_STATE_CHANGED_EVT); if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { if (s_a2d_state == APP_AV_STATE_DISCOVERED) { s_a2d_state = APP_AV_STATE_CONNECTING; @@ -397,9 +414,13 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) break; } case ESP_BT_GAP_RMT_SRVCS_EVT: + LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVCS_EVT); + break; case ESP_BT_GAP_RMT_SRVC_REC_EVT: + LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVC_REC_EVT); break; case ESP_BT_GAP_AUTH_CMPL_EVT: { + LOG_DEBUG_EVENT(ESP_BT_GAP_AUTH_CMPL_EVT); if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { LOG_INFO("authentication success: %s", param->auth_cmpl.device_name); //esp_log_buffer_hex(param->auth_cmpl.bda, ESP_BD_ADDR_LEN); @@ -409,6 +430,7 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) break; } case ESP_BT_GAP_PIN_REQ_EVT: { + LOG_DEBUG_EVENT(ESP_BT_GAP_PIN_REQ_EVT); LOG_INFO("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); if (param->pin_req.min_16_digit) { LOG_INFO("Input pin code: 0000 0000 0000 0000"); @@ -434,7 +456,6 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) case ESP_BT_GAP_KEY_NOTIF_EVT: LOG_INFO("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); break; - case ESP_BT_GAP_KEY_REQ_EVT: LOG_INFO("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); break; #endif @@ -452,6 +473,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) LOG_DEBUG("%s evt %d", __func__, event); switch (event) { case BT_APP_EVT_STACK_UP: { + LOG_INFO("BT Stack going up."); /* set up device name */ char *dev_name = CONFIG_A2DP_DEV_NAME; esp_bt_dev_set_device_name(dev_name); @@ -475,7 +497,7 @@ static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) /* create and start heart beat timer */ do { int tmr_id = 0; - s_tmr = xTimerCreate("connTmr", (10000 / portTICK_RATE_MS), + s_tmr = xTimerCreate("connTmr", (CONFIG_A2DP_CONTROL_DELAY_MS / portTICK_RATE_MS), pdTRUE, (void *)tmr_id, a2d_app_heart_beat); xTimerStart(s_tmr, portMAX_DELAY); } while (0); @@ -561,7 +583,7 @@ static void a2d_app_heart_beat(void *arg) static void bt_app_av_sm_hdlr(uint16_t event, void *param) { - LOG_INFO("%s state %d, evt 0x%x", __func__, s_a2d_state, event); + LOG_DEBUG("%s state %s, evt 0x%x, output state: %d", __func__, APP_AV_STATE_DESC[s_a2d_state], event, output.state); switch (s_a2d_state) { case APP_AV_STATE_DISCOVERING: case APP_AV_STATE_DISCOVERED: @@ -586,15 +608,23 @@ static void bt_app_av_sm_hdlr(uint16_t event, void *param) static void bt_app_av_state_unconnected(uint16_t event, void *param) { - switch (event) { + switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); + break; case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT: - break; + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; case BT_APP_HEART_BEAT_EVT: { uint8_t *p = s_peer_bda; - LOG_INFO("a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x", + LOG_INFO("BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5]); esp_a2d_source_connect(s_peer_bda); s_a2d_state = APP_AV_STATE_CONNECTING; @@ -610,8 +640,10 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param) static void bt_app_av_state_connecting(uint16_t event, void *param) { esp_a2d_cb_param_t *a2d = NULL; + switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); a2d = (esp_a2d_cb_param_t *)(param); if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) { LOG_INFO("a2dp connected"); @@ -624,10 +656,16 @@ static void bt_app_av_state_connecting(uint16_t event, void *param) break; } case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; case ESP_A2D_MEDIA_CTRL_ACK_EVT: - break; + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; case BT_APP_HEART_BEAT_EVT: + LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); if (++s_connecting_intv >= 2) { s_a2d_state = APP_AV_STATE_UNCONNECTED; s_connecting_intv = 0; @@ -644,21 +682,30 @@ static void bt_app_av_media_proc(uint16_t event, void *param) esp_a2d_cb_param_t *a2d = NULL; switch (s_media_state) { case APP_AV_MEDIA_STATE_IDLE: { - if (event == BT_APP_HEART_BEAT_EVT) { - LOG_INFO("a2dp media ready checking ..."); + if (event == BT_APP_HEART_BEAT_EVT) { + LOG_INFO("APP_AV_MEDIA_STATE_IDLE a2dp media ready checking ..."); esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); } else if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && - a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { - LOG_INFO("a2dp media ready, starting ..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); - s_media_state = APP_AV_MEDIA_STATE_STARTING; - } + a2d = (esp_a2d_cb_param_t *)(param); + if(output.state < OUTPUT_BUFFER ) + { + // TODO: anything to do while we are waiting? We should check if we're still connected. + } + else + { + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS + ) { + LOG_INFO("a2dp media ready, starting media playback ..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); + s_media_state = APP_AV_MEDIA_STATE_STARTING; + } + } } break; } case APP_AV_MEDIA_STATE_STARTING: { + LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STARTING); if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { a2d = (esp_a2d_cb_param_t *)(param); if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START && @@ -675,9 +722,10 @@ static void bt_app_av_media_proc(uint16_t event, void *param) break; } case APP_AV_MEDIA_STATE_STARTED: { + LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STARTED); if (event == BT_APP_HEART_BEAT_EVT) { - if (++s_intv_cnt >= 10) { - LOG_INFO("a2dp media stopping..."); + if(output.state <= OUTPUT_STOPPED) { + LOG_INFO("Output state is stopped. Stopping a2dp media ..."); esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); s_media_state = APP_AV_MEDIA_STATE_STOPPING; s_intv_cnt = 0; @@ -686,14 +734,15 @@ static void bt_app_av_media_proc(uint16_t event, void *param) break; } case APP_AV_MEDIA_STATE_STOPPING: { + LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STOPPING); if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { a2d = (esp_a2d_cb_param_t *)(param); if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP && a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { LOG_INFO("a2dp media stopped successfully, disconnecting..."); s_media_state = APP_AV_MEDIA_STATE_IDLE; - esp_a2d_source_disconnect(s_peer_bda); - s_a2d_state = APP_AV_STATE_DISCONNECTING; +// esp_a2d_source_disconnect(s_peer_bda); +// s_a2d_state = APP_AV_STATE_DISCONNECTING; } else { LOG_INFO("a2dp media stopping..."); esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); @@ -709,6 +758,7 @@ static void bt_app_av_state_connected(uint16_t event, void *param) esp_a2d_cb_param_t *a2d = NULL; switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); a2d = (esp_a2d_cb_param_t *)(param); if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { LOG_INFO("a2dp disconnected"); @@ -718,6 +768,7 @@ static void bt_app_av_state_connected(uint16_t event, void *param) break; } case ESP_A2D_AUDIO_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); a2d = (esp_a2d_cb_param_t *)(param); if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { s_pkt_cnt = 0; @@ -726,9 +777,15 @@ static void bt_app_av_state_connected(uint16_t event, void *param) } case ESP_A2D_AUDIO_CFG_EVT: // not suppposed to occur for A2DP source + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT: + case ESP_A2D_MEDIA_CTRL_ACK_EVT:{ + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + bt_app_av_media_proc(event, param); + break; + } case BT_APP_HEART_BEAT_EVT: { + LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); bt_app_av_media_proc(event, param); break; } @@ -743,6 +800,7 @@ static void bt_app_av_state_disconnecting(uint16_t event, void *param) esp_a2d_cb_param_t *a2d = NULL; switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); a2d = (esp_a2d_cb_param_t *)(param); if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { LOG_INFO("a2dp disconnected"); @@ -752,10 +810,17 @@ static void bt_app_av_state_disconnecting(uint16_t event, void *param) break; } case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; case ESP_A2D_MEDIA_CTRL_ACK_EVT: + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; case BT_APP_HEART_BEAT_EVT: - break; + LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); + break; default: LOG_ERROR("%s unhandled evt %d", __func__, event); break; diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 5cf6fc18..3b29fc8c 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -39,7 +39,13 @@ CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # CPU & threads options CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y +CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY= CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0= CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1=y CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=1 +CONFIG_ESPTOOLPY_BAUD_2MB=y +CONFIG_ESPTOOLPY_BAUD=2000000 +# Decreasing the delay here leads to a more responsive control of the playback. +# If debug logging set on output, this should be raised as it will generate a lot of noise in logs +CONFIG_A2DP_CONTROL_DELAY_MS=500 \ No newline at end of file From f7894b9ea32ab90cfad8f3ea29d4556441471ac1 Mon Sep 17 00:00:00 2001 From: Sebastien Leclerc Date: Wed, 29 May 2019 18:35:49 -0400 Subject: [PATCH 2/5] Stabilize BT connect --- main/Kconfig.projbuild | 8 +- main/bt_app_core.c | 4 +- main/output_bt.c | 243 +++++++++++++++++++++++++++-------------- sdkconfig.defaults | 3 +- 4 files changed, 172 insertions(+), 86 deletions(-) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 3dbac91f..4aa9e55a 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -152,7 +152,13 @@ menu "Squeezelite-ESP32" depends on BTAUDIO default 500 help - Decreasing this will lead to a more responsive BT control, but might lead to noisy log files if debug is enabled. + Decreasing this will lead to a more responsive BT control, but might lead to noisy log files if debug is enabled. + config A2DP_CONNECT_TIMEOUT_MS + int "Time out duration when trying to connect to an A2DP audio sink" + depends on BTAUDIO + default 1000 + help + Increasing this value will give more chance for less stable connections to be established. endmenu diff --git a/main/bt_app_core.c b/main/bt_app_core.c index 8f54b715..490274ca 100644 --- a/main/bt_app_core.c +++ b/main/bt_app_core.c @@ -33,7 +33,7 @@ void bt_set_log_level(log_level level){ } bool bt_app_work_dispatch(bt_app_cb_t p_cback, uint16_t event, void *p_params, int param_len, bt_app_copy_cb_t p_copy_cback) { - LOG_DEBUG("%s event 0x%x, param len %d", __func__, event, param_len); + LOG_SDEBUG("%s event 0x%x, param len %d", __func__, event, param_len); bt_app_msg_t msg; memset(&msg, 0, sizeof(bt_app_msg_t)); @@ -83,7 +83,7 @@ static void bt_app_task_handler(void *arg) bt_app_msg_t msg; for (;;) { if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) { - LOG_DEBUG("%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); + LOG_SDEBUG("%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); switch (msg.sig) { case BT_APP_SIG_WORK_DISPATCH: bt_app_work_dispatched(&msg); diff --git a/main/output_bt.c b/main/output_bt.c index 3e7ed2f8..dee1f1e9 100644 --- a/main/output_bt.c +++ b/main/output_bt.c @@ -31,6 +31,7 @@ extern struct outputstate output; extern struct buffer *outputbuf; extern struct buffer *streambuf; + #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) @@ -38,6 +39,9 @@ extern struct buffer *streambuf; extern u8_t *silencebuf; static u8_t *optr; +int64_t connecting_timeout = 0; +#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000) +#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr); @@ -53,6 +57,7 @@ void set_volume(unsigned left, unsigned right) { UNLOCK; } #define LOG_DEBUG_EVENT(e) LOG_DEBUG("evt: " STR(e)) +#define LOG_SDEBUG_EVENT(e) LOG_SDEBUG("evt: " STR(e)) /* event for handler "bt_av_hdl_stack_up */ enum { @@ -89,6 +94,7 @@ enum { APP_AV_MEDIA_STATE_STARTING, APP_AV_MEDIA_STATE_STARTED, APP_AV_MEDIA_STATE_STOPPING, + APP_AV_MEDIA_STATE_WAIT_DISCONNECT }; #define BT_APP_HEART_BEAT_EVT (0xff00) @@ -118,7 +124,6 @@ static uint8_t s_peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; static int s_a2d_state = APP_AV_STATE_IDLE; static int s_media_state = APP_AV_MEDIA_STATE_IDLE; static int s_intv_cnt = 0; -static int s_connecting_intv = 0; static uint32_t s_pkt_cnt = 0; static TimerHandle_t s_tmr; @@ -143,7 +148,7 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch memset(&output, 0, sizeof(output)); - output.start_frames = FRAME_BLOCK * 2; + output.start_frames = 0; //CONFIG_ //FRAME_BLOCK * 2; output.write_cb = &_write_frames; output.rate_delay = rate_delay; @@ -334,54 +339,79 @@ static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len return false; } - +#define LOG_INFO_NO_LF(fmt, ...) if (loglevel >= lINFO) logprint(fmt, ##__VA_ARGS__) static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) { char bda_str[18]; uint32_t cod = 0; int32_t rssi = -129; /* invalid value */ uint8_t *eir = NULL; + uint8_t nameLen = 0; esp_bt_gap_dev_prop_t *p; + memset(s_peer_bdname, 0x00,sizeof(s_peer_bdname)); - LOG_INFO("Scanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); + LOG_INFO("\n=======================\nScanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); for (int i = 0; i < param->disc_res.num_prop; i++) { p = param->disc_res.prop + i; switch (p->type) { case ESP_BT_GAP_DEV_PROP_COD: cod = *(uint32_t *)(p->val); - LOG_INFO("--Class of Device: 0x%x", cod); + LOG_INFO_NO_LF("\n-- Class of Device: 0x%x", cod); break; case ESP_BT_GAP_DEV_PROP_RSSI: rssi = *(int8_t *)(p->val); - LOG_INFO("--RSSI: %d", rssi); + LOG_INFO_NO_LF("\n-- RSSI: %d", rssi); break; case ESP_BT_GAP_DEV_PROP_EIR: eir = (uint8_t *)(p->val); + LOG_INFO_NO_LF("\n-- EIR: %d", eir); break; case ESP_BT_GAP_DEV_PROP_BDNAME: + nameLen = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN : (uint8_t)p->len; + memcpy(s_peer_bdname, (uint8_t *)(p->val), nameLen); + s_peer_bdname[nameLen] = '\0'; + LOG_INFO_NO_LF("\n-- Name: %s", s_peer_bdname); + break; default: break; } } - + if (!esp_bt_gap_is_valid_cod(cod)){ /* search for device with MAJOR service class as "rendering" in COD */ - if (!esp_bt_gap_is_valid_cod(cod) || - !(esp_bt_gap_get_cod_srvc(cod) & ESP_BT_COD_SRVC_RENDERING)) { - return; + LOG_INFO_NO_LF("\n--Invalid class of device. Skipping.\n"); + return; } + else if (!(esp_bt_gap_get_cod_srvc(cod) & ESP_BT_COD_SRVC_RENDERING)) + { + LOG_INFO_NO_LF("\n--Not a rendering device. Skipping.\n"); + return; + } + /* search for device named "ESP_SPEAKER" in its extended inqury response */ if (eir) { + LOG_INFO_NO_LF("\n--Getting details from eir.\n"); get_name_from_eir(eir, s_peer_bdname, NULL); - if (strcmp((char *)s_peer_bdname, CONFIG_A2DP_SINK_NAME) != 0) { - return; - } + LOG_INFO("--\nDevice name is %s",s_peer_bdname); + } - LOG_INFO("Found a target device, address %s, name %s", bda_str, s_peer_bdname); - s_a2d_state = APP_AV_STATE_DISCOVERED; - memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN); - LOG_INFO("Cancel device discovery ..."); - esp_bt_gap_cancel_discovery(); + if (strcmp((char *)s_peer_bdname, CONFIG_A2DP_SINK_NAME) == 0) { + LOG_INFO("Found a target device, address %s, name %s", bda_str, s_peer_bdname); + + if(esp_bt_gap_cancel_discovery()!=ESP_ERR_INVALID_STATE) + { + LOG_INFO("Cancel device discovery ..."); + memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN); + s_a2d_state = APP_AV_STATE_DISCOVERED; + } + else + { + LOG_ERROR("Cancel device discovery failed..."); + } + } + else + { + LOG_INFO("Not the device we are looking for. Continuing scan."); } } @@ -391,26 +421,41 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) switch (event) { case ESP_BT_GAP_DISC_RES_EVT: { - LOG_DEBUG_EVENT(ESP_BT_GAP_DISC_RES_EVT); filter_inquiry_scan_result(param); break; } case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { - LOG_DEBUG_EVENT(ESP_BT_GAP_DISC_STATE_CHANGED_EVT); - if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { - if (s_a2d_state == APP_AV_STATE_DISCOVERED) { - s_a2d_state = APP_AV_STATE_CONNECTING; - LOG_INFO("Device discovery stopped."); - LOG_INFO("a2dp connecting to peer: %s", s_peer_bdname); - esp_a2d_source_connect(s_peer_bda); - } else { + if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) + { + if (s_a2d_state == APP_AV_STATE_DISCOVERED) + { + if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) + { + s_a2d_state = APP_AV_STATE_CONNECTING; + LOG_INFO("Device discovery stopped. a2dp connecting to peer: %s", s_peer_bdname); + A2DP_TIMER_INIT; + } + else + { + // not discovered, continue to discover + LOG_INFO("Attempt at connecting failed, resuming discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + } + else + { // not discovered, continue to discover LOG_INFO("Device discovery failed, continue to discover..."); esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); } - } else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { + } + else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { LOG_INFO("Discovery started."); } + else + { + LOG_DEBUG("This shouldn't happen. Discovery has only 2 states (for now)."); + } break; } case ESP_BT_GAP_RMT_SRVCS_EVT: @@ -420,8 +465,7 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVC_REC_EVT); break; case ESP_BT_GAP_AUTH_CMPL_EVT: { - LOG_DEBUG_EVENT(ESP_BT_GAP_AUTH_CMPL_EVT); - if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { LOG_INFO("authentication success: %s", param->auth_cmpl.device_name); //esp_log_buffer_hex(param->auth_cmpl.bda, ESP_BD_ADDR_LEN); } else { @@ -430,8 +474,7 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) break; } case ESP_BT_GAP_PIN_REQ_EVT: { - LOG_DEBUG_EVENT(ESP_BT_GAP_PIN_REQ_EVT); - LOG_INFO("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + LOG_INFO("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); if (param->pin_req.min_16_digit) { LOG_INFO("Input pin code: 0000 0000 0000 0000"); esp_bt_pin_code_t pin_code = {0}; @@ -470,13 +513,14 @@ void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) { - LOG_DEBUG("%s evt %d", __func__, event); + switch (event) { case BT_APP_EVT_STACK_UP: { LOG_INFO("BT Stack going up."); /* set up device name */ char *dev_name = CONFIG_A2DP_DEV_NAME; esp_bt_dev_set_device_name(dev_name); + LOG_INFO("Preparing to connect to device: %s",CONFIG_A2DP_SINK_NAME); /* register GAP callback function */ esp_bt_gap_register_callback(bt_app_gap_cb); @@ -521,21 +565,28 @@ static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) static unsigned min_o = -1, max_o = 0, min_s = -1, max_s = 0; unsigned o, s; - if (len < 0 || data == NULL) { + + if (len < 0 || data == NULL ) { return 0; } - - LOCK; - -/* TODO - Normally, we would want BT to not call us back unless we are not in BUFFERING state. - That requires BT to not start until we are > OUTPUT_BUFFER - // come back later, we are buffering (or stopped, need to handle that case ...) but we don't want silence - if (output.state <= OUTPUT_BUFFER) { + // bail out if A2DP isn't connected + LOCK; + if(s_media_state != APP_AV_MEDIA_STATE_STARTED) + { UNLOCK; - return 0; - } -*/ + return 0; + } + + +// todo: fix me!! +/* Normally, we would want BT to not call us back unless we are not in BUFFERING state. + That requires BT to not start until we are > OUTPUT_BUFFER + // come back later, we are buffering (or stopped, need to handle that case ...) but we don't want silence */ +// if (output.state <= OUTPUT_BUFFER) { +// UNLOCK; +// return 0; +// } + frames = len / 4; output.device_frames = 0; @@ -583,10 +634,13 @@ static void a2d_app_heart_beat(void *arg) static void bt_app_av_sm_hdlr(uint16_t event, void *param) { - LOG_DEBUG("%s state %s, evt 0x%x, output state: %d", __func__, APP_AV_STATE_DESC[s_a2d_state], event, output.state); + //LOG_DEBUG("%s state %s, evt 0x%x, output state: %d", __func__, APP_AV_STATE_DESC[s_a2d_state], event, output.state); switch (s_a2d_state) { case APP_AV_STATE_DISCOVERING: + LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); + break; case APP_AV_STATE_DISCOVERED: + LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); break; case APP_AV_STATE_UNCONNECTED: bt_app_av_state_unconnected(event, param); @@ -618,17 +672,37 @@ static void bt_app_av_state_unconnected(uint16_t event, void *param) case ESP_A2D_AUDIO_CFG_EVT: LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT: LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); break; case BT_APP_HEART_BEAT_EVT: { uint8_t *p = s_peer_bda; - LOG_INFO("BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x", - p[0], p[1], p[2], p[3], p[4], p[5]); - esp_a2d_source_connect(s_peer_bda); - s_a2d_state = APP_AV_STATE_CONNECTING; - s_connecting_intv = 0; + LOG_INFO("BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x",p[0], p[1], p[2], p[3], p[4], p[5]); + switch (esp_bluedroid_get_status()) { + case ESP_BLUEDROID_STATUS_UNINITIALIZED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_UNINITIALIZED."); + break; + case ESP_BLUEDROID_STATUS_INITIALIZED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_INITIALIZED."); + break; + case ESP_BLUEDROID_STATUS_ENABLED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_ENABLED."); + break; + default: + break; + } + if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) + { + s_a2d_state = APP_AV_STATE_CONNECTING; + LOG_INFO("a2dp connecting to peer: %s", s_peer_bdname); + A2DP_TIMER_INIT; + } + else + { + // not discovered, continue to discover + LOG_INFO("Attempt at connecting failed, resuming discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } break; } default: @@ -643,10 +717,9 @@ static void bt_app_av_state_connecting(uint16_t event, void *param) switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: { - LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); a2d = (esp_a2d_cb_param_t *)(param); if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) { - LOG_INFO("a2dp connected"); + LOG_INFO("a2dp connected! Stopping scan. "); s_a2d_state = APP_AV_STATE_CONNECTED; s_media_state = APP_AV_MEDIA_STATE_IDLE; esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); @@ -665,11 +738,12 @@ static void bt_app_av_state_connecting(uint16_t event, void *param) LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); break; case BT_APP_HEART_BEAT_EVT: - LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); - if (++s_connecting_intv >= 2) { + if (IS_A2DP_TIMER_OVER) + { s_a2d_state = APP_AV_STATE_UNCONNECTED; - s_connecting_intv = 0; + LOG_DEBUG("Connect timed out. Setting state to Unconnected. "); } + LOG_SDEBUG("BT_APP_HEART_BEAT_EVT"); break; default: LOG_ERROR("%s unhandled evt %d", __func__, event); @@ -677,40 +751,43 @@ static void bt_app_av_state_connecting(uint16_t event, void *param) } } + static void bt_app_av_media_proc(uint16_t event, void *param) { esp_a2d_cb_param_t *a2d = NULL; switch (s_media_state) { case APP_AV_MEDIA_STATE_IDLE: { if (event == BT_APP_HEART_BEAT_EVT) { - LOG_INFO("APP_AV_MEDIA_STATE_IDLE a2dp media ready checking ..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); + if(output.state <= OUTPUT_STOPPED ) + { + // TODO: anything to do while we are waiting? Should we check if we're still connected? + } + else if(output.state <= OUTPUT_BUFFER ) + { + LOG_INFO("buffering output, a2dp media ready and connected. Starting checking if ready..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); + } + + } else if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { a2d = (esp_a2d_cb_param_t *)(param); - if(output.state < OUTPUT_BUFFER ) - { - // TODO: anything to do while we are waiting? We should check if we're still connected. - } - else - { - if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && - a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS - ) { - LOG_INFO("a2dp media ready, starting media playback ..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); - s_media_state = APP_AV_MEDIA_STATE_STARTING; - } - } + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS + ) { + LOG_INFO("a2dp media ready, starting media playback ..."); + s_media_state = APP_AV_MEDIA_STATE_STARTING; + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); + + } } break; } case APP_AV_MEDIA_STATE_STARTING: { - LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STARTING); - if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { a2d = (esp_a2d_cb_param_t *)(param); if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START && a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { - LOG_INFO("a2dp media start successfully."); + LOG_INFO("a2dp media started successfully."); s_intv_cnt = 0; s_media_state = APP_AV_MEDIA_STATE_STARTED; } else { @@ -722,12 +799,12 @@ static void bt_app_av_media_proc(uint16_t event, void *param) break; } case APP_AV_MEDIA_STATE_STARTED: { - LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STARTED); if (event == BT_APP_HEART_BEAT_EVT) { if(output.state <= OUTPUT_STOPPED) { LOG_INFO("Output state is stopped. Stopping a2dp media ..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); s_media_state = APP_AV_MEDIA_STATE_STOPPING; + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); + s_intv_cnt = 0; } } @@ -739,8 +816,11 @@ static void bt_app_av_media_proc(uint16_t event, void *param) a2d = (esp_a2d_cb_param_t *)(param); if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP && a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { - LOG_INFO("a2dp media stopped successfully, disconnecting..."); + LOG_INFO("a2dp media stopped successfully..."); + //s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT; + s_media_state = APP_AV_MEDIA_STATE_IDLE; + // todo: should we disconnect? // esp_a2d_source_disconnect(s_peer_bda); // s_a2d_state = APP_AV_STATE_DISCONNECTING; } else { @@ -758,8 +838,7 @@ static void bt_app_av_state_connected(uint16_t event, void *param) esp_a2d_cb_param_t *a2d = NULL; switch (event) { case ESP_A2D_CONNECTION_STATE_EVT: { - LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); - a2d = (esp_a2d_cb_param_t *)(param); + a2d = (esp_a2d_cb_param_t *)(param); if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { LOG_INFO("a2dp disconnected"); s_a2d_state = APP_AV_STATE_UNCONNECTED; @@ -785,7 +864,7 @@ static void bt_app_av_state_connected(uint16_t event, void *param) break; } case BT_APP_HEART_BEAT_EVT: { - LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); + LOG_SDEBUG_EVENT(BT_APP_HEART_BEAT_EVT); bt_app_av_media_proc(event, param); break; } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 3b29fc8c..e6242100 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -48,4 +48,5 @@ CONFIG_ESPTOOLPY_BAUD_2MB=y CONFIG_ESPTOOLPY_BAUD=2000000 # Decreasing the delay here leads to a more responsive control of the playback. # If debug logging set on output, this should be raised as it will generate a lot of noise in logs -CONFIG_A2DP_CONTROL_DELAY_MS=500 \ No newline at end of file +CONFIG_A2DP_CONTROL_DELAY_MS=500 +CONFIG_A2DP_CONNECT_TIMEOUT_MS=1000 \ No newline at end of file From 5b12187c930c82e759c62650fbce325bf9d3c626 Mon Sep 17 00:00:00 2001 From: Sebastien Leclerc Date: Sun, 2 Jun 2019 10:32:35 -0400 Subject: [PATCH 3/5] Temp save --- main/esp32.c | 797 ++++++++++++++++++++++++++++++++++++++++++++ main/output_bt.c | 815 +-------------------------------------------- main/squeezelite.h | 22 +- 3 files changed, 828 insertions(+), 806 deletions(-) diff --git a/main/esp32.c b/main/esp32.c index b758d04c..a53ff0e0 100644 --- a/main/esp32.c +++ b/main/esp32.c @@ -3,8 +3,42 @@ #include "sdkconfig.h" #include "esp_system.h" #include "squeezelite.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "esp_system.h" +#include "esp_log.h" + + +#include "esp_bt.h" +#include "bt_app_core.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_gap_bt_api.h" +#include "esp_a2dp_api.h" +#include "esp_avrc_api.h" + +#define BT_AV_TAG "BT_AV" +u8_t *bt_optr; extern log_level loglevel; +extern struct outputstate output; +extern struct buffer *outputbuf; +extern struct buffer *streambuf; +#define LOCK mutex_lock(outputbuf->mutex) +#define UNLOCK mutex_unlock(outputbuf->mutex) +int64_t connecting_timeout = 0; + +#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000) +#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout + void get_mac(u8_t mac[]) { esp_read_mac(mac, ESP_MAC_WIFI_STA); @@ -58,3 +92,766 @@ struct codec *register_alac(void) { } #endif +#define LOG_DEBUG_EVENT(e) LOG_DEBUG("evt: " STR(e)) +#define LOG_SDEBUG_EVENT(e) LOG_SDEBUG("evt: " STR(e)) + +/* event for handler "bt_av_hdl_stack_up */ +enum { + BT_APP_EVT_STACK_UP = 0, +}; + +/* A2DP global state */ +enum { + APP_AV_STATE_IDLE, + APP_AV_STATE_DISCOVERING, + APP_AV_STATE_DISCOVERED, + APP_AV_STATE_UNCONNECTED, + APP_AV_STATE_CONNECTING, + APP_AV_STATE_CONNECTED, + APP_AV_STATE_DISCONNECTING, +}; + +char * APP_AV_STATE_DESC[] = { + "APP_AV_STATE_IDLE", + "APP_AV_STATE_DISCOVERING", + "APP_AV_STATE_DISCOVERED", + "APP_AV_STATE_UNCONNECTED", + "APP_AV_STATE_CONNECTING", + "APP_AV_STATE_CONNECTED", + "APP_AV_STATE_DISCONNECTING" +}; + + + +/* sub states of APP_AV_STATE_CONNECTED */ + +enum { + APP_AV_MEDIA_STATE_IDLE, + APP_AV_MEDIA_STATE_STARTING, + APP_AV_MEDIA_STATE_STARTED, + APP_AV_MEDIA_STATE_STOPPING, + APP_AV_MEDIA_STATE_WAIT_DISCONNECT +}; + +#define BT_APP_HEART_BEAT_EVT (0xff00) + +/// handler for bluetooth stack enabled events +static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); + +/// callback function for A2DP source +static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); + +/// callback function for A2DP source audio data stream +static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len); + +static void a2d_app_heart_beat(void *arg); + +/// A2DP application state machine +static void bt_app_av_sm_hdlr(uint16_t event, void *param); + +/* A2DP application state machine handler for each state */ +static void bt_app_av_state_unconnected(uint16_t event, void *param); +static void bt_app_av_state_connecting(uint16_t event, void *param); +static void bt_app_av_state_connected(uint16_t event, void *param); +static void bt_app_av_state_disconnecting(uint16_t event, void *param); + +static esp_bd_addr_t s_peer_bda = {0}; +static uint8_t s_peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; +static int s_a2d_state = APP_AV_STATE_IDLE; +static int s_media_state = APP_AV_MEDIA_STATE_IDLE; +static int s_intv_cnt = 0; +static uint32_t s_pkt_cnt = 0; + +static TimerHandle_t s_tmr; + +static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) +{ + if (bda == NULL || str == NULL || size < 18) { + return NULL; + } + + uint8_t *p = bda; + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + return str; +} +void hal_bluetooth_init(log_level level) +{ + + /* + * Bluetooth audio source init Start + */ + loglevel = level; + //running_test = false; + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + + if (esp_bt_controller_init(&bt_cfg) != ESP_OK) { + LOG_ERROR("%s initialize controller failed\n", __func__); + return; + } + + if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) { + LOG_ERROR("%s enable controller failed\n", __func__); + return; + } + + if (esp_bluedroid_init() != ESP_OK) { + LOG_ERROR("%s initialize bluedroid failed\n", __func__); + return; + } + + if (esp_bluedroid_enable() != ESP_OK) { + LOG_ERROR("%s enable bluedroid failed\n", __func__); + return; + } + /* create application task */ + bt_app_task_start_up(); + + /* Bluetooth device name, connection mode and profile set up */ + bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); + + #if (CONFIG_BT_SSP_ENABLED == true) + /* Set default parameters for Secure Simple Pairing */ + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); + #endif + + /* + * Set default parameters for Legacy Pairing + * Use variable pin, input pin code when pairing + */ + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; + esp_bt_pin_code_t pin_code; + esp_bt_gap_set_pin(pin_type, 0, pin_code); + +} + +static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) +{ + uint8_t *rmt_bdname = NULL; + uint8_t rmt_bdname_len = 0; + + if (!eir) { + return false; + } + + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); + if (!rmt_bdname) { + rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); + } + + if (rmt_bdname) { + if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { + rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; + } + + if (bdname) { + memcpy(bdname, rmt_bdname, rmt_bdname_len); + bdname[rmt_bdname_len] = '\0'; + } + if (bdname_len) { + *bdname_len = rmt_bdname_len; + } + return true; + } + + return false; +} +#define LOG_INFO_NO_LF(fmt, ...) if (loglevel >= lINFO) logprint(fmt, ##__VA_ARGS__) +static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) +{ + char bda_str[18]; + uint32_t cod = 0; + int32_t rssi = -129; /* invalid value */ + uint8_t *eir = NULL; + uint8_t nameLen = 0; + esp_bt_gap_dev_prop_t *p; + memset(s_peer_bdname, 0x00,sizeof(s_peer_bdname)); + + LOG_INFO("\n=======================\nScanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); + for (int i = 0; i < param->disc_res.num_prop; i++) { + p = param->disc_res.prop + i; + switch (p->type) { + case ESP_BT_GAP_DEV_PROP_COD: + cod = *(uint32_t *)(p->val); + LOG_INFO_NO_LF("\n-- Class of Device: 0x%x", cod); + break; + case ESP_BT_GAP_DEV_PROP_RSSI: + rssi = *(int8_t *)(p->val); + LOG_INFO_NO_LF("\n-- RSSI: %d", rssi); + break; + case ESP_BT_GAP_DEV_PROP_EIR: + eir = (uint8_t *)(p->val); + LOG_INFO_NO_LF("\n-- EIR: %d", eir); + break; + case ESP_BT_GAP_DEV_PROP_BDNAME: + nameLen = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN : (uint8_t)p->len; + memcpy(s_peer_bdname, (uint8_t *)(p->val), nameLen); + s_peer_bdname[nameLen] = '\0'; + LOG_INFO_NO_LF("\n-- Name: %s", s_peer_bdname); + break; + default: + break; + } + } + if (!esp_bt_gap_is_valid_cod(cod)){ + /* search for device with MAJOR service class as "rendering" in COD */ + LOG_INFO_NO_LF("\n--Invalid class of device. Skipping.\n"); + return; + } + else if (!(esp_bt_gap_get_cod_srvc(cod) & ESP_BT_COD_SRVC_RENDERING)) + { + LOG_INFO_NO_LF("\n--Not a rendering device. Skipping.\n"); + return; + } + + + /* search for device named "ESP_SPEAKER" in its extended inqury response */ + if (eir) { + LOG_INFO_NO_LF("\n--Getting details from eir.\n"); + get_name_from_eir(eir, s_peer_bdname, NULL); + LOG_INFO("--\nDevice name is %s",s_peer_bdname); + } + + if (strcmp((char *)s_peer_bdname, CONFIG_A2DP_SINK_NAME) == 0) { + LOG_INFO("Found a target device, address %s, name %s", bda_str, s_peer_bdname); + + if(esp_bt_gap_cancel_discovery()!=ESP_ERR_INVALID_STATE) + { + LOG_INFO("Cancel device discovery ..."); + memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN); + s_a2d_state = APP_AV_STATE_DISCOVERED; + } + else + { + LOG_ERROR("Cancel device discovery failed..."); + } + } + else + { + LOG_INFO("Not the device we are looking for. Continuing scan."); + } +} + + +void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + + switch (event) { + case ESP_BT_GAP_DISC_RES_EVT: { + filter_inquiry_scan_result(param); + break; + } + case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { + if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) + { + if (s_a2d_state == APP_AV_STATE_DISCOVERED) + { + if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) + { + s_a2d_state = APP_AV_STATE_CONNECTING; + LOG_INFO("Device discovery stopped. a2dp connecting to peer: %s", s_peer_bdname); + A2DP_TIMER_INIT; + } + else + { + // not discovered, continue to discover + LOG_INFO("Attempt at connecting failed, resuming discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + } + else + { + // not discovered, continue to discover + LOG_INFO("Device discovery failed, continue to discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + } + else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { + LOG_INFO("Discovery started."); + } + else + { + LOG_DEBUG("This shouldn't happen. Discovery has only 2 states (for now)."); + } + break; + } + case ESP_BT_GAP_RMT_SRVCS_EVT: + LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVCS_EVT); + break; + case ESP_BT_GAP_RMT_SRVC_REC_EVT: + LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVC_REC_EVT); + break; + case ESP_BT_GAP_AUTH_CMPL_EVT: { + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + LOG_INFO("authentication success: %s", param->auth_cmpl.device_name); + //esp_log_buffer_hex(param->auth_cmpl.bda, ESP_BD_ADDR_LEN); + } else { + LOG_ERROR("authentication failed, status:%d", param->auth_cmpl.stat); + } + break; + } + case ESP_BT_GAP_PIN_REQ_EVT: { + LOG_INFO("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit) { + LOG_INFO("Input pin code: 0000 0000 0000 0000"); + esp_bt_pin_code_t pin_code = {0}; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + } else { + LOG_INFO("Input pin code: 1234"); + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + } + break; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + case ESP_BT_GAP_CFM_REQ_EVT: + LOG_INFO("ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + case ESP_BT_GAP_KEY_NOTIF_EVT: + LOG_INFO("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); + break; + LOG_INFO("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + break; +#endif + + default: { + LOG_INFO("event: %d", event); + break; + } + } + return; +} + +static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) +{ + + switch (event) { + case BT_APP_EVT_STACK_UP: { + LOG_INFO("BT Stack going up."); + /* set up device name */ + char *dev_name = CONFIG_A2DP_DEV_NAME; + esp_bt_dev_set_device_name(dev_name); + LOG_INFO("Preparing to connect to device: %s",CONFIG_A2DP_SINK_NAME); + + /* register GAP callback function */ + esp_bt_gap_register_callback(bt_app_gap_cb); + + /* initialize A2DP source */ + esp_a2d_register_callback(&bt_app_a2d_cb); + esp_a2d_source_register_data_callback(bt_app_a2d_data_cb); + esp_a2d_source_init(); + + /* set discoverable and connectable mode */ + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + + /* start device discovery */ + LOG_INFO("Starting device discovery..."); + s_a2d_state = APP_AV_STATE_DISCOVERING; + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + + /* create and start heart beat timer */ + do { + int tmr_id = 0; + s_tmr = xTimerCreate("connTmr", (CONFIG_A2DP_CONTROL_DELAY_MS / portTICK_RATE_MS), + pdTRUE, (void *)tmr_id, a2d_app_heart_beat); + xTimerStart(s_tmr, portMAX_DELAY); + } while (0); + break; + } + default: + LOG_ERROR("%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) +{ + bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL); +} + +static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) +{ + frames_t frames; + static int count = 0; + static unsigned min_o = -1, max_o = 0, min_s = -1, max_s = 0; + unsigned o, s; + + + if (len < 0 || data == NULL ) { + return 0; + } + + // bail out if A2DP isn't connected + LOCK; +// if(s_media_state != APP_AV_MEDIA_STATE_STARTED) +// { +// UNLOCK; +// return 0; +// } + + +// +///* Normally, we would want BT to not call us back unless we are not in BUFFERING state. +// That requires BT to not start until we are > OUTPUT_BUFFER +// // come back later, we are buffering (or stopped, need to handle that case ...) but we don't want silence */ +// if (output.state == OUTPUT_BUFFER) { +// UNLOCK; +// int32_t silence_bytes = (len >MAX_SILENCE_FRAMES * BYTES_PER_FRAME?MAX_SILENCE_FRAMES * BYTES_PER_FRAME:len; +// memcpy(bt_optr, (u8_t *)silencebuf, silence_bytes); +// return actual_len; +// } +// This is how the BTC layer calculates the number of bytes to +// for us to send. (BTC_SBC_DEC_PCM_DATA_LEN * sizeof(OI_INT16) - availPcmBytes + frames = len / BYTES_PER_FRAME; + output.device_frames = 0; + output.updated = gettime_ms(); + output.frames_played_dmp = output.frames_played; + //if (output.threshold < 20) output.threshold = 20; + int ret; + + frames_t wanted_frames=len/BYTES_PER_FRAME; + bt_optr = data; // needed for the _write_frames callback + do { + frames = _output_frames(wanted_frames); + wanted_frames -= frames; + } while (wanted_frames > 0 && frames != 0); + + if (wanted_frames > 0) { + LOG_DEBUG("need to pad with silence"); + memset(bt_optr, 0, wanted_frames * BYTES_PER_FRAME); + } + + + UNLOCK; + + o = _buf_used(outputbuf); + if (o < min_o) min_o = o; + if (o > max_o) max_o = o; + + s = _buf_used(streambuf); + if (s < min_s) min_s = s; + if (s > max_s) max_s = s; + + if (!(count++ & 0x1ff)) { + LOG_INFO("frames %d (count:%d) (out:%d/%d/%d, stream:%d/%d/%d)", frames, count, max_o, min_o, o, max_s, min_s, s); + min_o = min_s = -1; + max_o = max_s = -0; + } + + return frames * BYTES_PER_FRAME; +} +static bool running_test; +bool test_open(const char *device, unsigned rates[], bool userdef_rates) { + +// running_test = true; +// while(running_test) +// { +// // wait until BT playback has started +// // this will allow querying the sample rate +// usleep(100000); +// } + + memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned)); + if (!strcmp(device, "BT")) { + rates[0] = 44100; + } else { + unsigned _rates[] = { 96000, 88200, 48000, 44100, 32000, 0 }; + memcpy(rates, _rates, sizeof(_rates)); + } + return true; +} + +static void a2d_app_heart_beat(void *arg) +{ + bt_app_work_dispatch(bt_app_av_sm_hdlr, BT_APP_HEART_BEAT_EVT, NULL, 0, NULL); +} + +static void bt_app_av_sm_hdlr(uint16_t event, void *param) +{ + //LOG_DEBUG("%s state %s, evt 0x%x, output state: %d", __func__, APP_AV_STATE_DESC[s_a2d_state], event, output.state); + switch (s_a2d_state) { + case APP_AV_STATE_DISCOVERING: + LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); + break; + case APP_AV_STATE_DISCOVERED: + LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); + break; + case APP_AV_STATE_UNCONNECTED: + bt_app_av_state_unconnected(event, param); + break; + case APP_AV_STATE_CONNECTING: + bt_app_av_state_connecting(event, param); + break; + case APP_AV_STATE_CONNECTED: + bt_app_av_state_connected(event, param); + break; + case APP_AV_STATE_DISCONNECTING: + bt_app_av_state_disconnecting(event, param); + break; + default: + LOG_ERROR("%s invalid state %d", __func__, s_a2d_state); + break; + } +} + +static void bt_app_av_state_unconnected(uint16_t event, void *param) +{ + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); + break; + case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; + case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; + case BT_APP_HEART_BEAT_EVT: { + uint8_t *p = s_peer_bda; + LOG_INFO("BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x",p[0], p[1], p[2], p[3], p[4], p[5]); + switch (esp_bluedroid_get_status()) { + case ESP_BLUEDROID_STATUS_UNINITIALIZED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_UNINITIALIZED."); + break; + case ESP_BLUEDROID_STATUS_INITIALIZED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_INITIALIZED."); + break; + case ESP_BLUEDROID_STATUS_ENABLED: + LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_ENABLED."); + break; + default: + break; + } + if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) + { + s_a2d_state = APP_AV_STATE_CONNECTING; + LOG_INFO("a2dp connecting to peer: %s", s_peer_bdname); + A2DP_TIMER_INIT; + } + else + { + // not discovered, continue to discover + LOG_INFO("Attempt at connecting failed, resuming discover..."); + esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); + } + break; + } + default: + LOG_ERROR("%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_av_state_connecting(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) { + LOG_INFO("a2dp connected! Stopping scan. "); + s_a2d_state = APP_AV_STATE_CONNECTED; + s_media_state = APP_AV_MEDIA_STATE_IDLE; + esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); + } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + s_a2d_state = APP_AV_STATE_UNCONNECTED; + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; + case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; + case BT_APP_HEART_BEAT_EVT: + if (IS_A2DP_TIMER_OVER) + { + s_a2d_state = APP_AV_STATE_UNCONNECTED; + LOG_DEBUG("Connect timed out. Setting state to Unconnected. "); + } + LOG_SDEBUG("BT_APP_HEART_BEAT_EVT"); + break; + default: + LOG_ERROR("%s unhandled evt %d", __func__, event); + break; + } +} + + +static void bt_app_av_media_proc(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (s_media_state) { + case APP_AV_MEDIA_STATE_IDLE: { + if (event == BT_APP_HEART_BEAT_EVT) { + if(output.state < OUTPUT_STOPPED ) + { + // TODO: anything to do while we are waiting? Should we check if we're still connected? + } + else if(output.state >= OUTPUT_BUFFER ) + { + LOG_INFO("buffering output, a2dp media ready and connected. Starting checking if ready..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); + } +// else if(running_test) +// { +// LOG_INFO("buffering output, a2dp media ready and connected. Starting checking if ready..."); +// +// esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); +// } + + + } else if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS + ) { + LOG_INFO("a2dp media ready, starting media playback ..."); + s_media_state = APP_AV_MEDIA_STATE_STARTING; + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); + + } + } + break; + } + case APP_AV_MEDIA_STATE_STARTING: { + if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { + LOG_INFO("a2dp media started successfully."); + s_intv_cnt = 0; + s_media_state = APP_AV_MEDIA_STATE_STARTED; + } else { + // not started succesfully, transfer to idle state + LOG_INFO("a2dp media start failed."); + s_media_state = APP_AV_MEDIA_STATE_IDLE; + } + } + break; + } + case APP_AV_MEDIA_STATE_STARTED: { + if (event == BT_APP_HEART_BEAT_EVT) { + if(output.state <= OUTPUT_STOPPED) { + LOG_INFO("Output state is stopped. Stopping a2dp media ..."); + s_media_state = APP_AV_MEDIA_STATE_STOPPING; + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); + + s_intv_cnt = 0; + } + } + break; + } + case APP_AV_MEDIA_STATE_STOPPING: { + LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STOPPING); + if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP && + a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { + LOG_INFO("a2dp media stopped successfully..."); + //s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT; + + s_media_state = APP_AV_MEDIA_STATE_IDLE; + // todo: should we disconnect? +// esp_a2d_source_disconnect(s_peer_bda); +// s_a2d_state = APP_AV_STATE_DISCONNECTING; + } else { + LOG_INFO("a2dp media stopping..."); + esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); + } + } + break; + } + } +} + +static void bt_app_av_state_connected(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + LOG_INFO("a2dp disconnected"); + s_a2d_state = APP_AV_STATE_UNCONNECTED; + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + a2d = (esp_a2d_cb_param_t *)(param); + if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { + s_pkt_cnt = 0; + } + break; + } + case ESP_A2D_AUDIO_CFG_EVT: + // not suppposed to occur for A2DP source + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT:{ + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + bt_app_av_media_proc(event, param); + break; + } + case BT_APP_HEART_BEAT_EVT: { + LOG_SDEBUG_EVENT(BT_APP_HEART_BEAT_EVT); + bt_app_av_media_proc(event, param); + break; + } + default: + LOG_ERROR("%s unhandled evt %d", __func__, event); + break; + } +} + +static void bt_app_av_state_disconnecting(uint16_t event, void *param) +{ + esp_a2d_cb_param_t *a2d = NULL; + switch (event) { + case ESP_A2D_CONNECTION_STATE_EVT: { + LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); + a2d = (esp_a2d_cb_param_t *)(param); + if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { + LOG_INFO("a2dp disconnected"); + s_a2d_state = APP_AV_STATE_UNCONNECTED; + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } + break; + } + case ESP_A2D_AUDIO_STATE_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); + break; + case ESP_A2D_AUDIO_CFG_EVT: + LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); + break; + case ESP_A2D_MEDIA_CTRL_ACK_EVT: + LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); + break; + case BT_APP_HEART_BEAT_EVT: + LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); + break; + default: + LOG_ERROR("%s unhandled evt %d", __func__, event); + break; + } +} diff --git a/main/output_bt.c b/main/output_bt.c index dee1f1e9..62327cab 100644 --- a/main/output_bt.c +++ b/main/output_bt.c @@ -1,28 +1,6 @@ #include "squeezelite.h" -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/timers.h" -#include "nvs.h" -#include "nvs_flash.h" -#include "esp_system.h" -#include "esp_log.h" -#include "esp_bt.h" -#include "bt_app_core.h" -#include "esp_bt_main.h" -#include "esp_bt_device.h" -#include "esp_gap_bt_api.h" -#include "esp_a2dp_api.h" -#include "esp_avrc_api.h" - -#define BT_AV_TAG "BT_AV" - static log_level loglevel; static bool running = true; @@ -38,10 +16,7 @@ extern struct buffer *streambuf; #define FRAME_BLOCK MAX_SILENCE_FRAMES extern u8_t *silencebuf; -static u8_t *optr; -int64_t connecting_timeout = 0; -#define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000) -#define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout +extern u8_t *bt_optr; static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr); @@ -56,89 +31,7 @@ void set_volume(unsigned left, unsigned right) { output.gainR = FIXED_ONE; UNLOCK; } -#define LOG_DEBUG_EVENT(e) LOG_DEBUG("evt: " STR(e)) -#define LOG_SDEBUG_EVENT(e) LOG_SDEBUG("evt: " STR(e)) -/* event for handler "bt_av_hdl_stack_up */ -enum { - BT_APP_EVT_STACK_UP = 0, -}; - -/* A2DP global state */ -enum { - APP_AV_STATE_IDLE, - APP_AV_STATE_DISCOVERING, - APP_AV_STATE_DISCOVERED, - APP_AV_STATE_UNCONNECTED, - APP_AV_STATE_CONNECTING, - APP_AV_STATE_CONNECTED, - APP_AV_STATE_DISCONNECTING, -}; - -char * APP_AV_STATE_DESC[] = { - "APP_AV_STATE_IDLE", - "APP_AV_STATE_DISCOVERING", - "APP_AV_STATE_DISCOVERED", - "APP_AV_STATE_UNCONNECTED", - "APP_AV_STATE_CONNECTING", - "APP_AV_STATE_CONNECTED", - "APP_AV_STATE_DISCONNECTING" -}; - - - -/* sub states of APP_AV_STATE_CONNECTED */ - -enum { - APP_AV_MEDIA_STATE_IDLE, - APP_AV_MEDIA_STATE_STARTING, - APP_AV_MEDIA_STATE_STARTED, - APP_AV_MEDIA_STATE_STOPPING, - APP_AV_MEDIA_STATE_WAIT_DISCONNECT -}; - -#define BT_APP_HEART_BEAT_EVT (0xff00) - -/// handler for bluetooth stack enabled events -static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); - -/// callback function for A2DP source -static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); - -/// callback function for A2DP source audio data stream -static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len); - -static void a2d_app_heart_beat(void *arg); - -/// A2DP application state machine -static void bt_app_av_sm_hdlr(uint16_t event, void *param); - -/* A2DP application state machine handler for each state */ -static void bt_app_av_state_unconnected(uint16_t event, void *param); -static void bt_app_av_state_connecting(uint16_t event, void *param); -static void bt_app_av_state_connected(uint16_t event, void *param); -static void bt_app_av_state_disconnecting(uint16_t event, void *param); - -static esp_bd_addr_t s_peer_bda = {0}; -static uint8_t s_peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; -static int s_a2d_state = APP_AV_STATE_IDLE; -static int s_media_state = APP_AV_MEDIA_STATE_IDLE; -static int s_intv_cnt = 0; -static uint32_t s_pkt_cnt = 0; - -static TimerHandle_t s_tmr; - -static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) -{ - if (bda == NULL || str == NULL || size < 18) { - return NULL; - } - - uint8_t *p = bda; - sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", - p[0], p[1], p[2], p[3], p[4], p[5]); - return str; -} void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { @@ -156,77 +49,18 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch if (!rates[0]) { rates[0] = 44100; } - /* - * Bluetooth audio source init Start - */ - bt_set_log_level(level); - ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); - - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - - if (esp_bt_controller_init(&bt_cfg) != ESP_OK) { - LOG_ERROR("%s initialize controller failed\n", __func__); - return; - } - - if (esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT) != ESP_OK) { - LOG_ERROR("%s enable controller failed\n", __func__); - return; - } - - if (esp_bluedroid_init() != ESP_OK) { - LOG_ERROR("%s initialize bluedroid failed\n", __func__); - return; - } - - if (esp_bluedroid_enable() != ESP_OK) { - LOG_ERROR("%s enable bluedroid failed\n", __func__); - return; - } - /* create application task */ - bt_app_task_start_up(); - - /* Bluetooth device name, connection mode and profile set up */ - bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); - - #if (CONFIG_BT_SSP_ENABLED == true) - /* Set default parameters for Secure Simple Pairing */ - esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; - esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; - esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); - #endif - - /* - * Set default parameters for Legacy Pairing - * Use variable pin, input pin code when pairing - */ - esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; - esp_bt_pin_code_t pin_code; - esp_bt_gap_set_pin(pin_type, 0, pin_code); - + hal_bluetooth_init(loglevel); /* * Bluetooth audio source init Start */ device = "BT"; output_init_common(level, device, output_buf_size, rates, idle); -//#if LINUX || OSX || FREEBSD || POSIX -// pthread_attr_t attr; -// pthread_attr_init(&attr); -//#ifdef PTHREAD_STACK_MIN -// pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); -//#endif -// pthread_create(&thread, &attr, output_thread, NULL); -// pthread_attr_destroy(&attr); -//#endif -//#if WIN -// thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL); -//#endif + } void output_close_dac(void) { LOG_INFO("close output"); - LOCK; running = false; UNLOCK; @@ -237,8 +71,8 @@ void output_close_dac(void) { static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr) { - if (!silence) { - + if (!silence ) { + DEBUG_LOG_TIMED(200,"Not silence, Writing audio out."); /* TODO need 16 bit fix if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); @@ -250,12 +84,12 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g */ #if BYTES_PER_FRAME == 4 - memcpy(optr, outputbuf->readp, out_frames * BYTES_PER_FRAME); + memcpy(bt_optr, outputbuf->readp, out_frames * BYTES_PER_FRAME); #else { frames_t count = out_frames; s32_t *_iptr = (s32_t*) outputbuf->readp; - s16_t *_optr = (s16_t*) optr; + s16_t *_optr = (s16_t*) bt_optr; while (count--) { *_optr++ = *_iptr++ >> 16; *_optr++ = *_iptr++ >> 16; @@ -264,644 +98,15 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g #endif } else { + DEBUG_LOG_TIMED(200,"Silence flag true. Writing silence to audio out."); u8_t *buf = silencebuf; - memcpy(optr, buf, out_frames * 4); + memcpy(bt_optr, buf, out_frames * 4); } - optr += out_frames * 4; + bt_optr += out_frames * 4; return (int)out_frames; } -//static void *output_thread() { -// -// -// while (running) { -// -// //nothing to do here, for now. Feeding the buffer is -// usleep(500000); -// continue; -// } -// -// output.device_frames = 0; -// output.updated = gettime_ms(); -// output.frames_played_dmp = output.frames_played; -// -// _output_frames(FRAME_BLOCK); -// -// UNLOCK; -// -// if (buffill) { -//// Do Stuff here -// usleep((buffill * 1000 * 1000) / output.current_sample_rate); -// buffill = 0; -// } else { -// usleep((FRAME_BLOCK * 1000 * 1000) / output.current_sample_rate); -// } -// -// } -// -// return 0; -//} - - - -static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) -{ - uint8_t *rmt_bdname = NULL; - uint8_t rmt_bdname_len = 0; - - if (!eir) { - return false; - } - - rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); - if (!rmt_bdname) { - rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); - } - - if (rmt_bdname) { - if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { - rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; - } - - if (bdname) { - memcpy(bdname, rmt_bdname, rmt_bdname_len); - bdname[rmt_bdname_len] = '\0'; - } - if (bdname_len) { - *bdname_len = rmt_bdname_len; - } - return true; - } - - return false; -} -#define LOG_INFO_NO_LF(fmt, ...) if (loglevel >= lINFO) logprint(fmt, ##__VA_ARGS__) -static void filter_inquiry_scan_result(esp_bt_gap_cb_param_t *param) -{ - char bda_str[18]; - uint32_t cod = 0; - int32_t rssi = -129; /* invalid value */ - uint8_t *eir = NULL; - uint8_t nameLen = 0; - esp_bt_gap_dev_prop_t *p; - memset(s_peer_bdname, 0x00,sizeof(s_peer_bdname)); - - LOG_INFO("\n=======================\nScanned device: %s", bda2str(param->disc_res.bda, bda_str, 18)); - for (int i = 0; i < param->disc_res.num_prop; i++) { - p = param->disc_res.prop + i; - switch (p->type) { - case ESP_BT_GAP_DEV_PROP_COD: - cod = *(uint32_t *)(p->val); - LOG_INFO_NO_LF("\n-- Class of Device: 0x%x", cod); - break; - case ESP_BT_GAP_DEV_PROP_RSSI: - rssi = *(int8_t *)(p->val); - LOG_INFO_NO_LF("\n-- RSSI: %d", rssi); - break; - case ESP_BT_GAP_DEV_PROP_EIR: - eir = (uint8_t *)(p->val); - LOG_INFO_NO_LF("\n-- EIR: %d", eir); - break; - case ESP_BT_GAP_DEV_PROP_BDNAME: - nameLen = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN : (uint8_t)p->len; - memcpy(s_peer_bdname, (uint8_t *)(p->val), nameLen); - s_peer_bdname[nameLen] = '\0'; - LOG_INFO_NO_LF("\n-- Name: %s", s_peer_bdname); - break; - default: - break; - } - } - if (!esp_bt_gap_is_valid_cod(cod)){ - /* search for device with MAJOR service class as "rendering" in COD */ - LOG_INFO_NO_LF("\n--Invalid class of device. Skipping.\n"); - return; - } - else if (!(esp_bt_gap_get_cod_srvc(cod) & ESP_BT_COD_SRVC_RENDERING)) - { - LOG_INFO_NO_LF("\n--Not a rendering device. Skipping.\n"); - return; - } - - - /* search for device named "ESP_SPEAKER" in its extended inqury response */ - if (eir) { - LOG_INFO_NO_LF("\n--Getting details from eir.\n"); - get_name_from_eir(eir, s_peer_bdname, NULL); - LOG_INFO("--\nDevice name is %s",s_peer_bdname); - } - - if (strcmp((char *)s_peer_bdname, CONFIG_A2DP_SINK_NAME) == 0) { - LOG_INFO("Found a target device, address %s, name %s", bda_str, s_peer_bdname); - - if(esp_bt_gap_cancel_discovery()!=ESP_ERR_INVALID_STATE) - { - LOG_INFO("Cancel device discovery ..."); - memcpy(s_peer_bda, param->disc_res.bda, ESP_BD_ADDR_LEN); - s_a2d_state = APP_AV_STATE_DISCOVERED; - } - else - { - LOG_ERROR("Cancel device discovery failed..."); - } - } - else - { - LOG_INFO("Not the device we are looking for. Continuing scan."); - } -} - - -void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) -{ - - switch (event) { - case ESP_BT_GAP_DISC_RES_EVT: { - filter_inquiry_scan_result(param); - break; - } - case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { - if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) - { - if (s_a2d_state == APP_AV_STATE_DISCOVERED) - { - if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) - { - s_a2d_state = APP_AV_STATE_CONNECTING; - LOG_INFO("Device discovery stopped. a2dp connecting to peer: %s", s_peer_bdname); - A2DP_TIMER_INIT; - } - else - { - // not discovered, continue to discover - LOG_INFO("Attempt at connecting failed, resuming discover..."); - esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); - } - } - else - { - // not discovered, continue to discover - LOG_INFO("Device discovery failed, continue to discover..."); - esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); - } - } - else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { - LOG_INFO("Discovery started."); - } - else - { - LOG_DEBUG("This shouldn't happen. Discovery has only 2 states (for now)."); - } - break; - } - case ESP_BT_GAP_RMT_SRVCS_EVT: - LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVCS_EVT); - break; - case ESP_BT_GAP_RMT_SRVC_REC_EVT: - LOG_DEBUG_EVENT(ESP_BT_GAP_RMT_SRVC_REC_EVT); - break; - case ESP_BT_GAP_AUTH_CMPL_EVT: { - if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { - LOG_INFO("authentication success: %s", param->auth_cmpl.device_name); - //esp_log_buffer_hex(param->auth_cmpl.bda, ESP_BD_ADDR_LEN); - } else { - LOG_ERROR("authentication failed, status:%d", param->auth_cmpl.stat); - } - break; - } - case ESP_BT_GAP_PIN_REQ_EVT: { - LOG_INFO("ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); - if (param->pin_req.min_16_digit) { - LOG_INFO("Input pin code: 0000 0000 0000 0000"); - esp_bt_pin_code_t pin_code = {0}; - esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); - } else { - LOG_INFO("Input pin code: 1234"); - esp_bt_pin_code_t pin_code; - pin_code[0] = '1'; - pin_code[1] = '2'; - pin_code[2] = '3'; - pin_code[3] = '4'; - esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); - } - break; - } - -#if (CONFIG_BT_SSP_ENABLED == true) - case ESP_BT_GAP_CFM_REQ_EVT: - LOG_INFO("ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); - esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); - break; - case ESP_BT_GAP_KEY_NOTIF_EVT: - LOG_INFO("ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); - break; - LOG_INFO("ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); - break; -#endif - - default: { - LOG_INFO("event: %d", event); - break; - } - } - return; -} - -static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) -{ - - switch (event) { - case BT_APP_EVT_STACK_UP: { - LOG_INFO("BT Stack going up."); - /* set up device name */ - char *dev_name = CONFIG_A2DP_DEV_NAME; - esp_bt_dev_set_device_name(dev_name); - LOG_INFO("Preparing to connect to device: %s",CONFIG_A2DP_SINK_NAME); - - /* register GAP callback function */ - esp_bt_gap_register_callback(bt_app_gap_cb); - - /* initialize A2DP source */ - esp_a2d_register_callback(&bt_app_a2d_cb); - esp_a2d_source_register_data_callback(bt_app_a2d_data_cb); - esp_a2d_source_init(); - - /* set discoverable and connectable mode */ - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); - - /* start device discovery */ - LOG_INFO("Starting device discovery..."); - s_a2d_state = APP_AV_STATE_DISCOVERING; - esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); - - /* create and start heart beat timer */ - do { - int tmr_id = 0; - s_tmr = xTimerCreate("connTmr", (CONFIG_A2DP_CONTROL_DELAY_MS / portTICK_RATE_MS), - pdTRUE, (void *)tmr_id, a2d_app_heart_beat); - xTimerStart(s_tmr, portMAX_DELAY); - } while (0); - break; - } - default: - LOG_ERROR("%s unhandled evt %d", __func__, event); - break; - } -} - -static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) -{ - bt_app_work_dispatch(bt_app_av_sm_hdlr, event, param, sizeof(esp_a2d_cb_param_t), NULL); -} - -static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) -{ - frames_t frames; - static int count = 0; - static unsigned min_o = -1, max_o = 0, min_s = -1, max_s = 0; - unsigned o, s; - - - if (len < 0 || data == NULL ) { - return 0; - } - // bail out if A2DP isn't connected - LOCK; - if(s_media_state != APP_AV_MEDIA_STATE_STARTED) - { - UNLOCK; - return 0; - } - - -// todo: fix me!! -/* Normally, we would want BT to not call us back unless we are not in BUFFERING state. - That requires BT to not start until we are > OUTPUT_BUFFER - // come back later, we are buffering (or stopped, need to handle that case ...) but we don't want silence */ -// if (output.state <= OUTPUT_BUFFER) { -// UNLOCK; -// return 0; -// } - - - frames = len / 4; - output.device_frames = 0; - output.updated = gettime_ms(); - output.frames_played_dmp = output.frames_played; - if (output.threshold < 20) output.threshold = 20; - - optr = data; - frames = _output_frames(frames); - - UNLOCK; - - o = _buf_used(outputbuf); - if (o < min_o) min_o = o; - if (o > max_o) max_o = o; - - s = _buf_used(streambuf); - if (s < min_s) min_s = s; - if (s > max_s) max_s = s; - - if (!(count++ & 0x1ff)) { - LOG_INFO("frames %d (count:%d) (out:%d/%d/%d, stream:%d/%d/%d)", frames, count, max_o, min_o, o, max_s, min_s, s); - min_o = min_s = -1; - max_o = max_s = -0; - } - - return frames * 4; -} - -bool test_open(const char *device, unsigned rates[], bool userdef_rates) { - memset(rates, 0, MAX_SUPPORTED_SAMPLERATES * sizeof(unsigned)); - if (!strcmp(device, "BT")) { - rates[0] = 44100; - } else { - unsigned _rates[] = { 96000, 88200, 48000, 44100, 32000, 0 }; - memcpy(rates, _rates, sizeof(_rates)); - } - return true; -} - -static void a2d_app_heart_beat(void *arg) -{ - bt_app_work_dispatch(bt_app_av_sm_hdlr, BT_APP_HEART_BEAT_EVT, NULL, 0, NULL); -} - -static void bt_app_av_sm_hdlr(uint16_t event, void *param) -{ - //LOG_DEBUG("%s state %s, evt 0x%x, output state: %d", __func__, APP_AV_STATE_DESC[s_a2d_state], event, output.state); - switch (s_a2d_state) { - case APP_AV_STATE_DISCOVERING: - LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); - break; - case APP_AV_STATE_DISCOVERED: - LOG_DEBUG("state %s, evt 0x%x, output state: %d", APP_AV_STATE_DESC[s_a2d_state], event, output.state); - break; - case APP_AV_STATE_UNCONNECTED: - bt_app_av_state_unconnected(event, param); - break; - case APP_AV_STATE_CONNECTING: - bt_app_av_state_connecting(event, param); - break; - case APP_AV_STATE_CONNECTED: - bt_app_av_state_connected(event, param); - break; - case APP_AV_STATE_DISCONNECTING: - bt_app_av_state_disconnecting(event, param); - break; - default: - LOG_ERROR("%s invalid state %d", __func__, s_a2d_state); - break; - } -} - -static void bt_app_av_state_unconnected(uint16_t event, void *param) -{ - switch (event) { - case ESP_A2D_CONNECTION_STATE_EVT: - LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); - break; - case ESP_A2D_AUDIO_STATE_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); - break; - case ESP_A2D_AUDIO_CFG_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); - break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT: - LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); - break; - case BT_APP_HEART_BEAT_EVT: { - uint8_t *p = s_peer_bda; - LOG_INFO("BT_APP_HEART_BEAT_EVT a2dp connecting to peer: %02x:%02x:%02x:%02x:%02x:%02x",p[0], p[1], p[2], p[3], p[4], p[5]); - switch (esp_bluedroid_get_status()) { - case ESP_BLUEDROID_STATUS_UNINITIALIZED: - LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_UNINITIALIZED."); - break; - case ESP_BLUEDROID_STATUS_INITIALIZED: - LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_INITIALIZED."); - break; - case ESP_BLUEDROID_STATUS_ENABLED: - LOG_INFO("BlueDroid Status is ESP_BLUEDROID_STATUS_ENABLED."); - break; - default: - break; - } - if(esp_a2d_source_connect(s_peer_bda)!=ESP_ERR_INVALID_STATE) - { - s_a2d_state = APP_AV_STATE_CONNECTING; - LOG_INFO("a2dp connecting to peer: %s", s_peer_bdname); - A2DP_TIMER_INIT; - } - else - { - // not discovered, continue to discover - LOG_INFO("Attempt at connecting failed, resuming discover..."); - esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0); - } - break; - } - default: - LOG_ERROR("%s unhandled evt %d", __func__, event); - break; - } -} - -static void bt_app_av_state_connecting(uint16_t event, void *param) -{ - esp_a2d_cb_param_t *a2d = NULL; - - switch (event) { - case ESP_A2D_CONNECTION_STATE_EVT: { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED) { - LOG_INFO("a2dp connected! Stopping scan. "); - s_a2d_state = APP_AV_STATE_CONNECTED; - s_media_state = APP_AV_MEDIA_STATE_IDLE; - esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); - } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { - s_a2d_state = APP_AV_STATE_UNCONNECTED; - } - break; - } - case ESP_A2D_AUDIO_STATE_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); - break; - case ESP_A2D_AUDIO_CFG_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); - break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT: - LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); - break; - case BT_APP_HEART_BEAT_EVT: - if (IS_A2DP_TIMER_OVER) - { - s_a2d_state = APP_AV_STATE_UNCONNECTED; - LOG_DEBUG("Connect timed out. Setting state to Unconnected. "); - } - LOG_SDEBUG("BT_APP_HEART_BEAT_EVT"); - break; - default: - LOG_ERROR("%s unhandled evt %d", __func__, event); - break; - } -} - - -static void bt_app_av_media_proc(uint16_t event, void *param) -{ - esp_a2d_cb_param_t *a2d = NULL; - switch (s_media_state) { - case APP_AV_MEDIA_STATE_IDLE: { - if (event == BT_APP_HEART_BEAT_EVT) { - if(output.state <= OUTPUT_STOPPED ) - { - // TODO: anything to do while we are waiting? Should we check if we're still connected? - } - else if(output.state <= OUTPUT_BUFFER ) - { - LOG_INFO("buffering output, a2dp media ready and connected. Starting checking if ready..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY); - } - - - } else if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_CHECK_SRC_RDY && - a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS - ) { - LOG_INFO("a2dp media ready, starting media playback ..."); - s_media_state = APP_AV_MEDIA_STATE_STARTING; - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_START); - - } - } - break; - } - case APP_AV_MEDIA_STATE_STARTING: { - if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START && - a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { - LOG_INFO("a2dp media started successfully."); - s_intv_cnt = 0; - s_media_state = APP_AV_MEDIA_STATE_STARTED; - } else { - // not started succesfully, transfer to idle state - LOG_INFO("a2dp media start failed."); - s_media_state = APP_AV_MEDIA_STATE_IDLE; - } - } - break; - } - case APP_AV_MEDIA_STATE_STARTED: { - if (event == BT_APP_HEART_BEAT_EVT) { - if(output.state <= OUTPUT_STOPPED) { - LOG_INFO("Output state is stopped. Stopping a2dp media ..."); - s_media_state = APP_AV_MEDIA_STATE_STOPPING; - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); - - s_intv_cnt = 0; - } - } - break; - } - case APP_AV_MEDIA_STATE_STOPPING: { - LOG_DEBUG_EVENT(APP_AV_MEDIA_STATE_STOPPING); - if (event == ESP_A2D_MEDIA_CTRL_ACK_EVT) { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP && - a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) { - LOG_INFO("a2dp media stopped successfully..."); - //s_media_state = APP_AV_MEDIA_STATE_WAIT_DISCONNECT; - - s_media_state = APP_AV_MEDIA_STATE_IDLE; - // todo: should we disconnect? -// esp_a2d_source_disconnect(s_peer_bda); -// s_a2d_state = APP_AV_STATE_DISCONNECTING; - } else { - LOG_INFO("a2dp media stopping..."); - esp_a2d_media_ctrl(ESP_A2D_MEDIA_CTRL_STOP); - } - } - break; - } - } -} - -static void bt_app_av_state_connected(uint16_t event, void *param) -{ - esp_a2d_cb_param_t *a2d = NULL; - switch (event) { - case ESP_A2D_CONNECTION_STATE_EVT: { - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { - LOG_INFO("a2dp disconnected"); - s_a2d_state = APP_AV_STATE_UNCONNECTED; - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); - } - break; - } - case ESP_A2D_AUDIO_STATE_EVT: { - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); - a2d = (esp_a2d_cb_param_t *)(param); - if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { - s_pkt_cnt = 0; - } - break; - } - case ESP_A2D_AUDIO_CFG_EVT: - // not suppposed to occur for A2DP source - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); - break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT:{ - LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); - bt_app_av_media_proc(event, param); - break; - } - case BT_APP_HEART_BEAT_EVT: { - LOG_SDEBUG_EVENT(BT_APP_HEART_BEAT_EVT); - bt_app_av_media_proc(event, param); - break; - } - default: - LOG_ERROR("%s unhandled evt %d", __func__, event); - break; - } -} - -static void bt_app_av_state_disconnecting(uint16_t event, void *param) -{ - esp_a2d_cb_param_t *a2d = NULL; - switch (event) { - case ESP_A2D_CONNECTION_STATE_EVT: { - LOG_DEBUG_EVENT(ESP_A2D_CONNECTION_STATE_EVT); - a2d = (esp_a2d_cb_param_t *)(param); - if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { - LOG_INFO("a2dp disconnected"); - s_a2d_state = APP_AV_STATE_UNCONNECTED; - esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); - } - break; - } - case ESP_A2D_AUDIO_STATE_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_STATE_EVT); - break; - case ESP_A2D_AUDIO_CFG_EVT: - LOG_DEBUG_EVENT(ESP_A2D_AUDIO_CFG_EVT); - break; - case ESP_A2D_MEDIA_CTRL_ACK_EVT: - LOG_DEBUG_EVENT(ESP_A2D_MEDIA_CTRL_ACK_EVT); - break; - case BT_APP_HEART_BEAT_EVT: - LOG_DEBUG_EVENT(BT_APP_HEART_BEAT_EVT); - break; - default: - LOG_ERROR("%s unhandled evt %d", __func__, event); - break; - } -} diff --git a/main/squeezelite.h b/main/squeezelite.h index 98c048b6..44a78c48 100644 --- a/main/squeezelite.h +++ b/main/squeezelite.h @@ -467,7 +467,24 @@ void logprint(const char *fmt, ...); #define LOG_INFO(fmt, ...) if (loglevel >= lINFO) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) #define LOG_DEBUG(fmt, ...) if (loglevel >= lDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) #define LOG_SDEBUG(fmt, ...) if (loglevel >= lSDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) +static inline void DEBUG_LOG_TIMED(uint32_t delayms, char * strFmt, ...) +{ + static log_level loglevel; + va_list args; + va_start(args, strFmt); + static uint32_t nextDebugLog=0; + if(esp_timer_get_time()>nextDebugLog) + { + if (loglevel >= lDEBUG) + { + logprint("%s %s:%d ", logtime(), __FUNCTION__, __LINE__); + logprint(strFmt , args); + logprint("\n"); + } + nextDebugLog=esp_timer_get_time()+delayms*1000; + } +} // utils.c (non logging) typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type; #if WIN && USE_SSL @@ -721,13 +738,16 @@ void _pa_open(void); #endif // output_dac.c -#if DACAUDIO +// todo: do we need a distinction between DACAUDIO and BTAUDIO? +#if DACAUDIO || BTAUDIO void set_volume(unsigned left, unsigned right); bool test_open(const char *device, unsigned rates[], bool userdef_rates); void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); void output_close_dac(void); +void hal_bluetooth_init(log_level loglevel); #endif + // output_stdout.c void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay); void output_close_stdout(void); From 8f477fd05e3cb9cf796552583392d8e086763e89 Mon Sep 17 00:00:00 2001 From: Sebastien Leclerc Date: Sun, 2 Jun 2019 13:51:46 -0400 Subject: [PATCH 4/5] Robotic output on DAC First stab at DAC out. Right now, some robotic music is comming out --- main/esp32.c | 16 +- main/main.c | 4 +- main/output_bt.c | 10 +- main/{output_dac.c.sample => output_dac.c} | 53 +++++-- main/output_dac.c.tes | 163 +++++++++++++++++++++ main/squeezelite.h | 15 +- 6 files changed, 239 insertions(+), 22 deletions(-) rename main/{output_dac.c.sample => output_dac.c} (72%) create mode 100644 main/output_dac.c.tes diff --git a/main/esp32.c b/main/esp32.c index a53ff0e0..6a159435 100644 --- a/main/esp32.c +++ b/main/esp32.c @@ -35,7 +35,18 @@ extern struct buffer *streambuf; #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) int64_t connecting_timeout = 0; - +#ifndef CONFIG_A2DP_SINK_NAME +#define CONFIG_A2DP_SINK_NAME "btspeaker" // fix some compile errors when BT is not chosen +#endif +#ifndef CONFIG_A2DP_CONNECT_TIMEOUT_MS +#define CONFIG_A2DP_CONNECT_TIMEOUT_MS 2000 +#endif +#ifndef CONFIG_A2DP_DEV_NAME +#define CONFIG_A2DP_DEV_NAME "espsqueezelite" +#endif +#ifndef CONFIG_A2DP_CONTROL_DELAY_MS +#define CONFIG_A2DP_CONTROL_DELAY_MS 1000 +#endif #define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000) #define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout @@ -551,6 +562,7 @@ static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) return frames * BYTES_PER_FRAME; } static bool running_test; +#ifdef BTAUDIO bool test_open(const char *device, unsigned rates[], bool userdef_rates) { // running_test = true; @@ -570,7 +582,7 @@ bool test_open(const char *device, unsigned rates[], bool userdef_rates) { } return true; } - +#endif static void a2d_app_heart_beat(void *arg) { bt_app_work_dispatch(bt_app_av_sm_hdlr, BT_APP_HEART_BEAT_EVT, NULL, 0, NULL); diff --git a/main/main.c b/main/main.c index 8be1dd37..b2f4993c 100644 --- a/main/main.c +++ b/main/main.c @@ -749,7 +749,9 @@ int main(int argc, char **argv) { stream_init(log_stream, stream_buf_size); -#if DACAUDIO +#if BTAUDIO + output_init_bt(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); +#elif DACAUDIO output_init_dac(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); #else if (!strcmp(output_device, "-")) { diff --git a/main/output_bt.c b/main/output_bt.c index 62327cab..0af24d69 100644 --- a/main/output_bt.c +++ b/main/output_bt.c @@ -17,10 +17,10 @@ extern struct buffer *streambuf; extern u8_t *silencebuf; extern u8_t *bt_optr; - +void hal_bluetooth_init(log_level); static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr); - +#if BTAUDIO void set_volume(unsigned left, unsigned right) { LOG_DEBUG("setting internal gain left: %u right: %u", left, right); LOCK; @@ -31,10 +31,10 @@ void set_volume(unsigned left, unsigned right) { output.gainR = FIXED_ONE; UNLOCK; } +#endif - -void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { +void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { loglevel = level; LOG_INFO("init output BT"); @@ -59,7 +59,7 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch } -void output_close_dac(void) { +void output_close_bt(void) { LOG_INFO("close output"); LOCK; running = false; diff --git a/main/output_dac.c.sample b/main/output_dac.c similarity index 72% rename from main/output_dac.c.sample rename to main/output_dac.c index 7d755ff9..fa519c31 100644 --- a/main/output_dac.c.sample +++ b/main/output_dac.c @@ -1,8 +1,15 @@ #include "squeezelite.h" - +#include "driver/i2s.h" #include +#define I2S_NUM (0) +#define I2S_BCK_IO (GPIO_NUM_26) +#define I2S_WS_IO (GPIO_NUM_25) +#define I2S_DO_IO (GPIO_NUM_22) +#define I2S_DI_IO (-1) +// buffer length is expressed in number of samples +#define I2S_BUF_LEN 60 static log_level loglevel; static bool running = true; @@ -39,7 +46,11 @@ void set_volume(unsigned left, unsigned right) { void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { loglevel = level; - + optr = malloc(FRAME_BLOCK * BYTES_PER_FRAME); + if (!optr) { + LOG_ERROR("unable to malloc buf"); + return; + } LOG_INFO("init output DAC"); memset(&output, 0, sizeof(output)); @@ -65,6 +76,25 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch } output_init_common(level, device, output_buf_size, rates, idle); + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = output.current_sample_rate, + .bits_per_sample = BYTES_PER_FRAME * 8, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = I2S_COMM_FORMAT_I2S + | I2S_COMM_FORMAT_I2S_MSB, + .dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers + .dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth + .use_apll = false, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 + }; + i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num = + I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO //Not used + }; + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM, &pin_config); + i2s_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2); #if LINUX || OSX || FREEBSD || POSIX pthread_attr_t attr; @@ -86,7 +116,7 @@ void output_close_dac(void) { LOCK; running = false; UNLOCK; - + free(optr); output_close_common(); } @@ -144,7 +174,7 @@ static void *output_thread() { // buffer to hold output data so we can block on writing outside of output lock, allocated on init u8_t *obuf = malloc(FRAME_BLOCK * BYTES_PER_FRAME); int frames = 0; - + size_t i2s_bytes_write = 0; #if REPACK LOCK; @@ -174,7 +204,8 @@ static void *output_thread() { usleep(500000); continue; } - + // todo: call i2s_set_clock here if rate is changed + output.device_frames = 0; output.updated = gettime_ms(); output.frames_played_dmp = output.frames_played; @@ -185,13 +216,11 @@ static void *output_thread() { UNLOCK; if (frames) { - if (output.device[0] == '-' && memcmp(optr, silencebuf, frames * bytes_per_frame)) { - fwrite(obuf, bytes_per_frame, frames, stdout); - LOG_INFO("writing frames %d", frames); - } else { - // do something with some of these frames... - usleep((frames * 1000 * 1000) / output.current_sample_rate); - } + i2s_write(I2S_NUM, optr,frames*BYTES_PER_FRAME, &i2s_bytes_write, 100); + if(i2s_bytes_write!=frames*BYTES_PER_FRAME){ + LOG_WARN("Bytes available: %d, I2S wrote %d", frames*BYTES_PER_FRAME,i2s_bytes_write); + } + usleep((frames * 1000 * 1000) / output.current_sample_rate); frames = 0; } else { usleep((FRAME_BLOCK * 1000 * 1000) / output.current_sample_rate); diff --git a/main/output_dac.c.tes b/main/output_dac.c.tes new file mode 100644 index 00000000..427d9267 --- /dev/null +++ b/main/output_dac.c.tes @@ -0,0 +1,163 @@ +#include "squeezelite.h" +#include "driver/i2s.h" + +static log_level loglevel; + +static bool running = true; + +extern struct outputstate output; +extern struct buffer *outputbuf; +extern struct buffer *streambuf; + +#define LOCK mutex_lock(outputbuf->mutex) +#define UNLOCK mutex_unlock(outputbuf->mutex) + +#define FRAME_BLOCK MAX_SILENCE_FRAMES + +extern u8_t *silencebuf; +extern u8_t *buf; + +#define I2S_NUM (0) +#define WAVE_FREQ_HZ (100) +#define PI (3.14159265) +#define I2S_BCK_IO (GPIO_NUM_26) +#define I2S_WS_IO (GPIO_NUM_25) +#define I2S_DO_IO (GPIO_NUM_22) +#define I2S_DI_IO (-1) +// buffer length is expressed in number of samples +#define I2S_BUF_LEN 60 + +static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, + s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, + ISAMPLE_T **cross_ptr); + +void set_volume(unsigned left, unsigned right) { + LOG_DEBUG("setting internal gain left: %u right: %u", left, right); + LOCK; + output.gainL = left; + output.gainR = right; + // TODO + output.gainL = FIXED_ONE; + output.gainR = FIXED_ONE; + UNLOCK; +} + +static void *output_thread(void *arg) { + bool start = true; + bool output_off = (output.state == OUTPUT_OFF); + bool probe_device = (arg != NULL); + int err; + + while (running) { + // todo: implement output off logic? + // todo: call i2s_set_clock here if rate is changed + LOCK; + + output.device_frames = 0; + output.updated = gettime_ms(); + output.frames_played_dmp = output.frames_played; + + _output_frames(I2S_BUF_LEN*2); // fill at least one DMA buffer with stereo signal + + UNLOCK; + } + + return 0; +} + +static pthread_t thread; + +void output_init_dac(log_level level, char *device, unsigned output_buf_size, + char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { + loglevel = level; + + LOG_INFO("init output DAC"); + + memset(&output, 0, sizeof(output)); + + output.start_frames = 0; //CONFIG_ //FRAME_BLOCK * 2; + output.write_cb = &_write_frames; + output.rate_delay = rate_delay; + + // ensure output rate is specified to avoid test open + if (!rates[0]) { + rates[0] = 44100; + } + device = "DAC"; + output_init_common(level, device, output_buf_size, rates, idle); + + i2s_config_t i2s_config = { + .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX + .sample_rate = output.current_sample_rate, + .bits_per_sample = BYTES_PER_FRAME * 8, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels + .communication_format = I2S_COMM_FORMAT_I2S + | I2S_COMM_FORMAT_I2S_MSB, + .dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers + .dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth + .use_apll = false, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 + }; + i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num = + I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO //Not used + }; + i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM, &pin_config); + i2s_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2); + +#if LINUX || OSX || FREEBSD || POSIX + pthread_attr_t attr; + pthread_attr_init(&attr); +#ifdef PTHREAD_STACK_MIN + pthread_attr_setstacksize(&attr, + PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); +#endif + pthread_create(&thread, &attr, output_thread, NULL); + pthread_attr_destroy(&attr); +#endif +#if WIN + thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL); +#endif + +} + +void output_close_dac(void) { + LOG_INFO("close output"); + LOCK; + running = false; + UNLOCK; + + output_close_common(); +} + +static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, + s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, + ISAMPLE_T **cross_ptr) { + + u8_t *obuf; + size_t i2s_bytes_write = 0; + + if (!silence) { + + if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { + _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); + } + + obuf = outputbuf->readp; + + } else { + + obuf = silencebuf; + } + + //_scale_and_pack_frames(buf + buffill * bytes_per_frame, (s32_t *)(void *)obuf, out_frames, gainL, gainR, output.format); + +// buffill += out_frames; + i2s_write(I2S_NUM, obuf, out_frames *BYTES_PER_FRAME, &i2s_bytes_write, 100); + + + + return (int)i2s_bytes_write * BYTES_PER_FRAME; + +} + diff --git a/main/squeezelite.h b/main/squeezelite.h index 44a78c48..4af0e392 100644 --- a/main/squeezelite.h +++ b/main/squeezelite.h @@ -81,6 +81,9 @@ #if defined(DACAUDIO) #undef DACAUDIO #define DACAUDIO 1 +#elif defined(BTAUDIO) +#undef BTAUDIO +#define BTAUDIO 1 #elif LINUX && !defined(PORTAUDIO) #define ALSA 1 #define PORTAUDIO 0 @@ -738,8 +741,7 @@ void _pa_open(void); #endif // output_dac.c -// todo: do we need a distinction between DACAUDIO and BTAUDIO? -#if DACAUDIO || BTAUDIO +#if DACAUDIO void set_volume(unsigned left, unsigned right); bool test_open(const char *device, unsigned rates[], bool userdef_rates); void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); @@ -747,6 +749,15 @@ void output_close_dac(void); void hal_bluetooth_init(log_level loglevel); #endif +//output_bt.c +#if BTAUDIO +void set_volume(unsigned left, unsigned right); +bool test_open(const char *device, unsigned rates[], bool userdef_rates); +void output_init_bt(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); +void output_close_bt(void); +void hal_bluetooth_init(log_level loglevel); +#endif + // output_stdout.c void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay); From 9b9ef1154600f7e2bf9047e5a878e43fe7849b78 Mon Sep 17 00:00:00 2001 From: Sebastien Leclerc Date: Mon, 3 Jun 2019 20:10:38 -0400 Subject: [PATCH 5/5] More DAC WIP Stable DAC out. Needs refactoring! --- main/esp32.c | 46 ++++++++--- main/esp_app_main.c | 4 + main/main.c | 4 +- main/output_bt.c | 2 +- main/output_dac.c | 188 ++++++++++++++++++++++++++++++++++---------- main/squeezelite.h | 2 +- 6 files changed, 187 insertions(+), 59 deletions(-) diff --git a/main/esp32.c b/main/esp32.c index 6a159435..a40324fe 100644 --- a/main/esp32.c +++ b/main/esp32.c @@ -25,7 +25,8 @@ #include "esp_gap_bt_api.h" #include "esp_a2dp_api.h" #include "esp_avrc_api.h" - +#include "esp_pthread.h" +#include "pthread.h" #define BT_AV_TAG "BT_AV" u8_t *bt_optr; extern log_level loglevel; @@ -50,6 +51,17 @@ int64_t connecting_timeout = 0; #define A2DP_TIMER_INIT connecting_timeout = esp_timer_get_time() +(CONFIG_A2DP_CONNECT_TIMEOUT_MS * 1000) #define IS_A2DP_TIMER_OVER esp_timer_get_time() >= connecting_timeout +#define FRAME_TO_BYTES(f) f*BYTES_PER_FRAME +#define BYTES_TO_FRAME(b) b/BYTES_PER_FRAME +#define FRAMES_TO_MS(f) 1000*f/output.current_sample_rate +#define BYTES_TO_MS(b) FRAMES_TO_MS(BYTES_TO_FRAME(b)) + +#define SET_MIN_MAX(val,var) var=val; if(varmax_##var) max_##var=var +#define RESET_MIN_MAX(var,mv) min_##var=mv##_MAX; max_##var=mv##_MIN +#define DECLARE_MIN_MAX(var,t,mv) static t min_##var = mv##_MAX, max_##var = mv##_MIN; t var=0 +#define DECLARE_ALL_MIN_MAX DECLARE_MIN_MAX(req, long,LONG); DECLARE_MIN_MAX(o, long,LONG); DECLARE_MIN_MAX(s, long,LONG); DECLARE_MIN_MAX(d, long,LONG); +#define RESET_ALL_MIN_MAX RESET_MIN_MAX(d,LONG); RESET_MIN_MAX(o,LONG); RESET_MIN_MAX(s,LONG); RESET_MIN_MAX(req,LONG); + void get_mac(u8_t mac[]) { esp_read_mac(mac, ESP_MAC_WIFI_STA); @@ -494,9 +506,8 @@ static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) { frames_t frames; static int count = 0; - static unsigned min_o = -1, max_o = 0, min_s = -1, max_s = 0; - unsigned o, s; + DECLARE_ALL_MIN_MAX; if (len < 0 || data == NULL ) { return 0; @@ -545,18 +556,27 @@ static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len) UNLOCK; - o = _buf_used(outputbuf); - if (o < min_o) min_o = o; - if (o > max_o) max_o = o; - - s = _buf_used(streambuf); - if (s < min_s) min_s = s; - if (s > max_s) max_s = s; + SET_MIN_MAX(_buf_used(outputbuf),o); + SET_MIN_MAX(_buf_used(streambuf),s); + SET_MIN_MAX(frames,req); if (!(count++ & 0x1ff)) { - LOG_INFO("frames %d (count:%d) (out:%d/%d/%d, stream:%d/%d/%d)", frames, count, max_o, min_o, o, max_s, min_s, s); - min_o = min_s = -1; - max_o = max_s = -0; + LOG_INFO( "count:%d" + "\n ----------+----------+-----------+ +----------+----------+----------------+" + "\n max | min | current| | max | min | current |" + "\n (ms) | (ms) | (ms)| | (frames) | (frames) | (frames)|" + "\n ----------+----------+-----------+ +----------+----------+----------------+" + "\nout %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nstream %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nN/A %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nrequested %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\n ----------+----------+-----------+ +----------+----------+----------------+", + count, + BYTES_TO_MS(max_o), BYTES_TO_MS(min_o),BYTES_TO_MS(o),max_o,min_o,o, + BYTES_TO_MS(max_s), BYTES_TO_MS(min_s),BYTES_TO_MS(s),max_s,min_s,s, + BYTES_TO_MS(max_d),BYTES_TO_MS(min_d),BYTES_TO_MS(d),max_d,min_d,d, + FRAMES_TO_MS(req),FRAMES_TO_MS(req),FRAMES_TO_MS(req), req, req,req); + RESET_ALL_MIN_MAX; } return frames * BYTES_PER_FRAME; diff --git a/main/esp_app_main.c b/main/esp_app_main.c index ca798b21..b9f2dec1 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -132,6 +132,10 @@ void app_main() "decode=" CONFIG_LOGGING_DECODE, "-d", "output=" CONFIG_LOGGING_OUTPUT, +#ifdef CONFIG_LOG_OPTION + "-d", + CONFIG_LOG_OPTION, +#endif "-b", "500:2000" diff --git a/main/main.c b/main/main.c index b2f4993c..688c5c29 100644 --- a/main/main.c +++ b/main/main.c @@ -525,7 +525,7 @@ int main(int argc, char **argv) { pidfile = optarg; break; #endif -#ifndef DACAUDIO +#if !DACAUDIO && !BTAUDIO case 'l': list_devices(); exit(0); @@ -803,6 +803,8 @@ int main(int argc, char **argv) { #if DACAUDIO output_close_dac(); +#elif BTAUDIO + output_close_bt(); #else if (!strcmp(output_device, "-")) { output_close_stdout(); diff --git a/main/output_bt.c b/main/output_bt.c index 0af24d69..88a72255 100644 --- a/main/output_bt.c +++ b/main/output_bt.c @@ -41,7 +41,7 @@ void output_init_bt(log_level level, char *device, unsigned output_buf_size, cha memset(&output, 0, sizeof(output)); - output.start_frames = 0; //CONFIG_ //FRAME_BLOCK * 2; + output.start_frames = FRAME_BLOCK; //CONFIG_ //FRAME_BLOCK * 2; output.write_cb = &_write_frames; output.rate_delay = rate_delay; diff --git a/main/output_dac.c b/main/output_dac.c index fa519c31..5b4ecb91 100644 --- a/main/output_dac.c +++ b/main/output_dac.c @@ -1,22 +1,29 @@ #include "squeezelite.h" #include "driver/i2s.h" + #include + #define I2S_NUM (0) #define I2S_BCK_IO (GPIO_NUM_26) #define I2S_WS_IO (GPIO_NUM_25) #define I2S_DO_IO (GPIO_NUM_22) #define I2S_DI_IO (-1) -// buffer length is expressed in number of samples -#define I2S_BUF_LEN 60 +#define TIMED_SECTION_START_MS_FORCE(x,force) { static time_t __aa_time_start = 0; if(hasTimeElapsed(&__aa_time_start,x,force)) { +#define TIMED_SECTION_START_MS(x) { static time_t __aa_time_start = 0; if(hasTimeElapsed(&__aa_time_start,x,false)){ +#define TIMED_SECTION_START_FORCE(x,force) TIMED_SECTION_START_MS(x * 1000UL,force) +#define TIMED_SECTION_START(x) TIMED_SECTION_START_MS(x * 1000UL) +#define TIMED_SECTION_END }} + static log_level loglevel; static bool running = true; - +static bool isI2SStarted=false; extern struct outputstate output; +extern struct buffer *streambuf; extern struct buffer *outputbuf; - +static i2s_config_t i2s_config; #if REPACK && BYTES_PER_FRAMES == 4 #error "REPACK is not compatible with BYTES_PER_FRAME=4" #endif @@ -25,7 +32,19 @@ extern struct buffer *outputbuf; #define UNLOCK mutex_unlock(outputbuf->mutex) #define FRAME_BLOCK MAX_SILENCE_FRAMES +#define DAC_OUTPUT_BUFFER_FRAMES FRAME_BLOCK +#define DAC_OUTPUT_BUFFER_RESERVE FRAME_BLOCK/2 +#define I2S_FRAME_SIZE 256 +#define FRAME_TO_BYTES(f) f*BYTES_PER_FRAME +#define BYTES_TO_FRAME(b) b/BYTES_PER_FRAME +#define FRAMES_TO_MS(f) 1000*f/output.current_sample_rate +#define BYTES_TO_MS(b) FRAMES_TO_MS(BYTES_TO_FRAME(b)) +#define SET_MIN_MAX(val,var) var=val; if(varmax_##var) max_##var=var +#define RESET_MIN_MAX(var,mv) min_##var=mv##_MAX; max_##var=mv##_MIN +#define DECLARE_MIN_MAX(var,t,mv) static t min_##var = mv##_MAX, max_##var = mv##_MIN; t var=0 +#define DECLARE_ALL_MIN_MAX DECLARE_MIN_MAX(req, long,LONG); DECLARE_MIN_MAX(o, long,LONG); DECLARE_MIN_MAX(s, long,LONG); DECLARE_MIN_MAX(d, long,LONG); DECLARE_MIN_MAX(duration, long,LONG);DECLARE_MIN_MAX(buffering, long,LONG);DECLARE_MIN_MAX(totalprocess, long,LONG); +#define RESET_ALL_MIN_MAX RESET_MIN_MAX(d,LONG); RESET_MIN_MAX(o,LONG); RESET_MIN_MAX(s,LONG); RESET_MIN_MAX(req,LONG); RESET_MIN_MAX(duration,LONG);RESET_MIN_MAX(buffering,LONG);RESET_MIN_MAX(totalprocess,LONG); extern u8_t *silencebuf; static u8_t *optr; @@ -35,7 +54,16 @@ static thread_type thread; static int _dac_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, ISAMPLE_T **cross_ptr); static void *output_thread(); - +bool hasTimeElapsed(time_t * lastTime, time_t delayMS, bool bforce) +{ + if (*lastTime <= gettime_ms() ||bforce) + { + *lastTime = gettime_ms() + delayMS; + return true; + } + else + return false; +} void set_volume(unsigned left, unsigned right) { LOG_DEBUG("setting internal gain left: %u right: %u", left, right); LOCK; @@ -44,9 +72,10 @@ void set_volume(unsigned left, unsigned right) { UNLOCK; } + void output_init_dac(log_level level, char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { loglevel = level; - optr = malloc(FRAME_BLOCK * BYTES_PER_FRAME); + optr = malloc(FRAME_TO_BYTES(DAC_OUTPUT_BUFFER_FRAMES)); if (!optr) { LOG_ERROR("unable to malloc buf"); return; @@ -60,7 +89,7 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch #else output.format = S32_LE; #endif - output.start_frames = FRAME_BLOCK * 2; + output.start_frames = DAC_OUTPUT_BUFFER_FRAMES*2; output.write_cb = &_dac_write_frames; output.rate_delay = rate_delay; @@ -77,24 +106,28 @@ void output_init_dac(log_level level, char *device, unsigned output_buf_size, ch output_init_common(level, device, output_buf_size, rates, idle); - i2s_config_t i2s_config = { - .mode = I2S_MODE_MASTER | I2S_MODE_TX, // Only TX - .sample_rate = output.current_sample_rate, - .bits_per_sample = BYTES_PER_FRAME * 8, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, //2-channels - .communication_format = I2S_COMM_FORMAT_I2S - | I2S_COMM_FORMAT_I2S_MSB, - .dma_buf_count = 6, //todo: tune this parameter. Expressed in numbrer of buffers - .dma_buf_len = I2S_BUF_LEN, // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth - .use_apll = false, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 //Interrupt level 1 - }; + + i2s_config.mode = I2S_MODE_MASTER | I2S_MODE_TX; // Only TX + i2s_config.sample_rate = output.current_sample_rate; + i2s_config.bits_per_sample = BYTES_PER_FRAME * 8/2; + i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; //2-channels + i2s_config.communication_format = I2S_COMM_FORMAT_I2S + | (output.format==S16_LE||output.format==S32_LE||output.format==S24_3LE)?I2S_COMM_FORMAT_I2S_LSB:I2S_COMM_FORMAT_I2S_MSB; + i2s_config.dma_buf_count = 6; //todo: tune this parameter. Expressed in numbrer of buffers. + i2s_config.dma_buf_len = I2S_FRAME_SIZE; // todo: tune this parameter. Expressed in number of samples. Byte size depends on bit depth + i2s_config.use_apll = false; + i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; //Interrupt level 1 + i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCK_IO, .ws_io_num = I2S_WS_IO, .data_out_num = I2S_DO_IO, .data_in_num = I2S_DI_IO //Not used }; + LOG_INFO("Initializing I2S with rate: %d, bits per sample: %d, buffer len: %d, number of buffers: %d ", + i2s_config.sample_rate, i2s_config.bits_per_sample, i2s_config.dma_buf_len, i2s_config.dma_buf_count); i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM, &pin_config); i2s_set_clk(I2S_NUM, output.current_sample_rate, i2s_config.bits_per_sample, 2); + isI2SStarted=false; + i2s_stop(I2S_NUM); #if LINUX || OSX || FREEBSD || POSIX pthread_attr_t attr; @@ -166,24 +199,40 @@ static int _dac_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32 #if REPACK _scale_and_pack_frames(optr, (s32_t *)(void *)obuf, out_frames, gainL, gainR, output.format); #endif - +// TIMED_SECTION_START_MS(500); +// LOG_INFO("Done moving data to out buffer"); +// TIMED_SECTION_END; return (int)out_frames; } +void wait_for_frames(size_t frames) +{ + usleep((1000* frames/output.current_sample_rate) ); +} + static void *output_thread() { - // buffer to hold output data so we can block on writing outside of output lock, allocated on init - u8_t *obuf = malloc(FRAME_BLOCK * BYTES_PER_FRAME); - int frames = 0; - size_t i2s_bytes_write = 0; +// // buffer to hold output data so we can block on writing outside of output lock, allocated on init +// u8_t *obuf = malloc(FRAME_BLOCK * BYTES_PER_FRAME); + u8_t *opos=optr; + frames_t frames=0, requested_frames = 0; + size_t used_buffer=0; + static int count = 0, count2=0; + uint32_t start_writing=0, start_i2s=0; + DECLARE_ALL_MIN_MAX; + + size_t i2s_bytes_write, i2s_bytes_to_write = 0; #if REPACK LOCK; switch (output.format) { + case S32_BE: case S32_LE: bytes_per_frame = 4 * 2; break; case S24_3LE: + case S24_3BE: bytes_per_frame = 3 * 2; break; case S16_LE: + case S16_BE: bytes_per_frame = 2 * 2; break; default: bytes_per_frame = 4 * 2; break; @@ -195,37 +244,90 @@ static void *output_thread() { bytes_per_frame = BYTES_PER_FRAME; #endif - while (running) { + while (running) { + start_writing=esp_timer_get_time(); LOCK; if (output.state == OUTPUT_OFF) { UNLOCK; + LOG_INFO("Output state is off."); + isI2SStarted=false; + i2s_stop(I2S_NUM); usleep(500000); continue; } + requested_frames = 0; + frames=0; + if(used_buffer==0) + { + // replenish buffer when it's empty + opos=optr; + requested_frames =DAC_OUTPUT_BUFFER_FRAMES; + + frames = _output_frames( requested_frames ); // Keep the dma buffer full + used_buffer+=FRAME_TO_BYTES(frames); + + } + UNLOCK; + if(frames>0) SET_MIN_MAX((esp_timer_get_time()-start_writing)/1000,buffering); // todo: call i2s_set_clock here if rate is changed - output.device_frames = 0; - output.updated = gettime_ms(); - output.frames_played_dmp = output.frames_played; - optr = obuf + frames * bytes_per_frame; - frames += _output_frames(FRAME_BLOCK); - - UNLOCK; - - if (frames) { - i2s_write(I2S_NUM, optr,frames*BYTES_PER_FRAME, &i2s_bytes_write, 100); - if(i2s_bytes_write!=frames*BYTES_PER_FRAME){ - LOG_WARN("Bytes available: %d, I2S wrote %d", frames*BYTES_PER_FRAME,i2s_bytes_write); + if (used_buffer ) + { + start_i2s=esp_timer_get_time(); + if(!isI2SStarted) + { + isI2SStarted=true; + i2s_start(I2S_NUM); } - usleep((frames * 1000 * 1000) / output.current_sample_rate); - frames = 0; - } else { - usleep((FRAME_BLOCK * 1000 * 1000) / output.current_sample_rate); - } - + i2s_write(I2S_NUM, opos,used_buffer, &i2s_bytes_write, portMAX_DELAY); + if(i2s_bytes_write!=used_buffer) + { + LOG_WARN("I2S DMA Overflow! available bytes: %d, I2S wrote %d bytes", used_buffer,i2s_bytes_write); + } + used_buffer -= i2s_bytes_write; + opos+=i2s_bytes_write; + output.device_frames =BYTES_TO_FRAME(used_buffer); + output.updated = gettime_ms(); + output.frames_played_dmp = output.frames_played-output.device_frames; + SET_MIN_MAX((esp_timer_get_time()-start_i2s)/1000,duration); + } + SET_MIN_MAX(duration+frames>0?buffering:0,totalprocess); + SET_MIN_MAX(_buf_used(outputbuf),o); + SET_MIN_MAX(_buf_used(streambuf),s); + SET_MIN_MAX(used_buffer,d); + SET_MIN_MAX(requested_frames,req); + if (!(count++ & 0x1ff)) { + LOG_INFO( "count:%d" + "\n ----------+----------+-----------+ +----------+----------+----------------+" + "\n max | min | current| | max | min | current |" + "\n (ms) | (ms) | (ms)| | (frames) | (frames) | (frames)|" + "\n ----------+----------+-----------+ +----------+----------+----------------+" + "\nout %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nstream %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nDMA overflow %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\nrequested %10d|%10d|%11d|" " |%10d|%10d|%16d|" + "\n ----------+----------+-----------+ +----------+----------+----------------+" + "\n" + "\n max (us) | min (us) | total(us) | " + "\n ----------+----------+-----------+ " + "\ni2s time (us):%10d|%10d|%11d|" + "\nbuffering(us):%10d|%10d|%11d|" + "\ntotal(us) :%10d|%10d|%11d|" + "\n ----------+----------+-----------+ ", + count, + BYTES_TO_MS(max_o), BYTES_TO_MS(min_o),BYTES_TO_MS(o),max_o,min_o,o, + BYTES_TO_MS(max_s), BYTES_TO_MS(min_s),BYTES_TO_MS(s),max_s,min_s,s, + BYTES_TO_MS(max_d),BYTES_TO_MS(min_d),BYTES_TO_MS(d),max_d,min_d,d, + FRAMES_TO_MS(max_req),FRAMES_TO_MS(min_req),FRAMES_TO_MS(req), max_req, min_req,req, + max_duration, min_duration, duration, + max_buffering, min_buffering, buffering, + max_totalprocess,min_totalprocess,totalprocess + ); + RESET_ALL_MIN_MAX; + } } return 0; diff --git a/main/squeezelite.h b/main/squeezelite.h index 4af0e392..a8e8c06c 100644 --- a/main/squeezelite.h +++ b/main/squeezelite.h @@ -647,7 +647,7 @@ typedef enum { OUTPUT_OFF = -1, OUTPUT_STOPPED = 0, OUTPUT_BUFFER, OUTPUT_RUNNIN typedef enum { PCM, DOP, DSD_U8, DSD_U16_LE, DSD_U32_LE, DSD_U16_BE, DSD_U32_BE, DOP_S24_LE, DOP_S24_3LE } dsd_format; typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, U8, U16_LE, U16_BE, U32_LE, U32_BE } output_format; #else -typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE } output_format; +typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE,S32_BE, S24_BE, S24_3BE, S16_BE } output_format; #endif typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state;