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