arduino-audio-tools
Loading...
Searching...
No Matches
SPIRegAudioSlave.h
Go to the documentation of this file.
1#pragma once
2
7#include "TinySPISlave.h"
8
9namespace audio_tools {
10
16 int sck = -1;
17 int miso = -1;
18 int mosi = -1;
19 int cs = -1;
22 size_t max_payload = 256;
25};
26
56class SPIRegAudioSlave : public AudioStream, public MimeSource {
57 public:
59
61 end();
62 config = cfg;
63 if (p_buffer == nullptr || config.max_payload == 0) return false;
64 p_buffer->reset();
65 memset(info_raw, 0, sizeof(info_raw));
66 memset(mime_raw, 0, sizeof(mime_raw));
67 memset(mime_str, 0, sizeof(mime_str));
68 pending_info = false;
69 pending_mime = false;
71 if (cfg) {
72 encodeInfo(cfg, info_raw);
74 }
75
76 tinyspi::Config sc;
77 sc.sck = config.sck;
78 sc.miso = config.miso;
79 sc.mosi = config.mosi;
80 sc.cs = config.cs;
81 sc.mode = config.spi_mode;
82 sc.rxBufferSize = 2 * (config.max_payload + 2);
83 sc.txBufferSize = 64;
84 // a frame must not be split: [cmd] + payload
85 sc.maxFrameSize = config.max_payload + 2;
86 sc.slots = 1; // reply is clocked out in the very next frame
87 sc.spiHost = config.spi_host;
88 if (!spi.begin(sc)) {
89 LOGE("SPIRegAudioSlave: TinySPISlave begin failed");
90 return false;
91 }
92 spi.onFrameImmediate(&SPIRegAudioSlave::frameThunk, this);
93 active = true;
94 return true;
95 }
96
97 void end() override {
98 if (active) {
99 spi.onFrameImmediate(nullptr, nullptr);
100 spi.end();
101 }
102 active = false;
103 }
104
105 bool isActive() const { return active; }
106
108 void update() {
109 if (!active) return;
110 if (pending_mime) {
111 char tmp[SPIREG_MIME_SIZE];
112 {
113 tinyspi::detail::Critical c;
114 memcpy(tmp, mime_raw, sizeof(tmp));
115 pending_mime = false;
116 }
117 tmp[sizeof(tmp) - 1] = 0;
118 LOGI("SPIRegAudioSlave mime: %s", tmp);
119 memcpy(mime_str, tmp, sizeof(mime_str));
121 }
122 if (pending_info) {
124 {
125 tinyspi::detail::Critical c;
126 memcpy(tmp, info_raw, sizeof(tmp));
127 pending_info = false;
128 }
129 AudioInfo new_info;
130 decodeInfo(tmp, new_info);
131 setAudioInfo(new_info);
132 }
133 }
134
137 const char* mime() override { return mime_str[0] ? mime_str : nullptr; }
138
140 const char* mimeType() { return mime_str; }
141
144 void setMimeCallback(void (*cb)(const char* mime, void* ctx),
145 void* ctx = nullptr) {
146 mime_cb = cb;
147 mime_ctx = ctx;
148 }
149
151 int available() override {
152 return p_buffer == nullptr ? 0 : p_buffer->available();
153 }
154
156 int availableForWrite() override {
157 return p_buffer == nullptr ? 0 : p_buffer->availableForWrite();
158 }
159
160 size_t readBytes(uint8_t* data, size_t len) override {
161 if (!active || data == nullptr || len == 0 || p_buffer == nullptr) return 0;
162 int n = min((int)len, p_buffer->available());
163 return n > 0 ? p_buffer->readArray(data, n) : 0;
164 }
165
168
169 protected:
171 TinySPISlave spi;
173 bool active = false;
176 volatile bool pending_info = false;
177 volatile bool pending_mime = false;
180 void (*mime_cb)(const char*, void*) = nullptr;
181 void* mime_ctx = nullptr;
182
183 static void encodeInfo(const AudioInfo& in, uint8_t* out) {
184 out[0] = in.sample_rate & 0xFF;
185 out[1] = (in.sample_rate >> 8) & 0xFF;
186 out[2] = (in.sample_rate >> 16) & 0xFF;
187 out[3] = (in.sample_rate >> 24) & 0xFF;
188 out[4] = in.channels & 0xFF;
189 out[5] = (in.channels >> 8) & 0xFF;
190 out[6] = in.bits_per_sample;
191 out[7] = 0;
192 }
193
194 static void decodeInfo(const uint8_t* in, AudioInfo& out) {
195 out.sample_rate = (uint32_t)in[0] | ((uint32_t)in[1] << 8) |
196 ((uint32_t)in[2] << 16) | ((uint32_t)in[3] << 24);
197 out.channels = (uint16_t)in[4] | ((uint16_t)in[5] << 8);
198 out.bits_per_sample = in[6];
199 }
200
201 static void frameThunk(size_t len, void* self) {
202 static_cast<SPIRegAudioSlave*>(self)->onFrame(len);
203 }
204
206 void onFrame(size_t len) {
207 int c = spi.read();
208 if (c < 0) return;
209 len--;
210 uint8_t cmd = (uint8_t)c;
211 uint8_t addr = cmd & SPIREG_ADDR_MASK;
212 if (cmd & SPIREG_READ_MASK) {
213 size_t want = 0;
214 if (len >= 1) {
215 want = (uint8_t)spi.read();
216 len--;
217 }
218 while (len--) spi.read();
219 stageReply(addr, want);
220 } else {
221 storeWrite(addr, len);
222 }
223 }
224
225 void storeWrite(uint8_t addr, size_t len) {
226 switch (addr) {
227 case SPIREG_DATA: {
228 uint8_t chunk[32];
229 while (len) {
230 size_t n = len < sizeof(chunk) ? len : sizeof(chunk);
231 n = spi.read(chunk, n);
232 if (n == 0) break;
233 len -= n;
234 int free = p_buffer->availableForWrite();
235 int w = free > 0 ? p_buffer->writeArray(chunk, min((int)n, free)) : 0;
236 overflow_bytes = overflow_bytes + (n - w);
237 }
238 break;
239 }
241 if (len == SPIREG_AUDIO_INFO_SIZE) {
242 tinyspi::detail::Critical c;
244 pending_info = true;
245 len = 0;
246 }
247 break;
248 case SPIREG_MIME:
249 if (len > 0 && len <= SPIREG_MIME_SIZE) {
250 tinyspi::detail::Critical c;
251 memset(mime_raw, 0, sizeof(mime_raw));
252 spi.read((uint8_t*)mime_raw, len);
253 mime_raw[SPIREG_MIME_SIZE - 1] = 0;
254 pending_mime = true;
255 len = 0;
256 }
257 break;
258 default:
259 break;
260 }
261 while (len--) spi.read(); // discard what was not consumed
262 }
263
264 void stageReply(uint8_t addr, size_t want) {
266 size_t size = 0;
267 memset(reg, 0, sizeof(reg));
268 switch (addr) {
270 {
271 tinyspi::detail::Critical c;
272 memcpy(reg, info_raw, SPIREG_AUDIO_INFO_SIZE);
273 }
275 break;
276 case SPIREG_MIME:
277 {
278 tinyspi::detail::Critical c;
279 memcpy(reg, mime_raw, SPIREG_MIME_SIZE);
280 }
281 size = SPIREG_MIME_SIZE;
282 break;
283 case SPIREG_AVAILABLE: {
285 reg[0] = space & 0xFF;
286 reg[1] = (space >> 8) & 0xFF;
287 reg[2] = (space >> 16) & 0xFF;
288 reg[3] = (space >> 24) & 0xFF;
290 break;
291 }
292 default:
293 break;
294 }
295 spi.flushTx();
296 if (want == 0) want = size;
297 size_t room = (size_t)spi.availableForWrite();
298 if (want > room) want = room;
299 for (size_t j = 0; j < want; j++) {
300 spi.write(j < size ? reg[j] : 0);
301 }
302 }
303};
304
305} // namespace audio_tools
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:128
Shared functionality of all buffers.
Definition Buffers.h:23
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:34
virtual void reset()=0
clears the buffer
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:56
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
Abstract interface for classes that can provide MIME type information.
Definition AudioTypes.h:552
SPI audio slave based on the TinySPISlave library: the master controls us via a small register map (s...
Definition SPIRegAudioSlave.h:56
void setMimeCallback(void(*cb)(const char *mime, void *ctx), void *ctx=nullptr)
Definition SPIRegAudioSlave.h:144
bool active
Definition SPIRegAudioSlave.h:173
char mime_raw[SPIREG_MIME_SIZE]
Definition SPIRegAudioSlave.h:175
uint32_t overflowBytes() const
Number of audio bytes dropped because the master ignored the flow control.
Definition SPIRegAudioSlave.h:167
uint8_t info_raw[SPIREG_AUDIO_INFO_SIZE]
Definition SPIRegAudioSlave.h:174
BaseBuffer< uint8_t > * p_buffer
Definition SPIRegAudioSlave.h:172
void * mime_ctx
Definition SPIRegAudioSlave.h:181
bool isActive() const
Definition SPIRegAudioSlave.h:105
void onFrame(size_t len)
Processes one frame: runs in ISR / SPI task context!
Definition SPIRegAudioSlave.h:206
const char * mimeType()
Mime type defined by the master (empty if not set)
Definition SPIRegAudioSlave.h:140
void storeWrite(uint8_t addr, size_t len)
Definition SPIRegAudioSlave.h:225
size_t readBytes(uint8_t *data, size_t len) override
Definition SPIRegAudioSlave.h:160
void(* mime_cb)(const char *, void *)
Definition SPIRegAudioSlave.h:180
volatile bool pending_info
Definition SPIRegAudioSlave.h:176
void end() override
Definition SPIRegAudioSlave.h:97
int available() override
Readable audio bytes.
Definition SPIRegAudioSlave.h:151
volatile uint32_t overflow_bytes
Definition SPIRegAudioSlave.h:178
volatile bool pending_mime
Definition SPIRegAudioSlave.h:177
void stageReply(uint8_t addr, size_t want)
Definition SPIRegAudioSlave.h:264
int availableForWrite() override
Free space in the buffer: this is the value provided to the master.
Definition SPIRegAudioSlave.h:156
TinySPISlave spi
Definition SPIRegAudioSlave.h:171
char mime_str[SPIREG_MIME_SIZE]
Definition SPIRegAudioSlave.h:179
static void decodeInfo(const uint8_t *in, AudioInfo &out)
Definition SPIRegAudioSlave.h:194
SPIRegAudioSlaveConfig config
Definition SPIRegAudioSlave.h:170
static void frameThunk(size_t len, void *self)
Definition SPIRegAudioSlave.h:201
const char * mime() override
Definition SPIRegAudioSlave.h:137
bool begin(SPIRegAudioSlaveConfig cfg=SPIRegAudioSlaveConfig())
Definition SPIRegAudioSlave.h:60
static void encodeInfo(const AudioInfo &in, uint8_t *out)
Definition SPIRegAudioSlave.h:183
void update()
Call from loop(): applies AudioInfo / mime changes made by the master.
Definition SPIRegAudioSlave.h:108
SPIRegAudioSlave(BaseBuffer< uint8_t > &buffer)
Definition SPIRegAudioSlave.h:58
@ SPIREG_MIME
32 bytes (rw): zero terminated mime type
Definition SPIRegAudioRegisters.h:29
@ SPIREG_AUDIO_INFO
8 bytes (rw): sample_rate u32, channels u16, bits_per_sample u8, reserved
Definition SPIRegAudioRegisters.h:27
@ SPIREG_DATA
stream (wo): the bytes of the frame are appended to the audio buffer
Definition SPIRegAudioRegisters.h:33
@ SPIREG_AVAILABLE
4 bytes (ro): u32 free space in the slave audio buffer in bytes
Definition SPIRegAudioRegisters.h:31
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
constexpr size_t SPIREG_MIME_SIZE
Definition SPIRegAudioRegisters.h:39
constexpr uint8_t SPIREG_READ_MASK
Definition SPIRegAudioRegisters.h:36
constexpr size_t SPIREG_AUDIO_INFO_SIZE
Definition SPIRegAudioRegisters.h:38
constexpr size_t SPIREG_AVAILABLE_SIZE
Definition SPIRegAudioRegisters.h:40
constexpr uint8_t SPIREG_ADDR_MASK
Definition SPIRegAudioRegisters.h:37
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:56
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:58
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:60
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:62
Configuration for SPIRegAudioSlave.
Definition SPIRegAudioSlave.h:15
int mosi
Definition SPIRegAudioSlave.h:18
uint8_t spi_mode
Definition SPIRegAudioSlave.h:20
int sck
Definition SPIRegAudioSlave.h:16
uint8_t spi_host
ESP32 only: SPI host (2 = SPI2, 3 = SPI3)
Definition SPIRegAudioSlave.h:24
int miso
Definition SPIRegAudioSlave.h:17
size_t max_payload
Largest number of audio bytes the master is allowed to send in one frame.
Definition SPIRegAudioSlave.h:22
int cs
Definition SPIRegAudioSlave.h:19