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