Skip to content

Commit 4301286

Browse files
committed
setOnEOFCallback
1 parent 43f161d commit 4301286

File tree

1 file changed

+64
-44
lines changed

1 file changed

+64
-44
lines changed

src/AudioTools/CoreAudio/AudioPlayer.h

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
6262
* @param output
6363
* @param decoder
6464
*/
65-
AudioPlayer(AudioSource &source, AudioOutput &output, AudioDecoder &decoder) {
65+
AudioPlayer(AudioSource& source, AudioOutput& output, AudioDecoder& decoder) {
6666
TRACED();
6767
this->p_source = &source;
6868
this->p_decoder = &decoder;
@@ -81,8 +81,8 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
8181
* @param decoder
8282
* @param notify
8383
*/
84-
AudioPlayer(AudioSource &source, Print &output, AudioDecoder &decoder,
85-
AudioInfoSupport *notify = nullptr) {
84+
AudioPlayer(AudioSource& source, Print& output, AudioDecoder& decoder,
85+
AudioInfoSupport* notify = nullptr) {
8686
TRACED();
8787
this->p_source = &source;
8888
this->p_decoder = &decoder;
@@ -99,7 +99,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
9999
* @param output
100100
* @param decoder
101101
*/
102-
AudioPlayer(AudioSource &source, AudioStream &output, AudioDecoder &decoder) {
102+
AudioPlayer(AudioSource& source, AudioStream& output, AudioDecoder& decoder) {
103103
TRACED();
104104
this->p_source = &source;
105105
this->p_decoder = &decoder;
@@ -109,13 +109,13 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
109109
}
110110

111111
/// Non-copyable: copy constructor is deleted
112-
AudioPlayer(AudioPlayer const &) = delete;
112+
AudioPlayer(AudioPlayer const&) = delete;
113113

114114
/// Non-assignable: assignment operator is deleted
115-
AudioPlayer &operator=(AudioPlayer const &) = delete;
115+
AudioPlayer& operator=(AudioPlayer const&) = delete;
116116

117117
/// Sets the final output to an AudioOutput (adds Volume/Fade for PCM)
118-
void setOutput(AudioOutput &output) {
118+
void setOutput(AudioOutput& output) {
119119
if (p_decoder->isResultPCM()) {
120120
this->fade.setOutput(output);
121121
this->volume_out.setOutput(fade);
@@ -130,7 +130,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
130130
}
131131

132132
/// Sets the final output to a Print (adds Volume/Fade for PCM)
133-
void setOutput(Print &output) {
133+
void setOutput(Print& output) {
134134
if (p_decoder->isResultPCM()) {
135135
this->fade.setOutput(output);
136136
this->volume_out.setOutput(fade);
@@ -145,7 +145,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
145145
}
146146

147147
/// Sets the final output to an AudioStream (adds Volume/Fade for PCM)
148-
void setOutput(AudioStream &output) {
148+
void setOutput(AudioStream& output) {
149149
if (p_decoder->isResultPCM()) {
150150
this->fade.setOutput(output);
151151
this->volume_out.setOutput(fade);
@@ -182,15 +182,15 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
182182
// start dependent objects
183183
out_decoding.begin();
184184

185-
if (!p_source->begin()){
185+
if (!p_source->begin()) {
186186
LOGE("Could not start audio source");
187187
return false;
188188
}
189-
if (!meta_out.begin()){
189+
if (!meta_out.begin()) {
190190
LOGE("Could not start metadata output");
191191
return false;
192192
}
193-
if (!volume_out.begin()){
193+
if (!volume_out.begin()) {
194194
LOGE("Could not start volume control");
195195
return false;
196196
}
@@ -222,6 +222,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
222222
void end() {
223223
TRACED();
224224
active = false;
225+
eof_called = false;
225226
out_decoding.end();
226227
meta_out.end();
227228
// remove any data in the decoder
@@ -233,19 +234,19 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
233234
}
234235

235236
/// Returns the active AudioSource
236-
AudioSource &audioSource() { return *p_source; }
237+
AudioSource& audioSource() { return *p_source; }
237238

238239
/// Sets or replaces the AudioSource
239-
void setAudioSource(AudioSource &source) { this->p_source = &source; }
240+
void setAudioSource(AudioSource& source) { this->p_source = &source; }
240241

241242
/// Sets or replaces the AudioDecoder
242-
void setDecoder(AudioDecoder &decoder) {
243+
void setDecoder(AudioDecoder& decoder) {
243244
this->p_decoder = &decoder;
244245
out_decoding.setDecoder(p_decoder);
245246
}
246247

247248
/// Adds/updates a listener notified on audio info changes
248-
void addNotifyAudioChange(AudioInfoSupport *notify) {
249+
void addNotifyAudioChange(AudioInfoSupport* notify) {
249250
this->p_final_notify = notify;
250251
// notification for audio configuration
251252
if (p_decoder != nullptr) {
@@ -282,7 +283,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
282283
/// plays a complete audio file or url from start to finish (blocking call)
283284
/// @param path path to the audio file or url to play (depending on source)
284285
/// @return true if file was found and played successfully, false otherwise
285-
bool playPath(const char *path) {
286+
bool playPath(const char* path) {
286287
TRACED();
287288
if (!setPath(path)) {
288289
LOGW("Could not open file: %s", path);
@@ -300,8 +301,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
300301
}
301302

302303
/// Obsolete: use PlayPath!
303-
bool playFile(const char *path) { return playPath(path); }
304-
304+
bool playFile(const char* path) { return playPath(path); }
305305

306306
/// Halts playback; equivalent to setActive(false)
307307
void stop() {
@@ -328,7 +328,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
328328
}
329329

330330
/// Selects stream by path without changing the source iterator
331-
bool setPath(const char *path) {
331+
bool setPath(const char* path) {
332332
TRACED();
333333
writeEnd();
334334
stream_increment = 1;
@@ -346,10 +346,11 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
346346
}
347347

348348
/// Activates the provided Stream as current input
349-
bool setStream(Stream *input) {
349+
bool setStream(Stream* input) {
350350
end();
351351
out_decoding.begin();
352352
p_input_stream = input;
353+
eof_called = false; // reset EOF state for new stream
353354
if (p_input_stream != nullptr) {
354355
LOGD("open selected stream");
355356
meta_out.begin();
@@ -362,7 +363,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
362363
}
363364

364365
/// Returns the currently active input Stream (e.g., file)
365-
Stream *getStream() { return p_input_stream; }
366+
Stream* getStream() { return p_input_stream; }
366367

367368
/// Checks whether playback is active
368369
bool isActive() { return active; }
@@ -437,12 +438,21 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
437438
LOGD("copy: %d -> 0", (int)bytes);
438439
return 0;
439440
}
441+
// EOF callback: when no bytes were copied, trigger once per stream
442+
if (result == 0 && p_input_stream != nullptr && !eof_called) {
443+
eof_called = true;
444+
if (on_eof_callback != nullptr) {
445+
on_eof_callback(*this);
446+
}
447+
}
448+
440449
// handle sound
441450
result = copier.copyBytes(bytes);
442451
if (result > 0 || timeout == 0) {
443452
// reset timeout if we had any data
444453
timeout = millis() + p_source->timeoutAutoNext();
445454
}
455+
446456
// move to next stream after timeout
447457
moveToNextFileOnTimeout();
448458

@@ -462,11 +472,11 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
462472
}
463473

464474
/// Sets a custom VolumeControl implementation
465-
void setVolumeControl(VolumeControl &vc) { volume_out.setVolumeControl(vc); }
475+
void setVolumeControl(VolumeControl& vc) { volume_out.setVolumeControl(vc); }
466476

467477
/// Provides access to StreamCopy to register additional callbacks
468478
/// callbacks
469-
StreamCopy &getStreamCopy() { return copier; }
479+
StreamCopy& getStreamCopy() { return copier; }
470480

471481
/// When enabled, writes zeros while inactive to keep sinks alive
472482
void setSilenceOnInactive(bool active) { silence_on_inactive = active; }
@@ -485,7 +495,7 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
485495
}
486496

487497
/// Returns the VolumeStream used by the player
488-
VolumeStream &getVolumeStream() { return volume_out; }
498+
VolumeStream& getVolumeStream() { return volume_out; }
489499

490500
/// Enables/disables automatic fade in/out to prevent pops
491501
void setAutoFade(bool active) { is_auto_fade = active; }
@@ -497,10 +507,10 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
497507
void setMetaDataSize(int size) { meta_out.resize(size); }
498508

499509
/// Sets a user reference passed to the stream-change callback
500-
void setReference(void *ref) { p_reference = ref; }
510+
void setReference(void* ref) { p_reference = ref; }
501511

502512
/// Defines the metadata callback
503-
void setMetadataCallback(void (*callback)(MetaDataType type, const char *str,
513+
void setMetadataCallback(void (*callback)(MetaDataType type, const char* str,
504514
int len),
505515
ID3TypeSelection sel = SELECT_ID3) {
506516
TRACEI();
@@ -518,12 +528,19 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
518528
}
519529

520530
/// Defines a callback that is called when the stream is changed
521-
void setOnStreamChangeCallback(void (*callback)(Stream *stream_ptr,
522-
void *reference)) {
531+
void setOnStreamChangeCallback(void (*callback)(Stream* stream_ptr,
532+
void* reference)) {
523533
on_stream_change_callback = callback;
524-
if (p_input_stream!=nullptr) callback(p_input_stream, p_reference);
534+
if (p_input_stream != nullptr) callback(p_input_stream, p_reference);
525535
}
526-
536+
537+
/// Defines a callback that is invoked exactly once when the current stream
538+
/// reaches EOF (no more bytes can be read from the input stream).
539+
/// Signature: void onEOF(AudioPlayer& player)
540+
void setOnEOFCallback(void (*callback)(AudioPlayer& player)) {
541+
on_eof_callback = callback;
542+
}
543+
527544
/// Mutes or unmutes the audio player
528545
void setMuted(bool muted) {
529546
if (muted) {
@@ -542,22 +559,21 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
542559
/// Returns true if the player is currently muted
543560
bool isMuted() { return current_volume == 0.0f; }
544561

545-
546562
protected:
547563
bool active = false;
548564
bool autonext = true;
549565
bool silence_on_inactive = false;
550-
AudioSource *p_source = nullptr;
566+
AudioSource* p_source = nullptr;
551567
VolumeStream volume_out; // Volume control
552568
FadeStream fade; // Phase in / Phase Out to avoid popping noise
553569
MetaDataID3 meta_out; // Metadata parser
554570
EncodedAudioOutput out_decoding; // Decoding stream
555571
CopyDecoder no_decoder{true};
556-
AudioDecoder *p_decoder = &no_decoder;
557-
Stream *p_input_stream = nullptr;
558-
AudioOutput *p_final_print = nullptr;
559-
AudioStream *p_final_stream = nullptr;
560-
AudioInfoSupport *p_final_notify = nullptr;
572+
AudioDecoder* p_decoder = &no_decoder;
573+
Stream* p_input_stream = nullptr;
574+
AudioOutput* p_final_print = nullptr;
575+
AudioStream* p_final_stream = nullptr;
576+
AudioInfoSupport* p_final_notify = nullptr;
561577
StreamCopy copier; // copies sound into i2s
562578
AudioInfo info;
563579
bool meta_active = false;
@@ -567,9 +583,12 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
567583
float muted_volume = 0.0f;
568584
int delay_if_full = 100;
569585
bool is_auto_fade = true;
570-
void *p_reference = nullptr;
571-
void (*on_stream_change_callback)(Stream *stream_ptr,
572-
void *reference) = nullptr;
586+
void* p_reference = nullptr;
587+
void (*on_stream_change_callback)(Stream* stream_ptr,
588+
void* reference) = nullptr;
589+
// EOF callback and guard (invoked once when current stream reaches end)
590+
void (*on_eof_callback)(AudioPlayer& player) = nullptr;
591+
bool eof_called = false;
573592

574593
void setupFade() {
575594
if (p_final_print != nullptr) {
@@ -609,14 +628,15 @@ class AudioPlayer : public AudioInfoSupport, public VolumeSupport {
609628
// restart the decoder to make sure it does not contain any audio when we
610629
// continue
611630
p_decoder->begin();
631+
eof_called = false; // prepare for next stream
612632
}
613633

614634
/// Callback implementation which writes to metadata
615-
static void decodeMetaData(void *obj, void *data, size_t len) {
635+
static void decodeMetaData(void* obj, void* data, size_t len) {
616636
LOGD("%s, %zu", LOG_METHOD, len);
617-
AudioPlayer *p = (AudioPlayer *)obj;
637+
AudioPlayer* p = (AudioPlayer*)obj;
618638
if (p->meta_active) {
619-
p->meta_out.write((const uint8_t *)data, len);
639+
p->meta_out.write((const uint8_t*)data, len);
620640
}
621641
}
622642
};

0 commit comments

Comments
 (0)