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 true; }
64
66 Stream* operator[](int idx) { return setIndex(idx); }
67
69 virtual const char* toStr() { return nullptr; }
70
71 protected:
72 int timeout_auto_next_value = 500;
73};
74
82 public:
84
85 AudioSourceCallback(Stream* (*nextStreamCallback)(int offset),
86 void (*onStartCallback)() = nullptr) {
87 TRACED();
88 this->onStartCallback = onStartCallback;
89 this->nextStreamCallback = nextStreamCallback;
90 }
91
93 virtual bool begin() override {
94 TRACED();
95 if (onStartCallback != nullptr) onStartCallback();
96 return true;
97 };
98
101 virtual Stream* nextStream(int offset) override {
102 TRACED();
103 return nextStreamCallback == nullptr ? nullptr : nextStreamCallback(offset);
104 }
105
107 virtual Stream* selectStream(int index) override {
108 LOGI("selectStream: %d", index);
109 if (indexStreamCallback == nullptr) {
110 LOGI("setCallbackSelectStream not provided");
111 if (index > 0) {
112 begin();
113 return nextStream(index);
114 } else {
115 // nextStream(0) will return the directory but we need a file
116 return nextStream(1);
117 }
118 }
119 return indexStreamCallback(index);
120 }
122 virtual Stream* selectStream(const char* path) override {
123 this->path = path;
124 return indexStreamCallback == nullptr ? nullptr : indexStreamCallback(-1);
125 };
126
127 void setCallbackOnStart(void (*callback)()) { onStartCallback = callback; }
128
129 void setCallbackNextStream(Stream* (*callback)(int offset)) {
130 nextStreamCallback = callback;
131 }
132
133 void setCallbackSelectStream(Stream* (*callback)(int idx)) {
134 indexStreamCallback = callback;
135 }
136
137 virtual bool isAutoNext() override { return auto_next; }
138
139 virtual void setAutoNext(bool a) { auto_next = a; }
140
142 virtual const char* getPath() { return path; }
143
144 protected:
145 void (*onStartCallback)() = nullptr;
146 bool auto_next = true;
147 Stream* (*nextStreamCallback)(int offset) = nullptr;
148 Stream* (*indexStreamCallback)(int index) = nullptr;
149 const char* path = nullptr;
150};
151
155struct FileEntry {
156 int path_index; // Index into shared path registry
157 Str name;
158
159 FileEntry() : path_index(-1) {}
160
161 FileEntry(int pathIdx, const char* fileName) : path_index(pathIdx) {
162 name.set(fileName);
163 }
164};
165
170 public:
171 virtual void addName(const char* nameWithPath) = 0;
172};
173
179class NamePrinter : public Print {
180 public:
181 NamePrinter(PathNamesRegistry& dataSource, const char* prefix = nullptr)
182 : dataSource(dataSource), prefix(prefix) {}
183 void setPrefix(const char* prefix) { this->prefix = prefix; }
184 size_t write(uint8_t ch) override {
185 if (ch == '\n' || ch == '\r') {
186 // End of line - process the accumulated line
187 if (line_buffer.length() > 0) {
188 line_buffer.trim();
189 if (prefix != nullptr) {
190 // Prepend prefix if set
191 Str name{prefix};
192 name.add("/");
193 name.add(line_buffer.c_str());
194 LOGD("adding '%s'", name.c_str());
195 dataSource.addName(name.c_str());
196 } else {
197 // Add line as is
198 LOGD("adding '%s'", line_buffer.c_str());
199 dataSource.addName(line_buffer.c_str());
200 }
201 line_buffer.clear(false);
202 }
203 return 1;
204 } else {
205 // Accumulate characters
206 line_buffer.add((char)ch);
207 return 1;
208 }
209 }
210
211 size_t write(const uint8_t* buffer, size_t size) override {
212 for (size_t i = 0; i < size; i++) {
213 write(buffer[i]);
214 }
215 return size;
216 }
217
219 void flush() override {
220 if (line_buffer.length() > 0) {
221 dataSource.addName(line_buffer.c_str());
222 line_buffer.clear();
223 }
224 }
225
226 private:
227 PathNamesRegistry& dataSource;
228 Str line_buffer{200}; // Buffer to accumulate characters for each line
229 const char* prefix = nullptr;
230};
231
250template <typename FileType>
252 public:
253 typedef FileType* (*FileToStreamCallback)(const char* path,
254 FileType& oldFile);
255
256 AudioSourceVector() = default;
257
259 AudioSourceVector(FileToStreamCallback callback)
260 : nameToStreamCallback(callback) {}
261
263 bool begin() override {
264 TRACED();
265 current_index = 0;
266 current_stream = nullptr;
267 return true;
268 }
269
271 virtual FileType* nextStream(int offset) override {
272 TRACED();
273 if (files.empty()) return nullptr;
274
275 current_index += offset;
276 // Wrap around if out of bounds
277 if (current_index < 0) {
278 current_index = files.size() - 1;
279 } else if (current_index >= (int)files.size()) {
280 current_index = 0;
281 }
282
283 return selectStream(current_index);
284 }
285
287 virtual FileType* selectStream(int index) override {
288 TRACED();
289 if (files.empty() || index < 0 || index >= (int)files.size()) {
290 LOGE("Invalid index: %d (size: %d)", index, files.size());
291 return nullptr;
292 }
293
294 current_index = index;
295 Str fullPath = getFullPath(index);
296 LOGI("selectStream: %d -> %s", index, fullPath.c_str());
297
298 if (nameToStreamCallback) {
299 current_stream = nameToStreamCallback(fullPath.c_str(), getCurrentFile());
300 return current_stream;
301 }
302
303 LOGE("No file to stream callback set!");
304 return nullptr;
305 }
306
308 virtual FileType* selectStream(const char* path) override {
309 TRACED();
310 if (path == nullptr) return nullptr;
311
312 int idx = indexOf(path);
313 if (idx >= 0) {
314 return selectStream(idx);
315 }
316
317 LOGE("File not found: %s", path);
318 return nullptr;
319 }
320
322 virtual int index() override { return current_index; }
323
325 int indexOf(const char* path) {
326 if (path == nullptr) return -1;
327
328 // Find the file by path
329 for (int i = 0; i < (int)files.size(); i++) {
330 Str fullPath = getFullPath(i);
331 if (fullPath.equals(path)) {
332 return i;
333 }
334 }
335 return -1;
336 }
337
339 void addName(const char* nameWithPath) override {
340 TRACED();
341 if (nameWithPath == nullptr) return;
342 LOGI("addName: '%s'", nameWithPath);
343
344 // Split path and name
345 StrView nameWithPathStr(nameWithPath);
346 int lastSlashPos = nameWithPathStr.lastIndexOf("/");
347 int len = nameWithPathStr.length();
348
349 Str pathStr;
350 Str nameStr;
351 if (lastSlashPos < 0) {
352 // No path, just name
353 pathStr.set("");
354 nameStr.set(nameWithPath);
355 } else {
356 // Split into path and name
357 pathStr.substring(nameWithPath, 0, lastSlashPos);
358 nameStr.substring(nameWithPath, lastSlashPos + 1, len);
359 }
360
361 // Find or add path to registry
362 int pathIndex = findOrAddPath(pathStr.c_str());
363
364 // Create file entry with path index
365 FileEntry entry{pathIndex, nameStr.c_str()};
366 files.push_back(entry);
367
368 }
369
371 bool deleteName(const char* nameWithPath) {
372 TRACED();
373 if (nameWithPath == nullptr) return false;
374
375 int idx = indexOf(nameWithPath);
376 if (idx >= 0) {
377 LOGI("deleteName: '%s' at index %d", nameWithPath, idx);
378 return deleteIndex(idx);
379 }
380
381 LOGW("deleteName: File not found: '%s'", nameWithPath);
382 return false;
383 }
384
386 bool deleteIndex(size_t idx) {
387 TRACED();
388 if (idx >= files.size()) {
389 LOGW("deleteIndex: Invalid index: %d (size: %d)", (int)idx, files.size());
390 return false;
391 }
392
393 LOGI("deleteIndex: Removing file at index %d", (int)idx);
394 files.erase(files.begin() + idx);
395
396 // Adjust current_index if necessary
397 if (current_index >= (int)idx) {
398 current_index--;
399 if (current_index < 0 && !files.empty()) {
400 current_index = 0;
401 }
402 }
403
404 return true;
405 }
406
408 template <typename T, size_t N>
409 void addNames(T (&nameArray)[N]) {
410 for (size_t i = 0; i < N; i++) {
411 addName(nameArray[i]);
412 }
413 }
414
416 void clear() {
417 files.clear();
418 path_registry.clear();
419 current_index = 0;
420 current_stream = nullptr;
421 }
422
424 int size() { return files.size(); }
425
427 bool isEmpty() { return files.empty(); }
428
430 void setNameToStreamCallback(FileToStreamCallback callback) {
431 nameToStreamCallback = callback;
432 }
433
435 FileType& getCurrentFile() {
436 if (current_stream) {
437 return *current_stream;
438 }
439 static FileType empty;
440 return empty;
441 }
442
444 virtual const char* toStr() override {
445 if (current_index >= 0 && current_index < (int)files.size()) {
446 current_path = getFullPath(current_index);
447 return current_path.c_str();
448 }
449 return nullptr;
450 }
451
453 const char* name(int index) { return getFullPath(index).c_str(); }
454
455 protected:
456 Vector<FileEntry> files; // List of all files
457 Vector<Str> path_registry; // Shared registry of unique paths
458 int current_index = 0;
459 FileType* current_stream = nullptr;
460 FileToStreamCallback nameToStreamCallback = nullptr;
461 Str current_path; // Cache for toStr() method
462
464 int findOrAddPath(const char* path) {
465 if (path == nullptr) path = "";
466
467 // Search for existing path
468 for (int i = 0; i < (int)path_registry.size(); i++) {
469 if (strcmp(path_registry[i].c_str(), path) == 0) {
470 return i; // Found existing path
471 }
472 }
473
474 // Path not found, add new one
475 Str newPath;
476 newPath.set(path);
477 path_registry.push_back(newPath);
478 return path_registry.size() - 1;
479 }
480
482 FileEntry& getFileEntry(int index) const { return files[index]; }
483
486 if (index < 0 || index >= (int)files.size()) {
487 return Str();
488 }
489
490 FileEntry& entry = getFileEntry(index);
491
492 // Get path
493 Str result;
494 if (entry.path_index >= 0 && entry.path_index < (int)path_registry.size()) {
495 result = path_registry[entry.path_index].c_str();
496 }
497 // delimite with /
498 if (!result.endsWith("/") && !entry.name.startsWith("/")) {
499 result.add("/");
500 }
501 // add name
502 result.add(entry.name.c_str());
503
504 return result;
505 }
506
508 const char* getPath(int pathIndex) {
509 if (pathIndex >= 0 && pathIndex < (int)path_registry.size()) {
510 return path_registry[pathIndex].c_str();
511 }
512 return "";
513 }
514};
515
533template <typename FileType>
535 public:
536 typedef FileType* (*FileToStreamCallback)(const char* path, FileType& file);
537
538 AudioSourceArray() = default;
539
541 template <size_t N>
542 AudioSourceArray(const char* (&nameArray)[N], FileToStreamCallback callback)
543 : file_array(nameArray), array_size(N), nameToStreamCallback(callback) {}
544
546 AudioSourceArray(const char* const* nameArray, size_t size,
547 FileToStreamCallback callback)
548 : file_array(nameArray),
549 array_size(size),
550 nameToStreamCallback(callback) {}
551
553 virtual bool begin() override {
554 TRACED();
555 current_index = 0;
556 current_stream = nullptr;
557 return true;
558 }
559
561 virtual FileType* nextStream(int offset) override {
562 TRACED();
563 if (array_size == 0) return nullptr;
564
565 current_index += offset;
566 // Wrap around if out of bounds
567 if (current_index < 0) {
568 current_index = array_size - 1;
569 } else if (current_index >= (int)array_size) {
570 current_index = 0;
571 }
572
573 return selectStream(current_index);
574 }
575
577 virtual FileType* selectStream(int index) override {
578 TRACED();
579 if (array_size == 0 || index < 0 || index >= (int)array_size) {
580 LOGE("Invalid index: %d (size: %d)", index, array_size);
581 return nullptr;
582 }
583
584 current_index = index;
585 const char* filePath = file_array[index];
586 LOGI("selectStream: %d -> %s", index, filePath);
587
588 if (nameToStreamCallback && filePath) {
589 current_stream = nameToStreamCallback(filePath, getCurrentFile());
590 return current_stream;
591 }
592
593 LOGE("No file to stream callback set or invalid file path!");
594 return nullptr;
595 }
596
598 virtual FileType* selectStream(const char* path) override {
599 TRACED();
600 if (path == nullptr) return nullptr;
601
602 int idx = indexOf(path);
603 if (idx >= 0) {
604 return selectStream(idx);
605 }
606
607 LOGE("File not found: %s", path);
608 return nullptr;
609 }
610
612 virtual int index() override { return current_index; }
613
615 int indexOf(const char* path) {
616 if (path == nullptr) return -1;
617
618 // Find the file by path
619 for (int i = 0; i < (int)array_size; i++) {
620 const char* filePath = file_array[i];
621 if (filePath && StrView(path).equals(filePath)) {
622 return i;
623 }
624 }
625 return -1;
626 }
627
629 template <size_t N>
630 void setArray(const char* (&nameArray)[N]) {
631 file_array = nameArray;
632 array_size = N;
633 current_index = 0;
634 }
635
637 void setArray(const char* const* nameArray, size_t size) {
638 file_array = nameArray;
639 array_size = size;
640 current_index = 0;
641 }
642
644 int size() const { return array_size; }
645
647 bool isEmpty() const { return array_size == 0; }
648
650 void setNameToStreamCallback(FileToStreamCallback callback) {
651 nameToStreamCallback = callback;
652 }
653
654 FileType& getCurrentFile() {
655 if (current_stream) {
656 return *current_stream;
657 }
658 static FileType empty;
659 return empty;
660 }
661
663 const char* getFilePath(int index) const {
664 if (index >= 0 && index < (int)array_size && file_array) {
665 return file_array[index];
666 }
667 return nullptr;
668 }
669
671 virtual const char* toStr() override {
672 if (current_index >= 0 && current_index < (int)array_size && file_array) {
673 return file_array[current_index];
674 }
675 return nullptr;
676 }
677
679 const char* name(int index) { return getFilePath(index); }
680
681 protected:
682 const char* const* file_array = nullptr; // Pointer to array of const char*
683 size_t array_size = 0;
684 int current_index = 0;
685 FileType* current_stream = nullptr;
686 FileToStreamCallback nameToStreamCallback = nullptr;
687};
688
689} // namespace audio_tools
Audio Data Source managing a static array of file names (const char*). Designed for PROGMEM storage o...
Definition AudioSource.h:534
virtual FileType * selectStream(int index) override
Returns audio stream at the indicated index.
Definition AudioSource.h:577
void setArray(const char *const *nameArray, size_t size)
Set the array with pointer and size.
Definition AudioSource.h:637
void setArray(const char *(&nameArray)[N])
Set the array of file names.
Definition AudioSource.h:630
virtual const char * toStr() override
provides the actual stream (e.g. file) name or url
Definition AudioSource.h:671
void setNameToStreamCallback(FileToStreamCallback callback)
Set the callback for converting file path to stream.
Definition AudioSource.h:650
virtual bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:553
const char * getFilePath(int index) const
Get file path at index.
Definition AudioSource.h:663
const char * name(int index)
provides the name at the given index
Definition AudioSource.h:679
virtual FileType * nextStream(int offset) override
Returns next audio stream.
Definition AudioSource.h:561
AudioSourceArray(const char *(&nameArray)[N], FileToStreamCallback callback)
Constructor with array and callback.
Definition AudioSource.h:542
virtual FileType * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:598
virtual int index() override
Returns the actual index of the stream.
Definition AudioSource.h:612
bool isEmpty() const
Check if empty.
Definition AudioSource.h:647
AudioSourceArray(const char *const *nameArray, size_t size, FileToStreamCallback callback)
Constructor with array pointer, size and callback.
Definition AudioSource.h:546
int indexOf(const char *path)
Find index of file by path.
Definition AudioSource.h:615
int size() const
Get the number of files.
Definition AudioSource.h:644
Callback Audio Data Source which is used by the Audio Players.
Definition AudioSource.h:81
virtual Stream * selectStream(int index) override
Returns selected audio stream.
Definition AudioSource.h:107
virtual bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:93
virtual Stream * nextStream(int offset) override
Definition AudioSource.h:101
virtual const char * getPath()
Returns the requested path: relevant when provided idx in callback is -1.
Definition AudioSource.h:142
virtual bool isAutoNext() override
Returns default setting go to the next.
Definition AudioSource.h:137
virtual Stream * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:122
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:69
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:66
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:251
virtual FileType * selectStream(int index) override
Returns audio stream at the indicated index.
Definition AudioSource.h:287
virtual const char * toStr() override
provides the actual stream (e.g. file) name or url
Definition AudioSource.h:444
void setNameToStreamCallback(FileToStreamCallback callback)
Set the callback for converting file path to stream.
Definition AudioSource.h:430
AudioSourceVector(FileToStreamCallback callback)
Constructor with callback for file to stream conversion.
Definition AudioSource.h:259
Str getFullPath(int index)
Get full path of file at index.
Definition AudioSource.h:485
FileType & getCurrentFile()
Get the current file reference for use in callback.
Definition AudioSource.h:435
const char * name(int index)
provides the name at the given index
Definition AudioSource.h:453
virtual FileType * nextStream(int offset) override
Returns next audio stream.
Definition AudioSource.h:271
bool deleteName(const char *nameWithPath)
Remove a file by full path.
Definition AudioSource.h:371
int findOrAddPath(const char *path)
Find existing path in registry or add new one, returns index.
Definition AudioSource.h:464
virtual FileType * selectStream(const char *path) override
Returns audio stream by path.
Definition AudioSource.h:308
void addNames(T(&nameArray)[N])
Add multiple files at once.
Definition AudioSource.h:409
virtual int index() override
Returns the actual index of the stream.
Definition AudioSource.h:322
void clear()
Clear all files and path registry.
Definition AudioSource.h:416
bool begin() override
Reset actual stream and move to root.
Definition AudioSource.h:263
FileEntry & getFileEntry(int index) const
Get file entry at index.
Definition AudioSource.h:482
const char * getPath(int pathIndex)
Get path from registry at index.
Definition AudioSource.h:508
void addName(const char *nameWithPath) override
Add a file with full path (path and name will be separated automatically)
Definition AudioSource.h:339
bool deleteIndex(size_t idx)
Remove a file by index.
Definition AudioSource.h:386
int indexOf(const char *path)
Find index of file by path.
Definition AudioSource.h:325
bool isEmpty()
Check if empty.
Definition AudioSource.h:427
int size()
Get the number of files.
Definition AudioSource.h:424
Print class that calls addName for each printed line. Useful for collecting file names from text outp...
Definition AudioSource.h:179
void flush() override
Flush any remaining content in the line buffer.
Definition AudioSource.h:219
Interface for classes that can collect file names.
Definition AudioSource.h:169
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:155