-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNetAudio.cpp
More file actions
129 lines (110 loc) · 3.81 KB
/
NetAudio.cpp
File metadata and controls
129 lines (110 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "NetAudio.h"
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <esp_heap_caps.h>
#include <cstring>
NetAudio Net;
static NetAudio* s_self = nullptr;
static const uint8_t BCAST[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// Timeouts
static constexpr uint32_t LINK_TIMEOUT_MS = 1500; // "connected"
static constexpr uint32_t REC_TIMEOUT_MS = 150; // streaming stopped
static constexpr int MAX_GAP_FILL = 8; // frames of silence on loss
// ---------------------------------------------------------------------------
static void onRecvStatic(const uint8_t* mac, const uint8_t* data, int len) {
(void)mac;
if (s_self) s_self->onRecvPacket(data, len);
}
bool NetAudio::begin() {
s_self = this;
_ring = (int16_t*)heap_caps_malloc(RING_SAMPLES * sizeof(int16_t),
MALLOC_CAP_SPIRAM);
if (!_ring) return false;
WiFi.mode(WIFI_STA);
WiFi.disconnect();
esp_wifi_set_channel(LINK_CHANNEL, WIFI_SECOND_CHAN_NONE);
if (esp_now_init() != ESP_OK) return false;
esp_now_register_recv_cb(onRecvStatic);
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, BCAST, 6);
peer.channel = LINK_CHANNEL;
peer.encrypt = false;
esp_now_add_peer(&peer);
return true;
}
// ---------------------------------------------------------------------------
// Ring buffer (SPSC)
// ---------------------------------------------------------------------------
void NetAudio::push(const int16_t* s, size_t n) {
uint32_t head = _head;
for (size_t i = 0; i < n; ++i) {
uint32_t next = (head + 1) % RING_SAMPLES;
if (next == _tail) break; // full -> drop the rest
_ring[head] = s[i];
head = next;
}
_head = head;
}
void NetAudio::pushSilence(size_t n) {
uint32_t head = _head;
for (size_t i = 0; i < n; ++i) {
uint32_t next = (head + 1) % RING_SAMPLES;
if (next == _tail) break;
_ring[head] = 0;
head = next;
}
_head = head;
}
size_t NetAudio::pull(int16_t* dst, size_t maxSamples) {
size_t count = 0;
uint32_t tail = _tail;
while (count < maxSamples && tail != _head) {
dst[count++] = _ring[tail];
tail = (tail + 1) % RING_SAMPLES;
}
_tail = tail;
return count;
}
// ---------------------------------------------------------------------------
// Receive (Wi-Fi task) - keep it light: validate, decode, enqueue.
// ---------------------------------------------------------------------------
void NetAudio::onRecvPacket(const uint8_t* data, int len) {
if (len < (int)sizeof(AudioPacket)) return;
const AudioPacket* p = (const AudioPacket*)data;
if (p->magic != LINK_MAGIC || p->ver != LINK_VER || p->type != LT_AUDIO) return;
const uint32_t now = millis();
const bool rec = (p->flags & LF_RECORDING) != 0;
_lastFrameMs = now;
if (rec) {
// Conceal small losses with silence so the loop length stays correct.
if (_haveSeq && _lastWasRec) {
int gap = (int)(uint16_t)(p->seq - _lastSeq) - 1;
if (gap > 0 && gap <= MAX_GAP_FILL) pushSilence((size_t)gap * FRAME_SAMPLES);
}
int16_t frame[FRAME_SAMPLES];
adpcm_decodeFrame(p->data, frame, p->predictor, (int8_t)p->stepIndex);
push(frame, FRAME_SAMPLES);
_lastRecMs = now;
}
_lastSeq = p->seq;
_haveSeq = true;
_lastWasRec = rec;
}
// ---------------------------------------------------------------------------
bool NetAudio::connected() const {
return (millis() - _lastFrameMs) < LINK_TIMEOUT_MS;
}
bool NetAudio::remoteRecActive() const {
return (millis() - _lastRecMs) < REC_TIMEOUT_MS;
}
void NetAudio::sendStatus(uint8_t deckState, uint8_t selTrack, bool remoteRec) {
StatusPacket s;
s.magic = LINK_MAGIC;
s.ver = LINK_VER;
s.type = LT_STATUS;
s.deckState = deckState;
s.selTrack = selTrack;
s.flags = remoteRec ? LF_RECORDING : 0;
esp_now_send(BCAST, (const uint8_t*)&s, sizeof(s));
}