arduino-audio-tools
Loading...
Searching...
No Matches
SPIAudioMaster.h
Go to the documentation of this file.
1#pragma once
2
3#include <SPI.h>
4
10#include "SPIAudioCommand.h"
11
12#if defined(ESP32)
13#include "driver/spi_slave.h"
14#endif
15
16namespace audio_tools {
17
22#if defined(SS)
23 int cs_pin = SS;
24#else
25 int cs_pin = 10;
26#endif
27 uint32_t clock_hz = 8000000;
28 uint8_t bit_order = MSBFIRST;
29 uint8_t data_mode = SPI_MODE0;
30 size_t max_payload = 1024;
31 bool is_blocking_write = false;
32 void log() const {
33 LOGI(
34 "SPIAudioMasterConfig: sample_rate=%d, channels=%d, "
35 "bits_per_sample=%d, cs_pin=%d, clock_hz=%d, bit_order=%s, "
36 "data_mode=%s, max_payload=%d, is_blocking_write=%s",
38 bit_order == MSBFIRST ? "MSBFIRST" : "LSBFIRST",
39 data_mode == SPI_MODE0 ? "SPI_MODE0"
40 : data_mode == SPI_MODE1 ? "SPI_MODE1"
41 : data_mode == SPI_MODE2 ? "SPI_MODE2"
42 : data_mode == SPI_MODE3 ? "SPI_MODE3"
43 : "UNKNOWN",
44 (int)max_payload, is_blocking_write ? "true" : "false");
45 }
46};
47
138 public:
140 SPIAudioMaster() = default;
142 explicit SPIAudioMaster(SPIClass& spi) : p_spi(&spi) {}
143
146 config = cfg;
147 config.log();
148 if (p_spi == nullptr) {
149 p_spi = &SPI;
150 }
151 if (config.cs_pin >= 0) {
154 }
155 p_spi->begin();
156 active = true;
157
158 clear(); // Clear remote buffer on start
160 LOGI("SPIAudioMaster started with remote buffer size: %d bytes",
162 return true;
163 }
164
166 void end() {
167 clear();
168 if (p_spi != nullptr) {
169 p_spi->end();
170 }
171 active = false;
172 }
173
175 bool isActive() const { return active; }
176
178 bool setMime(const char* mime) {
179 if (mime == nullptr) return false;
181 reinterpret_cast<const uint8_t*>(mime), strlen(mime) + 1,
182 nullptr, nullptr);
183 }
184
186 void setAudioInfo(AudioInfo info) override {
188 uint8_t payload[6];
189 writeU32LE(payload, info.sample_rate);
190 payload[4] = static_cast<uint8_t>(info.channels);
191 payload[5] = static_cast<uint8_t>(info.bits_per_sample);
192 (void)sendCommand(SPIAudioCommand::SetAudioInfo, payload, sizeof(payload),
193 nullptr, nullptr);
194 }
195
197 size_t write(const uint8_t* data, size_t len) {
198 if (!active) return 0;
199 if (data == nullptr || len == 0) return 0;
200 if (len > buffer_size) {
201 LOGE("Attempting to write %d bytes, but remote buffer size is only %d",
202 (int)len, buffer_size);
203 return 0;
204 }
205
206 size_t chunk = min(config.max_payload, len);
207 if (chunk == 0) return 0;
208
211 chunk = min(chunk, available);
212 if (chunk == 0) return 0;
213 return sendCommand(SPIAudioCommand::WriteData, data, chunk, nullptr,
214 nullptr)
215 ? chunk
216 : 0;
217 }
218
219 // wait until enough space is available for the chunk
220 while (getAvailableBufferSize() < chunk) {
221 delay(10);
222 }
223 return sendCommand(SPIAudioCommand::WriteData, data, chunk, nullptr,
224 nullptr)
225 ? chunk
226 : 0;
227 }
228
231 return active ? static_cast<int>(getAvailableBufferSize()) : 0;
232 }
233
235 bool clear() {
236 return sendCommand(SPIAudioCommand::Clear, nullptr, 0, nullptr, nullptr);
237 }
238
240 int available() {
241 return active ? static_cast<int>(getFilledBufferSize()) : 0;
242 }
243
244 protected:
246 bool active = false;
247 SPIClass* p_spi = nullptr;
248 int buffer_size = 0;
249
252 uint8_t response[4] = {0, 0, 0, 0};
253 uint16_t response_len = sizeof(response);
255 response, &response_len)) {
256 return 0;
257 }
258 if (response_len < 4) {
259 return 0;
260 }
261 return readU32LE(response);
262 }
263
266 uint8_t response[4] = {0, 0, 0, 0};
267 uint16_t response_len = sizeof(response);
268 if (!sendCommand(SPIAudioCommand::GetFilledBufferSize, nullptr, 0, response,
269 &response_len)) {
270 return 0;
271 }
272 if (response_len < 4) {
273 return 0;
274 }
275 return readU32LE(response);
276 }
277
279 bool sendCommand(SPIAudioCommand command, const uint8_t* payload,
280 uint16_t payload_len, uint8_t* response,
281 uint16_t* response_len) {
282 if (!active || p_spi == nullptr) return false;
283
284 SPISettings settings(config.clock_hz, config.bit_order, config.data_mode);
285 p_spi->beginTransaction(settings);
286 if (config.cs_pin >= 0) {
288 }
289
290 p_spi->transfer(static_cast<uint8_t>(command));
291 transferU16LE(payload_len);
292
293 for (uint16_t i = 0; i < payload_len; i++) {
294 p_spi->transfer(payload != nullptr ? payload[i] : 0);
295 }
296
297 uint8_t status = p_spi->transfer(0x00);
298 uint16_t len = transferU16LE(0);
299 uint16_t response_capacity = 0;
300
301 if (response != nullptr && response_len != nullptr) {
302 response_capacity = *response_len;
303 }
304
305 if (response_len != nullptr) {
306 *response_len = len;
307 }
308
309 for (uint16_t i = 0; i < len; i++) {
310 uint8_t value = p_spi->transfer(0x00);
311 if (response != nullptr && i < response_capacity) {
312 response[i] = value;
313 }
314 }
315
316 if (config.cs_pin >= 0) {
318 }
319 p_spi->endTransaction();
320
321 return status == 0;
322 }
323
325 uint16_t transferU16LE(uint16_t value) {
326 uint8_t lo = p_spi->transfer(static_cast<uint8_t>(value & 0xFF));
327 uint8_t hi = p_spi->transfer(static_cast<uint8_t>((value >> 8) & 0xFF));
328 return static_cast<uint16_t>(lo) | (static_cast<uint16_t>(hi) << 8);
329 }
330
332 static void writeU32LE(uint8_t* out, uint32_t value) {
333 out[0] = static_cast<uint8_t>(value & 0xFF);
334 out[1] = static_cast<uint8_t>((value >> 8) & 0xFF);
335 out[2] = static_cast<uint8_t>((value >> 16) & 0xFF);
336 out[3] = static_cast<uint8_t>((value >> 24) & 0xFF);
337 }
338
340 static uint32_t readU32LE(const uint8_t* in) {
341 return static_cast<uint32_t>(in[0]) | (static_cast<uint32_t>(in[1]) << 8) |
342 (static_cast<uint32_t>(in[2]) << 16) |
343 (static_cast<uint32_t>(in[3]) << 24);
344 }
345};
346
347} // namespace audio_tools
#define HIGH
Definition Arduino.h:46
#define OUTPUT
Definition Arduino.h:38
#define LOW
Definition Arduino.h:49
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Abstract Audio Ouptut class.
Definition AudioOutput.h:25
AudioInfo cfg
Definition AudioOutput.h:88
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioOutput.h:49
SPI master endpoint for remote audio sink configuration and PCM data transfer.
Definition SPIAudioMaster.h:137
bool active
Definition SPIAudioMaster.h:246
uint16_t transferU16LE(uint16_t value)
Transfers and/or receives a 16-bit little-endian value over SPI.
Definition SPIAudioMaster.h:325
bool begin(SPIAudioMasterConfig cfg=SPIAudioMasterConfig())
Initializes SPI and stores the master configuration.
Definition SPIAudioMaster.h:145
bool isActive() const
Returns true when the master is active.
Definition SPIAudioMaster.h:175
static void writeU32LE(uint8_t *out, uint32_t value)
Writes a 32-bit little-endian integer to the target buffer.
Definition SPIAudioMaster.h:332
int available()
Queries how many bytes are currently filled/readable in the remote buffer.
Definition SPIAudioMaster.h:240
static uint32_t readU32LE(const uint8_t *in)
Reads a 32-bit little-endian integer from the source buffer.
Definition SPIAudioMaster.h:340
SPIAudioMaster()=default
Default constructor.
uint32_t getAvailableBufferSize()
Queries available space in the remote buffer in bytes.
Definition SPIAudioMaster.h:251
SPIClass * p_spi
Definition SPIAudioMaster.h:247
bool sendCommand(SPIAudioCommand command, const uint8_t *payload, uint16_t payload_len, uint8_t *response, uint16_t *response_len)
Sends a protocol command and reads the optional response.
Definition SPIAudioMaster.h:279
int availableForWrite()
Returns writable bytes on the remote side.
Definition SPIAudioMaster.h:230
bool clear()
Clears the remote audio buffer.
Definition SPIAudioMaster.h:235
void end()
Stops the master and closes the SPI bus.
Definition SPIAudioMaster.h:166
bool setMime(const char *mime)
Set stream mime type (e.g. "audio/wav", "audio/mp3")
Definition SPIAudioMaster.h:178
void setAudioInfo(AudioInfo info) override
Sends audio format information to the remote slave.
Definition SPIAudioMaster.h:186
SPIAudioMaster(SPIClass &spi)
Constructor with explicit SPI bus instance.
Definition SPIAudioMaster.h:142
uint32_t getFilledBufferSize()
Queries how many bytes are filled/readable in the remote buffer.
Definition SPIAudioMaster.h:265
int buffer_size
Definition SPIAudioMaster.h:248
size_t write(const uint8_t *data, size_t len)
Writes audio bytes in chunks using WriteData command.
Definition SPIAudioMaster.h:197
SPIAudioMasterConfig config
Definition SPIAudioMaster.h:245
SPIAudioCommand
SPI command ids used by SPIAudioMaster.
Definition SPIAudioCommand.h:19
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
void digitalWrite(digital_pin_t pin, bool value)
Definition Arduino.h:313
void delay(uint32_t ms)
Definition Arduino.h:255
void pinMode(digital_pin_t pin, int mode)
Definition Arduino.h:282
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
Configuration for SPIAudioMaster.
Definition SPIAudioMaster.h:21
uint32_t clock_hz
Definition SPIAudioMaster.h:27
uint8_t data_mode
Definition SPIAudioMaster.h:29
void log() const
Definition SPIAudioMaster.h:32
int cs_pin
Definition SPIAudioMaster.h:25
bool is_blocking_write
Definition SPIAudioMaster.h:31
uint8_t bit_order
Definition SPIAudioMaster.h:28
size_t max_payload
Definition SPIAudioMaster.h:30