arduino-audio-tools
SDDirect.h
1 #pragma once
2 
3 #define MAX_FILE_LEN 256
4 #define MAX_FILE_COUNT 1000000
5 // Special logic for SDTFAT
6 #ifdef SDT_FAT_VERSION
7 #define USE_SDFAT
8 #endif
9 
10 namespace audio_tools {
11 
17 template<class SDT, class FileT>
18 class SDDirect {
19  public:
20  SDDirect(SDT &sd) {
21  p_sd = &sd;
22  };
23 
24  void begin(const char *startDir, const char *extension,
25  const char *file_name_pattern) {
26  TRACED();
27  this->start_dir = startDir;
28  this->ext = extension;
29  this->file_name_pattern = file_name_pattern;
30  this->max_idx = -1;
31  }
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 (Str(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  Str strFileTName(file_name);
183  bool result = strFileTName.endsWithIgnoreCase(ext)
184  && strFileTName.matches(file_name_pattern)
185  && !isHidden(file);
186  LOGD("-> isValidAudioFile: '%s': %d", file_name, result);
187  return result;
188  }
189 
190 
192  const char* fileName(FileT&file){
193 #ifdef USE_SDFAT
194  // add name
195  static char name[MAX_FILE_LEN];
196  file.getName(name,MAX_FILE_LEN);
197  return name;
198 #else
199  Str tmp(file.name());
200  int pos=0;
201  // remove directories
202  if (tmp.contains("/")){
203  pos = tmp.lastIndexOf("/")+1;
204  }
205  return file.name()+pos;
206 #endif
207  }
208 
210  const char* fileNamePath(FileT &file){
211 #if defined(USE_SDFAT) || ESP_IDF_VERSION_MAJOR >= 4
212  LOGD("-> fileNamePath: %s", fileName(file));
213  file_path_str = start_dir;
214  if (!file_path_str.endsWith("/")){
215  file_path_str += "/";
216  }
217  for (int j=0; j<file_path_stack.size(); j++){
218  file_path_str += file_path_stack[j]+"/";
219  }
220  // add name
221  static char name[MAX_FILE_LEN];
222  strncpy(name, fileName(file), MAX_FILE_LEN);
223  file_path_str += name;
224  const char* result = file_path_str.c_str();
225  LOGD("<- fileNamePath: %s", result);
226  return result;
227 #else
228  return file.name();
229 #endif
230  }
231 
232  bool isHidden(FileT f){
233 #ifdef USE_SDFAT
234  return f.isHidden();
235 #else
236  return Str(fileNamePath(f)).contains("/.");
237 #endif
238 
239  }
240 
241  FileT open(const char* name){
242  TRACED();
243 #ifdef USE_SDFAT
244  FileT result;
245  if (!result.open(name)){
246  if (name!=nullptr){
247  LOGE("File open error: %s", name);
248  } else {
249  LOGE("File open error: name is null");
250  }
251  }
252  return result;
253 #else
254  return p_sd->open(name);
255 #endif
256  }
257 
258 };
259 
260 }
We access the files directy with an index. The index is determined by a recurseve tree walk thru the ...
Definition: SDDirect.h:18
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:210
const char * fileName(FileT &file)
Returns the filename w/o path.
Definition: SDDirect.h:192
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 char*. If the underlying char* is a const we do not a...
Definition: Str.h:27
virtual bool contains(const char *str)
checks if the string contains a substring
Definition: Str.h:281
virtual int lastIndexOf(const char *cont)
provides the position of the last occurrence of the indicated substring
Definition: Str.h:298
virtual bool matches(const char *pattern)
Definition: Str.h:204
virtual bool endsWithIgnoreCase(const char *str)
checks if the string ends with the indicated substring
Definition: Str.h:184
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10