BackgroundAudio 1.3.3
Loading...
Searching...
No Matches
BackgroundAudioMixer.h
1/*
2 BackgroundAudio
3 Plays an audio file using IRQ driven decompression. Main loop() writes
4 data to the buffer but isn't blocked while playing
5
6 Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#pragma once
23#include <Arduino.h>
24#include "WrappedAudioOutputBase.h"
25#include <vector>
26
39class BackgroundAudioMixerInput : public AudioOutputBase {
40private:
41 // Forward definitions
42 template<size_t> friend class BackgroundAudioMixer;
43 struct AudioBuffer;
44
45public:
49 BackgroundAudioMixerInput(int outputRate, size_t outputBufferWords) {
50 _outputRate = outputRate;
51 _outputBufferWords = outputBufferWords;
52 _outputBuffer = new uint32_t[outputBufferWords];
53 _bufferCount = 5;
54 _wordsPerBuffer = 2048;
55 _silenceSample = 0;
56 _inputRate = outputRate;
57 _running = false;
58 _cb = nullptr;
59 }
60
62 delete[] _outputBuffer;
63 while (_filled) {
64 auto x = _filled->next;
65 _deleteAudioBuffer(_filled);
66 _filled = x;
67 }
68 while (_empty) {
69 auto x = _empty->next;
70 _deleteAudioBuffer(_empty);
71 _empty = x;
72 }
73 }
74
84 virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample = 0) override {
85 _bufferCount = buffers;
86 _wordsPerBuffer = bufferWords;
87 _silenceSample = silenceSample;
88 return true;
89 }
90
91
99 virtual bool setBitsPerSample(int bps) override {
100 return bps == 16; // Only 16b for now
101 }
102
103
114 virtual bool setFrequency(int freq) override {
115 if ((int)_inputRate != freq) {
116 _readOff = 0;
117 }
118
119 _inputRate = freq;
120 // 2-step to ensure we do the math and not short-circuit
121 _resample16p16 = _inputRate << 16;
122 _resample16p16 /= _outputRate;
123 return true;
124 }
125
126
134 virtual bool setStereo(bool stereo = true) override {
135 return stereo; // Only stereo for now
136 }
137
147 virtual bool begin() override {
148 if (_running) {
149 return false;
150 }
151
152 // The raw input data
153 _empty = nullptr;
154 for (size_t i = 0; i < _bufferCount; i++) {
155 auto ab = new AudioBuffer;
156 ab->buff = new uint32_t[_wordsPerBuffer];
157 bzero(ab->buff, _wordsPerBuffer * 4);
158 ab->next = nullptr;
159 _addToList(&_empty, ab);
160 }
161 _filled = nullptr;
162 _userOff = 0;
163 _underflow = false;
164
165 // The offset in the filled buffer to use next for sending to mixer
166 _readOff = 0;
167 setFrequency(_outputRate);
168
169 _running = true;
170
171 return true;
172 }
173
179 virtual bool end() override {
180 return false;
181 }
182
188 virtual bool getUnderflow() override {
189 noInterrupts();
190 auto ret = _underflow;
191 _underflow = false;
192 interrupts();
193 return ret;
194 }
195
202 virtual void onTransmit(void (*cb)(void *), void *cbData) override {
203 _cb = cb;
204 _cbData = cbData;
205 }
206
213 virtual size_t write(uint8_t) {
214 return 0; // Can't do bytes, dude!
215 }
216
225 virtual size_t write(const uint8_t *buffer, size_t size) override {
226 size_t written = 0;
227
228 if (!_running) {
229 return 0;
230 }
231 size_t words = size / sizeof(uint32_t);
232 while (words) {
233 AudioBuffer **volatile p = (AudioBuffer * *volatile)&_empty;
234 if (!*p) {
235 break;
236 }
237 size_t availToWriteThisBuff = _wordsPerBuffer - _userOff;
238 size_t toWrite = std::min(availToWriteThisBuff, words);
239 memcpy(&((*p)->buff[_userOff]), buffer, toWrite * sizeof(uint32_t));
240 buffer += toWrite * sizeof(uint32_t);
241 written += toWrite;
242 _userOff += toWrite;
243 words -= toWrite;
244 if (_userOff == _wordsPerBuffer) {
245 _addToList(&_filled, _takeFromList(p));
246 _userOff = 0;
247 }
248 }
249 return written * sizeof(uint32_t);
250 }
251
258 virtual int availableForWrite() override {
259 if (!_empty) {
260 return 0;
261 }
262 AudioBuffer *p = _empty;
263 int avail = _wordsPerBuffer - _userOff; // Currently available in this buffer
264
265 // Each add'l buffer has wpb spaces...
266 auto x = p->next;
267 while (x) {
268 avail += _wordsPerBuffer;
269 x = x->next;
270 }
271 return avail;
272 }
273
274private:
275 void _addToList(AudioBuffer **list, AudioBuffer *element) {
276 noInterrupts();
277 // Find end of list, if any
278 while ((*list) && ((*list)->next != nullptr)) {
279 list = &(*list)->next;
280 }
281 if (*list) {
282 (*list)->next = element;
283 } else {
284 *list = element;
285 }
286 element->next = nullptr; // Belt and braces
287 interrupts();
288 }
289
290 AudioBuffer *_takeFromList(AudioBuffer **list) {
291 noInterrupts();
292 auto ret = *list;
293 if (ret) {
294 *list = ret->next;
295 }
296 interrupts();
297 return ret;
298 }
299
300 void _deleteAudioBuffer(AudioBuffer *ab) {
301 delete[] ab->buff;
302 delete ab;
303 }
304
305 uint32_t *getResampledBuffer() {
306 // Prime the pump when we're all out of frames
307 if (!_filled && _cb) {
308 _cb(_cbData);
309 }
310
311 if (!_filled) {
312 for (size_t i = 0; i < _outputBufferWords; i++) {
313 _outputBuffer[i] = _silenceSample;
314 _underflow = true;
315 }
316 return _outputBuffer; // Nothing to send in but the sound of silence...
317 }
318
319 uint32_t *p = _outputBuffer;
320 if (_outputRate == _inputRate) {
321 for (size_t i = 0; i < _outputBufferWords; i++) {
322 if (_readOff == _wordsPerBuffer) {
323 _addToList(&_empty, _takeFromList(&_filled));
324 _readOff = 0;
325 if (_cb) {
326 _cb(_cbData);
327 }
328 }
329 if (!_filled) {
330 _underflow = true;
331 }
332 *(p++) = _filled ? _filled->buff[_readOff++] : _silenceSample;
333 }
334 } else {
335 // _readOff will be a 16p16 fixed point
336 for (size_t i = 0; i < _outputBufferWords; i++) {
337 while (_readOff >= _wordsPerBuffer << 16) {
338 _addToList(&_empty, _takeFromList(&_filled));
339 _readOff -= _wordsPerBuffer << 16;
340 if (_cb) {
341 _cb(_cbData);
342 }
343 }
344 if (!_filled) {
345 _underflow = true;
346 }
347 *(p++) = _filled ? _filled->buff[_readOff >> 16] : _silenceSample;
348 if (_filled) {
349 _readOff += _resample16p16;
350 }
351 }
352 }
353 return _outputBuffer;
354 }
355
356private:
357 typedef struct AudioBuffer {
358 struct AudioBuffer *next;
359 uint32_t *buff;
360 } AudioBuffer;
361 AudioBuffer *_filled = nullptr; // List of buffers ready to be played
362 AudioBuffer *_empty = nullptr; // List of buffers waiting to be filled. *_empty = currently writing
363
364 size_t _bufferCount;
365 size_t _wordsPerBuffer;
366 int32_t _silenceSample;
367 uint32_t _inputRate;
368 uint32_t _outputRate;
369 bool _running;
370 size_t _userOff;
371 size_t _readOff;
372 uint32_t _resample16p16; // Incremental step to get from inputRate to outputRate in fixed point 16.16 format
373 bool _underflow;
374 size_t _outputBufferWords;
375 uint32_t *_outputBuffer;
376 void (*_cb)(void *);
377 void *_cbData;
378};
379
380
397template<size_t _outWords = 512>
399public:
406 BackgroundAudioMixer(AudioOutputBase &d, int outputRate) {
407 _running = false;
408 _out = &d;
409 _outRate = outputRate;
410 _running = false;
411 }
412
413 ~BackgroundAudioMixer() { /* Noop */
414 }
415
422 if (_running) {
423 return nullptr;
424 }
425 auto x = new BackgroundAudioMixerInput(_outRate, _outWords);
426 _input.push_back(x);
427 return x;
428 }
429
440 bool begin() {
441 if (_running) {
442 return false;
443 }
444
445 // We will use natural frame size to minimize mismatch
446 _out->setBuffers(4, _outWords); // Framelen is in samples, but buffers takes words, which means 2 samples per word so we're good!
447 _out->onTransmit(&_cb, (void *)this); // The pump we will use to generate our audio
448 _out->setBitsPerSample(16);
449 _out->setStereo(true);
450 _out->setFrequency(_outRate);
451 _out->begin();
452
453 // Stuff with silence to start
454 uint32_t zeros[32] __attribute__((aligned(4))) = {};
455 while (_out->availableForWrite() > 32) {
456 _out->write((uint8_t *)zeros, sizeof(zeros));
457 }
458
459 _running = true;
460
461 return true;
462 }
463
464private:
468 static void _cb(void *ptr) {
469 ((BackgroundAudioMixer *)ptr)->pump();
470 }
471
475 void generateOneFrame() {
476 // Collect all the input leg buffers
477 int16_t *leg[_input.size()];
478 for (size_t i = 0; i < _input.size(); i++) {
479 leg[i] = (int16_t *)_input[i]->getResampledBuffer();
480 }
481
482 // Sum them up with saturating arithmetic
483 for (size_t i = 0; i < _outWords * 2; i++) {
484 int32_t sum = 0;
485 for (size_t j = 0; j < _input.size(); j++) {
486 sum += leg[j][i];
487 }
488 if (sum > 32767) {
489 sum = 32767;
490 } else if (sum < -32767) {
491 sum = -32767;
492 }
493 _outBuff[i] = (int16_t)sum;
494 }
495 }
496
500 void pump() {
501 while (_out->availableForWrite() >= (int)_outWords) {
502 generateOneFrame();
503 _out->write((uint8_t *)_outBuff, _outWords * 4);
504 }
505 }
506
507 bool _running;
508 AudioOutputBase *_out;
509 int _outRate;
510 int16_t _outBuff[_outWords * 2];
511 std::vector<BackgroundAudioMixerInput *> _input;
512};
Implements an emulated AudioOutputBase and feeds resampled data from it up to an input of an AudioOut...
Definition BackgroundAudioMixer.h:39
virtual bool setStereo(bool stereo=true) override
Set mono or stereo mode. Only stereo supported.
Definition BackgroundAudioMixer.h:134
virtual bool getUnderflow() override
Determine if there was an underflow since the last time this was called. Cleared on read.
Definition BackgroundAudioMixer.h:188
BackgroundAudioMixerInput(int outputRate, size_t outputBufferWords)
Create a mixer with a defined sample rate and buffer size.
Definition BackgroundAudioMixer.h:49
virtual bool setFrequency(int freq) override
Set the sample rate (LRCLK/WS) of the emulated interface. Can be called while running.
Definition BackgroundAudioMixer.h:114
virtual void onTransmit(void(*cb)(void *), void *cbData) override
Set the callback function to be called every mixer buffer completion.
Definition BackgroundAudioMixer.h:202
virtual int availableForWrite() override
Determine the number of bytes we can write to the resample buffers at this instant.
Definition BackgroundAudioMixer.h:258
virtual size_t write(const uint8_t *buffer, size_t size) override
Write data to the input leg interface. Will not block and may write less than requested.
Definition BackgroundAudioMixer.h:225
virtual size_t write(uint8_t)
Write single byte to I2S buffers. Not supported.
Definition BackgroundAudioMixer.h:213
virtual bool setBitsPerSample(int bps) override
Set the bits per sample for the emulated output. Only 16-bit supported.
Definition BackgroundAudioMixer.h:99
virtual bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample=0) override
Set the size and number of the resample buffers before begin
Definition BackgroundAudioMixer.h:84
virtual bool end() override
Stop the mixer input leg, not implemented.
Definition BackgroundAudioMixer.h:179
virtual bool begin() override
Start the emulated interface.
Definition BackgroundAudioMixer.h:147
Real-time, IRQ driven mixer with configurable number of inputs.
Definition BackgroundAudioMixer.h:398
BackgroundAudioMixerInput * add()
Create a new input leg on the mixer, usable as an AudioOutputBase. Only legal before begin
Definition BackgroundAudioMixer.h:421
BackgroundAudioMixer(AudioOutputBase &d, int outputRate)
Construct a mixer with output device and constant sample rate.
Definition BackgroundAudioMixer.h:406
bool begin()
Start the mixer and attached physical interface.
Definition BackgroundAudioMixer.h:440