arduino-audio-tools
AudioPlayer.h
1 #pragma once
2 
3 #include "AudioConfig.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/AudioSource.h"
8 #include "AudioTools/CoreAudio/AudioStreams.h"
9 #include "AudioTools/CoreAudio/AudioTypes.h"
10 #include "AudioTools/CoreAudio/BaseConverter.h"
11 #include "AudioTools/CoreAudio/Buffers.h"
12 #include "AudioTools/CoreAudio/Fade.h"
13 #include "AudioTools/CoreAudio/StreamCopy.h"
14 
21 namespace audio_tools {
22 
35 class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
36 
37 public:
39  AudioPlayer() { TRACED(); }
40 
50  AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder) {
51  TRACED();
52  this->p_source = &source;
53  this->p_decoder = &decoder;
54  setOutput(output);
55  // notification for audio configuration
56  decoder.addNotifyAudioChange(*this);
57  }
58 
69  AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder,
70  AudioInfoSupport *notify = nullptr) {
71  TRACED();
72  this->p_source = &source;
73  this->p_decoder = &decoder;
74  setOutput(output);
75  addNotifyAudioChange(notify);
76  }
77 
87  AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder) {
88  TRACED();
89  this->p_source = &source;
90  this->p_decoder = &decoder;
91  setOutput(output);
92  // notification for audio configuration
93  decoder.addNotifyAudioChange(*this);
94  }
95 
96  AudioPlayer(AudioPlayer const &) = delete;
97 
98  AudioPlayer &operator=(AudioPlayer const &) = delete;
99 
100  void setOutput(AudioOutput &output) {
101  if (p_decoder->isResultPCM()) {
102  this->fade.setOutput(output);
103  this->volume_out.setOutput(fade);
104  out_decoding.setOutput(&volume_out);
105  out_decoding.setDecoder(p_decoder);
106  } else {
107  out_decoding.setOutput(&output);
108  out_decoding.setDecoder(p_decoder);
109  }
110  this->p_final_print = &output;
111  this->p_final_stream = nullptr;
112  }
113 
114  void setOutput(Print &output) {
115  if (p_decoder->isResultPCM()) {
116  this->fade.setOutput(output);
117  this->volume_out.setOutput(fade);
118  out_decoding.setOutput(&volume_out);
119  out_decoding.setDecoder(p_decoder);
120  } else {
121  out_decoding.setOutput(&output);
122  out_decoding.setDecoder(p_decoder);
123  }
124  this->p_final_print = nullptr;
125  this->p_final_stream = nullptr;
126  }
127 
128  void setOutput(AudioStream &output) {
129  if (p_decoder->isResultPCM()) {
130  this->fade.setOutput(output);
131  this->volume_out.setOutput(fade);
132  out_decoding.setOutput(&volume_out);
133  out_decoding.setDecoder(p_decoder);
134  } else {
135  out_decoding.setOutput(&output);
136  out_decoding.setDecoder(p_decoder);
137  }
138  this->p_final_print = nullptr;
139  this->p_final_stream = &output;
140  }
141 
143  virtual void setBufferSize(int size) { copier.resize(size); }
144 
146  virtual bool begin(int index = 0, bool isActive = true) {
147  TRACED();
148  bool result = false;
149  // initilaize volume
150  if (current_volume == -1.0f) {
151  setVolume(1.0f);
152  } else {
153  setVolume(current_volume);
154  }
155 
156  // take definition from source
157  autonext = p_source->isAutoNext();
158 
159  // initial audio info for fade from output when not defined yet
160  setupFade();
161 
162  // start dependent objects
163  out_decoding.begin();
164  p_source->begin();
165  meta_out.begin();
166  volume_out.begin();
167 
168  if (index >= 0) {
169  p_input_stream = p_source->selectStream(index);
170  if (p_input_stream != nullptr) {
171  if (meta_active) {
172  copier.setCallbackOnWrite(decodeMetaData, this);
173  }
174  copier.begin(out_decoding, *p_input_stream);
175  timeout = millis() + p_source->timeoutAutoNext();
176  active = isActive;
177  result = true;
178  } else {
179  LOGW("-> begin: no data found");
180  active = false;
181  result = false;
182  }
183  } else {
184  LOGW("-> begin: no stream selected");
185  active = isActive;
186  result = false;
187  }
188  return result;
189  }
190 
191  virtual void end() {
192  TRACED();
193  active = false;
194  out_decoding.end();
195  meta_out.end();
196  // remove any data in the decoder
197  if (p_decoder != nullptr) {
198  LOGI("reset codec");
199  p_decoder->end();
200  p_decoder->begin();
201  }
202  }
203 
205  void setAudioSource(AudioSource &source) { this->p_source = &source; }
206 
208  void setDecoder(AudioDecoder &decoder) {
209  this->p_decoder = &decoder;
210  out_decoding.setDecoder(p_decoder);
211  }
212 
215  this->p_final_notify = notify;
216  // notification for audio configuration
217  if (p_decoder != nullptr) {
218  p_decoder->addNotifyAudioChange(*this);
219  }
220  }
221 
223  virtual void setAudioInfo(AudioInfo info) override {
224  TRACED();
225  LOGI("sample_rate: %d", (int) info.sample_rate);
226  LOGI("bits_per_sample: %d", (int) info.bits_per_sample);
227  LOGI("channels: %d", (int) info.channels);
228  this->info = info;
229  // notifiy volume
230  volume_out.setAudioInfo(info);
231  fade.setAudioInfo(info);
232  // notifiy final ouput: e.g. i2s
233  if (p_final_print != nullptr)
234  p_final_print->setAudioInfo(info);
235  if (p_final_stream != nullptr)
236  p_final_stream->setAudioInfo(info);
237  if (p_final_notify != nullptr)
238  p_final_notify->setAudioInfo(info);
239  };
240 
241  virtual AudioInfo audioInfo() override { return info; }
242 
244  virtual void play() {
245  TRACED();
246  setActive(true);
247  }
248 
250  virtual void stop() {
251  TRACED();
252  setActive(false);
253  }
254 
257  virtual bool next(int offset = 1) {
258  TRACED();
259  writeEnd();
260  stream_increment = offset >= 0 ? 1 : -1;
261  active = setStream(p_source->nextStream(offset));
262  return active;
263  }
264 
266  virtual bool setIndex(int idx) {
267  TRACED();
268  writeEnd();
269  stream_increment = 1;
270  active = setStream(p_source->selectStream(idx));
271  return active;
272  }
273 
275  virtual bool setPath(const char *path) {
276  TRACED();
277  writeEnd();
278  stream_increment = 1;
279  active = setStream(p_source->selectStream(path));
280  return active;
281  }
282 
284  virtual bool previous(int offset = 1) {
285  TRACED();
286  writeEnd();
287  stream_increment = -1;
288  active = setStream(p_source->previousStream(abs(offset)));
289  return active;
290  }
291 
293  virtual bool setStream(Stream *input) {
294  end();
295  out_decoding.begin();
296  p_input_stream = input;
297  if (p_input_stream != nullptr) {
298  LOGD("open selected stream");
299  meta_out.begin();
300  copier.begin(out_decoding, *p_input_stream);
301  }
302  return p_input_stream != nullptr;
303  }
304 
306  virtual Stream *getStream() { return p_input_stream; }
307 
309  virtual bool isActive() { return active; }
310 
312  operator bool() { return isActive(); }
313 
315  virtual void setActive(bool isActive) {
316  if (is_auto_fade) {
317  if (isActive) {
318  fade.setFadeInActive(true);
319  } else {
320  fade.setFadeOutActive(true);
321  copier.copy();
322  writeSilence(2048);
323  }
324  }
325  active = isActive;
326  }
327 
329  bool setVolume(float volume) override {
330  bool result = true;
331  if (volume >= 0.0f && volume <= 1.0f) {
332  if (abs(volume - current_volume) > 0.01f) {
333  LOGI("setVolume(%f)", volume);
334  volume_out.setVolume(volume);
335  current_volume = volume;
336  }
337  } else {
338  LOGE("setVolume value '%f' out of range (0.0 -1.0)", volume);
339  result = false;
340  }
341  return result;
342  }
343 
345  float volume() override { return current_volume; }
346 
350  virtual void setAutoNext(bool next) { autonext = next; }
351 
353  virtual void setDelayIfOutputFull(int delayMs) { delay_if_full = delayMs; }
354 
355  virtual size_t copy() {
356  return copy(copier.bufferSize());
357  }
358 
360  virtual size_t copy(size_t bytes) {
361  size_t result = 0;
362  if (active) {
363  TRACED();
364  if (delay_if_full != 0 && ((p_final_print != nullptr &&
365  p_final_print->availableForWrite() == 0) ||
366  (p_final_stream != nullptr &&
367  p_final_stream->availableForWrite() == 0))) {
368  // not ready to do anything - so we wait a bit
369  delay(delay_if_full);
370  return 0;
371  }
372  // handle sound
373  result = copier.copyBytes(bytes);
374  if (result > 0 || timeout == 0) {
375  // reset timeout if we had any data
376  timeout = millis() + p_source->timeoutAutoNext();
377  }
378  // move to next stream after timeout
379  moveToNextFileOnTimeout();
380 
381  // return silence when there was no data
382  if (result < bytes && silence_on_inactive){
383  writeSilence(bytes - result);
384  }
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 
439  // /// Provides the Print object to which we send the decoding result
440  // Print *getVolumeOutput() { return &volume_out; }
441 
443  VolumeStream &getVolumeStream() { return volume_out; }
444 
447  void setAutoFade(bool active) { is_auto_fade = active; }
448 
449  bool isAutoFade() { return is_auto_fade; }
450 
451 protected:
452  bool active = false;
453  bool autonext = true;
454  bool silence_on_inactive = false;
455  AudioSource *p_source = nullptr;
456  VolumeStream volume_out; // Volume control
457  FadeStream fade; // Phase in / Phase Out to avoid popping noise
458  MetaDataID3 meta_out; // Metadata parser
459  EncodedAudioOutput out_decoding; // Decoding stream
460  CopyDecoder no_decoder{true};
461  AudioDecoder *p_decoder = &no_decoder;
462  Stream *p_input_stream = nullptr;
463  AudioOutput *p_final_print = nullptr;
464  AudioStream *p_final_stream = nullptr;
465  AudioInfoSupport *p_final_notify = nullptr;
466  StreamCopy copier; // copies sound into i2s
467  AudioInfo info;
468  bool meta_active = false;
469  uint32_t timeout = 0;
470  int stream_increment = 1; // +1 moves forward; -1 moves backward
471  float current_volume = -1.0f; // illegal value which will trigger an update
472  int delay_if_full = 100;
473  bool is_auto_fade = true;
474 
475  void setupFade() {
476  if (p_final_print != nullptr) {
477  fade.setAudioInfo(p_final_print->audioInfo());
478  } else if (p_final_stream != nullptr) {
479  fade.setAudioInfo(p_final_stream->audioInfo());
480  }
481  }
482 
483  virtual void moveToNextFileOnTimeout() {
484  if (!autonext)
485  return;
486  if (p_final_stream != nullptr && p_final_stream->availableForWrite() == 0)
487  return;
488  if (p_input_stream == nullptr || millis() > timeout) {
489  if (is_auto_fade)
490  fade.setFadeInActive(true);
491  if (autonext) {
492  LOGI("-> timeout - moving by %d", stream_increment);
493  // open next stream
494  if (!next(stream_increment)) {
495  LOGD("stream is null");
496  }
497  } else {
498  active = false;
499  }
500  timeout = millis() + p_source->timeoutAutoNext();
501  }
502  }
503 
504  void writeEnd() {
505  // end silently
506  TRACEI();
507  if (is_auto_fade) {
508  fade.setFadeOutActive(true);
509  copier.copy();
510  // start by fading in
511  fade.setFadeInActive(true);
512  }
513  // restart the decoder to make sure it does not contain any audio when we
514  // continue
515  p_decoder->begin();
516  }
517 
519  static void decodeMetaData(void *obj, void *data, size_t len) {
520  LOGD("%s, %zu", LOG_METHOD, len);
521  AudioPlayer *p = (AudioPlayer *)obj;
522  if (p->meta_active) {
523  p->meta_out.write((const uint8_t *)data, len);
524  }
525  }
526 };
527 
528 } // namespace audio_tools
Docoding of encoded audio into PCM data.
Definition: AudioCodecsBase.h:16
virtual bool isResultPCM()
Returns true to indicate that the decoding result is PCM data.
Definition: AudioCodecsBase.h:51
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition: AudioTypes.h:162
Supports changes to the sampling rate, bits and channels.
Definition: AudioTypes.h:139
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:35
static void decodeMetaData(void *obj, void *data, size_t len)
Callback implementation which writes to metadata.
Definition: AudioPlayer.h:519
void setAudioSource(AudioSource &source)
(Re)defines the audio source
Definition: AudioPlayer.h:205
virtual void setBufferSize(int size)
Defines the number of bytes used by the copier.
Definition: AudioPlayer.h:143
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:87
virtual Stream * getStream()
Provides the actual stream (=e.g.file)
Definition: AudioPlayer.h:306
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 the selected file w/o updating the actual file position.
Definition: AudioPlayer.h:275
virtual void setDelayIfOutputFull(int delayMs)
Defines the wait time in ms if the target output is full.
Definition: AudioPlayer.h:353
virtual bool next(int offset=1)
Definition: AudioPlayer.h:257
virtual void play()
starts / resumes the playing after calling stop(): same as setActive(true)
Definition: AudioPlayer.h:244
void setDecoder(AudioDecoder &decoder)
(Re)defines the decoder
Definition: AudioPlayer.h:208
virtual size_t copy(size_t bytes)
Call this method in the loop.
Definition: AudioPlayer.h:360
virtual bool isActive()
determines if the player is active
Definition: AudioPlayer.h:309
bool setVolume(float volume) override
sets the volume - values need to be between 0.0 and 1.0
Definition: AudioPlayer.h:329
float volume() override
Determines the actual volume.
Definition: AudioPlayer.h:345
virtual bool setIndex(int idx)
moves to the selected file position
Definition: AudioPlayer.h:266
virtual void setVolumeControl(VolumeControl &vc)
Change the VolumeControl implementation.
Definition: AudioPlayer.h:414
void addNotifyAudioChange(AudioInfoSupport *notify)
(Re)defines the notify
Definition: AudioPlayer.h:214
VolumeStream & getVolumeStream()
Provides the reference to the volume stream.
Definition: AudioPlayer.h:443
AudioPlayer()
Default constructor.
Definition: AudioPlayer.h:39
void setAutoFade(bool active)
Definition: AudioPlayer.h:447
virtual 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:146
virtual void setAudioInfo(AudioInfo info) override
Updates the audio info in the related objects.
Definition: AudioPlayer.h:223
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:284
virtual void stop()
halts the playing: same as setActive(false)
Definition: AudioPlayer.h:250
virtual bool setStream(Stream *input)
start selected input stream
Definition: AudioPlayer.h:293
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition: AudioPlayer.h:241
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:50
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:69
virtual void setAutoNext(bool next)
Definition: AudioPlayer.h:350
virtual void setActive(bool isActive)
The same like start() / stop()
Definition: AudioPlayer.h:315
Abstract Audio Data Source for the AudioPlayer which is used by the Audio Players.
Definition: AudioSource.h:13
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:27
virtual bool isAutoNext()
Returns default setting go to the next.
Definition: AudioSource.h:59
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:46
virtual Stream * nextStream(int offset)=0
Returns next audio stream.
virtual Stream * previousStream(int offset)
Returns previous audio stream.
Definition: AudioSource.h:22
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: BaseStream.h:117
virtual void writeSilence(size_t len)
Writes len bytes of silence (=0).
Definition: BaseStream.h:146
void end() override
Ends the processing.
Definition: AudioEncoded.h:165
bool begin() override
Starts the processing - sets the status to active.
Definition: AudioEncoded.h:136
void setOutput(Print &outputStream)
Defines/Changes the output target.
Definition: AudioEncoded.h:96
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 len)
Provide tha audio data to the API to parse for Meta Data.
Definition: MetaDataID3.h:581
Definition: NoArduino.h:58
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:99
void resize(int len)
resizes the copy buffer
Definition: StreamCopy.h:306
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:264
void begin()
(Re)starts the processing
Definition: StreamCopy.h:45
int bufferSize()
Provides the buffer size.
Definition: StreamCopy.h:281
size_t copy()
copies the data from the source to the destination and returns the processed number of bytes
Definition: StreamCopy.h:87
Definition: NoArduino.h:125
Abstract class for handling of the linear input volume to determine the multiplication factor which s...
Definition: VolumeControl.h:17
Adjust the volume of the related input or output: To work properly the class needs to know the bits p...
Definition: VolumeStream.h:34
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:207
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:450
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
uint32_t millis()
Returns the milliseconds since the start.
Definition: Time.h:12
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
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