71 void setOutput(
Print &out){
78 auto thc = throttle.defaultConfig();
80 thc.correction_us = cfg.throttle_correction_us;
82 if (cfg.mode == TX_MODE) {
94 if (cfg.mode == TX_MODE) {
96 LOGE(
"Only 16 bits supported")
99 tx_buffer.resize(VBAN_PACKET_NUM_SAMPLES);
103 rx_buffer.
resize(DEFAULT_BUFFER_SIZE * cfg.rx_buffer_count);
104 rx_buffer.setReadMaxWait(10);
106 rx_buffer.
resize(DEFAULT_BUFFER_SIZE, cfg.rx_buffer_count);
112 size_t write(
const uint8_t* data,
size_t len)
override {
113 if (!udp_connected)
return 0;
115 int16_t* adc_data = (int16_t*)data;
119 if (cfg.throttle_active) {
120 throttle.delayFrames(samples / cfg.
channels);
123 for (
int j = 0; j < samples; j++) {
124 tx_buffer.
write(adc_data[j]);
126 memcpy(vban.data_frame, tx_buffer.
data(), vban.packet_data_bytes);
127 *vban.packet_counter = packet_counter;
129 if (cfg.
target_ip == broadcast_address) {
130 udp.broadcastTo((uint8_t*)&vban.packet, vban.packet_total_bytes,
133 udp.writeTo((uint8_t*)&vban.packet, vban.packet_total_bytes,
144 int availableForWrite() {
return cfg.max_write_size; }
146 size_t readBytes(uint8_t* data,
size_t len)
override {
149 if (cfg.throttle_active) {
150 throttle.delayFrames(samples / cfg.
channels);
155 int available() {
return available_active ? rx_buffer.
available() : 0; }
158 const IPAddress broadcast_address{0, 0, 0, 0};
162 SingleBuffer<int16_t> tx_buffer{0};
164 BufferRTOS<uint8_t> rx_buffer{ 0};
166 NBuffer<uint8_t> rx_buffer{DEFAULT_BUFFER_SIZE, 0};
168 bool udp_connected =
false;
169 uint32_t packet_counter = 0;
171 size_t bytes_received = 0;
172 bool available_active =
false;
173 Print *p_out =
nullptr;
176 if (!configure_tx()) {
180 if (WiFi.status() != WL_CONNECTED) {
181 LOGE(
"Wifi not connected");
184 WiFi.setSleep(
false);
185 IPAddress myIP = WiFi.localIP();
186 udp_connected = udp.connect(myIP, cfg.udp_port);
187 return udp_connected;
192 if (WiFi.status() != WL_CONNECTED) {
193 LOGE(
"Wifi not connected");
196 WiFi.setSleep(
false);
198 this->available_active =
false;
200 if (!udp.listen(cfg.udp_port)) {
201 LOGE(
"Could not connect to '%s:%d' target", toString(cfg.target_ip),
205 udp.onPacket([
this](AsyncUDPPacket packet) {
receive_udp(packet); });
210 bool configure_tx() {
211 int rate = vban_sample_rate();
213 LOGE(
"Invalid sample rate: %d", cfg.sample_rate);
216 configure_vban((VBanSampleRates)rate);
221 if (cfg.ssid ==
nullptr)
return;
222 if (cfg.password ==
nullptr)
return;
223 LOGI(
"ssid %s", cfg.ssid);
225 WiFi.begin(cfg.ssid, cfg.password);
226 while (WiFi.status() != WL_CONNECTED) {
232 LOGI(
"Wifi connected to IP (%d.%d.%d.%d)", WiFi.localIP()[0],
233 WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
236 void configure_vban(VBanSampleRates rate) {
240 vban.packet_counter = (uint32_t*)&vban.packet[VBAN_PACKET_HEADER_BYTES];
243 .packet[VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES];
246 strncpy(vban.hdr->preamble,
"VBAN", 4);
247 vban.hdr->sample_rate =
248 static_cast<int>(VBAN_PROTOCOL_AUDIO) |
250 vban.hdr->num_samples =
251 (VBAN_PACKET_NUM_SAMPLES / cfg.channels) - 1;
252 vban.hdr->num_channels = cfg.channels - 1;
253 vban.hdr->sample_format =
254 static_cast<int>(VBAN_BITFMT_16_INT) | VBAN_CODEC_PCM;
255 strncpy(vban.hdr->stream_name, cfg.stream_name,
256 min((
int)strlen(cfg.stream_name), VBAN_STREAM_NAME_SIZE));
258 vban.packet_data_bytes =
259 (vban.hdr->num_samples + 1) * (vban.hdr->num_channels + 1) *
260 ((vban.hdr->sample_format & VBAN_BIT_RESOLUTION_MASK) + 1);
261 vban.packet_total_bytes = vban.packet_data_bytes +
262 VBAN_PACKET_HEADER_BYTES +
263 VBAN_PACKET_COUNTER_BYTES;
266 int vban_sample_rate() {
268 switch (cfg.sample_rate) {
270 result = SAMPLE_RATE_6000_HZ;
273 result = SAMPLE_RATE_12000_HZ;
276 result = SAMPLE_RATE_24000_HZ;
279 result = SAMPLE_RATE_48000_HZ;
282 result = SAMPLE_RATE_96000_HZ;
285 result = SAMPLE_RATE_192000_HZ;
288 result = SAMPLE_RATE_384000_HZ;
291 result = SAMPLE_RATE_8000_HZ;
294 result = SAMPLE_RATE_16000_HZ;
297 result = SAMPLE_RATE_32000_HZ;
300 result = SAMPLE_RATE_64000_HZ;
303 result = SAMPLE_RATE_128000_HZ;
306 result = SAMPLE_RATE_256000_HZ;
309 result = SAMPLE_RATE_512000_HZ;
312 result = SAMPLE_RATE_11025_HZ;
315 result = SAMPLE_RATE_22050_HZ;
318 result = SAMPLE_RATE_44100_HZ;
321 result = SAMPLE_RATE_88200_HZ;
324 result = SAMPLE_RATE_176400_HZ;
327 result = SAMPLE_RATE_352800_HZ;
330 result = SAMPLE_RATE_705600_HZ;
336 const char* toString(IPAddress adr) {
337 static char str[11] = {0};
338 snprintf(str, 11,
"%d.%d.%d.%d", adr[0], adr[1], adr[2], adr[3]);
358 uint16_t vban_rx_data_bytes, vban_rx_sample_count;
359 int16_t* vban_rx_data;
360 uint32_t* vban_rx_pkt_nbr;
361 uint16_t outBuf[VBAN_PACKET_MAX_SAMPLES + 1];
364 int len = packet.length();
366 LOGD(
"receive_udp %d", len);
367 uint8_t* udpIncomingPacket = packet.data();
371 if (len < VBAN_PACKET_HEADER_BYTES) {
372 LOGE(
"Too short to be VBAN (%u bytes)", len);
377 if (strncmp(
"VBAN", (
const char*)udpIncomingPacket, 4) != 0) {
378 LOGE(
"Unrecognized preamble %.4s", udpIncomingPacket);
382 uint8_t protocol = udpIncomingPacket[4] & VBAN_PROTOCOL_MASK;
384 if (protocol == VBAN_PROTOCOL_SERVICE) {
387 LOGE(
"Service packet length invalid: %u bytes", len);
392 if (len <= (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES) || len > VBAN_PACKET_MAX_LEN_BYTES) {
393 LOGE(
"Audio/other packet length invalid: %u bytes", len);
406 if ( protocol == VBAN_PROTOCOL_SERVICE ) {
408 uint8_t service_type = udpIncomingPacket[5];
409 uint8_t service_fnct = udpIncomingPacket[6];
411 if (service_type == VBAN_SERVICE_IDENTIFICATION) {
412 bool isReply = (service_fnct & VBAN_SERVICE_FNCT_REPLY) != 0;
413 uint8_t function = service_fnct & 0x7F;
415 if (!isReply && function == 0) {
416 LOGI(
"Received VBAN PING0 request");
417 sendVbanPing0Reply(packet);
425 len - (VBAN_PACKET_HEADER_BYTES + VBAN_PACKET_COUNTER_BYTES);
426 vban_rx_pkt_nbr = (uint32_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES];
427 vban_rx_data = (int16_t*)&udpIncomingPacket[VBAN_PACKET_HEADER_BYTES +
428 VBAN_PACKET_COUNTER_BYTES];
429 vban_rx_sample_count = vban_rx_data_bytes / (cfg.bits_per_sample / 8);
430 uint8_t vbanSampleRateIdx = udpIncomingPacket[4] & VBAN_SR_MASK;
431 uint8_t vbchannels = udpIncomingPacket[6] + 1;
432 uint8_t vbframes = udpIncomingPacket[5] + 1;
433 uint8_t vbformat = udpIncomingPacket[7] & VBAN_PROTOCOL_MASK;
434 uint8_t vbformat_bits = udpIncomingPacket[7] & VBAN_BIT_RESOLUTION_MASK;
435 uint32_t vbanSampleRate = VBanSRList[vbanSampleRateIdx];
441 if (vbformat != cfg.format){
442 LOGE(
"Format ignored: 0x%x", vbformat);
447 if (vbformat_bits != VBAN_BITFMT_16_INT){
448 LOGE(
"Format only 16 bits supported");
454 if (vban_rx_sample_count > VBAN_PACKET_MAX_SAMPLES) {
455 LOGE(
"unexpected packet size: %u", vban_rx_sample_count);
460 if (cfg.sample_rate != vbanSampleRate || cfg.channels != vbchannels) {
462 cfg.sample_rate = vbanSampleRate;
463 cfg.channels = vbchannels;
467 available_active =
false;
471 int size_written = p_out->write((uint8_t*)vban_rx_data, vban_rx_data_bytes);
472 if (size_written != vban_rx_data_bytes) {
473 LOGE(
"buffer overflow %d -> %d", vban_rx_data_bytes, size_written);
479 int size_written = rx_buffer.
writeArray((uint8_t*)vban_rx_data, vban_rx_data_bytes);
480 if (size_written != vban_rx_data_bytes) {
481 LOGE(
"buffer overflow %d -> %d", vban_rx_data_bytes, size_written);
485 if (!available_active) {
486 bytes_received += vban_rx_data_bytes;
487 if (bytes_received >= cfg.rx_buffer_count * DEFAULT_BUFFER_SIZE * 0.75){
488 available_active =
true;
489 LOGI(
"Activating vban");
496 void sendVbanPing0Reply(AsyncUDPPacket& sourcePacket) {
500 memset(header, 0,
sizeof(header));
501 memcpy(header,
"VBAN", 4);
502 header[4] = VBAN_PROTOCOL_SERVICE;
503 header[5] = VBAN_SERVICE_FNCT_PING0 | VBAN_SERVICE_FNCT_REPLY;
506 const uint8_t* data = sourcePacket.data();
507 memcpy(&header[8], &data[8], 16);
510 uint32_t frameNumber = (uint32_t)((data[24] & 0xFF) | ((data[25] & 0xFF) << 8) | ((data[26] & 0xFF) << 16) | ((data[27] & 0xFF) << 24));
511 memcpy(&header[24], &frameNumber, 4);
515 memset(&ping0, 0,
sizeof(ping0));
518 ping0.bitType = cfg.device_flags;
519 ping0.bitfeature = cfg.bitfeature;
520 ping0.bitfeatureEx = 0x00000000;
521 ping0.PreferedRate = 44100;
522 ping0.MinRate = 8000;
523 ping0.MaxRate = 96000;
524 ping0.color_rgb = cfg.device_color;
527 memcpy(ping0.nVersion,
"v1.0", 4);
531 memcpy(ping0.USER_Position,
"USRPOS", 6);
533 memset(ping0.LangCode_ascii, 0,
sizeof(ping0.LangCode_ascii));
534 memcpy(ping0.LangCode_ascii,
"EN", 2);
539 sprintf(ipStr,
"%d.%d.%d.%d", WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]);
540 safe_strncpy(ping0.DistantIP_ascii, ipStr,
sizeof(ping0.DistantIP_ascii));
543 ping0.DistantPort = cfg.udp_port;
544 ping0.DistantReserved = 0;
547 if (cfg.device_name && cfg.device_name[0] !=
'\0') {
548 safe_strncpy(ping0.DeviceName_ascii, cfg.device_name,
sizeof(ping0.DeviceName_ascii));
551 WiFi.macAddress(mac);
553 snprintf(macStr,
sizeof(macStr),
"%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
554 safe_strncpy(ping0.DeviceName_ascii, macStr,
sizeof(ping0.DeviceName_ascii));
558 safe_strncpy(ping0.ManufacturerName_ascii, cfg.manufacturer_name,
sizeof(ping0.ManufacturerName_ascii));
560 safe_strncpy(ping0.ApplicationName_ascii, cfg.application_name,
sizeof(ping0.ApplicationName_ascii));
562 const char* hostName = cfg.host_name;
563 if (!hostName || hostName[0] ==
'\0') {
564 hostName = WiFi.getHostname();
565 if (!hostName) hostName =
"ESP32";
567 safe_strncpy(ping0.HostName_ascii, hostName,
sizeof(ping0.HostName_ascii));
570 safe_strncpy(ping0.UserName_utf8, cfg.user_name,
sizeof(ping0.UserName_utf8));
572 safe_strncpy(ping0.UserComment_utf8, cfg.user_comment,
sizeof(ping0.UserComment_utf8));
576 memcpy(packet, header, 28);
577 memcpy(packet + 28, &ping0,
sizeof(
VBAN_PING0));
580 udp.writeTo(packet,
sizeof(packet), sourcePacket.remoteIP(), sourcePacket.remotePort());
584 void safe_strncpy(
char* dest,
const char* src,
size_t dest_size) {
585 if (dest_size == 0)
return;
586 strncpy(dest, src, dest_size - 1);
587 dest[dest_size - 1] =
'\0';