arduino-audio-tools
VBANStream.h
1 
2 #include <AsyncUDP.h>
3 #include <WiFi.h>
4 
5 #include "AudioTools/AudioLibs/vban/vban.h"
6 #include "AudioTools/CoreAudio/AudioStreams.h"
7 #include "AudioTools/Concurrency/BufferRTOS.h"
8 
9 namespace audio_tools {
10 
11 class VBANConfig : public AudioInfo {
12  public:
13  VBANConfig() {
14  sample_rate = 11025;
15  channels = 1;
16  bits_per_sample = 16;
17  }
18  RxTxMode mode;
20  const char* stream_name = "Stream1";
22  uint16_t udp_port = 6980;
24  IPAddress target_ip{0, 0, 0, 0};
26  const char* ssid = nullptr;
28  const char* password = nullptr;
29  int rx_buffer_count = 30;
30  // set to true if samples are generated faster then sample rate
31  bool throttle_active = false;
32  // when negative the number of ms that are subtracted from the calculated wait
33  // time to fine tune Overload and Underruns
34  int throttle_correction_us = 0;
35  // defines the max write size
36  int max_write_size =
37  DEFAULT_BUFFER_SIZE * 2; // just good enough for 44100 stereo
38  uint8_t format = 0;
39 };
40 
51 class VBANStream : public AudioStream {
52  public:
53  VBANConfig defaultConfig(RxTxMode mode = TX_MODE) {
54  VBANConfig def;
55  def.mode = mode;
56  return def;
57  }
58 
59  void setOutput(Print &out){
60  p_out = &out;
61  }
62 
63  void setAudioInfo(AudioInfo info) override {
64  cfg.copyFrom(info);
66  auto thc = throttle.defaultConfig();
67  thc.copyFrom(info);
68  thc.correction_us = cfg.throttle_correction_us;
69  throttle.begin(thc);
70  if (cfg.mode == TX_MODE) {
71  configure_tx();
72  }
73  }
74 
75  bool begin(VBANConfig cfg) {
76  this->cfg = cfg;
77  setAudioInfo(cfg);
78  return begin();
79  }
80 
81  bool begin() {
82  if (cfg.mode == TX_MODE) {
83  if (cfg.bits_per_sample != 16) {
84  LOGE("Only 16 bits supported")
85  return false;
86  }
87  tx_buffer.resize(VBAN_PACKET_NUM_SAMPLES);
88  return begin_tx();
89  } else {
90 #ifdef ESP32
91  rx_buffer.resize(DEFAULT_BUFFER_SIZE * cfg.rx_buffer_count);
92  rx_buffer.setReadMaxWait(10);
93 #else
94  rx_buffer.resize(DEFAULT_BUFFER_SIZE, cfg.rx_buffer_count);
95 #endif
96  return begin_rx();
97  }
98  }
99 
100  size_t write(const uint8_t* data, size_t len) override {
101  if (!udp_connected) return 0;
102 
103  int16_t* adc_data = (int16_t*)data;
104  size_t samples = len / (cfg.bits_per_sample/8);
105 
106  // limit output speed
107  if (cfg.throttle_active) {
108  throttle.delayFrames(samples / cfg.channels);
109  }
110 
111  for (int j = 0; j < samples; j++) {
112  tx_buffer.write(adc_data[j]);
113  if (tx_buffer.availableForWrite() == 0) {
114  memcpy(vban.data_frame, tx_buffer.data(), vban.packet_data_bytes);
115  *vban.packet_counter = packet_counter; // increment packet counter
116  // Send packet
117  if (cfg.target_ip == broadcast_address) {
118  udp.broadcastTo((uint8_t*)&vban.packet, vban.packet_total_bytes,
119  cfg.udp_port);
120  } else {
121  udp.writeTo((uint8_t*)&vban.packet, vban.packet_total_bytes,
122  cfg.target_ip, cfg.udp_port);
123  }
124  // defile delay start time
125  packet_counter++;
126  tx_buffer.reset();
127  }
128  }
129  return len;
130  }
131 
132  int availableForWrite() { return cfg.max_write_size; }
133 
134  size_t readBytes(uint8_t* data, size_t len) override {
135  TRACED();
136  size_t samples = len / (cfg.bits_per_sample/8);
137  if (cfg.throttle_active) {
138  throttle.delayFrames(samples / cfg.channels);
139  }
140  return rx_buffer.readArray(data, len);
141  }
142 
143  int available() { return available_active ? rx_buffer.available() : 0; }
144 
145  protected:
146  const IPAddress broadcast_address{0, 0, 0, 0};
147  AsyncUDP udp;
148  VBan vban;
149  VBANConfig cfg;
150  SingleBuffer<int16_t> tx_buffer{0};
151  #ifdef ESP32
152  BufferRTOS<uint8_t> rx_buffer{ 0};
153  #else
154  NBuffer<uint8_t> rx_buffer{DEFAULT_BUFFER_SIZE, 0};
155  #endif
156  bool udp_connected = false;
157  uint32_t packet_counter = 0;
158  Throttle throttle;
159  size_t bytes_received = 0;
160  bool available_active = false;
161  Print *p_out = nullptr;
162 
163  bool begin_tx() {
164  if (!configure_tx()) {
165  return false;
166  }
167  start_wifi();
168  if (WiFi.status() != WL_CONNECTED) {
169  LOGE("Wifi not connected");
170  return false;
171  }
172  WiFi.setSleep(false);
173  IPAddress myIP = WiFi.localIP();
174  udp_connected = udp.connect(myIP, cfg.udp_port);
175  return udp_connected;
176  }
177 
178  bool begin_rx() {
179  start_wifi();
180  if (WiFi.status() != WL_CONNECTED) {
181  LOGE("Wifi not connected");
182  return false;
183  }
184  WiFi.setSleep(false);
185  bytes_received = 0;
186  this->available_active = false;
187  // connect to target
188  if (!udp.listen(cfg.udp_port)) {
189  LOGE("Could not connect to '%s:%d' target", toString(cfg.target_ip),
190  cfg.udp_port);
191  }
192  // handle data
193  udp.onPacket([this](AsyncUDPPacket packet) { receive_udp(packet); });
194 
195  return true;
196  }
197 
198  bool configure_tx() {
199  int rate = vban_sample_rate();
200  if (rate < 0) {
201  LOGE("Invalid sample rate: %d", cfg.sample_rate);
202  return false;
203  }
204  configure_vban((VBanSampleRates)rate);
205  return true;
206  }
207 
208  void start_wifi() {
209  if (cfg.ssid == nullptr) return;
210  if (cfg.password == nullptr) return;
211  LOGI("ssid %s", cfg.ssid);
212  // Setup Wifi:
213  WiFi.begin(cfg.ssid, cfg.password); // Connect to your WiFi router
214  while (WiFi.status() != WL_CONNECTED) { // Wait for connection
215  delay(500);
216  Serial.print(".");
217  }
218  Serial.println();
219 
220  LOGI("Wifi connected to IP (%d.%d.%d.%d)", WiFi.localIP()[0],
221  WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
222  }
223 
224  void configure_vban(VBanSampleRates rate) {
225  // Set vban packet header, counter, and data frame pointers to respective
226  // parts of packet:
227  vban.hdr = (VBanHeader*)&vban.packet[0];
228  vban.packet_counter = (uint32_t*)&vban.packet[VBAN_PACKET_HEADER_BYTES];
229  vban.data_frame =
230  (uint8_t*)&vban
231  .packet[VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES];
232 
233  // Setup the packet header:
234  strncpy(vban.hdr->preamble, "VBAN", 4);
235  vban.hdr->sample_rate =
236  static_cast<int>(VBAN_PROTOCOL_AUDIO) |
237  rate; // 11025 Hz, which matches default sample rate for soundmodem
238  vban.hdr->num_samples =
239  (VBAN_PACKET_NUM_SAMPLES / cfg.channels) - 1; // 255 = 256 samples
240  vban.hdr->num_channels = cfg.channels - 1; // 0 = 1 channel
241  vban.hdr->sample_format =
242  static_cast<int>(VBAN_BITFMT_16_INT) | VBAN_CODEC_PCM; // int16 PCM
243  strncpy(vban.hdr->stream_name, cfg.stream_name,
244  min((int)strlen(cfg.stream_name), VBAN_STREAM_NAME_SIZE));
245 
246  vban.packet_data_bytes =
247  (vban.hdr->num_samples + 1) * (vban.hdr->num_channels + 1) *
248  ((vban.hdr->sample_format & VBAN_BIT_RESOLUTION_MASK) + 1);
249  vban.packet_total_bytes = vban.packet_data_bytes +
250  VBAN_PACKET_HEADER_BYTES +
251  VBAN_PACKET_COUNTER_BYTES;
252  }
253 
254  int vban_sample_rate() {
255  int result = -1;
256  switch (cfg.sample_rate) {
257  case 6000:
258  result = SAMPLE_RATE_6000_HZ;
259  break;
260  case 12000:
261  result = SAMPLE_RATE_12000_HZ;
262  break;
263  case 24000:
264  result = SAMPLE_RATE_24000_HZ;
265  break;
266  case 48000:
267  result = SAMPLE_RATE_48000_HZ;
268  break;
269  case 96000:
270  result = SAMPLE_RATE_96000_HZ;
271  break;
272  case 192000:
273  result = SAMPLE_RATE_192000_HZ;
274  break;
275  case 384000:
276  result = SAMPLE_RATE_384000_HZ;
277  break;
278  case 8000:
279  result = SAMPLE_RATE_8000_HZ;
280  break;
281  case 16000:
282  result = SAMPLE_RATE_16000_HZ;
283  break;
284  case 32000:
285  result = SAMPLE_RATE_32000_HZ;
286  break;
287  case 64000:
288  result = SAMPLE_RATE_64000_HZ;
289  break;
290  case 128000:
291  result = SAMPLE_RATE_128000_HZ;
292  break;
293  case 256000:
294  result = SAMPLE_RATE_256000_HZ;
295  break;
296  case 512000:
297  result = SAMPLE_RATE_512000_HZ;
298  break;
299  case 11025:
300  result = SAMPLE_RATE_11025_HZ;
301  break;
302  case 22050:
303  result = SAMPLE_RATE_22050_HZ;
304  break;
305  case 44100:
306  result = SAMPLE_RATE_44100_HZ;
307  break;
308  case 88200:
309  result = SAMPLE_RATE_88200_HZ;
310  break;
311  case 176400:
312  result = SAMPLE_RATE_176400_HZ;
313  break;
314  case 352800:
315  result = SAMPLE_RATE_352800_HZ;
316  break;
317  case 705600:
318  result = SAMPLE_RATE_705600_HZ;
319  break;
320  }
321  return result;
322  }
323 
324  const char* toString(IPAddress adr) {
325  static char str[11] = {0};
326  snprintf(str, 11, "%d.%d.%d.%d", adr[0], adr[1], adr[2], adr[3]);
327  return str;
328  }
329 
345  void receive_udp(AsyncUDPPacket& packet) {
346  uint16_t vban_rx_data_bytes, vban_rx_sample_count;
347  int16_t* vban_rx_data;
348  uint32_t* vban_rx_pkt_nbr;
349  uint16_t outBuf[VBAN_PACKET_MAX_SAMPLES + 1];
350  size_t bytesOut;
351 
352  int len = packet.length();
353  if (len > 0) {
354  LOGD("receive_udp %d", len);
355  uint8_t* udpIncomingPacket = packet.data();
356 
357  // receive incoming UDP packet
358  // Check if packet length meets VBAN specification:
359  if (len <= (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES) ||
360  len > VBAN_PACKET_MAX_LEN_BYTES) {
361  LOGE("Packet length %u bytes", len);
362  rx_buffer.reset();
363  return;
364  }
365 
366  // Check if preamble matches VBAN format:
367  if (strncmp("VBAN", (const char*)udpIncomingPacket, 4) != 0) {
368  LOGE("Unrecognized preamble %.4s", udpIncomingPacket);
369  return;
370  }
371 
372  vban_rx_data_bytes =
373  len - (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES);
374  vban_rx_pkt_nbr = (uint32_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES];
375  vban_rx_data = (int16_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES +
376  VBAN_PACKET_COUNTER_BYTES];
377  vban_rx_sample_count = vban_rx_data_bytes / (cfg.bits_per_sample / 8);
378  uint8_t vbanSampleRateIdx = udpIncomingPacket[4] & VBAN_SR_MASK;
379  uint8_t vbchannels = udpIncomingPacket[6] + 1;
380  uint8_t vbframes = udpIncomingPacket[5] + 1;
381  uint8_t vbformat = udpIncomingPacket[7] & VBAN_PROTOCOL_MASK;;
382  uint8_t vbformat_bits = udpIncomingPacket[7] & VBAN_BIT_RESOLUTION_MASK;;
383  uint32_t vbanSampleRate = VBanSRList[vbanSampleRateIdx];
384 
385  //LOGD("sample_count: %d - frames: %d", vban_rx_sample_count, vbframes);
386  //assert (vban_rx_sample_count == vbframes*vbchannels);
387 
388  // E.g. do not process any text
389  if (vbformat != cfg.format){
390  LOGE("Format ignored: 0x%x", vbformat);
391  return;
392  }
393 
394  // Currently we support only 16 bits.
395  if (vbformat_bits != VBAN_BITFMT_16_INT){
396  LOGE("Format only 16 bits supported");
397  return;
398  }
399 
400  // Just to be safe, re-check sample count against max sample count to
401  // avoid overrunning outBuf later
402  if (vban_rx_sample_count > VBAN_PACKET_MAX_SAMPLES) {
403  LOGE("unexpected packet size: %u", vban_rx_sample_count);
404  return;
405  }
406 
407  // update sample rate
408  if (cfg.sample_rate != vbanSampleRate || cfg.channels != vbchannels) {
409  // update audio info
410  cfg.sample_rate = vbanSampleRate;
411  cfg.channels = vbchannels;
412  setAudioInfo(cfg);
413  // remove any buffered data
414  rx_buffer.reset();
415  available_active = false;
416  }
417 
418  if (p_out!=nullptr){
419  int size_written = p_out->write((uint8_t*)vban_rx_data, vban_rx_data_bytes);
420  if (size_written != vban_rx_data_bytes) {
421  LOGE("buffer overflow %d -> %d", vban_rx_data_bytes, size_written);
422  }
423  return;
424  }
425 
426  // write data to buffer
427  int size_written = rx_buffer.writeArray((uint8_t*)vban_rx_data, vban_rx_data_bytes);
428  if (size_written != vban_rx_data_bytes) {
429  LOGE("buffer overflow %d -> %d", vban_rx_data_bytes, size_written);
430  }
431 
432  // report available bytes only when buffer is 50% full
433  if (!available_active) {
434  bytes_received += vban_rx_data_bytes;
435  if (bytes_received >= cfg.rx_buffer_count * DEFAULT_BUFFER_SIZE * 0.75){
436  available_active = true;
437  LOGI("Activating vban");
438  }
439  }
440  }
441  }
442 };
443 
444 } // namespace audio_tools
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: BaseStream.h:117
int available() override
provides the number of entries that are available to read
Definition: BufferRTOS.h:137
int writeArray(const T data[], int len)
Fills the buffer data.
Definition: BufferRTOS.h:95
bool resize(size_t size)
Re-Allocats the memory and the queue.
Definition: BufferRTOS.h:50
void reset() override
clears the buffer
Definition: BufferRTOS.h:134
int readArray(T data[], int len)
reads multiple values
Definition: BufferRTOS.h:77
Definition: NoArduino.h:58
T * data()
Provides address of actual data.
Definition: Buffers.h:252
bool write(T sample) override
write add an entry to the buffer
Definition: Buffers.h:194
int availableForWrite() override
provides the number of entries that are available to write
Definition: Buffers.h:224
void reset() override
clears the buffer
Definition: Buffers.h:254
Definition: VBANStream.h:11
const char * ssid
ssid for wifi connection
Definition: VBANStream.h:26
uint16_t udp_port
default port is 6980
Definition: VBANStream.h:22
IPAddress target_ip
Use {0,0,0,0}; as broadcast address.
Definition: VBANStream.h:24
const char * password
password for wifi connection
Definition: VBANStream.h:28
const char * stream_name
name of the stream
Definition: VBANStream.h:20
VBAN Audio Source and Sink for the ESP32. For further details please see https://vb-audio....
Definition: VBANStream.h:51
void receive_udp(AsyncUDPPacket &packet)
VBAN adjusts the number of samples per packet according to sample rate. Assuming 16-bit PCM mono,...
Definition: VBANStream.h:345
void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition: VBANStream.h:63
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868
Definition: vban.h:50
Definition: vban.h:60
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
void copyFrom(AudioInfo info)
Same as set.
Definition: AudioTypes.h:107
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:59