Description
Hello! I'm trying to prototype a Bluetooth speaker with audio visualization, but I've stumbled on weird behavior of A2DPSink class.
Sketch code
#include <BluetoothAudio.h>
class DummyConsumer : public BluetoothAudioConsumer_ {
virtual bool init(uint8_t channels, uint32_t samplerate, A2DPSink *a2dpSink) override {
_channels = channels;
_samplerate = samplerate;
_a2dpSink = a2dpSink;
_state = STATE_INITIALIZED;
return true;
}
virtual void setVolume(uint8_t gain) override {
}
virtual void startStream() override {
if (_state != STATE_INITIALIZED) {
return;
}
_state = STATE_STREAMING;
}
virtual void stopStream() override {
if (_state != STATE_STREAMING) {
return;
}
_state = STATE_INITIALIZED;
}
virtual void close() override {
if (_state == STATE_STREAMING) {
stopStream();
}
_state = STATE_OFF;
}
};
A2DPSink a2dp;
DummyConsumer consumer;
void volumeCB(void *param, int pct) {
(void)param;
Serial.printf("New volume: %d", pct);
Serial.println();
}
void connectCB(void *param, bool connected) {
(void)param;
Serial.printf("Connected: %s", connected ? "true" : "false");
Serial.println();
}
void playbackCB(void *param, A2DPSink::PlaybackStatus state) {
(void)param;
Serial.printf("Playback: %d", state);
Serial.println();
}
void transmitCB(void *param) {
(void)param;
Serial.println("Transmit call");
}
void setup() {
Serial.begin(115200);
a2dp.setName("PicoW Boom 00:00:00:00:00:00");
a2dp.setConsumer(&consumer);
a2dp.onVolume(volumeCB);
a2dp.onConnect(connectCB);
a2dp.onPlaybackStatus(playbackCB);
a2dp.onTransmit(transmitCB);
a2dp.begin();
}
void loop() {
}
Connecting my Android phone to Pico W and playing around with some music and volume controls, I get following output:
Playback: 2
Playback: 1
Connected: true
Playback: 2
Playback: 1
Connected: true
Playback: 2
Playback: 1
Connected: true
Playback: 2
Playback: 1
Connected: true
Playback: 2
There are several oddities here:
- Connected callback is called multiple times, even though there is only one connection.
- Connected callback is called not when connection happened, but when sound starts. This is also why Playback is observed earlier, than a Connection callback.
- No volume callbacks ever happen when changing volume on a phone, regardless of it's "Media Volume Sync" setting
- No Transmit callbacks as well
Looking through A2DPSink.cpp code reveals that while onTransmit and onVolume callbacks does get saved in class fields, there are no calls to it at all! Is this intended? If yes, how could I get access to volume controls?
Also, since I need to do some processing on audio samples, there is no obvious way to either get audio data in a callback (since there is no relevant arguments in onTransmit callback) or by reading a stream of it (while A2DPSink is a Stream, all of it's Stream methods are effectively stubs).
Looking into BluetoothAudioConsumerI2S, it seems to me that it relies on callbacks from I2S to do its job, which means that I need to setup some external timer that will tick for samples every now and then so I could read samples from A2DPSink myself. I would prefer to either have a callback to that, or a read-only Stream so that I could process data as they arrive and not rely on I2S callbacks. Is there any way to do this that I miss here?
Thank you!