BackgroundAudio 1.3.3
Loading...
Searching...
No Matches
BackgroundAudioAAC.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 "BackgroundAudioGain.h"
26#include "BackgroundAudioBuffers.h"
27#include "libhelix-aac/aacdec.h"
28
34template<class DataBuffer>
36public:
38 _playing = false;
39 _paused = false;
40 _out = nullptr;
41 }
42
48 BackgroundAudioAACClass(AudioOutputBase &d) {
49 _playing = false;
50 _paused = false;
51 setDevice(&d);
52 }
53
55
63 bool setDevice(AudioOutputBase *d) {
64 if (!_playing) {
65 _out = d;
66 return true;
67 }
68 return false;
69 }
70
76 void setGain(float scale) {
77 _gain = (int32_t)(scale * (1 << 16));
78 }
79
89 bool begin() {
90 if (_playing || !_out) {
91 return false;
92 }
93
94 _hAACDecoder = AACInitDecoderPre(_private, sizeof(_private));
95 if (!_hAACDecoder) {
96 return false;
97 }
98
99 // We will use natural frame size to minimize mismatch
100 _out->setBuffers(5, framelen); // Framelen is in samples, but buffers takes words, which means 2 samples per word so we're good!
101 _out->onTransmit(&_cb, (void *)this); // The pump we will use to generate our audio
102 _out->setBitsPerSample(16);
103 _out->setStereo(true);
104 _out->setFrequency(44100);
105 _out->begin();
106
107 // Stuff with silence to start
108 uint16_t zeros[32] __attribute__((aligned(4))) = {};
109 while (_out->availableForWrite() > 32) {
110 _out->write((uint8_t *)zeros, sizeof(zeros));
111 }
112 _playing = true;
113
114 return true;
115 }
116
120 void end() {
121 if (_playing) {
122 _out->end();
123 }
124 }
130 bool playing() {
131 return _playing;
132 }
133
152 size_t write(const void *data, size_t len) {
153 return _ib.write((const uint8_t *)data, len);
154 }
155
168 return _ib.availableForWrite();
169 }
170
176 size_t available() {
177 return _ib.available() - _accumShift;
178 }
179
185 bool done() {
186 return available() <= 16; // Some minimum framesize
187 }
188
194 uint32_t frames() {
195 return _frames;
196 }
197
203 uint32_t shifts() {
204 return _shifts;
205 }
206
212 uint32_t underflows() {
213 return _underflows;
214 }
215
221 uint32_t errors() {
222 return _errors;
223 }
224
230 uint32_t dumps() {
231 return _dumps;
232 }
233
241 void flush() {
242 noInterrupts();
243 _ib->flush();
244 _accumShift = 0;
245 interrupts();
246 }
247
251 void pause() {
252 _paused = true;
253 }
254
260 bool paused() {
261 return _paused;
262 }
263
267 void unpause() {
268 _paused = false;
269 }
270
271private:
272 static void _cb(void *ptr) {
273 ((BackgroundAudioAACClass*)ptr)->pump();
274 }
275
276 void generateOneFrame() {
277 // Every frame requires shifting all remaining data (6K?) before processing.
278 // We're not decoding AACs, we're shifting data! Instead, scroll down and only
279 // shift when we're > 1/2 of the total buffer size. We'll still shift to
280 // allow new data to be written, but we'll do it much less frequently.
281
282 int nextFrame = AACFindSyncWord((uint8_t *)_ib.buffer() + _accumShift, _ib.available() - _accumShift);
283 if (nextFrame == -1) {
284 // Could not find a sync word but we need to send a frame now do dump entire buffer and play silence
285 _ib.shiftUp(_ib.available());
286 _accumShift = 0;
287 bzero(_outSample, sizeof(_outSample));
288 _errors++;
289 _dumps++;
290 } else {
291 _accumShift += nextFrame;
292 const unsigned char *inBuff = _ib.buffer() + _accumShift;
293 int bytesLeft = _ib.available() - _accumShift;
294 int ret = AACDecode(_hAACDecoder, (unsigned char **)&inBuff, &bytesLeft, (int16_t *)_outSample);
295 if (ret) {
296 // Error in decode, play silence and skip
297 _accumShift++; // Just go one past the current bad sync and try again
298 _errors++;
299 bzero(_outSample, sizeof(_outSample));
300 } else {
301 AACFrameInfo fi;
302 AACGetLastFrameInfo(_hAACDecoder, &fi);
303 _sampleRate = fi.sampRateOut;
304 _outSamples = fi.outputSamps / 2;
305 _accumShift = inBuff - _ib.buffer();
306 _frames++;
307 if (fi.nChans == 1) {
308 for (int i = 0; i < _outSamples; i++) {
309 _outSample[i][1] = _outSample[1][0];
310 }
311 }
312 }
313 }
314
315 // If we accumulate too large of a shift, actually do the shift so more space for writer
316 if (_accumShift > _ib.size() / 2) {
317 _ib.shiftUp(_accumShift);
318 _accumShift = 0;
319 _shifts++;
320 }
321
322 ApplyGain((int16_t *)_outSample, _outSamples * 2, _gain);
323 }
324
325 void pump() {
326 while (_out->availableForWrite() >= (int)framelen) {
327 if (_paused) {
328 bzero((uint8_t *)_outSample, _outSamples * 2 * sizeof(int16_t));
329 } else {
330 generateOneFrame();
331 if (_sampleRate) {
332 _out->setFrequency(_sampleRate);
333 }
334 }
335 _out->write((uint8_t *)_outSample, _outSamples * 2 * sizeof(int16_t));
336 }
337 }
338
339private:
340 AudioOutputBase *_out = nullptr;
341 HAACDecoder _hAACDecoder;
342 uint8_t _private[/*sizeof(AACDecInfo)*/ 96 + /*sizeof(PSInfoBase)*/ 28752 + /*sizeof(PSInfoSBR)*/ 50788 + 16];
343 bool _playing = false;
344 bool _paused = false;
345 static const size_t framelen = 2048;
346 int16_t _outSample[framelen][2] __attribute__((aligned(4)));
347 int _outSamples = 1024;
348 int _sampleRate = 44000;
349 DataBuffer _ib;
350 int32_t _gain = 1 << 16;
351 uint32_t _accumShift = 0;
352
353 // AAC quality stats, cumulative
354 uint32_t _frames = 0;
355 uint32_t _shifts = 0;
356 uint32_t _underflows = 0;
357 uint32_t _errors = 0;
358 uint32_t _dumps = 0;
359};
360
361
366
using ROMBackgroundAudioAAC = BackgroundAudioAACClass<ROMDataBuffer>;
Interrupt-driven AAC decoder. Generates a full frame of samples each cycle and uses the RawBuffer to ...
Definition BackgroundAudioAAC.h:35
uint32_t underflows()
Get the number of times the MP3 decoder has underflowed waiting on raw data since begin
Definition BackgroundAudioAAC.h:212
void pause()
Pause the decoder. Won't process raw input data and will transmit silence.
Definition BackgroundAudioAAC.h:251
bool begin()
Starts the background AAC decoder/player. Will initialize the output device and start sending silence...
Definition BackgroundAudioAAC.h:89
void unpause()
Unpause previously paused playback. Will start processing input data again.
Definition BackgroundAudioAAC.h:267
bool setDevice(AudioOutputBase *d)
Set an output device before begin
Definition BackgroundAudioAAC.h:63
size_t write(const void *data, size_t len)
Writes a block of raw data to the decoder's buffer.
Definition BackgroundAudioAAC.h:152
uint32_t dumps()
Get the number of full buffer dumps (catastrophic data error) since begin
Definition BackgroundAudioAAC.h:230
void flush()
Flushes any existing raw data, resets the processor to start a new MP3.
Definition BackgroundAudioAAC.h:241
uint32_t frames()
Get number of "frames" (1024 or 2048 stereo samples) processed by decoder.
Definition BackgroundAudioAAC.h:194
uint32_t errors()
Get the number of decoder errors since begin
Definition BackgroundAudioAAC.h:221
BackgroundAudioAACClass(AudioOutputBase &d)
Construct an AAC decoder with a given AudioOutputBase.
Definition BackgroundAudioAAC.h:48
bool playing()
Determines if the AAC decoder has been started.
Definition BackgroundAudioAAC.h:130
void end()
Stops the AAC decoder process and the calls the output device's end to shut it down,...
Definition BackgroundAudioAAC.h:120
size_t available()
Gets number of bytes already in the raw buffer.
Definition BackgroundAudioAAC.h:176
bool paused()
Determine if the playback is paused.
Definition BackgroundAudioAAC.h:260
bool done()
Determine if no more AAC file is present in the buffer.
Definition BackgroundAudioAAC.h:185
uint32_t shifts()
Get the number of input data shifts processed by decoder since begin
Definition BackgroundAudioAAC.h:203
size_t availableForWrite()
Gets number of bytes available to write to raw buffer.
Definition BackgroundAudioAAC.h:167
void setGain(float scale)
Set the gain multiplier (volume) for the stream. Takes effect immediately.
Definition BackgroundAudioAAC.h:76