arduino-audio-tools
Loading...
Searching...
No Matches
AudioSource.h
1#pragma once
2#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h"
3#include "AudioTools/CoreAudio/AudioBasic/Str.h"
4#include "AudioTools/CoreAudio/AudioMetaData/AbstractMetaData.h"
5
6namespace audio_tools {
7
17 public:
19 virtual bool begin() = 0;
20
22 virtual Stream* nextStream(int offset) = 0;
23
25 virtual Stream* previousStream(int offset) { return nextStream(-offset); };
26
29 virtual Stream* selectStream(int index) {
30 LOGE("Not Supported!");
31 return nullptr;
32 }
33
35 virtual Stream* setIndex(int index) { return selectStream(index); }
36
38 virtual int index() { return -1; }
39
41 virtual Stream* selectStream(const char* path) = 0;
42
45 virtual void setTimeoutAutoNext(int millisec) {
46 timeout_auto_next_value = millisec;
47 }
48
50 virtual int timeoutAutoNext() { return timeout_auto_next_value; }
51
52 // only the ICYStream supports this
53 virtual bool setMetadataCallback(void (*fn)(MetaDataType info,
54 const char* str, int len),
55 ID3TypeSelection sel = SELECT_ICY) {
56 return false;
57 }
58
60 virtual void setTimeout(int millisec) {};
61
63 virtual bool isAutoNext() { return is_auto_next; }
64
65 // Prevent automatic move to the next stream
66 virtual void setAutoNext(bool flag) { is_auto_next = flag; }
67
69 Stream* operator[](int idx) { return setIndex(idx); }
70
72 virtual const char* toStr() { return nullptr; }
73
74 protected:
75 int timeout_auto_next_value = 500;
76 bool is_auto_next = true;
77};
78
86 public:
88
89 AudioSourceCallback(Stream* (*nextStreamCallback)(int offset),
90 void (*onStartCallback)() = nullptr) {
91 TRACED();
92 this->onStartCallback = onStartCallback;
93 this->nextStreamCallback = nextStreamCallback;
94 }
95
97 virtual bool begin() override {
98 TRACED();
99 if (onStartCallback != nullptr) onStartCallback();
100 return true;
101 };
102
105 virtual Stream* nextStream(int offset) override {
106 TRACED();
107 return nextStreamCallback == nullptr ? nullptr : nextStreamCallback(offset);
108 }
109
111 virtual Stream* selectStream(int index) override {
112 LOGI("selectStream: %d", index);
113 if (indexStreamCallback == nullptr) {
114 LOGI("setCallbackSelectStream not provided");
115 if (index > 0) {
116 begin();
117 return nextStream(index);
118 } else {
119 // nextStream(0) will return the directory but we need a file
120 return nextStream(1);
121 }
122 }
123 return indexStreamCallback(index);
124 }
126 virtual Stream* selectStream(const char* path) override {
127 this->path = path;
128 return indexStreamCallback == nullptr ? nullptr : indexStreamCallback(-1);
129 };
130
131 void setCallbackOnStart(void (*callback)()) { onStartCallback = callback; }
132
133 void setCallbackNextStream(Stream* (*callback)(int offset)) {
134 nextStreamCallback = callback;
135 }
136
137 void setCallbackSelectStream(Stream* (*callback)(int idx)) {
138 indexStreamCallback = callback;
139 }
140
141 virtual bool isAutoNext() override { return auto_next; }
142
143 virtual void setAutoNext(bool a) { auto_next = a; }
144
146 virtual const char* getPath() { return path; }
147
148 protected:
149 void (*onStartCallback)() = nullptr;
150 bool auto_next = true;
151 Stream* (*nextStreamCallback)(int offset) = nullptr;
152 Stream* (*indexStreamCallback)(int index) = nullptr;
153 const char* path = nullptr;
154};
155
159struct FileEntry {
160 int path_index; // Index into shared path registry
161 Str name;
162
163 FileEntry() : path_index(-1) {}
164
165 FileEntry(int pathIdx, const char* fileName) : path_index(pathIdx) {
166 name.set(fileName);
167 }
168};
169
174 public:
175 virtual void addName(const char* nameWithPath) = 0;
176};
177
183class NamePrinter : public Print {
184 public:
185 NamePrinter(PathNamesRegistry& dataSource, const char* prefix = nullptr)
186 : dataSource(dataSource), prefix(prefix) {}
187 void setPrefix(const char* prefix) { this->prefix = prefix; }
188 size_t write(uint8_t ch) override {
189 if (ch == '\n' || ch == '\r') {
190 // End of line - process the accumulated line
191 if (line_buffer.length() > 0) {
192 line_buffer.trim();
193 if (prefix != nullptr) {
194 // Prepend prefix if set
195 Str name{prefix};
196 name.add("/");
197 name.add(line_buffer.c_str());
198 LOGD("adding '%s'", name.c_str());
199 dataSource.addName(name.c_str());
200 } else {
201 // Add line as is
202 LOGD("adding '%s'", line_buffer.c_str());
203 dataSource.addName(line_buffer.c_str());
204 }
205 line_buffer.clear(false);
206 }
207 return 1;
208 } else {
209 // Accumulate characters
210 line_buffer.add((char)ch);
211 return 1;
212 }
213 }
214
215 size_t write(const uint8_t* buffer, size_t size) override {
216 for (size_t i = 0; i < size; i++) {
217 write(buffer[i]);
218 }
219 return size;
220 }
221
223 void flush() override {
224 if (line_buffer.length() > 0) {
225 dataSource.addName(line_buffer.c_str());
226 line_buffer.clear();
227 }
228 }
229
230 private:
231 PathNamesRegistry& dataSource;
232 Str line_buffer{200}; // Buffer to accumulate characters for each line
233 const char* prefix = nullptr;
234};
235
254template <typename FileType>
256 public:
257 typedef FileType* (*FileToStreamCallback)(const char* path,
258 FileType& oldFile);
259
260 AudioSourceVector() = default;
261
263 AudioSourceVector(FileToStreamCallback callback)
264 : nameToStreamCallback(callback) {}
265
267 bool begin() override {
268 TRACED();
269 current_index = 0;
270 current_stream = nullptr;
271 return true;
272 }
273
275 virtual FileType* nextStream(int offset) override {
276 TRACED();
277 if (files.empty()) return nullptr;
278
279 current_index += offset;
280 // Wrap around if out of bounds
281 if (current_index < 0) {
282 current_index = files.size() - 1;
283 } else if (current_index >= (int)files.size()) {
284 current_index = 0;
285 }
286
287 return selectStream(current_index);
288 }
289
291 virtual FileType* selectStream(int index) override {
292 TRACED();
293 if (files.empty() || index < 0 || index >= (int)files.size()) {
294 LOGE("Invalid index: %d (size: %d)", index, files.size());
295 return nullptr;
296 }
297
298 current_index = index;
299 Str fullPath = getFullPath(index);
300 LOGI("selectStream: %d -> %s", index, fullPath.c_str());
301
302 if (nameToStreamCallback) {
303 current_stream = nameToStreamCallback(fullPath.c_str(), getCurrentFile());
304 return current_stream;
305 }
306
307 LOGE("No file to stream callback set!");
308 return nullptr;
309 }
310
312 virtual FileType* selectStream(const char* path) override {
313 TRACED();
314 if (path == nullptr) return nullptr;
315
316 int idx = indexOf(path);
317 if (idx >= 0) {
318 return selectStream(idx);
319 }
320
321 LOGE("File not found: %s", path);
322 return nullptr;
323 }
324
326 virtual int index() override { return current_index; }
327
329 int indexOf(const char* path) {
330 if (path == nullptr) return -1;
331
332 // Find the file by path
333 for (int i = 0; i < (int)files.size(); i++) {
334 Str fullPath = getFullPath(i);
335 if (fullPath.equals(path)) {
336 return i;
337 }
338 }
339 return -1;
340 }
341
343 void addName(const char* nameWithPath) override {
344 TRACED();
345 if (nameWithPath == nullptr) return;
346 LOGI("addName: '%s'", nameWithPath);
347
348 // Split path and name
349 StrView nameWithPathStr(nameWithPath);
350 int lastSlashPos = nameWithPathStr.lastIndexOf("/");
351 int len = nameWithPathStr.length();
352
353 Str pathStr;
354 Str nameStr;
355 if (lastSlashPos < 0) {
356 // No path, just name
357 pathStr.set("");
358 nameStr.set(nameWithPath);
359 } else {
360 // Split into path and name
361 pathStr.substring(nameWithPath, 0, lastSlashPos);
362 nameStr.substring(nameWithPath, lastSlashPos + 1, len);
363 }
364
365 // Find or add path to registry
366 int pathIndex = findOrAddPath(pathStr.c_str());
367
368 // Create file entry with path index
369 FileEntry entry{pathIndex, nameStr.c_str()};
370 files.push_back(entry);
371
372 }
373
375 bool deleteName(const char* nameWithPath) {
376 TRACED();
377 if (nameWithPath == nullptr) return false;
378
379 int idx = indexOf(nameWithPath);
380 if (idx >= 0) {
381 LOGI("deleteName: '%s' at index %d", nameWithPath, idx);
382 return deleteIndex(idx);
383 }
384
385 LOGW("deleteName: File not found: '%s'", nameWithPath);
386 return false;
387 }
388
390 bool deleteIndex(size_t idx) {
391 TRACED();
392 if (idx >= files.size()) {
393 LOGW("deleteIndex: Invalid index: %d (size: %d)", (int)idx, files.size());
394 return false;
395 }
396
397 LOGI("deleteIndex: Removing file at index %d", (int)idx);
398 files.erase(files.begin() + idx);
399
400 // Adjust current_index if necessary
401 if (current_index >= (int)idx) {
402 current_index--;
403 if (current_index < 0 && !files.empty()) {
404 current_index = 0;
405 }
406 }
407
408 return true;
409 }
410
412 template <typename T, size_t N>
413 void addNames(T (&nameArray)[N]) {
414 for (size_t i = 0; i < N; i++) {
415 addName(nameArray[i]);
416 }
417 }
418
420 void clear() {
421 files.clear();
422 path_registry.clear();
423 current_index = 0;
424 current_stream = nullptr;
425 }
426
428 int size() { return files.size(); }
429
431 bool isEmpty() { return files.empty(); }
432
434 void setNameToStreamCallback(FileToStreamCallback callback) {
435 nameToStreamCallback = callback;
436 }
437
439 FileType& getCurrentFile() {
440 if (current_stream) {
441 return *current_stream;
442 }
443 static FileType empty;
444 return empty;
445 }
446
448 virtual const char* toStr() override {
449 if (current_index >= 0 && current_index < (int)files.size()) {
450 current_path = getFullPath(current_index);
451 return current_path.c_str();
452 }
453 return nullptr;
454 }
455
457 const char* name(int index) { return getFullPath(index).c_str(); }
458
459 protected:
460 Vector<FileEntry> files; // List of all files
461 Vector<Str> path_registry; // Shared registry of unique paths
462 int current_index = 0;
463 FileType* current_stream = nullptr;
464 FileToStreamCallback nameToStreamCallback = nullptr;
465 Str current_path; // Cache for toStr() method
466
468 int findOrAddPath(const char* path) {
469 if (path == nullptr) path = "";
470
471 // Search for existing path
472 for (int i = 0; i < (int)path_registry.size(); i++) {
473 if (strcmp(path_registry[i].c_str(), path) == 0) {
474 return i; // Found existing path
475 }
476 }
477
478 // Path not found, add new one
479 Str newPath;
480 newPath.set(path);
481 path_registry.push_back(newPath);
482 return path_registry.size() - 1;
483 }
484
486 FileEntry& getFileEntry(int index) const { return files[index]; }
487
490 if (index < 0 || index >= (int)files.size()) {
491 return Str();
492 }
493
494 FileEntry& entry = getFileEntry(index);
495
496 // Get path
497 Str result;
498 if (entry.path_index >= 0 && entry.path_index < (int)path_registry.size()) {
499 result = path_registry[entry.path_index].c_str();
500 }
501 // delimite with /
502 if (!result.endsWith("/") && !entry.name.startsWith("/")) {
503 result.add("/");
504 }
505 // add name
506 result.add(entry.name.c_str());
507
508 return result;
509 }
510
512 const char* getPath(int pathIndex) {
513 if (pathIndex >= 0 && pathIndex < (int)path_registry.size()) {
514 return path_registry[pathIndex].c_str();
515 }
516 return "";
517 }
518};
519
537template <typename FileType>
539 public:
540 typedef FileType* (*FileToStreamCallback)(const char* path, FileType& file);
541
542 AudioSourceArray() = default;
543
545 template <size_t N>
546 AudioSourceArray(const char* (&nameArray)[N], FileToStreamCallback callback)
547 : file_array(nameArray), array_size(N), nameToStreamCallback(callback) {}
548
550 AudioSourceArray(const char* const* nameArray, size_t size,
551 FileToStreamCallback callback)
552 : file_array(nameArray),
553 array_size(size),
554 nameToStreamCallback(callback) {}
555
557 virtual bool begin() override {
558 TRACED();
559 current_index = 0;
560 current_stream = nullptr;
561 return true;
562 }
563
565 virtual FileType* nextStream(int offset) override {
566 TRACED();
567 if (array_size == 0) return nullptr;
568
569 current_index += offset;
570 // Wrap around if out of bounds
571 if (current_index < 0) {
572 current_index = array_size - 1;
573 } else if (current_index >= (int)array_size) {
574 current_index = 0;
575 }
576
577 return selectStream(current_index);
578 }
579
581 virtual FileType* selectStream(int index) override {
582 TRACED();
583 if (array_size == 0 || index < 0 || index >= (int)array_size) {
584 LOGE("Invalid index: %d (size: %d)", index, array_size);
585 return nullptr;
586 }
587
588 current_index = index;
589 const char* filePath = file_array[index];
590 LOGI("selectStream: %d -> %s", index, filePath);
591
592 if (nameToStreamCallback && filePath) {
593 current_stream = nameToStreamCallback(filePath, getCurrentFile());
594 return current_stream;
595 }
596
597 LOGE("No file to stream callback set or invalid file path!");
598 return nullptr;
599 }
600
602 virtual FileType* selectStream(const char* path) override {
603 TRACED();
604 if (path == nullptr) return nullptr;
605
606 int idx = indexOf(path);
607 if (idx >= 0) {
608 return selectStream(idx);
609 }
610
611 LOGE("File not found: %s", path);
612 return nullptr;
613 }
614
616 virtual int index() override { return current_index; }
617
619 int indexOf(const char* path) {
620 if (path == nullptr) return -1;
621
622 // Find the file by path
623 for (int i = 0; i < (int)array_size; i++) {
624 const char* filePath = file_array[i];
625 if (filePath && StrView(path).equals(filePath)) {
626 return i;
627 }
628 }
629 return -1;
630 }
631
633 template <size_t N>
634 void setArray(const char* (&nameArray)[N]) {
635 file_array = nameArray;
636 array_size = N;
637 current_index = 0;
638 }
639
641 void setArray(const char* const* nameArray, size_t size) {
642 file_array = nameArray;
643 array_size = size;
644 current_index = 0;
645 }
646
648 int size() const { return array_size; }
649
651 bool isEmpty() const { return array_size == 0; }
652
654 void setNameToStreamCallback(FileToStreamCallback callback) {
655 nameToStreamCallback = callback;
656 }
657
658 FileType& getCurrentFile() {
659 if (current_stream) {
660 return *current_stream;
661 }
662 static FileType empty;
663 return empty;
664 }
665
667 const char* getFilePath(int index) const {
668 if (index >= 0 && index < (int)array_size && file_array) {
669 return file_array[index];
670 }
671 return nullptr;
672 }
673
675 virtual const char* toStr() override {
676 if (current_index >= 0 && current_index < (int)array_size && file_array) {
677 return file_array[current_index];
678 }
679 return nullptr;
680 }
681
683 const char* name(int index) { return getFilePath(index); }
684
685 protected:
686 const char* const* file_array = nullptr; // Pointer to array of const char*
687 size_t array_size = 0;
688 int current_index = 0;
689 FileType* current_stream = nullptr;
690 FileToStreamCallback nameToStreamCallback = nullptr;
691};
692
693} // namespace audio_tools
Audio Data Source managing a static array of file names (const char*). Designed for PROGMEM storage o...
Definition AudioSource.h:538
virtual FileType * selectStream(int index) override
Returns audio stream at the indicated index.
Definition AudioSource.h:581
void setArray(const char *const *nameArray, size_t size)
Set the array with pointer and size.
Definition AudioSource.h:641
void setArray(const char *(&nameArray)[N])
Set the array of file names.
Definition AudioSource.h:634
virtual const char * toStr() override
provides the actual stream (e.g. file) name or url
Definition AudioSource.h:675
void setNameToStreamCallback(FileToStreamCallback callback)
Set the callback for converting file path to stream.
Definition AudioSource.h:654
virtual bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:557
const char * getFilePath(int index) const
Get file path at index.
Definition AudioSource.h:667
const char * name(int index)
provides the name at the given index
Definition AudioSource.h:683
virtual FileType * nextStream(int offset) override
Returns next audio stream.
Definition AudioSource.h:565
AudioSourceArray(const char *(&nameArray)[N], FileToStreamCallback callback)
Constructor with array and callback.
Definition AudioSource.h:546
virtual FileType * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:602
virtual int index() override
Returns the actual index of the stream.
Definition AudioSource.h:616
bool isEmpty() const
Check if empty.
Definition AudioSource.h:651
AudioSourceArray(const char *const *nameArray, size_t size, FileToStreamCallback callback)
Constructor with array pointer, size and callback.
Definition AudioSource.h:550
int indexOf(const char *path)
Find index of file by path.
Definition AudioSource.h:619
int size() const
Get the number of files.
Definition AudioSource.h:648
Callback Audio Data Source which is used by the Audio Players.
Definition AudioSource.h:85
virtual Stream * selectStream(int index) override
Returns selected audio stream.
Definition AudioSource.h:111
virtual bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:97
virtual Stream * nextStream(int offset) override
Definition AudioSource.h:105
virtual const char * getPath()
Returns the requested path: relevant when provided idx in callback is -1.
Definition AudioSource.h:146
virtual bool isAutoNext() override
Returns default setting go to the next.
Definition AudioSource.h:141
virtual Stream * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:126
Abstract Audio Data Source for the AudioPlayer which is used by the Audio Players.
Definition AudioSource.h:16
virtual int index()
Returns the actual index of the stream.
Definition AudioSource.h:38
virtual Stream * selectStream(const char *path)=0
Returns audio stream by path: The index is not changed!
virtual void setTimeoutAutoNext(int millisec)
Definition AudioSource.h:45
virtual void setTimeout(int millisec)
Sets the timeout of Stream in milliseconds.
Definition AudioSource.h:60
virtual Stream * selectStream(int index)
Definition AudioSource.h:29
virtual Stream * previousStream(int offset)
Returns previous audio stream.
Definition AudioSource.h:25
virtual Stream * setIndex(int index)
same as selectStream - I just prefer this name
Definition AudioSource.h:35
virtual bool isAutoNext()
Returns default setting go to the next.
Definition AudioSource.h:63
virtual Stream * nextStream(int offset)=0
Returns next audio stream.
virtual const char * toStr()
provides the actual stream (e.g. file) name or url
Definition AudioSource.h:72
virtual int timeoutAutoNext()
Provides the timeout which is triggering to move to the next stream.
Definition AudioSource.h:50
Stream * operator[](int idx)
access with array syntax
Definition AudioSource.h:69
virtual bool begin()=0
Reset actual stream and move to root.
Flexible Audio Data Source using a Vector of (file) names with minimal RAM usage. Files are stored wi...
Definition AudioSource.h:255
virtual FileType * selectStream(int index) override
Returns audio stream at the indicated index.
Definition AudioSource.h:291
virtual const char * toStr() override
provides the actual stream (e.g. file) name or url
Definition AudioSource.h:448
void setNameToStreamCallback(FileToStreamCallback callback)
Set the callback for converting file path to stream.
Definition AudioSource.h:434
AudioSourceVector(FileToStreamCallback callback)
Constructor with callback for file to stream conversion.
Definition AudioSource.h:263
Str getFullPath(int index)
Get full path of file at index.
Definition AudioSource.h:489
FileType & getCurrentFile()
Get the current file reference for use in callback.
Definition AudioSource.h:439
const char * name(int index)
provides the name at the given index
Definition AudioSource.h:457
virtual FileType * nextStream(int offset) override
Returns next audio stream.
Definition AudioSource.h:275
bool deleteName(const char *nameWithPath)
Remove a file by full path.
Definition AudioSource.h:375
int findOrAddPath(const char *path)
Find existing path in registry or add new one, returns index.
Definition AudioSource.h:468
virtual FileType * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:312
void addNames(T(&nameArray)[N])
Add multiple files at once.
Definition AudioSource.h:413
virtual int index() override
Returns the actual index of the stream.
Definition AudioSource.h:326
void clear()
Clear all files and path registry.
Definition AudioSource.h:420
bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:267
FileEntry & getFileEntry(int index) const
Get file entry at index.
Definition AudioSource.h:486
const char * getPath(int pathIndex)
Get path from registry at index.
Definition AudioSource.h:512
void addName(const char *nameWithPath) override
Add a file with full path (path and name will be separated automatically)
Definition AudioSource.h:343
bool deleteIndex(size_t idx)
Remove a file by index.
Definition AudioSource.h:390
int indexOf(const char *path)
Find index of file by path.
Definition AudioSource.h:329
bool isEmpty()
Check if empty.
Definition AudioSource.h:431
int size()
Get the number of files.
Definition AudioSource.h:428
Print class that calls addName for each printed line. Useful for collecting file names from text outp...
Definition AudioSource.h:183
void flush() override
Flush any remaining content in the line buffer.
Definition AudioSource.h:223
Interface for classes that can collect file names.
Definition AudioSource.h:173
Definition NoArduino.h:62
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition Str.h:24
void clear() override
clears and resizes to 0
Definition Str.h:167
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual bool equals(const char *str)
checks if the string equals indicated parameter string
Definition StrView.h:165
virtual bool endsWith(const char *str)
checks if the string ends with the indicated substring
Definition StrView.h:178
virtual int length()
Definition StrView.h:383
virtual bool startsWith(const char *str)
checks if the string starts with the indicated substring
Definition StrView.h:171
virtual int lastIndexOf(const char *cont)
provides the position of the last occurrence of the indicated substring
Definition StrView.h:297
virtual void trim()
remove leading and traling spaces
Definition StrView.h:504
virtual void set(const char *alt)
assigs a value
Definition StrView.h:47
virtual void substring(StrView &from, int start, int end)
copies a substring into the current string
Definition StrView.h:477
virtual const char * c_str()
provides the string value as const char*
Definition StrView.h:379
virtual void add(int value)
adds a int value
Definition StrView.h:126
Definition NoArduino.h:142
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
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 AudioCodecsBase.h:10
File entry to minimize RAM usage by using path index and name.
Definition AudioSource.h:159