Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 12 of 12

Thread: Teensy 4.1 Bluetooth Metadata

  1. #1

    Teensy 4.1 Bluetooth Metadata

    Code:
    #include <arduino.h>
    #include "esp32_bt_music_receiver.h"
    
    #define CONFIG_EXAMPLE_I2S_BCK_PIN 26 // (A0 on huzzah32, BCLK2 pin 4 on T4.1)
    #define CONFIG_EXAMPLE_I2S_LRCK_PIN 25 // (A1 on huzzah32, LRCK2 pin 3 on T4.1)
    #define CONFIG_EXAMPLE_I2S_DATA_PIN 12 // (21 on huzzah32, IN2 pin 5 on T4.1)
    // T4.1 GND is connected to huzzah32 GND (down 4 on left side)
    
    void setup() {
    
      static const i2s_config_t i2s_config = {
        .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = 44100,
        .bits_per_sample = (i2s_bits_per_sample_t)16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
        .intr_alloc_flags = 0, // default interrupt priority
        .dma_buf_count = 8,
        .dma_buf_len = 128,//64,
        .use_apll = true,
        .tx_desc_auto_clear = true                                              //Auto clear tx descriptor on underflow
      };
      i2s_pin_config_t pin_config = {
        .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
        .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
        .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
        .data_in_num = -1                                                       //Not used
      };
    
      a2d_sink.set_i2s_config(i2s_config);
      a2d_sink.set_pin_config(pin_config);
      a2d_sink.start("MyMusic");
      Serial.begin(115200);
    }
    
    unsigned long last = 0;
    
    void loop() {
      if ((millis() - last) > 100) {
        last = millis();
        Serial.print( ESP_AVRC_MD_ATTR_TITLE);
        Serial.print( ESP_AVRC_MD_ATTR_ARTIST);
       // Serial.print( ESP_AVRC_MD_ATTR_ALBUM);
        //Serial.println(ESP_AVRC_MD_ATTR_GENRE);
    
        //Serial.print(a2d_sink.get_audio_state());
        // Serial.println(a2d_sink.get_audio_type());
      }
    }

    With the code above, I am trying to extract the title and artist of any song. I was unsuccessful. I am using a ESP32, Teensy 4.1, and the REV D Audio shield. In addition, I am using the library
    HTML Code:
    https://github.com/JayShoe/esp32_T4_bt_music_receiver
    . The Bluetooth signal is flawless. Can anyone, please, point me in the right direction to get the audio files attributes?. Thanks.

  2. #2
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    It looks like pschatzmann added some functionality for this in his code.

    https://github.com/pschatzmann/ESP32-A2DP/issues/29

  3. #3
    Thanks for your reply. I made the proper code changes but nothing shows on Serial port. I tried youtube audio, vimeo audio, a song I purchased at Amazon, and other songs I had laying around. No luck. I also used and LGClick image for larger version. 

