arduino-audio-tools
AudioPlayer.h
1 #pragma once
2 
3 #include "AudioBasic/Debouncer.h"
4 #include "AudioBasic/Str.h"
5 #include "AudioConfig.h"
6 #include "AudioHttp/AudioHttp.h"
7 #include "AudioTools/AudioLogger.h"
8 #include "AudioTools/AudioSource.h"
9 #include "AudioTools/AudioStreams.h"
10 #include "AudioTools/AudioTypes.h"
11 #include "AudioTools/BaseConverter.h"
12 #include "AudioTools/Buffers.h"
13 #include "AudioTools/Fade.h"
14 #include "AudioTools/StreamCopy.h"
15 
16 // support for legacy USE_SDFAT
17 #ifdef USE_SDFAT
18 #include "AudioLibs/AudioSourceSDFAT.h"
19 #endif
20 
27 namespace audio_tools {
28 
41 class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
42 
43 public:
45  AudioPlayer() { TRACED(); }
46 
56  AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder) {
57  TRACED();
58  this->p_source = &source;
59  this->p_decoder = &decoder;
60  setOutput(output);
61  // notification for audio configuration
62  decoder.addNotifyAudioChange(*this);
63  }
64 
75  AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder,
76  AudioInfoSupport *notify = nullptr) {
77  TRACED();
78  this->p_source = &source;
79  this->p_decoder = &decoder;
80  setOutput(output);
81  addNotifyAudioChange(notify);
82  }
83 
93  AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder) {
94  TRACED();
95  this->p_source = &source;
96  this->p_decoder = &decoder;
97  setOutput(output);
98  // notification for audio configuration
99  decoder.addNotifyAudioChange(*this);
100  }
101 
102  AudioPlayer(AudioPlayer const &) = delete;
103 
104  AudioPlayer &operator=(AudioPlayer const &) = delete;
105 
106  void setOutput(AudioOutput &output) {
107  if (p_decoder->isResultPCM()) {
108  this->fade.setOutput(output);
109  this->volume_out.setOutput(fade);
110  out_decoding.setOutput(&volume_out);
111  out_decoding.setDecoder(p_decoder);
112  } else {
113  out_decoding.setOutput(&output);
114  out_decoding.setDecoder(p_decoder);
115  }
116  this->p_final_print = &output;
117  this->p_final_stream = nullptr;
118  }
119 
120  void setOutput(Print &output) {
121  if (p_decoder->isResultPCM()) {
122  this->fade.setOutput(output);
123  this->volume_out.setOutput(fade);
124  out_decoding.setOutput(&volume_out);
125  out_decoding.setDecoder(p_decoder);
126  } else {
127  out_decoding.setOutput(&output);
128  out_decoding.setDecoder(p_decoder);
129  }
130  this->p_final_print = nullptr;
131  this->p_final_stream = nullptr;
132  }
133 
134  void setOutput(AudioStream &output) {
135  if (p_decoder->isResultPCM()) {
136  this->fade.setOutput(output);
137  this->volume_out.setOutput(fade);
138  out_decoding.setOutput(&volume_out);
139  out_decoding.setDecoder(p_decoder);
140  } else {
141  out_decoding.setOutput(&output);
142  out_decoding.setDecoder(p_decoder);
143  }
144  this->p_final_print = nullptr;
145  this->p_final_stream = &output;
146  }
147 
149  virtual void setBufferSize(int size) { copier.resize(size); }
150 
152  virtual bool begin(int index = 0, bool isActive = true) {
153  TRACED();
154  bool result = false;
155  // initilaize volume
156  if (current_volume == -1.0f) {
157  setVolume(1.0f);
158  } else {
159  setVolume(current_volume);
160  }
161 
162  // take definition from source
163  autonext = p_source->isAutoNext();
164 
165  // initial audio info for fade from output when not defined yet
166  setupFade();
167 
168  // start dependent objects
169  out_decoding.begin();
170  p_source->begin();
171  meta_out.begin();
172 
173  if (index >= 0) {
174  p_input_stream = p_source->selectStream(index);
175  if (p_input_stream != nullptr) {
176  if (meta_active) {
177  copier.setCallbackOnWrite(decodeMetaData, this);
178  }
179  copier.begin(out_decoding, *p_input_stream);
180  timeout = millis() + p_source->timeoutAutoNext();
181  active = isActive;
182  result = true;
183  } else {
184  LOGW("-> begin: no data found");
185  active = false;
186  result = false;
187  }
188  } else {
189  LOGW("-> begin: no stream selected");
190  active = isActive;
191  result = false;
192  }
193  return result;
194  }
195 
196  virtual void end() {
197  TRACED();
198  active = false;
199  out_decoding.end();
200  meta_out.end();
201  // remove any data in the decoder
202  if (p_decoder != nullptr) {
203  LOGI("reset codec");
204  p_decoder->end();
205  p_decoder->begin();
206  }
207  }
208 
210  void setAudioSource(AudioSource &source) { this->p_source = &source; }
211 
213  void setDecoder(AudioDecoder &decoder) {
214  this->p_decoder = &decoder;
215  out_decoding.setDecoder(p_decoder);
216  }
217 
220  this->p_final_notify = notify;
221  // notification for audio configuration
222  if (p_decoder != nullptr) {
223  p_decoder->addNotifyAudioChange(*this);
224  }
225  }
226 
228  virtual void setAudioInfo(AudioInfo info) override {
229  TRACED();
230  LOGI("sample_rate: %d", (int) info.sample_rate);
231  LOGI("bits_per_sample: %d", (int) info.bits_per_sample);
232  LOGI("channels: %d", (int) info.channels);
233  this->info = info;
234  // notifiy volume
235  volume_out.setAudioInfo(info);
236  fade.setAudioInfo(info);
237  // notifiy final ouput: e.g. i2s
238  if (p_final_print != nullptr)
239  p_final_print->setAudioInfo(info);
240  if (p_final_stream != nullptr)
241  p_final_stream->setAudioInfo(info);
242  if (p_final_notify != nullptr)
243  p_final_notify->setAudioInfo(info);
244  };
245 
246  virtual AudioInfo audioInfo() override { return info; }
247 
249  virtual void play() {
250  TRACED();
251  setActive(true);
252  }
253 
255  virtual void stop() {
256  TRACED();
257  setActive(false);
258  }
259 
262  virtual bool next(int offset = 1) {
263  TRACED();
264  writeEnd();
265  stream_increment = offset >= 0 ? 1 : -1;
266  active = setStream(p_source->nextStream(offset));
267  return active;
268  }
269 
271  virtual bool setIndex(int idx) {
272  TRACED();
273  writeEnd();
274  stream_increment = 1;
275  active = setStream(p_source->selectStream(idx));
276  return active;
277  }
278 
280  virtual bool setPath(const char *path) {
281  TRACED();
282  writeEnd();
283  stream_increment = 1;
284  active = setStream(p_source->selectStream(path));
285  return active;
286  }
287 
289  virtual bool previous(int offset = 1) {
290  TRACED();
291  writeEnd();
292  stream_increment = -1;
293  active = setStream(p_source->previousStream(abs(offset)));
294  return active;
295  }
296 
298  virtual bool setStream(Stream *input) {
299  end();
300  out_decoding.begin();
301  p_input_stream = input;
302  if (p_input_stream != nullptr) {
303  LOGD("open selected stream");
304  meta_out.begin();
305  copier.begin(out_decoding, *p_input_stream);
306  }
307  return p_input_stream != nullptr;
308  }
309 
311  virtual Stream *getStream() { return p_input_stream; }
312 
314  virtual bool isActive() { return active; }
315 
317  operator bool() { return isActive(); }
318 
320  virtual void setActive(bool isActive) {
321  if (is_auto_fade) {
322  if (isActive) {
323  fade.setFadeInActive(true);
324  } else {
325  fade.setFadeOutActive(true);
326  copier.copy();
327  writeSilence(2048);
328  }
329  }
330  active = isActive;
331  }
332 
334  bool setVolume(float volume) override {
335  bool result = true;
336  if (volume >= 0.0f && volume <= 1.0f) {
337  if (abs(volume - current_volume) > 0.01f) {
338  LOGI("setVolume(%f)", volume);
339  volume_out.setVolume(volume);
340  current_volume = volume;
341  }
342  } else {
343  LOGE("setVolume value '%f' out of range (0.0 -1.0)", volume);
344  result = false;
345  }
346  return result;
347  }
348 
350  float volume() override { return current_volume; }
351 
355  virtual void setAutoNext(bool next) { autonext = next; }
356 
358  virtual void setDelayIfOutputFull(int delayMs) { delay_if_full = delayMs; }
359 
361  virtual size_t copy() {
362  size_t result = 0;
363  if (active) {
364  TRACED();
365  if (delay_if_full != 0 && ((p_final_print != nullptr &&
366  p_final_print->availableForWrite() == 0) ||
367  (p_final_stream != nullptr &&
368  p_final_stream->availableForWrite() == 0))) {
369  // not ready to do anything - so we wait a bit
370  delay(delay_if_full);
371  return 0;
372  }
373  // handle sound
374  result = copier.copy();
375  if (result > 0 || timeout == 0) {
376  // reset timeout if we had any data
377  timeout = millis() + p_source->timeoutAutoNext();
378  }
379  // move to next stream after timeout
380  moveToNextFileOnTimeout();
381 
382  // return silence when there was no data
383  if (result == 0 && silence_on_inactive){
384  writeSilence(1024);
385  }
386  } else {
387  // e.g. A2DP should still receive data to keep the connection open
388  if (silence_on_inactive) {
389  writeSilence(1024);
390  }
391  }
392  return result;
393  }
394 
396  virtual void setMetadataCallback(void (*callback)(MetaDataType type,
397  const char *str, int len),
398  ID3TypeSelection sel = SELECT_ID3) {
399  TRACEI();
400  // setup metadata.
401  if (p_source->setMetadataCallback(callback)) {
402  // metadata is handled by source
403  LOGI("Using ICY Metadata");
404  meta_active = false;
405  } else {
406  // metadata is handled here
407  meta_out.setCallback(callback);
408  meta_out.setFilter(sel);
409  meta_active = true;
410  }
411  }
412 
414  virtual void setVolumeControl(VolumeControl &vc) {
415  volume_out.setVolumeControl(vc);
416  }
417 
420  StreamCopy &getStreamCopy() { return copier; }
421 
424  void setSilenceOnInactive(bool active) { silence_on_inactive = active; }
425 
427  bool isSilenceOnInactive() { return silence_on_inactive; }
428 
430  void writeSilence(size_t bytes) {
431  TRACEI();
432  if (p_final_print != nullptr) {
433  p_final_print->writeSilence(bytes);
434  } else if (p_final_stream != nullptr) {
435  p_final_stream->writeSilence(bytes);
436  }
437  }
438 
440  Print *getVolumeOutput() { return &volume_out; }
441 
444  void setAutoFade(bool active) { is_auto_fade = active; }
445 
446  bool isAutoFade() { return is_auto_fade; }
447 
448 protected:
449  bool active = false;
450  bool autonext = true;
451  bool silence_on_inactive = false;
452  AudioSource *p_source = nullptr;
453  VolumeStream volume_out; // Volume control
454  FadeStream fade; // Phase in / Phase Out to avoid popping noise
455  MetaDataID3 meta_out; // Metadata parser
456  EncodedAudioOutput out_decoding; // Decoding stream
457  CopyDecoder no_decoder{true};
458  AudioDecoder *p_decoder = &no_decoder;
459  Stream *p_input_stream = nullptr;
460  AudioOutput *p_final_print = nullptr;
461  AudioStream *p_final_stream = nullptr;
462  AudioInfoSupport *p_final_notify = nullptr;
463  StreamCopy copier; // copies sound into i2s
464  AudioInfo info;
465  bool meta_active = false;
466  uint32_t timeout = 0;
467  int stream_increment = 1; // +1 moves forward; -1 moves backward
468  float current_volume = -1.0f; // illegal value which will trigger an update
469  int delay_if_full = 100;
470  bool is_auto_fade = true;
471 
472  void setupFade() {
473  if (p_final_print != nullptr) {
474  fade.setAudioInfo(p_final_print->audioInfo());
475  } else if (p_final_stream != nullptr) {
476  fade.setAudioInfo(p_final_stream->audioInfo());
477  }
478  }
479 
480  virtual void moveToNextFileOnTimeout() {
481  if (!autonext)
482  return;
483  if (p_final_stream == nullptr)
484  return;
485  if (p_final_stream->availableForWrite() == 0)
486  return;
487  if (p_input_stream == nullptr || millis() > timeout) {
488  if (is_auto_fade)
489  fade.setFadeInActive(true);
490  if (autonext) {
491  LOGI("-> timeout - moving by %d", stream_increment);
492  // open next stream
493  if (!next(stream_increment)) {
494  LOGD("stream is null");
495  }
496  } else {
497  active = false;
498  }
499  timeout = millis() + p_source->timeoutAutoNext();
500  }
501  }
502 
503  void writeEnd() {
504  // end silently
505  TRACEI();
506  if (is_auto_fade) {
507  fade.setFadeOutActive(true);
508  copier.copy();
509  // start by fading in
510  fade.setFadeInActive(true);
511  }
512  // restart the decoder to make sure it does not contain any audio when we
513  // continue
514  p_decoder->begin();
515  }
516 
518  static void decodeMetaData(void *obj, void *data, size_t len) {
519  LOGD("%s, %zu", LOG_METHOD, len);
520  AudioPlayer *p = (AudioPlayer *)obj;
521  if (p->meta_active) {
522  p->meta_out.write((const uint8_t *)data, len);
523  }
524  }
525 };
526 
527 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioEncoded.h:18
virtual bool isResultPCM()
If true, the decoding result is PCM data.
Definition: AudioEncoded.h:52
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition: AudioTypes.h:158
Supports changes to the sampling rate, bits and channels.
Definition: AudioTypes.h:136
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:41
static void decodeMetaData(void *obj, void *data, size_t len)
Callback implementation which writes to metadata.
Definition: AudioPlayer.h:518
void setAudioSource(AudioSource &source)
(Re)defines the audio source
Definition: AudioPlayer.h:210
virtual void setBufferSize(int size)
Defines the number of bytes used by the copier.
Definition: AudioPlayer.h:149
void writeSilence(size_t bytes)
Sends the requested bytes as 0 values to the output.
Definition: AudioPlayer.h:430
AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder)
Construct a new Audio Player object. The processing chain is AudioSource -> Stream-copy -> EncodedAud...
Definition: AudioPlayer.h:93
virtual Stream * getStream()
Provides the actual stream (=e.g.file)
Definition: AudioPlayer.h:311
void setSilenceOnInactive(bool active)
Definition: AudioPlayer.h:424
bool isSilenceOnInactive()
Checks if silence_on_inactive has been activated (default false)
Definition: AudioPlayer.h:427
virtual bool setPath(const char *path)
moves to selected file
Definition: AudioPlayer.h:280
virtual void setDelayIfOutputFull(int delayMs)
Defines the wait time in ms if the target output is full.
Definition: AudioPlayer.h:358
virtual bool next(int offset=1)
Definition: AudioPlayer.h:262
virtual void play()
starts / resumes the playing after calling stop()
Definition: AudioPlayer.h:249
void setDecoder(AudioDecoder &decoder)
(Re)defines the decoder
Definition: AudioPlayer.h:213
virtual bool isActive()
determines if the player is active
Definition: AudioPlayer.h:314
bool setVolume(float volume) override
sets the volume - values need to be between 0.0 and 1.0
Definition: AudioPlayer.h:334
float volume() override
Determines the actual volume.
Definition: AudioPlayer.h:350
virtual bool setIndex(int idx)
moves to selected file
Definition: AudioPlayer.h:271
virtual void setVolumeControl(VolumeControl &vc)
Change the VolumeControl implementation.
Definition: AudioPlayer.h:414
void addNotifyAudioChange(AudioInfoSupport *notify)
(Re)defines the notify
Definition: AudioPlayer.h:219
AudioPlayer()
Default constructor.
Definition: AudioPlayer.h:45
void setAutoFade(bool active)
Definition: AudioPlayer.h:444
virtual bool begin(int index=0, bool isActive=true)
(Re)Starts the playing of the music (from the beginning)
Definition: AudioPlayer.h:152
virtual void setAudioInfo(AudioInfo info) override
Updates the audio info in the related objects.
Definition: AudioPlayer.h:228
virtual void setMetadataCallback(void(*callback)(MetaDataType type, const char *str, int len), ID3TypeSelection sel=SELECT_ID3)
Defines the medatadata callback.
Definition: AudioPlayer.h:396
virtual bool previous(int offset=1)
moves to previous file
Definition: AudioPlayer.h:289
Print * getVolumeOutput()
Provides the Print object to which we send the decoding result.
Definition: AudioPlayer.h:440
virtual void stop()
halts the playing
Definition: AudioPlayer.h:255
virtual bool setStream(Stream *input)
start selected input stream
Definition: AudioPlayer.h:298
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: AudioPlayer.h:246
StreamCopy & getStreamCopy()
Definition: AudioPlayer.h:420
AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder)
Construct a new Audio Player object. The processing chain is AudioSource -> Stream-copy -> EncodedAud...
Definition: AudioPlayer.h:56
virtual size_t copy()
Call this method in the loop.
Definition: AudioPlayer.h:361
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:75
virtual void setAutoNext(bool next)
Definition: AudioPlayer.h:355
virtual void setActive(bool isActive)
The same like start() / stop()
Definition: AudioPlayer.h:320
Abstract Audio Data Source for the AudioPlayer which is used by the Audio Players.
Definition: AudioSource.h:12
virtual Stream * selectStream(int index)
Returns audio stream at the indicated index (the index is zero based, so the first value is 0!...
Definition: AudioSource.h:26
virtual bool isAutoNext()
Returns default setting go to the next.
Definition: AudioSource.h:58
virtual void begin()=0
Reset actual stream and move to root.
virtual int timeoutAutoNext()
Provides the timeout which is triggering to move to the next stream.
Definition: AudioSource.h:45
virtual Stream * nextStream(int offset)=0
Returns next audio stream.
virtual Stream * previousStream(int offset)
Returns previous audio stream.
Definition: AudioSource.h:21
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: AudioStreams.h:24
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: AudioStreams.h:32
virtual void writeSilence(size_t len)
Writes len bytes of silence (=0).
Definition: AudioStreams.h:61
void end() override
Ends the processing.
Definition: AudioEncoded.h:410
bool begin() override
Starts the processing - sets the status to active.
Definition: AudioEncoded.h:385
void setOutput(Print &outputStream)
Defines/Changes the output target.
Definition: AudioEncoded.h:345
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
virtual size_t write(const uint8_t *data, size_t length)
Provide tha audio data to the API to parse for Meta Data.
Definition: MetaDataID3.h:581
Definition: NoArduino.h:58
We provide the typeless StreamCopy as a subclass of StreamCopyT.
Definition: StreamCopy.h:433
size_t copy(BaseConverter &converter)
copies a buffer length of data and applies the converter
Definition: StreamCopy.h:458
void resize(int len)
resizes the copy buffer
Definition: StreamCopy.h:291
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:249
void begin()
(Re)starts the processing
Definition: StreamCopy.h:54
Definition: NoArduino.h:125
Abstract class for handling of the linear input volume to determine the multiplication factor which s...
Definition: VolumeControl.h:17
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 setOutput(Print &out)
Defines/Changes the output target.
Definition: VolumeStream.h:70
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:210
ID3TypeSelection
Enum to filter by type of metadata.
Definition: AbstractMetaData.h:8
MetaDataType
Type of meta info.
Definition: AbstractMetaData.h:11
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
void delay(uint32_t ms)
Waits for the indicated milliseconds.
Definition: Millis.h:11
uint32_t millis()
Returns the milliseconds since the start.
Definition: Millis.h:18
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
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