arduino-audio-tools
Loading...
Searching...
No Matches
A2DPStream.h
Go to the documentation of this file.
1
8#pragma once
9
10#include "AudioToolsConfig.h"
11
12#include "AudioTools.h"
13#include "BluetoothA2DPSink.h"
14#include "BluetoothA2DPSource.h"
18
19
20namespace audio_tools {
21
22class A2DPStream;
24// buffer which is used to exchange data
26// flag to indicated that we are ready to process data
27static bool is_a2dp_active = false;
28
31
36
64
65
84class A2DPStream : public AudioStream, public VolumeSupport {
85
86 public:
88 TRACED();
89 // A2DPStream can only be used once
90 assert(A2DPStream_self==nullptr);
91 A2DPStream_self = this;
93 info.sample_rate = 44100;
94 info.channels = 2;
95 }
96
99 TRACED();
100 if (a2dp_source!=nullptr) delete a2dp_source;
101 if (a2dp_sink!=nullptr) delete a2dp_sink;
102 A2DPStream_self = nullptr;
103 }
104
106 A2DPConfig cfg;
107 cfg.mode = mode;
108 if(mode==TX_MODE){
109 cfg.name="[Unknown]";
110 }
111 return cfg;
112 }
113
116 if (a2dp_source==nullptr){
118 }
119 return *a2dp_source;
120 }
121
124 if (a2dp_sink==nullptr){
126 }
127 return *a2dp_sink;
128 }
129
131 bool begin(RxTxMode mode, const char* name, bool wait_for_connection=true){
132 A2DPConfig cfg;
133 cfg.mode = mode;
134 cfg.name = name;
135 cfg.wait_for_connection = wait_for_connection;
136 return begin(cfg);
137 }
138
140 bool begin(A2DPConfig cfg){
141 this->config = cfg;
142 bool result = false;
143 LOGI("Connecting to %s",cfg.name);
144
145 if (!a2dp_buffer.resize(cfg.buffer_size)){
146 LOGE("a2dp_buffer resize failed");
147 return false;
148 }
149
150 // initialize a2dp_silence_timeout
152 LOGI("Using StartOnConnect")
154 }
155
156 switch (cfg.mode){
157 case TX_MODE:
158 LOGI("Starting a2dp_source...");
159 source(); // allocate object
160 a2dp_source->set_auto_reconnect(cfg.auto_reconnect);
161 a2dp_source->set_volume(volume() * A2DP_MAX_VOL);
162 if(StrView(cfg.name).equals("[Unknown]")){
163 //search next available device
164 a2dp_source->set_ssid_callback(detected_device);
165 }
166 a2dp_source->set_on_connection_state_changed(a2dp_state_callback, this);
167 a2dp_source->start_raw((char*)cfg.name, a2dp_stream_source_sound_data);
168 if (cfg.wait_for_connection){
169 while(!a2dp_source->is_connected()){
170 LOGD("waiting for connection");
171 delay(1000);
172 }
173 LOGI("a2dp_source is connected...");
174 notify_base_Info(44100);
175 //is_a2dp_active = true;
176 }
177 else{
178 LOGI("a2dp_source started without connecting");
179 }
180 result = true;
181 break;
182
183 case RX_MODE:
184 LOGI("Starting a2dp_sink...");
185 sink(); // allocate object
186 a2dp_sink->set_auto_reconnect(cfg.auto_reconnect);
187 a2dp_sink->set_stream_reader(&a2dp_stream_sink_sound_data, false);
188 a2dp_sink->set_volume(volume() * A2DP_MAX_VOL);
189 a2dp_sink->set_on_connection_state_changed(a2dp_state_callback, this);
190 a2dp_sink->set_sample_rate_callback(sample_rate_callback);
191 a2dp_sink->start((char*)cfg.name);
192 if (cfg.wait_for_connection){
193 while(!a2dp_sink->is_connected()){
194 LOGD("waiting for connection");
195 delay(1000);
196 }
197 LOGI("a2dp_sink is connected...");
198 }
199 else{
200 LOGI("a2dp_sink started without connection");
201 }
202 is_a2dp_active = true;
203 result = true;
204 break;
205 default:
206 LOGE("Undefined mode: %d", cfg.mode);
207 break;
208 }
209
210 return result;
211 }
212
213 void end() override {
214 if (a2dp != nullptr) {
215 a2dp->disconnect();
216 }
218 }
219
221 bool isConnected() {
222 if (a2dp_source==nullptr && a2dp_sink==nullptr) return false;
223 if (a2dp_source!=nullptr) return a2dp_source->is_connected();
224 return a2dp_sink->is_connected();
225 }
226
228 bool isReady() {
229 return is_a2dp_active;
230 }
231
233 operator bool() {
234 return isReady();
235 }
236
238 size_t write(const uint8_t* data, size_t len) override {
239 LOGD("%s: %zu", LOG_METHOD, len);
240 if (config.mode == TX_MODE){
241 // at 80% we activate the processing
242 if(!is_a2dp_active
244 && a2dp_buffer.available() >= 0.8f * a2dp_buffer.size()){
245 LOGI("set active");
246 is_a2dp_active = true;
247 }
248
249 // blocking write: if buffer is full we wait
250 int timeout = config.tx_write_timeout_ms;
251 int wait_time = 5;
252 size_t free = a2dp_buffer.availableForWrite();
253 while(len > free){
254 LOGD("Waiting for buffer: writing %d > available %d", (int) len, (int) free);
255 if (timeout > 0) {
256 timeout -= wait_time;
257 if (timeout <= 0) return 0;
258 }
261 }
262 }
263
264 // write to buffer
265 size_t result = a2dp_buffer.writeArray(data, len);
266 LOGD("write %d -> %d", len, result);
267 if (config.mode == TX_MODE){
268 // give the callback a chance to retrieve the data
270 }
271 return result;
272 }
273
275 size_t readBytes(uint8_t *data, size_t len) override {
276 if (!is_a2dp_active){
277 LOGW( "readBytes failed because !is_a2dp_active");
278 return 0;
279 }
280 LOGD("readBytes %d", len);
281 size_t result = a2dp_buffer.readArray(data, len);
282 LOGI("readBytes %d->%d", len,result);
283 return result;
284 }
285
287 int available() override {
288 // only supported in tx mode
289 if (config.mode!=RX_MODE) return 0;
290 return a2dp_buffer.available();
291 }
292
294 int availableForWrite() override {
295 // only supported in tx mode
296 if (config.mode!=TX_MODE ) return 0;
297 // return infor from buffer
299 }
300
302 bool setVolume(float volume) override {
304 // 128 is max volume
305 if (a2dp!=nullptr) a2dp->set_volume(volume * A2DP_MAX_VOL);
306 return true;
307 }
308
311 return a2dp_buffer;
312 }
313
318
320 void clear(){
321 // set inactive if necessary
323 is_a2dp_active = false;
324 }
326 }
327
328 protected:
333 const int A2DP_MAX_VOL = 128;
334
335 // auto-detect device to send audio to (TX-Mode)
336 static bool detected_device(const char* ssid, esp_bd_addr_t address, int rssi){
337 LOGW("found Device: %s rssi: %d", ssid, rssi);
338 //filter out weak signals
339 return (rssi > -75);
340 }
341
343 TRACED();
344 A2DPStream *self = (A2DPStream*)caller;
346 is_a2dp_active = true;
347 }
348 LOGW("==> state: %s", self->a2dp->to_str(state));
349 }
350
351
352 // callback used by A2DP to provide the a2dp_source sound data
355 if (data == nullptr || len == 0) return 0;
357
358 // at first call we start with some empty data
359 if (is_a2dp_active){
360 // the data in the file must be in int16 with 2 channels
361 yield();
363
364 // provide silence data
366 memset(data,0, len);
367 result_len = len;
368 }
369 } else {
370
371 // prevent underflow on first call
372 switch (config.startup_nodata) {
373 case A2DPSilence:
374 memset(data, 0, len);
375 break;
376 case A2DPWhoosh:
377 int16_t *data16 = (int16_t*)data;
378 for (int j=0;j<len/4;j+=2){
379 data16[j+1] = data16[j] = (rand() % 50) - 25;
380 }
381 break;
382 }
383 result_len = len;
384
385 // Priority: 22 on core 0
386 // LOGI("Priority: %d on core %d", uxTaskPriorityGet(NULL), xPortGetCoreID());
387
388 }
389 LOGD("a2dp_stream_source_sound_data: %d -> %d", len, result_len);
390 return result_len;
391 }
392
394 static void a2dp_stream_sink_sound_data(const uint8_t* data, uint32_t len) {
395 if (is_a2dp_active){
397 LOGD("a2dp_stream_sink_sound_data %d -> %d", len, result_len);
398 }
399 }
400
402 void notify_base_Info(int rate){
404 info.channels = 2;
406 info.sample_rate = rate;
408 }
409
415
416};
417
418}
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define A2DP_BUFFER_COUNT
Definition AudioToolsConfig.h:132
#define A2DP_BUFFER_SIZE
Definition AudioToolsConfig.h:128
#define LOG_METHOD
Definition AudioToolsConfig.h:74
#define portMAX_DELAY
Definition QueueZephyr.h:14
#define assert(T)
Definition avr.h:10
Configuration for A2DPStream.
Definition A2DPStream.h:42
bool silence_on_nodata
when a2dp source is active but has no data we generate silence data (default false)
Definition A2DPStream.h:58
bool auto_reconnect
automatically reconnect if connection is lost (default false)
Definition A2DPStream.h:53
int delay_ms
Delay in ms which is added to each write (default 1)
Definition A2DPStream.h:56
A2DPNoData startup_nodata
Action when a2dp is not active yet (default A2DPSilence)
Definition A2DPStream.h:47
int tx_write_timeout_ms
write timeout in ms: -1 is blocking write (default -1)
Definition A2DPStream.h:60
const char * name
A2DP name (default A2DP)
Definition A2DPStream.h:51
A2DPStartLogic startup_logic
Logic when the processing is activated (default StartWhenBufferFull)
Definition A2DPStream.h:45
bool wait_for_connection
begin should wait for connection to be established (default true)
Definition A2DPStream.h:62
RxTxMode mode
Mode: TX_MODE or RX_MODE (default RX_MODE)
Definition A2DPStream.h:49
int buffer_size
Definition A2DPStream.h:54
Stream support for A2DP using https://github.com/pschatzmann/ESP32-A2DP: begin(TX_MODE) opens a a2dp_...
Definition A2DPStream.h:84
BluetoothA2DPSource & source()
provides access to the BluetoothA2DPSource
Definition A2DPStream.h:115
void notify_base_Info(int rate)
notify subscriber with AudioInfo
Definition A2DPStream.h:402
A2DPConfig defaultConfig(RxTxMode mode=RX_MODE)
Definition A2DPStream.h:105
A2DPStream()
Definition A2DPStream.h:87
size_t readBytes(uint8_t *data, size_t len) override
Reads the data from the temporary buffer.
Definition A2DPStream.h:275
BluetoothA2DPSource * a2dp_source
Definition A2DPStream.h:330
static int32_t a2dp_stream_source_sound_data(uint8_t *data, int32_t len)
Definition A2DPStream.h:353
const int A2DP_MAX_VOL
Definition A2DPStream.h:333
static void a2dp_stream_sink_sound_data(const uint8_t *data, uint32_t len)
callback used by A2DP to write the sound data
Definition A2DPStream.h:394
BluetoothA2DPSink * a2dp_sink
Definition A2DPStream.h:331
bool setVolume(float volume) override
Define the volume (values between 0.0 and 1.0)
Definition A2DPStream.h:302
void end() override
Definition A2DPStream.h:213
int available() override
Provides the number of bytes available to read.
Definition A2DPStream.h:287
BluetoothA2DPCommon * a2dp
Definition A2DPStream.h:332
static void sample_rate_callback(uint16_t rate)
callback to update audio info with used a2dp sample rate
Definition A2DPStream.h:411
size_t write(const uint8_t *data, size_t len) override
Writes the data into a temporary send buffer - where it can be picked up by the callback.
Definition A2DPStream.h:238
bool isConnected()
checks if we are connected
Definition A2DPStream.h:221
void setSilenceOnNoData(bool silence)
Manage config.silence_on_nodata dynamically.
Definition A2DPStream.h:315
int availableForWrite() override
Provides the number of bytes available to write.
Definition A2DPStream.h:294
A2DPConfig config
Definition A2DPStream.h:329
BaseBuffer< uint8_t > & buffer()
Provides access to the buffer.
Definition A2DPStream.h:310
BluetoothA2DPSink & sink()
provides access to the BluetoothA2DPSink
Definition A2DPStream.h:123
~A2DPStream()
Release the allocate a2dp_source or a2dp_sink.
Definition A2DPStream.h:98
static bool detected_device(const char *ssid, esp_bd_addr_t address, int rssi)
Definition A2DPStream.h:336
void clear()
Clears the buffer.
Definition A2DPStream.h:320
bool isReady()
is ready to process data
Definition A2DPStream.h:228
bool begin(RxTxMode mode, const char *name, bool wait_for_connection=true)
Starts the processing.
Definition A2DPStream.h:131
static void a2dp_state_callback(esp_a2d_connection_state_t state, void *caller)
Definition A2DPStream.h:342
bool begin(A2DPConfig cfg)
Starts the processing.
Definition A2DPStream.h:140
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:174
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
AudioInfo info
Definition BaseStream.h:171
Shared functionality of all buffers.
Definition Buffers.h:23
void clear()
same as reset
Definition Buffers.h:96
virtual bool begin()
Definition BaseStream.h:40
virtual void end()
Definition BaseStream.h:41
Buffer implementation which is using a FreeRTOS StreamBuffer. The default allocator uses psram is ava...
Definition BufferRTOS.h:33
size_t size()
Definition BufferRTOS.h:168
int available() override
provides the number of entries that are available to read
Definition BufferRTOS.h:151
int availableForWrite() override
provides the number of entries that are available to write
Definition BufferRTOS.h:157
int writeArray(const T data[], int len)
Fills the buffer data.
Definition BufferRTOS.h:100
bool resize(size_t size)
Re-Allocats the memory and the queue.
Definition BufferRTOS.h:54
int readArray(T data[], int len)
reads multiple values
Definition BufferRTOS.h:80
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition StrView.h:165
Supports the setting and getting of the volume.
Definition AudioTypes.h:187
virtual float volume()
provides the actual volume in the range of 0.0f to 1.0f
Definition AudioTypes.h:190
virtual bool setVolume(float volume)
define the actual volume in the range of 0.0f to 1.0f
Definition AudioTypes.h:192
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
static BufferRTOS< uint8_t > a2dp_buffer
Definition A2DPStream.h:25
A2DPNoData
A2DP Action when there is no data.
Definition A2DPStream.h:35
@ A2DPWhoosh
Definition A2DPStream.h:35
@ A2DPSilence
Definition A2DPStream.h:35
static bool is_a2dp_active
Definition A2DPStream.h:27
A2DPStartLogic
A2DP Startup Logic.
Definition A2DPStream.h:33
@ StartOnConnect
Definition A2DPStream.h:33
@ StartWhenBufferFull
Definition A2DPStream.h:33
int32_t a2dp_stream_source_sound_data(Frame *data, int32_t len)
void yield()
Definition Arduino.h:262
void delay(uint32_t ms)
Definition Arduino.h:255
void a2dp_stream_sink_sound_data(const uint8_t *data, uint32_t len)
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
static A2DPStream * A2DPStream_self
Definition A2DPStream.h:23
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57