Name:	Bluetooth Protocols.JPG 
Views:	7 
Size:	49.3 KB 
ID:	28795 cell phone (BT protocols on image) and a Galaxy 20 to see if it was BT and/or app issue. Thanks for your help



    Code:
    #include <arduino.h>
    #include "esp32_bt_music_receiver.h"
    
    #define CONFIG_EXAMPLE_I2S_BCK_PIN 26 // (A0 on huzzah32, BCLK2 pin 4 on T4.1)
    #define CONFIG_EXAMPLE_I2S_LRCK_PIN 25 // (A1 on huzzah32, LRCK2 pin 3 on T4.1)
    #define CONFIG_EXAMPLE_I2S_DATA_PIN 12 // (21 on huzzah32, IN2 pin 5 on T4.1)
    // T4.1 GND is connected to huzzah32 GND (down 4 on left side)
    
    BlootoothA2DSink a2d_sink;
    void data_received_callback() {
      Serial.println("Data packet received");
    }
    void avrc_metadata_callback(uint8_t data1, const uint8_t *data2)
    {
      Serial.printf("AVRC metadata rsp: attribute id 0x%x, %s", data1, data2);
    }
    
    
    void setup() {
       Serial.begin(115200);
      static const i2s_config_t i2s_config = {
        .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = 44100,
        .bits_per_sample = (i2s_bits_per_sample_t)16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
        .intr_alloc_flags = 0, // default interrupt priority
        .dma_buf_count = 8,
        .dma_buf_len = 128,//64,
        .use_apll = true,
        .tx_desc_auto_clear = true                                              //Auto clear tx descriptor on underflow
      };
      i2s_pin_config_t pin_config = {
        .bck_io_num = CONFIG_EXAMPLE_I2S_BCK_PIN,
        .ws_io_num = CONFIG_EXAMPLE_I2S_LRCK_PIN,
        .data_out_num = CONFIG_EXAMPLE_I2S_DATA_PIN,
        .data_in_num = -1                                                       //Not used
      };
    
    
      a2d_sink.set_i2s_config(i2s_config);
      a2d_sink.set_pin_config(pin_config);
      a2d_sink.set_AVRC_metadata(avrc_metadata_callback);
      a2d_sink.start("MyMusic");
     
    }
    
    unsigned long last = 0;
    
    void loop() {
      if ((millis() - last) > 100) {
        last = millis();
      
      }
    }
    Code:
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    
    //     http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    //
    // Copyright 2020 Phil Schatzmann
    // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
    
    
    #include "esp32_bt_music_receiver.h"
    
    /**
       Some data that must be avaliable for C calls
    */
    // to support static callback functions
    BlootoothA2DSink* actualBlootoothA2DSink;
    i2s_port_t i2s_port;
    
    // Forward declarations for C Callback functions for ESP32 Framework
    extern "C" void app_task_handler_2(void *arg);
    extern "C" void audio_data_callback_2(const uint8_t *data, uint32_t len);
    extern "C" void app_a2d_callback_2(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
    extern "C" void app_rc_ct_callback_2(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
    static void av_hdl_stack_evt_2(uint16_t event, void *p_param);
    static void av_hdl_a2d_evt_2(uint16_t event, void *p_param);
    static void av_hdl_avrc_evt_2(uint16_t event, void *p_param);
    
    
    /**
       Constructor
    */
    BlootoothA2DSink::BlootoothA2DSink() {
      actualBlootoothA2DSink = this;
      // default i2s port is 0
      i2s_port = (i2s_port_t) 0;
    
      // setup default i2s config
      i2s_config = {
        .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
        .sample_rate = 44100,
        .bits_per_sample = (i2s_bits_per_sample_t)16,
        .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
        .communication_format = (i2s_comm_format_t) (I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
        .intr_alloc_flags = 0, // default interrupt priority
        .dma_buf_count = 8,
        .dma_buf_len = 64,
        .use_apll = false
      };
    
      // setup default pins
      pin_config = {
        .bck_io_num = 26,
        .ws_io_num = 25,
        .data_out_num = 12,
        .data_in_num = I2S_PIN_NO_CHANGE
      };
    
    }
    BlootoothA2DSink::~BlootoothA2DSink() {
      app_task_shut_down();
    
      if (i2s_driver_uninstall(i2s_port) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to uninstall i2s");
      }
    
      if (esp_a2d_sink_deinit() != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to deinit a2d sink");
      }
    
      if (esp_a2d_sink_disconnect != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to disconnect a2d sink");
      }
    
      if (esp_bluedroid_deinit() != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to deinit bluetooth");
      }
    
      if (esp_bluedroid_disable() != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to disable bluetooth");
      }
    }
    ////**********
    void BlootoothA2DSink::set_AVRC_metadata(void (*callBack)(uint8_t, const uint8_t*)) {
      this->AVRC_metadata = callBack;
    }
    ////**********
    void BlootoothA2DSink::set_pin_config(i2s_pin_config_t pin_config) {
      this->pin_config = pin_config;
    }
    
    void BlootoothA2DSink::set_i2s_port(i2s_port_t i2s_num) {
      i2s_port = i2s_num;
    }
    
    void BlootoothA2DSink::set_i2s_config(i2s_config_t i2s_config) {
      this->i2s_config = i2s_config;
    }
    
    
    /**
       Main function to start the Bluetooth Processing
    */
    void BlootoothA2DSink::start(char* name)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      //store parameters
      if (name) {
        this->bt_name = name;
      }
      ESP_LOGI(BT_AV_TAG, "Device name will be set to '%s'", this->bt_name);
    
      // setup bluetooth
      init_bluetooth();
    
      // create application task
      app_task_start_up();
    
      // Bluetooth device name, connection mode and profile set up
      app_work_dispatch(av_hdl_stack_evt_2, BT_APP_EVT_STACK_UP, NULL, 0);
    
      // setup i2s
      i2s_driver_install(i2s_port, &i2s_config, 0, NULL);
    
      // pins are only relevant when music is not sent to internal DAC
      if (i2s_config.mode & I2S_MODE_DAC_BUILT_IN) {
        ESP_LOGI(BT_AV_TAG, "Output will go to DAC pins");
        i2s_set_pin(i2s_port, NULL);
      } else {
        i2s_set_pin(i2s_port, &pin_config);
      }
    
    }
    
    
    esp_a2d_audio_state_t BlootoothA2DSink::get_audio_state() {
      return audio_state;
    }
    
    esp_a2d_mct_t BlootoothA2DSink::get_audio_type() {
      return audio_type;
    }
    
    
    int BlootoothA2DSink::init_bluetooth()
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (!btStart()) {
        ESP_LOGE(BT_AV_TAG, "Failed to initialize controller");
        return false;
      }
      ESP_LOGI(BT_AV_TAG, "controller initialized");
    
      if (esp_bluedroid_init() != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to initialize bluedroid");
        return false;
      }
      ESP_LOGI(BT_AV_TAG, "bluedroid initialized");
    
      if (esp_bluedroid_enable() != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "Failed to enable bluedroid");
        return false;
      }
      ESP_LOGI(BT_AV_TAG, "bluedroid enabled");
    
    }
    
    bool BlootoothA2DSink::app_work_dispatch(app_callback_t p_cback, uint16_t event, void *p_params, int param_len)
    {
      ESP_LOGD(BT_APP_CORE_TAG, "%s event 0x%x, param len %d", __func__, event, param_len);
    
      app_msg_t msg;
      memset(&msg, 0, sizeof(app_msg_t));
    
      msg.sig = APP_SIG_WORK_DISPATCH;
      msg.event = event;
      msg.cb = p_cback;
    
      if (param_len == 0) {
        return app_send_msg(&msg);
      } else if (p_params && param_len > 0) {
        if ((msg.param = malloc(param_len)) != NULL) {
          memcpy(msg.param, p_params, param_len);
          return app_send_msg(&msg);
        }
      }
    
      return false;
    }
    
    void BlootoothA2DSink::app_work_dispatched(app_msg_t *msg)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (msg->cb) {
        msg->cb(msg->event, msg->param);
      }
    }
    
    
    bool BlootoothA2DSink::app_send_msg(app_msg_t *msg)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (msg == NULL) {
        return false;
      }
    
      if (xQueueSend(app_task_queue, msg, 10 / portTICK_RATE_MS) != pdTRUE) {
        ESP_LOGE(BT_APP_CORE_TAG, "%s xQueue send failed", __func__);
        return false;
      }
      return true;
    }
    
    
    void BlootoothA2DSink::app_task_handler()
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      app_msg_t msg;
      for (;;) {
        if (pdTRUE == xQueueReceive(app_task_queue, &msg, (portTickType)portMAX_DELAY)) {
          ESP_LOGD(BT_APP_CORE_TAG, "%s, sig 0x%x, 0x%x", __func__, msg.sig, msg.event);
          switch (msg.sig) {
            case APP_SIG_WORK_DISPATCH:
              ESP_LOGW(BT_APP_CORE_TAG, "%s, APP_SIG_WORK_DISPATCH sig: %d", __func__, msg.sig);
              app_work_dispatched(&msg);
              break;
            default:
              ESP_LOGW(BT_APP_CORE_TAG, "%s, unhandled sig: %d", __func__, msg.sig);
              break;
          } // switch (msg.sig)
    
          if (msg.param) {
            free(msg.param);
          }
        }
      }
    }
    
    void BlootoothA2DSink::app_task_start_up(void)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      app_task_queue = xQueueCreate(10, sizeof(app_msg_t));
      xTaskCreate(app_task_handler_2, "BtAppT", 2048, NULL, configMAX_PRIORITIES - 3, &app_task_handle);
      return;
    }
    
    void  BlootoothA2DSink::app_task_shut_down(void)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (app_task_handle) {
        vTaskDelete(app_task_handle);
        app_task_handle = NULL;
      }
      if (app_task_queue) {
        vQueueDelete(app_task_queue);
        app_task_queue = NULL;
      }
    }
    
    
    void  BlootoothA2DSink::app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(param);
      uint8_t *attr_text = (uint8_t *) malloc (rc->meta_rsp.attr_length + 1);
      memcpy(attr_text, rc->meta_rsp.attr_text, rc->meta_rsp.attr_length);
      attr_text[rc->meta_rsp.attr_length] = 0;
    
      rc->meta_rsp.attr_text = attr_text;
    }
    
    void  BlootoothA2DSink::app_rc_ct_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
    
      switch (event) {
        case ESP_AVRC_CT_METADATA_RSP_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_CT_METADATA_RSP_EVT", __func__);
          app_alloc_meta_buffer(param);
          app_work_dispatch(av_hdl_avrc_evt_2, event, param, sizeof(esp_avrc_ct_cb_param_t));
          break;
        case ESP_AVRC_CT_CONNECTION_STATE_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_CT_CONNECTION_STATE_EVT", __func__);
          app_work_dispatch(av_hdl_avrc_evt_2, event, param, sizeof(esp_avrc_ct_cb_param_t));
          break;
        case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_CT_PASSTHROUGH_RSP_EVT", __func__);
          app_work_dispatch(av_hdl_avrc_evt_2, event, param, sizeof(esp_avrc_ct_cb_param_t));
          break;
        case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_CT_CHANGE_NOTIFY_EVT", __func__);
          app_work_dispatch(av_hdl_avrc_evt_2, event, param, sizeof(esp_avrc_ct_cb_param_t));
          break;
        case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
            ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_CT_REMOTE_FEATURES_EVT", __func__);
            app_work_dispatch(av_hdl_avrc_evt_2, event, param, sizeof(esp_avrc_ct_cb_param_t));
            break;
          }
        default:
          ESP_LOGE(BT_AV_TAG, "Invalid AVRC event: %d", event);
          break;
      }
    }
    
    void  BlootoothA2DSink::av_hdl_a2d_evt(uint16_t event, void *p_param)
    {
      ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event);
      esp_a2d_cb_param_t *a2d = NULL;
      switch (event) {
        case ESP_A2D_CONNECTION_STATE_EVT: {
            ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_CONNECTION_STATE_EVT", __func__);
            a2d = (esp_a2d_cb_param_t *)(p_param);
            uint8_t *bda = a2d->conn_stat.remote_bda;
            ESP_LOGI(BT_AV_TAG, "A2DP connection state: %s, [%02x:%02x:%02x:%02x:%02x:%02x]",
                     m_a2d_conn_state_str[a2d->conn_stat.state], bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
            break;
          }
        case ESP_A2D_AUDIO_STATE_EVT: {
            ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_AUDIO_STATE_EVT", __func__);
            a2d = (esp_a2d_cb_param_t *)(p_param);
            ESP_LOGI(BT_AV_TAG, "A2DP audio state: %s", m_a2d_audio_state_str[a2d->audio_stat.state]);
            m_audio_state = a2d->audio_stat.state;
            if (ESP_A2D_AUDIO_STATE_STARTED == a2d->audio_stat.state) {
              m_pkt_cnt = 0;
            }
            break;
          }
        case ESP_A2D_AUDIO_CFG_EVT: {
            ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_AUDIO_CFG_EVT", __func__);
            esp_a2d_cb_param_t *esp_a2d_callback_param = (esp_a2d_cb_param_t *)(p_param);
            audio_type = esp_a2d_callback_param->audio_cfg.mcc.type;
            a2d = (esp_a2d_cb_param_t *)(p_param);
            ESP_LOGI(BT_AV_TAG, "a2dp audio_cfg_cb , codec type %d", a2d->audio_cfg.mcc.type);
            // for now only SBC stream is supported
            if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
              i2s_config.sample_rate = 16000;
              char oct0 = a2d->audio_cfg.mcc.cie.sbc[0];
              if (oct0 & (0x01 << 6)) {
                i2s_config.sample_rate = 32000;
              } else if (oct0 & (0x01 << 5)) {
                i2s_config.sample_rate = 44100;
              } else if (oct0 & (0x01 << 4)) {
                i2s_config.sample_rate = 48000;
              }
    
              i2s_set_clk(i2s_port, i2s_config.sample_rate, i2s_config.bits_per_sample, (i2s_channel_t)2);
    
              ESP_LOGI(BT_AV_TAG, "configure audio player %x-%x-%x-%x\n",
                       a2d->audio_cfg.mcc.cie.sbc[0],
                       a2d->audio_cfg.mcc.cie.sbc[1],
                       a2d->audio_cfg.mcc.cie.sbc[2],
                       a2d->audio_cfg.mcc.cie.sbc[3]);
              ESP_LOGI(BT_AV_TAG, "audio player configured, samplerate=%d", i2s_config.sample_rate);
            }
            break;
          }
        default:
          ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
          break;
      }
    }
    
    void  BlootoothA2DSink::av_new_track()
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      //Register notifications and request metadata
      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);
      //esp_avrc_ct_send_register_notification_cmd(1, ESP_AVRC_RN_TRACK_CHANGE, 0);
    }
    
    void  BlootoothA2DSink::av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      switch (event_id) {
        case ESP_AVRC_RN_TRACK_CHANGE:
          ESP_LOGD(BT_AV_TAG, "%s ESP_AVRC_RN_TRACK_CHANGE %d", __func__, event_id);
          av_new_track();
          break;
        default:
          ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event_id);
          break;
      }
    }
    
    void  BlootoothA2DSink::av_hdl_avrc_evt(uint16_t event, void *p_param)
    {
      ESP_LOGD(BT_AV_TAG, "%s evt %d", __func__, event);
      esp_avrc_ct_cb_param_t *rc = (esp_avrc_ct_cb_param_t *)(p_param);
      switch (event) {
        case ESP_AVRC_CT_CONNECTION_STATE_EVT: {
            uint8_t *bda = rc->conn_stat.remote_bda;
            ESP_LOGI(BT_AV_TAG, "AVRC conn_state evt: state %d, [%02x:%02x:%02x:%02x:%02x:%02x]",
                     rc->conn_stat.connected, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
    
            if (rc->conn_stat.connected) {
              av_new_track();
            }
            break;
          }
        case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
            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);
            break;
          }
        ////**********
        case ESP_AVRC_CT_METADATA_RSP_EVT: {
            if (AVRC_metadata != NULL) {
              AVRC_metadata(rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
            }
            ESP_LOGI(BT_AV_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
            free(rc->meta_rsp.attr_text);
            break;
          }
        ////**********
        case ESP_AVRC_CT_CHANGE_NOTIFY_EVT: {
            ESP_LOGI(BT_AV_TAG, "AVRC event notification: %d, param: %d", rc->change_ntf.event_id, rc->change_ntf.event_parameter);
            av_notify_evt_handler(rc->change_ntf.event_id, rc->change_ntf.event_parameter);
            break;
          }
        case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
            ESP_LOGI(BT_AV_TAG, "AVRC remote features %x", rc->rmt_feats.feat_mask);
            break;
          }
        default:
          ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
          break;
      }
    }
    
    
    void  BlootoothA2DSink::av_hdl_stack_evt(uint16_t event, void *p_param)
    {
      switch (event) {
        case BT_APP_EVT_STACK_UP: {
            ESP_LOGD(BT_AV_TAG, "%s av_hdl_stack_evt %s", __func__, "BT_APP_EVT_STACK_UP");
            /* set up device name */
            esp_bt_dev_set_device_name(bt_name);
    
            /* initialize A2DP sink */
            esp_a2d_register_callback(app_a2d_callback_2);
            esp_a2d_sink_register_data_callback(audio_data_callback_2);
            esp_a2d_sink_init();
    
            /* initialize AVRCP controller */
            esp_avrc_ct_init();
            esp_avrc_ct_register_callback(app_rc_ct_callback_2);
    
            /* set discoverable and connectable mode, wait to be connected */
            esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
            break;
          }
        default:
          ESP_LOGE(BT_AV_TAG, "%s unhandled evt %d", __func__, event);
          break;
      }
    }
    
    
    /* callback for A2DP sink */
    void  BlootoothA2DSink::app_a2d_callback(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param)
    {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      switch (event) {
        case ESP_A2D_CONNECTION_STATE_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_CONNECTION_STATE_EVT", __func__);
          app_work_dispatch(av_hdl_a2d_evt_2, event, param, sizeof(esp_a2d_cb_param_t));
          break;
        case ESP_A2D_AUDIO_STATE_EVT:
          ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_AUDIO_STATE_EVT", __func__);
          audio_state = param->audio_stat.state;
          app_work_dispatch(av_hdl_a2d_evt_2, event, param, sizeof(esp_a2d_cb_param_t));
          break;
        case ESP_A2D_AUDIO_CFG_EVT: {
            ESP_LOGD(BT_AV_TAG, "%s ESP_A2D_AUDIO_CFG_EVT", __func__);
            app_work_dispatch(av_hdl_a2d_evt_2, event, param, sizeof(esp_a2d_cb_param_t));
            break;
          }
        default:
          ESP_LOGE(BT_AV_TAG, "Invalid A2DP event: %d", event);
          break;
      }
    }
    
    void  BlootoothA2DSink::audio_data_callback(const uint8_t *data, uint32_t len) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
    
      size_t i2s_bytes_written;
      if (i2s_write(i2s_port, (void*) data, len, &i2s_bytes_written, portMAX_DELAY) != ESP_OK) {
        ESP_LOGE(BT_AV_TAG, "i2s_write has failed");
      }
    
      if (i2s_bytes_written < len) {
        ESP_LOGE(BT_AV_TAG, "Timeout: not all bytes were written to I2S");
      }
    }
    
    
    /**
       Static methods as internal callbacks
    */
    void av_hdl_stack_evt_2(uint16_t event, void *p_param) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->av_hdl_stack_evt(event, p_param);
    }
    void av_hdl_a2d_evt_2(uint16_t event, void *p_param) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->av_hdl_a2d_evt(event, p_param);
    }
    void av_hdl_avrc_evt_2(uint16_t event, void *p_param) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->av_hdl_avrc_evt(event, p_param);
    }
    
    
    /**
       C Callback Functions needed for the ESP32 API
    */
    extern "C" void app_task_handler_2(void *arg) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->app_task_handler();
    }
    
    extern "C" void audio_data_callback_2(const uint8_t *data, uint32_t len) {
      //ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->audio_data_callback(data, len);
    }
    
    extern "C" void app_a2d_callback_2(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->app_a2d_callback(event, param);
    }
    
    extern "C" void app_rc_ct_callback_2(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) {
      ESP_LOGD(BT_AV_TAG, "%s", __func__);
      if (actualBlootoothA2DSink)
        actualBlootoothA2DSink->app_rc_ct_callback(event, param);
    }
    Code:
    #ifndef A2D_SINK
    #define A2D_SINK
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include "freertos/FreeRTOS.h"
    #include "freertos/task.h"
    #include "nvs.h"
    #include "nvs_flash.h"
    #include "esp_system.h"
    #include "esp_bt.h"
    #include "esp_bt_main.h"
    #include "esp_bt_device.h"
    #include "esp_gap_bt_api.h"
    #include "esp_a2dp_api.h"
    #include "driver/i2s.h"
    #include "esp_avrc_api.h"
    
    #ifdef ARDUINO_ARCH_ESP32
    #include "esp32-hal-log.h"
    #include "esp32-hal-bt.h"
    #endif
    
    #define APP_CORE_TAG  "BT_APP_CORE"
    #define APP_SIG_WORK_DISPATCH (0x01)
    
    /**
     * @brief     handler for the dispatched work
     */
    typedef void (* app_callback_t) (uint16_t event, void *param);
    
    /* message to be sent */
    typedef struct {
        uint16_t             sig;      /*!< signal to app_task */
        uint16_t             event;    /*!< message event id */
        app_callback_t          cb;       /*!< context switch callback */
        void                 *param;   /*!< parameter area needs to be last */
    } app_msg_t;
    
    /* event for handler "bt_av_hdl_stack_up */
    enum {
        BT_APP_EVT_STACK_UP = 0,
    };
    
    class BlootoothA2DSink {
      public: 
        BlootoothA2DSink();
        ~BlootoothA2DSink();
        void set_pin_config(i2s_pin_config_t pin_config);
        void set_i2s_port(i2s_port_t i2s_num);
        void set_i2s_config(i2s_config_t i2s_config);
        ////**********
        virtual void set_AVRC_metadata(void (*callBack)(uint8_t, const uint8_t*));
        ////**********
        void start(char* name);
        esp_a2d_audio_state_t get_audio_state();
        esp_a2d_mct_t get_audio_type();
    
        /**
         * Wrappbed methods called from callbacks
         */
        void app_a2d_callback(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param);
        void app_rc_ct_callback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param);
        void app_task_handler();
        // Callback for music stream 
        void audio_data_callback(const uint8_t *data, uint32_t len);
        // av event handler
        void av_hdl_stack_evt(uint16_t event, void *p_param);
        // a2dp event handler 
        void av_hdl_a2d_evt(uint16_t event, void *p_param);
        // avrc event handler 
        void av_hdl_avrc_evt(uint16_t event, void *p_param);
            
      private:
        // private data
        xQueueHandle app_task_queue;
        xTaskHandle app_task_handle;
        i2s_config_t i2s_config;
        i2s_pin_config_t pin_config;    
        char * bt_name;
        uint32_t m_pkt_cnt = 0;
        esp_a2d_audio_state_t m_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
        const char *m_a2d_conn_state_str[4] = {"Disconnected", "Connecting", "Connected", "Disconnecting"};
        const char *m_a2d_audio_state_str[3] = {"Suspended", "Stopped", "Started"};
        esp_a2d_audio_state_t audio_state;
        esp_a2d_mct_t audio_type;
    
        // private methods
        int init_bluetooth();
        void app_task_start_up(void);
        void app_task_shut_down(void);
        bool app_send_msg(app_msg_t *msg);
        bool app_work_dispatch(app_callback_t p_cback, uint16_t event, void *p_params, int param_len);
        void app_work_dispatched(app_msg_t *msg);
        void app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param);
        void av_new_track();
        void av_notify_evt_handler(uint8_t event_id, uint32_t event_parameter);
    
    
    
    
         ////**********
        void (*AVRC_metadata)(uint8_t, const uint8_t*) = NULL;
        ////**********
        
      
    };
    
    
    #ifdef __cplusplus
    }
    #endif
    
    
    #endif

  4. #4
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    Good afternoon. Without looking too hard at the code, I'm now wondering about your setup.

    If the ESP32 is connected with only I2S to the Teensy, then there is no way for the Teensy to get the metadata. There must be some bridge for the data (ESP32 Serial <-> Teensy Serial, or i2c, or SPI) and supporting code. The code reference should allow metadata support with the ESP32's USB programmer. If that's what you're trying then we need to look further into pschatzmann's code. The code that then bridges it to the Teensy is yet another portion.

    How do you want this to work? You have an screen on the Teensy or on the ESP32? I have a board I'm working on with the ESP32 running the screen, which I felt makes sense. I'm going to use the ESP32 as the "controller" for the teensy, and it will show the GUI not the teensy. Then it will connect via serial to the Teensy to tell it what to do, hopefully using OSC. The project has been on while I get through my busy season at work. I'm excited to get back to it, hopefully soon!
    Last edited by JayShoe; 06-24-2022 at 08:06 PM.

  5. #5
    Quote Originally Posted by JayShoe View Post
    Good afternoon. Without looking too hard at the code, I'm now wondering about your setup.
    Hello and Thanks for your time.

    Quote Originally Posted by JayShoe View Post
    If the ESP32 is connected with only I2S to the Teensy, then there is no way for the Teensy to get the metadata.
    I am afraid this is the case

    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    #include "async_input.h"
    #include "input_i2s2_16bit.h"
    #include "output_i2s2_16bit.h"
    
    
    
    
    //i2sSlaveInput parameters
    bool dither = false;
    bool noiseshaping = false;
    float attenuation = 100;
    int32_t minHalfFilterLength = 80;
    int32_t maxHalfFilterLength = 1;
    AsyncAudioInput<AsyncAudioInputI2S2_16bitslave> i2sSlaveInput(dither, noiseshaping, attenuation, minHalfFilterLength, maxHalfFilterLength);
    
    
    AudioInputI2S            i2s1;
    AudioOutputI2S           i2s2;
    AudioInputUSB            usb1;
    AudioOutputUSB           usb2;
    AudioMixer4              mixer1;
    AudioConnection          patchCord1(i2sSlaveInput, 0, mixer1, 0);
    AudioConnection          patchCord2(i2sSlaveInput, 1, mixer1, 1);
    AudioConnection          patchCord3(usb1, 0, mixer1, 2);
    AudioConnection          patchCord4(usb1, 1, mixer1, 3);
    AudioConnection          patchCord7(mixer1, 0, usb2, 0);
    AudioConnection          patchCord8(mixer1, 1, usb2, 1);
    AudioConnection          patchCord9(mixer1, 0, i2s2, 0);
    AudioConnection          patchCord10(mixer1, 1, i2s2, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=302,184

    Quote Originally Posted by JayShoe View Post
    You have an screen on the Teensy or on the ESP32?
    On the teensy side I have a touchScreen.

    Code:
    #include <XPT2046_Touchscreen.h>  // touch driver for a TFT display
    #define CS_PIN  37//==>CS for Touchscreen (any digital pin)
    
    #define TFT_RST 255//TFT_RST 255
    #define TFT_DC 9// TFT_DC 9
    #define TFT_CS 36//TFT_CS 36
    #define TFT_SCK 13//TFT_SCK 27
    #define TFT_MISO 12//TFT_MISO 39
    #define TFT_MOSI 11//TFT_MOSI 26
    
    XPT2046_Touchscreen Touch(CS_PIN);
    
    TS_Point TouchPoint;
    //#define TIRQ_PIN  2
    ILI9341_t3 Display = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);
    const int myInput = AUDIO_INPUT_LINEIN;
    void setup() {
      Serial.begin(9600);
    
      // fire up the display
      Display.begin();
      Display.setRotation(1);
    
      Touch.begin();
    
      Touch.setRotation(3);
    AudioMemory(80);
    
      // Enable the audio shield, select input, and enable output
      sgtl5000_1.enable();
      sgtl5000_1.inputSelect(myInput);
      sgtl5000_1.volume(0.1);
    If understood correctly, it is more difficult to send the metadata to the touchscreen, which is attached to the teensy. ? Thanks so much

  6. #6
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    Can you connect the screen to the ESP32? Communication between the ESP32 and Tenney is probably required for something in your device. So you can go either way with the screen. Definitely plan for a serial connection between the two. My reasoning for putting the screen on the ESP32 was that it is powerful enough to run the gui so why not? Then let the Teensy run the audio only.

    Plus the OSC library is optimized with a serial interface, as that's how it was debugged. So I figure control over the teensy is at least well defined.

  7. #7
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    Oh man, the forum just deleted my post again. I'm using a Google Pixel 6 pro and when you go to edit the post on mobile it will delete the post. Basically the entire screen becomes a delete post button.

    If my reply came via email just post a quote if you can please.

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,202
    Quote Originally Posted by JayShoe View Post
    Oh man, the forum just deleted my post again. I'm using a Google Pixel 6 pro and when you go to edit the post on mobile it will delete the post. Basically the entire screen becomes a delete post button.
    ...
    Post restored ...

  9. #9
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    Quote Originally Posted by defragster View Post
    Post restored ...
    Thank you, are you aware of the bug on Android? This is a new phone so it was occurring on my old phone too.

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,202
    Quote Originally Posted by JayShoe View Post
    Thank you, are you aware of the bug on Android? This is a new phone so it was occurring on my old phone too.
    No, I use android - but not much for posting. Also not noticed a trend of that. At this point this old BBulletin forum is basically 'what it is' - ideally PJRC was looking to move to a new platform - but time/work hasn't allowed for that, but to assure smooth transition 'mods' have been put on hold.

  11. #11
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    255
    I can understand that I've managed a BB form before myself and it can get kind of cumbersome towards the end of life.

    But in all seriousness post something and then choose edit post and I can almost guarantee that by the end of it your post will be deleted.

    I wonder what platform is next? Super off topic but I'm very curious. There's so much data in this forum that shouldn't be lost.

  12. #12
    Quote Originally Posted by JayShoe View Post
    Can you connect the screen to the ESP32?
    Yes, I will give it a try. I have to go over "everything" to see what this change will bring. I agree 100% with letting teensy doing just the audio heavy-lifting. Good call. I will be posting my findings sometime Monday. Thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •