├── .gitignore ├── .gitmodules ├── README.md └── examples ├── A2DPInput ├── CMakeLists.txt ├── Makefile ├── README.md └── main │ ├── CMakeLists.txt │ ├── bt_app_av.c │ ├── bt_app_av.h │ ├── bt_app_core.c │ ├── bt_app_core.h │ ├── component.mk │ ├── input_a2dp.cpp │ ├── input_a2dp.h │ ├── main.c │ └── main.cpp ├── Sine ├── CMakeLists.txt ├── Makefile ├── README.md └── main │ ├── CMakeLists.txt │ ├── component.mk │ └── main.cpp └── SineEnvelope ├── CMakeLists.txt ├── Makefile ├── README.md └── main ├── CMakeLists.txt ├── component.mk └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .config 2 | *.o 3 | *.pyc 4 | 5 | # Example project files 6 | **/sdkconfig 7 | **/sdkconfig.old 8 | **/build 9 | 10 | # VS Code Settings 11 | .vscode/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ESP32AudioLibrary"] 2 | path = ESP32AudioLibrary 3 | url = https://github.com/macaba/ESP32AudioLibrary.git -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32AudioExamples 2 | 3 | ## Clone 4 | 5 | `git clone --recursive https://github.com/macaba/ESP32AudioExamples.git` 6 | 7 | ## Running under windows in WSL 8 | 9 | `sudo chmod 0666 /dev/ttyS4` 10 | 11 | ## Misc 12 | 13 | `git submodule update --recursive --remote` 14 | -------------------------------------------------------------------------------- /examples/A2DPInput/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(sine) 7 | -------------------------------------------------------------------------------- /examples/A2DPInput/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := a2dpinput 7 | 8 | # EXTRA_COMPONENT_DIRS += $(PROJECT_PATH)/../../../ 9 | EXTRA_COMPONENT_DIRS += ../../ESP32AudioLibrary 10 | include $(IDF_PATH)/make/project.mk 11 | 12 | -------------------------------------------------------------------------------- /examples/A2DPInput/README.md: -------------------------------------------------------------------------------- 1 | # Sine Example 2 | 3 | Outputs a sine wave to the I2S output 4 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS "main.c") 2 | set(COMPONENT_ADD_INCLUDEDIRS "") 3 | 4 | register_component() -------------------------------------------------------------------------------- /examples/A2DPInput/main/bt_app_av.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | 5 | Unless required by applicable law or agreed to in writing, this 6 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 7 | CONDITIONS OF ANY KIND, either express or implied. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "esp_log.h" 15 | 16 | #include "bt_app_core.h" 17 | #include "bt_app_av.h" 18 | #include "esp_bt_main.h" 19 | #include "esp_bt_device.h" 20 | #include "esp_gap_bt_api.h" 21 | #include "esp_a2dp_api.h" 22 | #include "esp_avrc_api.h" 23 | 24 | #include "freertos/FreeRTOS.h" 25 | #include "freertos/task.h" 26 | #include "driver/i2s.h" 27 | 28 | /* a2dp event handler */ 29 | static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param); 30 | /* avrc event handler */ 31 | static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param); 32 | 33 | static uint32_t s_pkt_cnt = 0; 34 | static esp_a2d_audio_state_t s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED; 35 | static const char *s_a2d_conn_state_str[] = {"Disconnected", "Connecting", "Connected", "Disconnecting"}; 36 | static const char *s_a2d_audio_state_str[] = {"Suspended", "Stopped", "Started"}; 37 | 38 | /* callback for A2DP sink */ 39 | void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) 40 | { 41 | switch (event) { 42 | case ESP_A2D_CONNECTION_STATE_EVT: 43 | case ESP_A2D_AUDIO_STATE_EVT: 44 | case ESP_A2D_AUDIO_CFG_EVT: { 45 | bt_app_work_dispatch(bt_av_hdl_a2d_evt, event, param, sizeof(esp_a2d_cb_param_t), NULL); 46 | break; 47 | } 48 | default: 49 | ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event); 50 | break; 51 | } 52 | } 53 | 54 | void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len) 55 | { 56 | size_t bytes_written; 57 | i2s_write(0, data, len, &bytes_written, portMAX_DELAY); 58 | if (++s_pkt_cnt % 100 == 0) { 59 | ESP_LOGI(BT_AV_TAG, "Audio packet count %u", s_pkt_cnt); 60 | } 61 | } 62 | 63 | void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param) 64 | { 65 | esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param); 66 | uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1); 67 | memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length); 68 | attr_text[rc->meta_rsp.attr_length] = 0; 69 | 70 | rc->meta_rsp.attr_text = attr_text; 71 | } 72 | 73 | void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) 74 | { 75 | switch (event) { 76 | case ESP_AVRC_CT_METADATA_RSP_EVT: 77 | bt_app_alloc_meta_buffer(param); 78 | /* fall through */ 79 | case ESP_AVRC_CT_CONNECTION_STATE_EVT: 80 | case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: 81 | case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: 82 | case ESP_AVRC_CT_REMOTE_FEATURES_EVT: { 83 | bt_app_work_dispatch(bt_av_hdl_avrc_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL); 84 | break; 85 | } 86 | default: 87 | ESP_LOGE(BT_AV_TAG, "Invalid AVRC event: %d", event); 88 | break; 89 | } 90 | } 91 | 92 | static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param) 93 | { 94 | ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); 95 | esp_a2d_cb_param_t *a2d = NULL; 96 | switch (event) { 97 | case ESP_A2D_CONNECTION_STATE_EVT: { 98 | a2d = (esp_a2d_cb_param_t *)(p_param); 99 | uint8_t *bda = a2d->conn_stat.remote_bda; 100 | ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]", 101 | s_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); 102 | if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) { 103 | esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); 104 | } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){ 105 | esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_NONE); 106 | } 107 | break; 108 | } 109 | case ESP_A2D_AUDIO_STATE_EVT: { 110 | a2d = (esp_a2d_cb_param_t *)(p_param); 111 | ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", s_a2d_audio_state_str[a2d->audio_stat.state]); 112 | s_audio_state = a2d->audio_stat.state; 113 | if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) { 114 | s_pkt_cnt = 0; 115 | } 116 | break; 117 | } 118 | case ESP_A2D_AUDIO_CFG_EVT: { 119 | a2d = (esp_a2d_cb_param_t *)(p_param); 120 | ESP_LOGI(BT_AV_TAG, "A2DP audio stream configuration, codec type %d", a2d->audio_cfg.mcc.type); 121 | // for now only SBC stream is supported 122 | if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) { 123 | int sample_rate = 16000; 124 | char oct0 = a2d->audio_cfg.mcc.cie.sbc[0]; 125 | if (oct0 & (0x01 << 6)) { 126 | sample_rate = 32000; 127 | } else if (oct0 & (0x01 << 5)) { 128 | sample_rate = 44100; 129 | } else if (oct0 & (0x01 << 4)) { 130 | sample_rate = 48000; 131 | } 132 | i2s_set_clk(0, sample_rate, 16, 2); 133 | 134 | ESP_LOGI(BT_AV_TAG, "Configure audio player %x-%x-%x-%x", 135 | a2d->audio_cfg.mcc.cie.sbc[0], 136 | a2d->audio_cfg.mcc.cie.sbc[1], 137 | a2d->audio_cfg.mcc.cie.sbc[2], 138 | a2d->audio_cfg.mcc.cie.sbc[3]); 139 | ESP_LOGI(BT_AV_TAG, "Audio player configured, sample rate=%d", sample_rate); 140 | } 141 | break; 142 | } 143 | default: 144 | ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); 145 | break; 146 | } 147 | } 148 | 149 | static void bt_av_new_track() 150 | { 151 | //Register notifications and request metadata 152 | esp_avrc_ct_send_metadata_cmd(0, ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_ARTIST | ESP_AVRC_MD_ATTR_ALBUM | ESP_AVRC_MD_ATTR_GENRE); 153 | esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_TRACK_CHANGE, 0); 154 | } 155 | 156 | void bt_av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter) 157 | { 158 | switch (event_id) { 159 | case ESP_AVRC_RN_TRACK_CHANGE: 160 | bt_av_new_track(); 161 | break; 162 | } 163 | } 164 | 165 | static void bt_av_hdl_avrc_evt(uint16_t event, void *p_param) 166 | { 167 | ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); 168 | esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param); 169 | switch (event) { 170 | case ESP_AVRC_CT_CONNECTION_STATE_EVT: { 171 | uint8_t *bda = rc->conn_stat.remote_bda; 172 | ESP_LOGI(BT_AV_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]", 173 | rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); 174 | 175 | if (rc->conn_stat.connected) { 176 | bt_av_new_track(); 177 | } 178 | break; 179 | } 180 | case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: { 181 | ESP_LOGI(BT_AV_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d", rc->psth_rsp.key_code, rc->psth_rsp.key_state); 182 | break; 183 | } 184 | case ESP_AVRC_CT_METADATA_RSP_EVT: { 185 | ESP_LOGI(BT_AV_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text); 186 | free(rc->meta_rsp.attr_text); 187 | break; 188 | } 189 | case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: { 190 | ESP_LOGI(BT_AV_TAG, "AVRC event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter); 191 | bt_av_notify_evt_handler(rc->change_ntf.event_id, rc->change_ntf.event_parameter); 192 | break; 193 | } 194 | case ESP_AVRC_CT_REMOTE_FEATURES_EVT: { 195 | ESP_LOGI(BT_AV_TAG, "AVRC remote features %x", rc->rmt_feats.feat_mask); 196 | break; 197 | } 198 | default: 199 | ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); 200 | break; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/bt_app_av.h: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #ifndef __BT_APP_AV_H__ 10 | #define __BT_APP_AV_H__ 11 | 12 | #include 13 | #include "esp_a2dp_api.h" 14 | #include "esp_avrc_api.h" 15 | 16 | #define BT_AV_TAG "BT_AV" 17 | 18 | /** 19 | * @brief callback function for A2DP sink 20 | */ 21 | void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); 22 | 23 | /** 24 | * @brief callback function for A2DP sink audio data stream 25 | */ 26 | void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len); 27 | 28 | /** 29 | * @brief callback function for AVRCP controller 30 | */ 31 | void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param); 32 | 33 | #endif /* __BT_APP_AV_H__*/ 34 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/bt_app_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/xtensa_api.h" 13 | #include "freertos/FreeRTOSConfig.h" 14 | #include "freertos/FreeRTOS.h" 15 | #include "freertos/queue.h" 16 | #include "freertos/task.h" 17 | #include "esp_log.h" 18 | #include "bt_app_core.h" 19 | 20 | static void bt_app_task_handler(void *arg); 21 | static bool bt_app_send_msg(bt_app_msg_t *msg); 22 | static void bt_app_work_dispatched(bt_app_msg_t *msg); 23 | 24 | static xQueueHandle s_bt_app_task_queue = NULL; 25 | static xTaskHandle s_bt_app_task_handle = NULL; 26 | 27 | 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) 28 | { 29 | ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len); 30 | 31 | bt_app_msg_t msg; 32 | memset(&msg, 0, sizeof(bt_app_msg_t)); 33 | 34 | msg.sig = BT_APP_SIG_WORK_DISPATCH; 35 | msg.event = event; 36 | msg.cb = p_cback; 37 | 38 | if (param_len == 0) { 39 | return bt_app_send_msg(&msg); 40 | } else if (p_params && param_len > 0) { 41 | if ((msg.param = malloc(param_len)) != NULL) { 42 | memcpy(msg.param, p_params, param_len); 43 | /* check if caller has provided a copy callback to do the deep copy */ 44 | if (p_copy_cback) { 45 | p_copy_cback(&msg, msg.param, p_params); 46 | } 47 | return bt_app_send_msg(&msg); 48 | } 49 | } 50 | 51 | return false; 52 | } 53 | 54 | static bool bt_app_send_msg(bt_app_msg_t *msg) 55 | { 56 | if (msg == NULL) { 57 | return false; 58 | } 59 | 60 | if (xQueueSend(s_bt_app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) { 61 | ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__); 62 | return false; 63 | } 64 | return true; 65 | } 66 | 67 | static void bt_app_work_dispatched(bt_app_msg_t *msg) 68 | { 69 | if (msg->cb) { 70 | msg->cb(msg->event, msg->param); 71 | } 72 | } 73 | 74 | static void bt_app_task_handler(void *arg) 75 | { 76 | bt_app_msg_t msg; 77 | for (;;) { 78 | if (pdTRUE == xQueueReceive(s_bt_app_task_queue, &msg, (portTickType)portMAX_DELAY)) { 79 | ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event); 80 | switch (msg.sig) { 81 | case BT_APP_SIG_WORK_DISPATCH: 82 | bt_app_work_dispatched(&msg); 83 | break; 84 | default: 85 | ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig); 86 | break; 87 | } // switch (msg.sig) 88 | 89 | if (msg.param) { 90 | free(msg.param); 91 | } 92 | } 93 | } 94 | } 95 | 96 | void bt_app_task_start_up(void) 97 | { 98 | s_bt_app_task_queue = xQueueCreate(10, sizeof(bt_app_msg_t)); 99 | xTaskCreate(bt_app_task_handler, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &s_bt_app_task_handle); 100 | return; 101 | } 102 | 103 | void bt_app_task_shut_down(void) 104 | { 105 | if (s_bt_app_task_handle) { 106 | vTaskDelete(s_bt_app_task_handle); 107 | s_bt_app_task_handle = NULL; 108 | } 109 | if (s_bt_app_task_queue) { 110 | vQueueDelete(s_bt_app_task_queue); 111 | s_bt_app_task_queue = NULL; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/bt_app_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | This example code is in the Public Domain (or CC0 licensed, at your option.) 3 | 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #ifndef __BT_APP_CORE_H__ 10 | #define __BT_APP_CORE_H__ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define BT_APP_CORE_TAG "BT_APP_CORE" 17 | 18 | #define BT_APP_SIG_WORK_DISPATCH (0x01) 19 | 20 | /** 21 | * @brief handler for the dispatched work 22 | */ 23 | typedef void (* bt_app_cb_t) (uint16_t event, void *param); 24 | 25 | /* message to be sent */ 26 | typedef struct { 27 | uint16_t sig; /*!< signal to bt_app_task */ 28 | uint16_t event; /*!< message event id */ 29 | bt_app_cb_t cb; /*!< context switch callback */ 30 | void *param; /*!< parameter area needs to be last */ 31 | } bt_app_msg_t; 32 | 33 | /** 34 | * @brief parameter deep-copy function to be customized 35 | */ 36 | typedef void (* bt_app_copy_cb_t) (bt_app_msg_t *msg, void *p_dest, void *p_src); 37 | 38 | /** 39 | * @brief work dispatcher for the application task 40 | */ 41 | 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); 42 | 43 | void bt_app_task_start_up(void); 44 | 45 | void bt_app_task_shut_down(void); 46 | 47 | #endif /* __BT_APP_CORE_H__ */ 48 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -------------------------------------------------------------------------------- /examples/A2DPInput/main/input_a2dp.cpp: -------------------------------------------------------------------------------- 1 | #include "input_a2dp.h" 2 | #include "freertos/FreeRTOS.h" 3 | #include "freertos/task.h" 4 | #include "nvs.h" 5 | #include "nvs_flash.h" 6 | #include "esp_system.h" 7 | #include "esp_log.h" 8 | 9 | #include "esp_bt.h" 10 | #include "bt_app_core.h" 11 | #include "bt_app_av.h" 12 | #include "esp_bt_main.h" 13 | #include "esp_bt_device.h" 14 | #include "esp_gap_bt_api.h" 15 | #include "esp_a2dp_api.h" 16 | #include "esp_avrc_api.h" 17 | 18 | // #define BT_AV_TAG "BT_AV" 19 | 20 | // /* event for handler "bt_av_hdl_stack_up */ 21 | // enum { 22 | // BT_APP_EVT_STACK_UP = 0, 23 | // }; 24 | 25 | // /* A2DP global state */ 26 | // enum { 27 | // APP_AV_STATE_IDLE, 28 | // APP_AV_STATE_DISCOVERING, 29 | // APP_AV_STATE_DISCOVERED, 30 | // APP_AV_STATE_UNCONNECTED, 31 | // APP_AV_STATE_CONNECTING, 32 | // APP_AV_STATE_CONNECTED, 33 | // APP_AV_STATE_DISCONNECTING, 34 | // }; 35 | 36 | // /* sub states of APP_AV_STATE_CONNECTED */ 37 | // enum { 38 | // APP_AV_MEDIA_STATE_IDLE, 39 | // APP_AV_MEDIA_STATE_STARTING, 40 | // APP_AV_MEDIA_STATE_STARTED, 41 | // APP_AV_MEDIA_STATE_STOPPING, 42 | // }; 43 | 44 | // #define BT_APP_HEART_BEAT_EVT (0xff00) 45 | 46 | // /// handler for bluetooth stack enabled events 47 | // static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); 48 | 49 | // /// callback function for A2DP source 50 | // static void bt_app_a2d_cb(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param); 51 | 52 | // /// callback function for A2DP source audio data stream 53 | // static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len); 54 | 55 | // static void a2d_app_heart_beat(void *arg); 56 | 57 | // /// A2DP application state machine 58 | // static void bt_app_av_sm_hdlr(uint16_t event, void *param); 59 | 60 | // /* A2DP application state machine handler for each state */ 61 | // static void bt_app_av_state_unconnected(uint16_t event, void *param); 62 | // static void bt_app_av_state_connecting(uint16_t event, void *param); 63 | // static void bt_app_av_state_connected(uint16_t event, void *param); 64 | // static void bt_app_av_state_disconnecting(uint16_t event, void *param); 65 | 66 | // static esp_bd_addr_t peer_bda = {0}; 67 | // static uint8_t peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; 68 | // static int m_a2d_state = APP_AV_STATE_IDLE; 69 | // static int m_media_state = APP_AV_MEDIA_STATE_IDLE; 70 | // static int m_intv_cnt = 0; 71 | // static int m_connecting_intv = 0; 72 | // static uint32_t m_pkt_cnt = 0; 73 | 74 | // TimerHandle_t tmr; 75 | 76 | // static char *bda2str(esp_bd_addr_t bda, char *str, size_t size) 77 | // { 78 | // if (bda == NULL || str == NULL || size < 18) { 79 | // return NULL; 80 | // } 81 | 82 | // uint8_t *p = bda; 83 | // sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", 84 | // p[0], p[1], p[2], p[3], p[4], p[5]); 85 | // return str; 86 | // } 87 | 88 | /* event for handler "bt_av_hdl_stack_up */ 89 | enum { 90 | BT_APP_EVT_STACK_UP = 0, 91 | }; 92 | 93 | /* handler for bluetooth stack enabled events */ 94 | static void bt_av_hdl_stack_evt(uint16_t event, void *p_param); 95 | 96 | void AudioInputA2DP::init() 97 | { 98 | if(initialised) 99 | return; 100 | 101 | update_setup(); //This signals that the update method will block so we don't end up with an update loop out of control 102 | 103 | /* Initialize NVS — it is used to store PHY calibration data */ 104 | esp_err_t err = nvs_flash_init(); 105 | if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { 106 | ESP_ERROR_CHECK(nvs_flash_erase()); 107 | err = nvs_flash_init(); 108 | } 109 | ESP_ERROR_CHECK(err); 110 | 111 | ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); 112 | 113 | esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); 114 | if ((err = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { 115 | ESP_LOGE(BT_AV_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(err)); 116 | return; 117 | } 118 | 119 | if ((err = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { 120 | ESP_LOGE(BT_AV_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(err)); 121 | return; 122 | } 123 | 124 | if ((err = esp_bluedroid_init()) != ESP_OK) { 125 | ESP_LOGE(BT_AV_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(err)); 126 | return; 127 | } 128 | 129 | if ((err = esp_bluedroid_enable()) != ESP_OK) { 130 | ESP_LOGE(BT_AV_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(err)); 131 | return; 132 | } 133 | 134 | /* create application task */ 135 | bt_app_task_start_up(); 136 | 137 | /* Bluetooth device name, connection mode and profile set up */ 138 | bt_app_work_dispatch(bt_av_hdl_stack_evt, BT_APP_EVT_STACK_UP, NULL, 0, NULL); 139 | 140 | /* Set default parameters for Secure Simple Pairing */ 141 | esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; 142 | esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; 143 | esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); 144 | 145 | /* 146 | * Set default parameters for Legacy Pairing 147 | * Use fixed pin code 148 | */ 149 | esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED; 150 | esp_bt_pin_code_t pin_code; 151 | pin_code[0] = '1'; 152 | pin_code[1] = '2'; 153 | pin_code[2] = '3'; 154 | pin_code[3] = '4'; 155 | esp_bt_gap_set_pin(pin_type, 4, pin_code); 156 | 157 | printf("Bluetooth initialised.\n"); 158 | initialised = true; 159 | } 160 | 161 | void IRAM_ATTR AudioInputA2DP::update(void) 162 | { 163 | audio_block_t *new_out=NULL; 164 | 165 | //Serial.println("update"); 166 | if(!initialised) 167 | { 168 | return; 169 | } 170 | 171 | size_t bytesRead = 0; 172 | //i2s_adc_enable(I2S_NUM_0); 173 | //i2s_read(I2S_NUM_0, (char*)&inputSampleBuffer, (AUDIO_BLOCK_SAMPLES * sizeof(uint32_t)), &bytesRead, portMAX_DELAY); //Block but yield to other tasks 174 | //i2s_adc_disable(I2S_NUM_0); 175 | 176 | new_out = allocate(); 177 | if(new_out != NULL){ 178 | for(int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) 179 | { 180 | //new_out->data[i] = ((float)(inputSampleBuffer[i] & 0xfff)/2048.0f) - 1.0f; 181 | } 182 | 183 | transmit(new_out); 184 | release(new_out); 185 | } 186 | 187 | //Serial.println(new_out->data[0]); 188 | } 189 | 190 | void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) 191 | { 192 | switch (event) { 193 | case ESP_BT_GAP_AUTH_CMPL_EVT: { 194 | if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { 195 | ESP_LOGI(BT_AV_TAG, "authentication success: %s", param->auth_cmpl.device_name); 196 | esp_log_buffer_hex(BT_AV_TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); 197 | } else { 198 | ESP_LOGE(BT_AV_TAG, "authentication failed, status:%d", param->auth_cmpl.stat); 199 | } 200 | break; 201 | } 202 | case ESP_BT_GAP_CFM_REQ_EVT: 203 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); 204 | esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); 205 | break; 206 | case ESP_BT_GAP_KEY_NOTIF_EVT: 207 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); 208 | break; 209 | case ESP_BT_GAP_KEY_REQ_EVT: 210 | ESP_LOGI(BT_AV_TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); 211 | break; 212 | default: { 213 | ESP_LOGI(BT_AV_TAG, "event: %d", event); 214 | break; 215 | } 216 | } 217 | return; 218 | } 219 | 220 | static void bt_av_hdl_stack_evt(uint16_t event, void *p_param) 221 | { 222 | ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event); 223 | switch (event) { 224 | case BT_APP_EVT_STACK_UP: { 225 | /* set up device name */ 226 | char *dev_name = "ESP_SPEAKER"; 227 | esp_bt_dev_set_device_name(dev_name); 228 | 229 | esp_bt_gap_register_callback(bt_app_gap_cb); 230 | /* initialize A2DP sink */ 231 | esp_a2d_register_callback(&bt_app_a2d_cb); 232 | esp_a2d_sink_register_data_callback(bt_app_a2d_data_cb); 233 | esp_a2d_sink_init(); 234 | 235 | /* initialize AVRCP controller */ 236 | esp_avrc_ct_init(); 237 | esp_avrc_ct_register_callback(bt_app_rc_ct_cb); 238 | 239 | /* set discoverable and connectable mode, wait to be connected */ 240 | esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE); 241 | break; 242 | } 243 | default: 244 | ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event); 245 | break; 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /examples/A2DPInput/main/input_a2dp.h: -------------------------------------------------------------------------------- 1 | #ifndef input_a2dp_h_ 2 | #define input_a2dp_h_ 3 | 4 | #include "Audio.h" 5 | #include "AudioStream.h" 6 | 7 | // #include "freertos/FreeRTOS.h" 8 | // #include "freertos/task.h" 9 | // #include "nvs.h" 10 | // #include "nvs_flash.h" 11 | // #include "esp_system.h" 12 | // #include "esp_log.h" 13 | 14 | // #include "esp_bt.h" 15 | // #include 16 | // //#include 17 | // #include "esp_bt_main.h" 18 | // #include "esp_bt_device.h" 19 | // #include "esp_gap_bt_api.h" 20 | // #include "esp_a2dp_api.h" 21 | // #include "esp_avrc_api.h" 22 | 23 | // #include "driver/i2s.h" 24 | 25 | // #include "freertos/xtensa_api.h" 26 | // #include "freertos/FreeRTOSConfig.h" 27 | // #include "freertos/queue.h" 28 | 29 | class AudioInputA2DP : public AudioStream 30 | { 31 | public: 32 | AudioInputA2DP() : AudioStream(0, NULL, "AudioInputA2DP") { blocking = true; } 33 | virtual void update(void); 34 | void start(){ init(); } 35 | private: 36 | void init(); 37 | //uint32_t inputSampleBuffer[AUDIO_BLOCK_SAMPLES]; 38 | }; 39 | 40 | #endif -------------------------------------------------------------------------------- /examples/A2DPInput/main/main.c: -------------------------------------------------------------------------------- 1 | #include "Audio.h" 2 | #include 3 | #include "freertos/FreeRTOS.h" 4 | #include "freertos/task.h" 5 | 6 | extern "C" { 7 | void app_main(); 8 | } 9 | 10 | void app_main() 11 | { 12 | 13 | } -------------------------------------------------------------------------------- /examples/A2DPInput/main/main.cpp: -------------------------------------------------------------------------------- 1 | //Example: A2DP Input 2 | 3 | #include "Audio.h" 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "esp_system.h" 8 | #include "esp_spi_flash.h" 9 | 10 | AudioControlI2S i2s; 11 | AudioControlPCM3060 pcm3060; 12 | 13 | // GUItool: begin automatically generated code 14 | AudioSynthWaveformSine sine1; //xy=398,548 15 | AudioSynthWaveformSine sine2; 16 | AudioInputI2S i2sInput1; //xy=401,596 17 | AudioOutputI2S i2sOutput1; //xy=750,560 18 | AudioEffectCalibration calibrationOutL; 19 | AudioEffectCalibration calibrationOutR; 20 | AudioEffectCalibration calibrationInL; 21 | AudioEffectCalibration calibrationInR; 22 | AudioConnection patchCord1(sine1, calibrationOutL); 23 | AudioConnection patchCord2(sine2, calibrationOutR); 24 | AudioConnection patchCord3(calibrationOutL, 0, i2sOutput1, 0); 25 | AudioConnection patchCord4(calibrationOutR, 0, i2sOutput1, 1); 26 | AudioConnection patchCord5(i2sInput1, 0, calibrationInL, 0); 27 | AudioConnection patchCord6(i2sInput1, 1, calibrationInR, 0); 28 | // GUItool: end automatically generated code 29 | 30 | void audioTask( void * parameter ) 31 | { 32 | AudioStream *p; 33 | for(;;){ 34 | p->update_all(); 35 | } 36 | } 37 | 38 | extern "C" { 39 | void app_main(); 40 | } 41 | 42 | void app_main() 43 | { 44 | /* Print chip information */ 45 | esp_chip_info_t chip_info; 46 | esp_chip_info(&chip_info); 47 | printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ", 48 | chip_info.cores, 49 | (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", 50 | (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); 51 | 52 | printf("silicon revision %d, ", chip_info.revision); 53 | 54 | printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), 55 | (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); 56 | 57 | AudioMemory(10); 58 | i2s.default_codec_rx_tx_24bit(); 59 | vTaskDelay(1000/portTICK_RATE_MS); 60 | pcm3060.init(); 61 | sine1.frequency(440); 62 | sine1.amplitude(0.1); 63 | sine2.frequency(880); 64 | sine2.amplitude(0.1); 65 | calibrationOutL.calibrate(0, 0.0156, 10, 11.34); 66 | calibrationOutR.calibrate(0, 0.069, 10, 11.372); 67 | calibrationInL.calibrate(0, -0.000757, 1, 0.958241); 68 | calibrationInR.calibrate(0, -0.000757, 1, 0.958241); //Made up these values 69 | 70 | xTaskCreatePinnedToCore(audioTask, "AudioTask", 10000, NULL, configMAX_PRIORITIES - 1, NULL, 1); 71 | } -------------------------------------------------------------------------------- /examples/Sine/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(sine) 7 | -------------------------------------------------------------------------------- /examples/Sine/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := sine 7 | 8 | EXTRA_COMPONENT_DIRS += ../../ESP32AudioLibrary 9 | include $(IDF_PATH)/make/project.mk 10 | 11 | -------------------------------------------------------------------------------- /examples/Sine/README.md: -------------------------------------------------------------------------------- 1 | # Sine Example 2 | 3 | Outputs a sine wave to the I2S output 4 | -------------------------------------------------------------------------------- /examples/Sine/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS "main.c") 2 | set(COMPONENT_ADD_INCLUDEDIRS "") 3 | 4 | register_component() -------------------------------------------------------------------------------- /examples/Sine/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -------------------------------------------------------------------------------- /examples/Sine/main/main.cpp: -------------------------------------------------------------------------------- 1 | //Example: Sine 2 | 3 | #include "Audio.h" 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | #include "esp_system.h" 8 | #include "esp_spi_flash.h" 9 | 10 | AudioControlI2S i2s; 11 | AudioControlPCM3060 pcm3060; 12 | 13 | // GUItool: begin automatically generated code 14 | AudioSynthWaveformSine sine1; //xy=398,548 15 | AudioSynthWaveformSine sine2; 16 | AudioInputI2S i2sInput1; //xy=401,596 17 | AudioOutputI2S i2sOutput1; //xy=750,560 18 | AudioEffectCalibration calibrationOutL; 19 | AudioEffectCalibration calibrationOutR; 20 | AudioEffectCalibration calibrationInL; 21 | AudioEffectCalibration calibrationInR; 22 | AudioConnection patchCord1(sine1, calibrationOutL); 23 | AudioConnection patchCord2(sine2, calibrationOutR); 24 | AudioConnection patchCord3(calibrationOutL, 0, i2sOutput1, 0); 25 | AudioConnection patchCord4(calibrationOutR, 0, i2sOutput1, 1); 26 | AudioConnection patchCord5(i2sInput1, 0, calibrationInL, 0); 27 | AudioConnection patchCord6(i2sInput1, 1, calibrationInR, 0); 28 | // GUItool: end automatically generated code 29 | 30 | void audioTask( void * parameter ) 31 | { 32 | AudioStream *p; 33 | for(;;){ 34 | p->update_all(); 35 | } 36 | } 37 | 38 | extern "C" { 39 | void app_main(); 40 | } 41 | 42 | void app_main() 43 | { 44 | /* Print chip information */ 45 | esp_chip_info_t chip_info; 46 | esp_chip_info(&chip_info); 47 | printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ", 48 | chip_info.cores, 49 | (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", 50 | (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : ""); 51 | 52 | printf("silicon revision %d, ", chip_info.revision); 53 | 54 | printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), 55 | (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); 56 | 57 | AudioMemory(10); 58 | i2s.default_codec_rx_tx_24bit(); 59 | vTaskDelay(1000/portTICK_RATE_MS); 60 | pcm3060.init(); 61 | sine1.frequency(440); 62 | sine1.amplitude(0.1); 63 | sine2.frequency(880); 64 | sine2.amplitude(0.1); 65 | calibrationOutL.calibrate(0, 0.0156, 10, 11.34); 66 | calibrationOutR.calibrate(0, 0.069, 10, 11.372); 67 | calibrationInL.calibrate(0, -0.000757, 1, 0.958241); 68 | calibrationInR.calibrate(0, -0.000757, 1, 0.958241); //Made up these values 69 | 70 | xTaskCreatePinnedToCore(audioTask, "AudioTask", 10000, NULL, configMAX_PRIORITIES - 1, NULL, 1); 71 | } -------------------------------------------------------------------------------- /examples/SineEnvelope/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(sine) 7 | -------------------------------------------------------------------------------- /examples/SineEnvelope/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := sine 7 | 8 | EXTRA_COMPONENT_DIRS += ../../ESP32AudioLibrary 9 | include $(IDF_PATH)/make/project.mk 10 | 11 | -------------------------------------------------------------------------------- /examples/SineEnvelope/README.md: -------------------------------------------------------------------------------- 1 | # Sine Example 2 | 3 | Outputs a sine wave to the I2S output 4 | -------------------------------------------------------------------------------- /examples/SineEnvelope/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COMPONENT_SRCS "main.c") 2 | set(COMPONENT_ADD_INCLUDEDIRS "") 3 | 4 | register_component() -------------------------------------------------------------------------------- /examples/SineEnvelope/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # "main" pseudo-component makefile. 3 | # 4 | # (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) -------------------------------------------------------------------------------- /examples/SineEnvelope/main/main.cpp: -------------------------------------------------------------------------------- 1 | //Example: SineEnvelope 2 | 3 | #include "Audio.h" 4 | #include 5 | #include "freertos/FreeRTOS.h" 6 | #include "freertos/task.h" 7 | 8 | // GUItool: begin automatically generated code 9 | AudioInputI2S i2sInput1; //xy=215,226 10 | AudioSynthWaveformSine sine1; //xy=306,401 11 | AudioEffectCalibration calibrationInL; //xy=374,212 12 | AudioEffectCalibration calibrationInR; //xy=375,246 13 | AudioEffectEnvelope envelope1; //xy=497,398 14 | AudioEffectCalibration calibrationOutR; //xy=809,245 15 | AudioEffectCalibration calibrationOutL; //xy=811,211 16 | AudioOutputI2S i2sOutput1; //xy=985,229 17 | AudioConnection patchCord1(i2sInput1, 0, calibrationInL, 0); 18 | AudioConnection patchCord2(i2sInput1, 1, calibrationInR, 0); 19 | AudioConnection patchCord3(sine1, envelope1); 20 | AudioConnection patchCord4(envelope1, calibrationOutL); 21 | AudioConnection patchCord5(envelope1, calibrationOutR); 22 | AudioConnection patchCord6(calibrationOutR, 0, i2sOutput1, 1); 23 | AudioConnection patchCord7(calibrationOutL, 0, i2sOutput1, 0); 24 | AudioControlI2S i2s; //xy=553,55 25 | AudioControlPCM3060 pcm3060; //xy=697,60 26 | // GUItool: end automatically generated code 27 | 28 | 29 | void audioTask( void * parameter ) 30 | { 31 | AudioStream *p; 32 | for(;;){ 33 | p->update_all(); 34 | } 35 | } 36 | 37 | void noteTask( void * parameter ) 38 | { 39 | for(;;){ 40 | envelope1.noteOn(); 41 | vTaskDelay(50 / portTICK_PERIOD_MS); 42 | envelope1.noteOff(); 43 | vTaskDelay(950 / portTICK_PERIOD_MS); 44 | } 45 | } 46 | 47 | extern "C" { 48 | void app_main(); 49 | } 50 | 51 | void app_main() 52 | { 53 | AudioMemory(10); 54 | i2s.default_codec_rx_tx_24bit(); 55 | vTaskDelay(1000/portTICK_RATE_MS); 56 | pcm3060.init(); 57 | sine1.frequency(220); 58 | sine1.amplitude(0.1); 59 | calibrationOutL.calibrate(0, 0.0156, 10, 11.34); 60 | calibrationOutR.calibrate(0, 0.069, 10, 11.372); 61 | calibrationInL.calibrate(0, -0.000757, 1, 0.958241); 62 | calibrationInR.calibrate(0, -0.000757, 1, 0.958241); //Made up these values 63 | 64 | xTaskCreatePinnedToCore(audioTask, "AudioTask", 10000, NULL, configMAX_PRIORITIES - 1, NULL, 1); 65 | xTaskCreatePinnedToCore(noteTask, "NoteTask", 1000, NULL, 2, NULL, 1); 66 | } --------------------------------------------------------------------------------