From 98ada55e2ed04d6a41cce4e2725129b8c2ff49fd Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 31 May 2024 13:00:38 -0700 Subject: [PATCH] Add track info support for BT audio sink --- .../examples/A2DPSink/A2DPSink.ino | 7 ++++ libraries/BluetoothAudio/src/A2DPSink.cpp | 16 +++++++++ libraries/BluetoothAudio/src/A2DPSink.h | 33 +++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/libraries/BluetoothAudio/examples/A2DPSink/A2DPSink.ino b/libraries/BluetoothAudio/examples/A2DPSink/A2DPSink.ino index f62533b4e..32dc30f23 100644 --- a/libraries/BluetoothAudio/examples/A2DPSink/A2DPSink.ino +++ b/libraries/BluetoothAudio/examples/A2DPSink/A2DPSink.ino @@ -43,6 +43,8 @@ void setup() { a2dp.begin(); } +char *nowPlaying = nullptr; + void loop() { if (BOOTSEL) { if (status == A2DPSink::PAUSED) { @@ -54,4 +56,9 @@ void loop() { } while (BOOTSEL); } + if (!nowPlaying || strcmp(nowPlaying, a2dp.trackTitle())) { + free(nowPlaying); + nowPlaying = strdup(a2dp.trackTitle()); + Serial.printf("NOW PLAYING: %s\n", nowPlaying); + } } diff --git a/libraries/BluetoothAudio/src/A2DPSink.cpp b/libraries/BluetoothAudio/src/A2DPSink.cpp index b6485598c..b589d11ac 100644 --- a/libraries/BluetoothAudio/src/A2DPSink.cpp +++ b/libraries/BluetoothAudio/src/A2DPSink.cpp @@ -559,6 +559,14 @@ void A2DPSink::avrcp_controller_packet_handler(uint8_t packet_type, uint16_t cha break; case AVRCP_SUBEVENT_NOTIFICATION_TRACK_CHANGED: + _title[0] = 0; + _artist[0] = 0; + _album[0] = 0; + _genre[0] = 0; + avrcp_controller_get_now_playing_info(avrcp_connection->avrcp_cid); + if (_trackChangedCB) { + _trackChangedCB(_trackChangedData); + } DEBUGV("AVRCP Controller: Track changed\n"); break; @@ -585,6 +593,8 @@ void A2DPSink::avrcp_controller_packet_handler(uint8_t packet_type, uint16_t cha case AVRCP_SUBEVENT_NOW_PLAYING_TITLE_INFO: if (avrcp_subevent_now_playing_title_info_get_value_len(packet) > 0) { memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_title_info_get_value(packet), avrcp_subevent_now_playing_title_info_get_value_len(packet)); + strncpy(_title, (char *)avrcp_subevent_value, sizeof(_title)); + _title[sizeof(_title) - 1] = 0; DEBUGV("AVRCP Controller: Title %s\n", avrcp_subevent_value); } break; @@ -592,6 +602,8 @@ void A2DPSink::avrcp_controller_packet_handler(uint8_t packet_type, uint16_t cha case AVRCP_SUBEVENT_NOW_PLAYING_ARTIST_INFO: if (avrcp_subevent_now_playing_artist_info_get_value_len(packet) > 0) { memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_artist_info_get_value(packet), avrcp_subevent_now_playing_artist_info_get_value_len(packet)); + strncpy(_artist, (char *)avrcp_subevent_value, sizeof(_artist)); + _artist[sizeof(_artist) - 1] = 0; DEBUGV("AVRCP Controller: Artist %s\n", avrcp_subevent_value); } break; @@ -599,6 +611,8 @@ void A2DPSink::avrcp_controller_packet_handler(uint8_t packet_type, uint16_t cha case AVRCP_SUBEVENT_NOW_PLAYING_ALBUM_INFO: if (avrcp_subevent_now_playing_album_info_get_value_len(packet) > 0) { memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_album_info_get_value(packet), avrcp_subevent_now_playing_album_info_get_value_len(packet)); + strncpy(_album, (char *)avrcp_subevent_value, sizeof(_album)); + _album[sizeof(_album) - 1] = 0; DEBUGV("AVRCP Controller: Album %s\n", avrcp_subevent_value); } break; @@ -606,6 +620,8 @@ void A2DPSink::avrcp_controller_packet_handler(uint8_t packet_type, uint16_t cha case AVRCP_SUBEVENT_NOW_PLAYING_GENRE_INFO: if (avrcp_subevent_now_playing_genre_info_get_value_len(packet) > 0) { memcpy(avrcp_subevent_value, avrcp_subevent_now_playing_genre_info_get_value(packet), avrcp_subevent_now_playing_genre_info_get_value_len(packet)); + strncpy(_genre, (char *)avrcp_subevent_value, sizeof(_genre)); + _genre[sizeof(_genre) - 1] = 0; DEBUGV("AVRCP Controller: Genre %s\n", avrcp_subevent_value); } break; diff --git a/libraries/BluetoothAudio/src/A2DPSink.h b/libraries/BluetoothAudio/src/A2DPSink.h index 57bd73dd0..27069e6bd 100644 --- a/libraries/BluetoothAudio/src/A2DPSink.h +++ b/libraries/BluetoothAudio/src/A2DPSink.h @@ -33,6 +33,10 @@ class A2DPSink : public Stream { public: A2DPSink() { + _title[0] = 0; + _artist[0] = 0; + _album[0] = 0; + _genre[0] = 0; } virtual int available() override { return 0; // Unreadable, this is output only @@ -96,6 +100,11 @@ class A2DPSink : public Stream { _connectData = cbData; } + void onTrackChanged(void (*cb)(void *), void *cbData = nullptr) { + _trackChangedCB = cb; + _trackChangedData = cbData; + } + typedef enum { STOPPED, PLAYING, PAUSED } PlaybackStatus; void onPlaybackStatus(void (*cb)(void *, PlaybackStatus), void *cbData = nullptr) { _playbackStatusCB = cb; @@ -190,6 +199,22 @@ class A2DPSink : public Stream { } } + const char *trackTitle() { + return _title; + } + + const char *trackArtist() { + return _artist; + } + + const char *trackAlbum() { + return _album; + } + + const char *trackGenre() { + return _genre; + } + private: void handle_pcm_data(int16_t * data, int num_audio_frames, int num_channels, int sample_rate, void * context); @@ -224,6 +249,9 @@ class A2DPSink : public Stream { void *_connectData; void (*_playbackStatusCB)(void *, PlaybackStatus) = nullptr; void *_playbackStatusData; + void (*_trackChangedCB)(void *) = nullptr; + void *_trackChangedData; + char *_name = nullptr; uint8_t _sourceAddress[6]; @@ -301,4 +329,9 @@ class A2DPSink : public Stream { a2dp_sink_avrcp_connection_t a2dp_sink_avrcp_connection; int16_t output_buffer[(128 + 16) * NUM_CHANNELS]; // 16 * 8 * 2 + + char _title[64]; + char _artist[64]; + char _album[64]; + char _genre[32]; };