arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
SDDirect.h
1#pragma once
2
3#include "AudioTools/CoreAudio/AudioBasic/Str.h"
4#include "AudioTools/CoreAudio/AudioBasic/Collections/List.h"
5
6#define MAX_FILE_LEN 256
7#define MAX_FILE_COUNT 1000000
8// Special logic for SDTFAT
9#ifdef SDT_FAT_VERSION
10# define USE_SDFAT
11#endif
12
13
14namespace audio_tools {
15
21template <class SDT, class FileT>
22class SDDirect {
23 public:
24 SDDirect(SDT &sd) { p_sd = &sd; };
25
26 void begin(const char *startDir, const char *extension,
27 const char *file_name_pattern) {
28 TRACED();
29 this->start_dir = startDir;
30 this->ext = extension;
31 this->file_name_pattern = file_name_pattern;
32 this->max_idx = -1;
33 }
34
36 const char *operator[](int idx) {
37 if (max_idx != -1 && idx > max_idx) {
38 return nullptr;
39 }
40
41 requested_idx = idx;
42 actual_idx = -1;
43 found = false;
44 listDir(start_dir);
45 if (!found) return nullptr;
46 return result.c_str();
47 }
48
50 long size() {
51 if (max_idx == -1) {
52 requested_idx = MAX_FILE_COUNT;
53 actual_idx = -1;
54 found = false;
55 listDir(start_dir);
56 max_idx = actual_idx;
57 }
58 return max_idx + 1;
59 }
60
61 protected:
62 String result;
63 const char *start_dir;
64 SDT *p_sd = nullptr;
65 int32_t actual_idx;
66 size_t requested_idx;
67 long max_idx = -1;
68 bool found = false;
69 List<String> file_path_stack;
70 String file_path_str;
71
72 const char *ext = nullptr;
73 const char *file_name_pattern = nullptr;
74
76 void listDir(const char *dirname) {
77 if (dirname == nullptr) return;
78 LOGD("listDir: %s", dirname);
79 FileT root = open(dirname);
80 if (!root) {
81 LOGE("Open failed: %s", dirname);
82 popPath();
83 return;
84 }
85 if (!isDirectory(root)) {
86 LOGD("Is not directory: %s", dirname);
87 popPath();
88 return;
89 }
90 if (StrView(dirname).startsWith(".")) {
91 LOGD("Invalid file: %s", dirname);
92 popPath();
93 return;
94 }
95 if (isDirectory(root)) {
96 rewind(root);
97 }
98 found = false;
99 FileT file = openNext(root);
100 while (file && !found) {
101 if (isDirectory(file)) {
102 String name = String(fileNamePath(file));
103 LOGD("name: %s", name.c_str());
104 pushPath(fileName(file));
105 // recurseve call to get all files of this directory
106 listDir(name.c_str());
107 } else {
108 const char *fn = fileNamePath(file);
109 if (isValidAudioFile(file)) {
110 actual_idx++;
111 LOGD("File %s at index: %d", fn, actual_idx);
112 if (actual_idx == requested_idx) {
113 result = String(fn);
114 found = true;
115 }
116 } else {
117 LOGD("Ignoring %s", fn);
118 }
119 }
120 file = openNext(root);
121 }
122 // stop processing and record maximum index
123 if (!found && file_path_stack.size() == 0) {
124 max_idx = actual_idx;
125 }
126 popPath();
127 }
128
129 void rewind(FileT &f) {
130 TRACED();
131#ifdef USE_SDFAT
132 f.rewind();
133#else
134 f.rewindDirectory();
135#endif
136 }
137
138 bool isDirectory(FileT &f) {
139 bool result;
140#ifdef USE_SDFAT
141 result = f.isDir();
142#else
143 result = f.isDirectory();
144#endif
145 LOGD("isDirectory %s: %d", fileName(f), result);
146 return result;
147 }
148
149 FileT openNext(FileT &dir) {
150 TRACED();
151#ifdef USE_SDFAT
152 FileT result;
153 if (!result.openNext(&dir, O_READ)) {
154 LOGD("No next file");
155 }
156 return result;
157#else
158 return dir.openNextFile();
159#endif
160 }
161
162 void pushPath(const char *name) {
163 TRACED();
164 LOGD("pushPath: %s", name);
165 String nameStr(name);
166 file_path_stack.push_back(nameStr);
167 }
168
169 void popPath() {
170 TRACED();
171 String str;
172 file_path_stack.pop_back(str);
173 LOGD("popPath: %s", str.c_str());
174 }
175
177 bool isValidAudioFile(FileT &file) {
178 const char *file_name = fileName(file);
179 if (file.isDirectory()) {
180 LOGD("-> isValidAudioFile: '%s': %d", file_name, false);
181 return false;
182 }
183 StrView strFileTName(file_name);
184 bool result = strFileTName.endsWithIgnoreCase(ext) &&
185 strFileTName.matches(file_name_pattern) && !isHidden(file);
186 LOGD("-> isValidAudioFile: '%s': %d", file_name, result);
187 return result;
188 }
189
191 const char *fileName(FileT &file) {
192#ifdef USE_SDFAT
193 // add name
194 static char name[MAX_FILE_LEN];
195 file.getName(name, MAX_FILE_LEN);
196 return name;
197#else
198 StrView tmp(file.name());
199 int pos = 0;
200 // remove directories
201 if (tmp.contains("/")) {
202 pos = tmp.lastIndexOf("/") + 1;
203 }
204 return file.name() + pos;
205#endif
206 }
207
209 const char *fileNamePath(FileT &file) {
210#if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4
211 LOGD("-> fileNamePath: %s", fileName(file));
212 file_path_str = start_dir;
213 if (!file_path_str.endsWith("/")) {
214 file_path_str += "/";
215 }
216 for (int j = 0; j < file_path_stack.size(); j++) {
217 file_path_str += file_path_stack[j] + "/";
218 }
219 // add name
220 static char name[MAX_FILE_LEN];
221 strncpy(name, fileName(file), MAX_FILE_LEN);
222 file_path_str += name;
223 const char *result = file_path_str.c_str();
224 LOGD("<- fileNamePath: %s", result);
225 return result;
226#else
227 return file.name();
228#endif
229 }
230
231 bool isHidden(FileT &f) {
232#ifdef USE_SDFAT
233 return f.isHidden();
234#else
235 return StrView(fileNamePath(f)).contains("/.");
236#endif
237 }
238
239 FileT open(const char *name) {
240 TRACED();
241#ifdef USE_SDFAT
242 FileT result;
243 if (!result.open(name)) {
244 if (name != nullptr) {
245 LOGE("File open error: %s", name);
246 } else {
247 LOGE("File open error: name is null");
248 }
249 }
250 return result;
251#else
252 return p_sd->open(name, FILE_READ);
253#endif
254 }
255};
256
257} // namespace audio_tools
Double linked list.
Definition List.h:18
We access the files directy with an index. The index is determined by a recurseve tree walk thru the ...
Definition SDDirect.h:22
long size()
Provides the number of files.
Definition SDDirect.h:50
const char * fileNamePath(FileT &file)
Returns the filename including the path.
Definition SDDirect.h:209
void listDir(const char *dirname)
Writes the index file.
Definition SDDirect.h:76
bool isValidAudioFile(FileT &file)
checks if the file is a valid audio file
Definition SDDirect.h:177
const char * fileName(FileT &file)
Returns the filename w/o path.
Definition SDDirect.h:191
const char * operator[](int idx)
Access file name by index.
Definition SDDirect.h:36
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition StrView.h:28
virtual bool contains(const char *str)
checks if the string contains a substring
Definition StrView.h:280
virtual int lastIndexOf(const char *cont)
provides the position of the last occurrence of the indicated substring
Definition StrView.h:297
virtual bool matches(const char *pattern)
Definition StrView.h:193
virtual bool endsWithIgnoreCase(const char *str)
checks if the string ends with the indicated substring
Definition StrView.h:185
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10