arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
AudioPlayer.h
1#pragma once
2
3#include "AudioToolsConfig.h"
4#include "AudioTools/CoreAudio/AudioBasic/Debouncer.h"
5#include "AudioTools/CoreAudio/AudioHttp/AudioHttp.h"
6#include "AudioTools/CoreAudio/AudioLogger.h"
7#include "AudioTools/CoreAudio/AudioStreams.h"
8#include "AudioTools/CoreAudio/AudioTypes.h"
9#include "AudioTools/CoreAudio/BaseConverter.h"
10#include "AudioTools/CoreAudio/Buffers.h"
11#include "AudioTools/CoreAudio/Fade.h"
12#include "AudioTools/CoreAudio/StreamCopy.h"
13#include "AudioTools/CoreAudio/AudioMetaData/MetaData.h"
14#include "AudioTools/CoreAudio/VolumeStream.h"
15#include "AudioTools/Disk/AudioSource.h"
16#include "AudioTools/AudioCodecs/AudioCodecs.h"
17
24namespace audio_tools {
25
39
40public:
42 AudioPlayer() { TRACED(); }
43
53 AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder) {
54 TRACED();
55 this->p_source = &source;
56 this->p_decoder = &decoder;
57 setOutput(output);
58 // notification for audio configuration
59 decoder.addNotifyAudioChange(*this);
60 }
61
72 AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder,
73 AudioInfoSupport *notify = nullptr) {
74 TRACED();
75 this->p_source = &source;
76 this->p_decoder = &decoder;
77 setOutput(output);
79 }
80
90 AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder) {
91 TRACED();
92 this->p_source = &source;
93 this->p_decoder = &decoder;
94 setOutput(output);
95 // notification for audio configuration
96 decoder.addNotifyAudioChange(*this);
97 }
98
99 AudioPlayer(AudioPlayer const &) = delete;
100
101 AudioPlayer &operator=(AudioPlayer const &) = delete;
102
103 void setOutput(AudioOutput &output) {
104 if (p_decoder->isResultPCM()) {
105 this->fade.setOutput(output);
106 this->volume_out.setOutput(fade);
107 out_decoding.setOutput(&volume_out);
108 out_decoding.setDecoder(p_decoder);
109 } else {
110 out_decoding.setOutput(&output);
111 out_decoding.setDecoder(p_decoder);
112 }
113 this->p_final_print = &output;
114 this->p_final_stream = nullptr;
115 }
116
117 void setOutput(Print &output) {
118 if (p_decoder->isResultPCM()) {
119 this->fade.setOutput(output);
120 this->volume_out.setOutput(fade);
121 out_decoding.setOutput(&volume_out);
122 out_decoding.setDecoder(p_decoder);
123 } else {
124 out_decoding.setOutput(&output);
125 out_decoding.setDecoder(p_decoder);
126 }
127 this->p_final_print = nullptr;
128 this->p_final_stream = nullptr;
129 }
130
131 void setOutput(AudioStream &output) {
132 if (p_decoder->isResultPCM()) {
133 this->fade.setOutput(output);
134 this->volume_out.setOutput(fade);
135 out_decoding.setOutput(&volume_out);
136 out_decoding.setDecoder(p_decoder);
137 } else {
138 out_decoding.setOutput(&output);
139 out_decoding.setDecoder(p_decoder);
140 }
141 this->p_final_print = nullptr;
142 this->p_final_stream = &output;
143 }
144
146 void setBufferSize(int size) { copier.resize(size); }
147
149 bool begin(int index = 0, bool isActive = true) {
150 TRACED();
151 bool result = false;
152 // initilaize volume
153 if (current_volume == -1.0f) {
154 setVolume(1.0f);
155 } else {
156 setVolume(current_volume);
157 }
158
159 // take definition from source
160 autonext = p_source->isAutoNext();
161
162 // initial audio info for fade from output when not defined yet
163 setupFade();
164
165 // start dependent objects
166 out_decoding.begin();
167 p_source->begin();
168 meta_out.begin();
169 volume_out.begin();
170
171 if (index >= 0) {
172 p_input_stream = p_source->selectStream(index);
173 if (p_input_stream != nullptr) {
174 if (meta_active) {
176 }
177 copier.begin(out_decoding, *p_input_stream);
178 timeout = millis() + p_source->timeoutAutoNext();
179 active = isActive;
180 result = true;
181 } else {
182 LOGW("-> begin: no data found");
183 active = false;
184 result = false;
185 }
186 } else {
187 LOGW("-> begin: no stream selected");
188 active = isActive;
189 result = false;
190 }
191 return result;
192 }
193
194 void end() {
195 TRACED();
196 active = false;
197 out_decoding.end();
198 meta_out.end();
199 // remove any data in the decoder
200 if (p_decoder != nullptr) {
201 LOGI("reset codec");
202 p_decoder->end();
203 p_decoder->begin();
204 }
205 }
206
208 AudioSource &audioSource() { return *p_source; }
209
211 void setAudioSource(AudioSource &source) { this->p_source = &source; }
212
214 void setDecoder(AudioDecoder &decoder) {
215 this->p_decoder = &decoder;
216 out_decoding.setDecoder(p_decoder);
217 }
218
221 this->p_final_notify = notify;
222 // notification for audio configuration
223 if (p_decoder != nullptr) {
224 p_decoder->addNotifyAudioChange(*this);
225 }
226 }
227
229 void setAudioInfo(AudioInfo info) override {
230 TRACED();
231 LOGI("sample_rate: %d", (int) info.sample_rate);
232 LOGI("bits_per_sample: %d", (int) info.bits_per_sample);
233 LOGI("channels: %d", (int) info.channels);
234 this->info = info;
235 // notifiy volume
236 volume_out.setAudioInfo(info);
237 fade.setAudioInfo(info);
238 // notifiy final ouput: e.g. i2s
239 if (p_final_print != nullptr)
240 p_final_print->setAudioInfo(info);
241 if (p_final_stream != nullptr)
242 p_final_stream->setAudioInfo(info);
243 if (p_final_notify != nullptr)
244 p_final_notify->setAudioInfo(info);
245 };
246
247 AudioInfo audioInfo() override { return info; }
248
250 void play() {
251 TRACED();
252 setActive(true);
253 }
254
256 void stop() {
257 TRACED();
258 setActive(false);
259 }
260
263 bool next(int offset = 1) {
264 TRACED();
265 writeEnd();
266 stream_increment = offset >= 0 ? 1 : -1;
267 active = setStream(p_source->nextStream(offset));
268 return active;
269 }
270
272 bool setIndex(int idx) {
273 TRACED();
274 writeEnd();
275 stream_increment = 1;
276 active = setStream(p_source->selectStream(idx));
277 return active;
278 }
279
281 bool setPath(const char *path) {
282 TRACED();
283 writeEnd();
284 stream_increment = 1;
285 active = setStream(p_source->selectStream(path));
286 return active;
287 }
288
290 bool previous(int offset = 1) {
291 TRACED();
292 writeEnd();
293 stream_increment = -1;
294 active = setStream(p_source->previousStream(abs(offset)));
295 return active;
296 }
297
299 bool setStream(Stream *input) {
300 end();
301 out_decoding.begin();
302 p_input_stream = input;
303 if (p_input_stream != nullptr) {
304 LOGD("open selected stream");
305 meta_out.begin();
306 copier.begin(out_decoding, *p_input_stream);
307 }
308 return p_input_stream != nullptr;
309 }
310
312 Stream *getStream() { return p_input_stream; }
313
315 bool isActive() { return active; }
316
318 operator bool() { return isActive(); }
319
321 void setActive(bool isActive) {
322 if (is_auto_fade) {
323 if (isActive) {
324 fade.setFadeInActive(true);
325 } else {
326 fade.setFadeOutActive(true);
327 copier.copy();
328 writeSilence(2048);
329 }
330 }
331 active = isActive;
332 }
333
335 bool setVolume(float volume) override {
336 bool result = true;
337 if (volume >= 0.0f && volume <= 1.0f) {
338 if (abs(volume - current_volume) > 0.01f) {
339 LOGI("setVolume(%f)", volume);
340 volume_out.setVolume(volume);
341 current_volume = volume;
342 }
343 } else {
344 LOGE("setVolume value '%f' out of range (0.0 -1.0)", volume);
345 result = false;
346 }
347 return result;
348 }
349
351 float volume() override { return current_volume; }
352
356 void setAutoNext(bool next) { autonext = next; }
357
359 void setDelayIfOutputFull(int delayMs) { delay_if_full = delayMs; }
360
362 size_t copy() {
363 return copy(copier.bufferSize());
364 }
365
367 size_t copy(size_t bytes) {
368 size_t result = 0;
369 if (active) {
370 TRACED();
371 if (delay_if_full != 0 && ((p_final_print != nullptr &&
372 p_final_print->availableForWrite() == 0) ||
373 (p_final_stream != nullptr &&
374 p_final_stream->availableForWrite() == 0))) {
375 // not ready to do anything - so we wait a bit
376 delay(delay_if_full);
377 return 0;
378 }
379 // handle sound
380 result = copier.copyBytes(bytes);
381 if (result > 0 || timeout == 0) {
382 // reset timeout if we had any data
383 timeout = millis() + p_source->timeoutAutoNext();
384 }
385 // move to next stream after timeout
386 moveToNextFileOnTimeout();
387
388 // return silence when there was no data
389 if (result < bytes && silence_on_inactive){
390 writeSilence(bytes - result);
391 }
392
393 } else {
394 // e.g. A2DP should still receive data to keep the connection open
395 if (silence_on_inactive) {
396 writeSilence(1024);
397 }
398 }
399 return result;
400 }
401
403 void setMetadataCallback(void (*callback)(MetaDataType type,
404 const char *str, int len),
405 ID3TypeSelection sel = SELECT_ID3) {
406 TRACEI();
407 // setup metadata.
408 if (p_source->setMetadataCallback(callback)) {
409 // metadata is handled by source
410 LOGI("Using ICY Metadata");
411 meta_active = false;
412 } else {
413 // metadata is handled here
414 meta_out.setCallback(callback);
415 meta_out.setFilter(sel);
416 meta_active = true;
417 }
418 }
419
422 volume_out.setVolumeControl(vc);
423 }
424
427 StreamCopy &getStreamCopy() { return copier; }
428
431 void setSilenceOnInactive(bool active) { silence_on_inactive = active; }
432
434 bool isSilenceOnInactive() { return silence_on_inactive; }
435
437 void writeSilence(size_t bytes) {
438 TRACEI();
439 if (p_final_print != nullptr) {
440 p_final_print->writeSilence(bytes);
441 } else if (p_final_stream != nullptr) {
442 p_final_stream->writeSilence(bytes);
443 }
444 }
445
446 // /// Provides the Print object to which we send the decoding result
447 // Print *getVolumeOutput() { return &volume_out; }
448
450 VolumeStream &getVolumeStream() { return volume_out; }
451
454 void setAutoFade(bool active) { is_auto_fade = active; }
455
456 bool isAutoFade() { return is_auto_fade; }
457
459 void setMetaDataSize(int size){
460 meta_out.resize(size);
461 }
462
463
464protected:
465 bool active = false;
466 bool autonext = true;
467 bool silence_on_inactive = false;
468 AudioSource *p_source = nullptr;
469 VolumeStream volume_out; // Volume control
470 FadeStream fade; // Phase in / Phase Out to avoid popping noise
471 MetaDataID3 meta_out; // Metadata parser
472 EncodedAudioOutput out_decoding; // Decoding stream
473 CopyDecoder no_decoder{true};
474 AudioDecoder *p_decoder = &no_decoder;
475 Stream *p_input_stream = nullptr;
476 AudioOutput *p_final_print = nullptr;
477 AudioStream *p_final_stream = nullptr;
478 AudioInfoSupport *p_final_notify = nullptr;
479 StreamCopy copier; // copies sound into i2s
480 AudioInfo info;
481 bool meta_active = false;
482 uint32_t timeout = 0;
483 int stream_increment = 1; // +1 moves forward; -1 moves backward
484 float current_volume = -1.0f; // illegal value which will trigger an update
485 int delay_if_full = 100;
486 bool is_auto_fade = true;
487
488 void setupFade() {
489 if (p_final_print != nullptr) {
490 fade.setAudioInfo(p_final_print->audioInfo());
491 } else if (p_final_stream != nullptr) {
492 fade.setAudioInfo(p_final_stream->audioInfo());
493 }
494 }
495
496 void moveToNextFileOnTimeout() {
497 if (!autonext)
498 return;
499 if (p_final_stream != nullptr && p_final_stream->availableForWrite() == 0)
500 return;
501 if (p_input_stream == nullptr || millis() > timeout) {
502 if (is_auto_fade)
503 fade.setFadeInActive(true);
504 if (autonext) {
505 LOGI("-> timeout - moving by %d", stream_increment);
506 // open next stream
507 if (!next(stream_increment)) {
508 LOGD("stream is null");
509 }
510 } else {
511 active = false;
512 }
513 timeout = millis() + p_source->timeoutAutoNext();
514 }
515 }
516
517 void writeEnd() {
518 // end silently
519 TRACEI();
520 if (is_auto_fade) {
521 fade.setFadeOutActive(true);
522 copier.copy();
523 // start by fading in
524 fade.setFadeInActive(true);
525 }
526 // restart the decoder to make sure it does not contain any audio when we
527 // continue
528 p_decoder->begin();
529 }
530
532 static void decodeMetaData(void *obj, void *data, size_t len) {
533 LOGD("%s, %zu", LOG_METHOD, len);
534 AudioPlayer *p = (AudioPlayer *)obj;
535 if (p->meta_active) {
536 p->meta_out.write((const uint8_t *)data, len);
537 }
538 }
539};
540
541} // namespace audio_tools
Decoding of encoded audio into PCM data.
Definition AudioCodecsBase.h:18
virtual bool isResultPCM()
Returns true to indicate that the decoding result is PCM data.
Definition AudioCodecsBase.h:53
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition AudioTypes.h:151
Supports changes to the sampling rate, bits and channels.
Definition AudioTypes.h:133
virtual void setAudioInfo(AudioInfo info)=0
Defines the input AudioInfo.
Abstract Audio Ouptut class.
Definition AudioOutput.h:22
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition AudioOutput.h:46
virtual void writeSilence(size_t len)
Definition AudioOutput.h:63
Implements a simple audio player which supports the following commands:
Definition AudioPlayer.h:38
static void decodeMetaData(void *obj, void *data, size_t len)
Callback implementation which writes to metadata.
Definition AudioPlayer.h:532
void setAudioSource(AudioSource &source)
(Re)defines the audio source
Definition AudioPlayer.h:211
void writeSilence(size_t bytes)
Sends the requested bytes as 0 values to the output.
Definition AudioPlayer.h:437
AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder)
Construct a new Audio Player object. The processing chain is AudioSource -> Stream-copy -> EncodedAud...
Definition AudioPlayer.h:90
AudioSource & audioSource()
Provides the actual audio source.
Definition AudioPlayer.h:208
size_t copy(size_t bytes)
Copies the indicated number of bytes from the source to the decoder: Call this method in the loop.
Definition AudioPlayer.h:367
void setSilenceOnInactive(bool active)
Definition AudioPlayer.h:431
bool isSilenceOnInactive()
Checks if silence_on_inactive has been activated (default false)
Definition AudioPlayer.h:434
void setDelayIfOutputFull(int delayMs)
Defines the wait time in ms if the target output is full.
Definition AudioPlayer.h:359
void setMetaDataSize(int size)
Change the default ID3 max metadata size (256)
Definition AudioPlayer.h:459
bool setStream(Stream *input)
start selected input stream
Definition AudioPlayer.h:299
void setDecoder(AudioDecoder &decoder)
(Re)defines the decoder
Definition AudioPlayer.h:214
StreamCopy & getStreamCopy()
Definition AudioPlayer.h:427
bool setVolume(float volume) override
sets the volume - values need to be between 0.0 and 1.0
Definition AudioPlayer.h:335
Stream * getStream()
Provides the actual stream (=e.g.file)
Definition AudioPlayer.h:312
void play()
starts / resumes the playing after calling stop(): same as setActive(true)
Definition AudioPlayer.h:250
bool next(int offset=1)
Definition AudioPlayer.h:263
float volume() override
Determines the actual volume.
Definition AudioPlayer.h:351
void addNotifyAudioChange(AudioInfoSupport *notify)
(Re)defines the notify
Definition AudioPlayer.h:220
void stop()
halts the playing: same as setActive(false)
Definition AudioPlayer.h:256
void setAutoNext(bool next)
Definition AudioPlayer.h:356
AudioPlayer()
Default constructor.
Definition AudioPlayer.h:42
void setAutoFade(bool active)
Definition AudioPlayer.h:454
void setMetadataCallback(void(*callback)(MetaDataType type, const char *str, int len), ID3TypeSelection sel=SELECT_ID3)
Defines the medatadata callback.
Definition AudioPlayer.h:403
bool isActive()
determines if the player is active
Definition AudioPlayer.h:315
bool previous(int offset=1)
moves to previous file
Definition AudioPlayer.h:290
VolumeStream & getVolumeStream()
Provides the reference to the volume stream.
Definition AudioPlayer.h:450
bool setPath(const char *path)
Moves to the selected file w/o updating the actual file position.
Definition AudioPlayer.h:281
bool begin(int index=0, bool isActive=true)
(Re)Starts the playing of the music (from the beginning or the indicated index)
Definition AudioPlayer.h:149
void setAudioInfo(AudioInfo info) override
Updates the audio info in the related objects.
Definition AudioPlayer.h:229
void setActive(bool isActive)
The same like start() / stop()
Definition AudioPlayer.h:321
void setBufferSize(int size)
Defines the number of bytes used by the copier.
Definition AudioPlayer.h:146
size_t copy()
Copies DEFAULT_BUFFER_SIZE (=1024 bytes) from the source to the decoder: Call this method in the loop...
Definition AudioPlayer.h:362
AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition AudioPlayer.h:247
bool setIndex(int idx)
moves to the selected file position
Definition AudioPlayer.h:272
AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder)
Construct a new Audio Player object. The processing chain is AudioSource -> Stream-copy -> EncodedAud...
Definition AudioPlayer.h:53
void setVolumeControl(VolumeControl &vc)
Change the VolumeControl implementation.
Definition AudioPlayer.h:421
AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder, AudioInfoSupport *notify=nullptr)
Construct a new Audio Player object. The processing chain is AudioSource -> Stream-copy -> EncodedAud...
Definition AudioPlayer.h:72
Abstract Audio Data Source for the AudioPlayer which is used by the Audio Players.
Definition AudioSource.h:15
virtual Stream * selectStream(int index)
Definition AudioSource.h:28
virtual Stream * previousStream(int offset)
Returns previous audio stream.
Definition AudioSource.h:24
virtual bool isAutoNext()
Returns default setting go to the next.
Definition AudioSource.h:62
virtual void begin()=0
Reset actual stream and move to root.
virtual Stream * nextStream(int offset)=0
Returns next audio stream.
virtual int timeoutAutoNext()
Provides the timeout which is triggering to move to the next stream.
Definition AudioSource.h:49
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:119
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:127
virtual void writeSilence(size_t len)
Writes len bytes of silence (=0).
Definition BaseStream.h:156
Dummy Decoder which just copies the provided data to the output. You can define if it is PCM data.
Definition CodecCopy.h:18
A more natural Print class to process encoded data (aac, wav, mp3...). Just define the output and the...
Definition AudioEncoded.h:21
void end() override
Ends the processing.
Definition AudioEncoded.h:160
void setOutput(Print &outputStream) override
Defines/Changes the output target.
Definition AudioEncoded.h:97
bool begin() override
Starts the processing - sets the status to active.
Definition AudioEncoded.h:137
Stream which can be used to manage fade in and fade out. Before you read or write data you need to ca...
Definition Fade.h:240
void setOutput(Print &out) override
Defines/Changes the output target.
Definition Fade.h:251
void setAudioInfo(AudioInfo info) override
Defines the input AudioInfo.
Definition Fade.h:267
Simple ID3 Meta Data Parser which supports ID3 V1 and V2 and implements the Stream interface....
Definition MetaDataID3.h:559
virtual size_t write(const uint8_t *data, size_t len)
Provide tha audio data to the API to parse for Meta Data.
Definition MetaDataID3.h:590
void resize(int size)
Defines the ID3V3 result buffer size (default is 256);.
Definition MetaDataID3.h:600
Definition NoArduino.h:62
size_t copyBytes(size_t bytes)
copies the inicated number of bytes from the source to the destination and returns the processed numb...
Definition StreamCopy.h:107
void resize(int len)
resizes the copy buffer
Definition StreamCopy.h:305
void setCallbackOnWrite(void(*onWrite)(void *obj, void *buffer, size_t len), void *obj)
Defines a callback that is notified with the wirtten data.
Definition StreamCopy.h:263
void begin()
(Re)starts the processing
Definition StreamCopy.h:50
int bufferSize()
Provides the buffer size.
Definition StreamCopy.h:280
size_t copy()
copies the data from the source to the destination and returns the processed number of bytes
Definition StreamCopy.h:95
Definition NoArduino.h:142
Abstract class for handling of the linear input volume to determine the multiplication factor which s...
Definition VolumeControl.h:19
Adjust the volume of the related input or output: To work properly the class needs to know the bits p...
Definition VolumeStream.h:34
void setOutput(Print &out) override
Defines/Changes the output target.
Definition VolumeStream.h:70
bool setVolume(float vol) override
Defines the volume for all channels: needs to be in the range of 0 to 1.0 (if allow boost has not bee...
Definition VolumeStream.h:179
void setAudioInfo(AudioInfo cfg) override
Detines the Audio info - The bits_per_sample are critical to work properly!
Definition VolumeStream.h:165
void setVolumeControl(VolumeControl &vc)
Defines the volume control logic.
Definition VolumeStream.h:122
Supports the setting and getting of the volume.
Definition AudioTypes.h:189
ID3TypeSelection
Enum to filter by type of metadata.
Definition AbstractMetaData.h:8
MetaDataType
Type of meta info.
Definition AbstractMetaData.h:11
StreamCopyT< uint8_t > StreamCopy
We provide the typeless StreamCopy.
Definition StreamCopy.h:430
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
uint32_t millis()
Returns the milliseconds since the start.
Definition Time.h:12
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59