arduino-audio-tools
LegacyAudioSourceSDFAT.h
1 #pragma once
2 
3 #include "AudioLogger.h"
4 #include "AudioTools/CoreAudio/AudioSource.h"
5 #include "AudioTools/CoreAudio/AudioBasic/Str.h"
6 #include <SPI.h>
7 #include <SdFat.h>
8 namespace audio_tools {
9 
10 // SD_FAT_TYPE = 0 for SdFat/File as defined in SdFatConfig.h,
11 // 1 for FAT16/FAT32, 2 for exFAT, 3 for FAT16/FAT32 and exFAT.
12 #ifndef SD_FAT_TYPE
13 #define SD_FAT_TYPE 1
14 #endif
15 // Try max SPI clock for an SD. Reduce SPI_CLOCK if errors occur. (40?)
16 #define SPI_CLOCK SD_SCK_MHZ(50)
17 // Max file name length including directory path
18 #define MAX_FILE_LEN 256
19 
20 #if defined(ARDUINO_ARCH_RP2040) && !defined(PICO)
21  // only RP2040 from Earle Phil Hower is using the library with a sdfat namespace
22  typedef sdfat::SdSpiConfig SdSpiConfig;
23  typedef sdfat::SdFs AudioFs;
24 #else
25 #if SD_FAT_TYPE == 0
26  typedef SdFat AudioFs;
27  typedef File AudioFile;
28 #elif SD_FAT_TYPE == 1
29  typedef SdFat32 AudioFs;
30  typedef File32 AudioFile;
31 #elif SD_FAT_TYPE == 2
32  typedef SdExFat AudioFs;
33  typedef ExFile AudioFile;
34 #elif SD_FAT_TYPE == 3
35  typedef SdFs AudioFs;
36  typedef FsFile AudioFile;
37 #else // SD_FAT_TYPE
38 #endif
39 #endif
40 
41 
48 class AudioSourceSDFAT : public AudioSource {
49 public:
51  AudioSourceSDFAT(const char* startFilePath = "/", const char* ext = ".mp3", int chipSelect = PIN_CS, int speedMHz = 10) {
52  TRACED();
53  LOGI("SD chipSelect: %d", chipSelect);
54  LOGI("SD speedMHz: %d", speedMHz);
55  LOGI("ext: %s", ext);
56  p_cfg = new SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(speedMHz));
57  owns_cfg = true;
58  start_path = startFilePath;
59  exension = ext;
60  }
61 
63  AudioSourceSDFAT(const char* startFilePath, const char* ext, SdSpiConfig &config) {
64  TRACED();
65  p_cfg = &config;
66  owns_cfg = false;
67  start_path = startFilePath;
68  exension = ext;
69  }
70 
72  virtual ~AudioSourceSDFAT(){
73  TRACED();
74  if (p_cfg!=nullptr && owns_cfg){
75  delete p_cfg;
76  }
77  }
78 
79  virtual void begin() override {
80  TRACED();
81  static bool is_sd_setup = false;
82  if (!is_sd_setup){
83  while (!sd.begin(*p_cfg)) {
84  LOGE("SD.begin failed with cs=%d!", p_cfg->csPin);
85  sd.initErrorHalt(&Serial);
86  delay(500);
87  }
88  is_sd_setup = true;
89  }
90  idx_pos = 0;
91  }
92 
93  virtual Stream* nextStream(int offset) override {
94  LOGW("-> nextStream: %d", offset);
95  return selectStream(idx_pos+offset);
96  }
97 
98  virtual Stream* selectStream(int index) override {
99  idx_pos = index<0 ? 0 : index;
100  file.close();
101  file = getFileByPos(start_path, idx_pos);
102  file.getName(file_name, MAX_FILE_LEN);
103  LOGW("-> selectStream: %d '%s'", idx_pos, file_name);
104  return file ? &file : nullptr;
105  }
106 
107  virtual Stream* selectStream(const char* path) override {
108  file.close();
109  file = getFileByPath(path);
110  LOGW("-> selectStream: %s", path);
111  return file ? &file : nullptr;
112  }
113 
115  void setFileFilter(const char* filter) {
116  file_name_pattern = (char*)filter;
117  }
118 
120  int index() {
121  return idx_pos;
122  }
123 
125  const char *toStr() {
126  return file_name;
127  }
128 
129  // provides default setting go to the next
130  virtual bool isAutoNext() {
131  return true;
132  };
133 
135  virtual void setPath(const char* p) {
136  start_path = p;
137  }
138 
139  virtual void setTimeout(int ms){
140  timeout = ms;
141  }
142 
143 protected:
144  AudioFile file;
145  SdSpiConfig *p_cfg = nullptr;
146  bool owns_cfg=false;
147  AudioFs sd;
148  size_t idx_pos = 0;
149  char file_name[MAX_FILE_LEN];
150  const char* exension = nullptr;
151  const char* start_path = nullptr;
152  char* file_name_pattern = (char*) "*";
153  int timeout;
154 
156  bool isValidAudioFile(AudioFile& file) {
157  if (file.isDir()){
158  LOGD("-> isValidAudioFile: '%s': %d", file_name, false);
159  return false;
160  }
161  char file_name[MAX_FILE_LEN];
162  file.getName(file_name, MAX_FILE_LEN);
163  StrView strFileName(file_name);
164  bool result = strFileName.endsWithIgnoreCase(exension) && strFileName.matches(file_name_pattern);
165  LOGD("-> isValidAudioFile: '%s': %d", file_name, result);
166  return result;
167  }
168 
169  AudioFile getFileByPath(const char* path) {
170  AudioFile dir;
171  StrView inPath(path);
172  Str strPath;
173  Str strfileName;
174  int pos = inPath.lastIndexOf("/") + 1;
175  strPath.substring(path, 0, pos);
176  strfileName.substring(path, pos, inPath.length());
177  if (!dir.open(strPath.c_str())) {
178  LOGE("directory: %s not open", path);
179  } else {
180  if (!dir.isDir()) {
181  LOGE("directory: %s is not dictory", path);
182  } else {
183  if (!file.open(&dir, strfileName.c_str(), O_RDWR)) {
184  LOGE("file: %s not open", path);
185  } else{
186  LOGD("-> getFileByPath: %s , %s", strPath.c_str(), strfileName.c_str());
187  }
188  }
189  }
190  dir.close();
191  return file;
192  }
193 
195  AudioFile getFileByPos(const char* dirStr, int pos) {
196  AudioFile dir;
197  AudioFile result;
198  if (sd.exists(dirStr)){
199  LOGI("directory: '%s'", dirStr);
200  if (dir.open(dirStr)){
201  if (dir.isDir()) {
202  size_t count = 0;
203  getFileAtIndex(dir, pos, count, result);
204  result.getName(file_name, MAX_FILE_LEN);
205  result.setTimeout(timeout);
206  LOGI("-> getFile: '%s': %d - %s", file_name, pos, result.isOpen() ? "open":"closed");
207  } else {
208  LOGE("'%s' is not a directory!", dirStr);
209  }
210  } else {
211  LOGE("Could not open direcotry: '%s'", dirStr);
212  }
213  } else {
214  LOGE("directory: '%s' does not exist", dirStr);
215  }
216  dir.close();
217 
218  return result;
219  }
220 
221  void getFileAtIndex(AudioFile dir, size_t pos, size_t& idx, AudioFile& result) {
222  LOGD("%s: %d", LOG_METHOD, idx);
223  char file_name_act[MAX_FILE_LEN];
224  dir.getName(file_name_act, MAX_FILE_LEN);
225  LOGD("-> processing directory: %s ", file_name_act);
226  AudioFile file;
227  dir.rewind();
228  while (!result && file.openNext(&dir, O_RDONLY)) {
229  // indent for dir level
230  if (!file.isHidden()) {
231  file.getName(file_name_act, MAX_FILE_LEN);
232  LOGD("-> processing: %s with index %d", file_name_act, idx);
233 
234  if (isValidAudioFile(file)){
235  if (idx == pos) {
236  result = file;
237  result.getName(file_name, MAX_FILE_LEN);
238  LOGD("==> found: '%s' at index %d", file_name, idx);
239  }
240  idx++;
241  }
242 
243  if (file.isDir()) {
244  getFileAtIndex(file, pos, idx, result);
245  }
246  }
247 
248  if (file.dirIndex()!=result.dirIndex()){
249  LOGD("Close: %s", file_name_act);
250  file.close();
251  }
252  }
253  return;
254  }
255 };
256 
257 }
int index()
Provides the current index position.
Definition: AudioSourceSDFAT.h:129
const char * toStr()
provides the actual file name
Definition: LegacyAudioSourceSDFAT.h:125
AudioFile getFileByPos(const char *dirStr, int pos)
Determines the file at the indicated index (starting with 0)
Definition: LegacyAudioSourceSDFAT.h:195
virtual void setTimeout(int ms)
Sets the timeout of Stream in milliseconds.
Definition: LegacyAudioSourceSDFAT.h:139
AudioSourceSDFAT(const char *startFilePath="/", const char *ext=".mp3", int chipSelect=PIN_CS, int speedMHz=10)
Default constructor.
Definition: LegacyAudioSourceSDFAT.h:51
virtual void begin() override
Reset actual stream and move to root.
Definition: LegacyAudioSourceSDFAT.h:79
virtual void setPath(const char *p)
Allows to "correct" the start path if not defined in the constructor.
Definition: LegacyAudioSourceSDFAT.h:135
virtual ~AudioSourceSDFAT()
Destructor.
Definition: LegacyAudioSourceSDFAT.h:72
bool isValidAudioFile(AudioFile &file)
checks if the file is a valid audio file
Definition: LegacyAudioSourceSDFAT.h:156
AudioSourceSDFAT(const char *startFilePath, const char *ext, SdSpiConfig &config)
Costructor with SdSpiConfig.
Definition: LegacyAudioSourceSDFAT.h:63
virtual bool isAutoNext()
Returns default setting go to the next.
Definition: LegacyAudioSourceSDFAT.h:130
void setFileFilter(const char *filter)
Defines the regex filter criteria for selecting files. E.g. ".*Bob Dylan.*".
Definition: LegacyAudioSourceSDFAT.h:115
virtual Stream * selectStream(int index) override
Returns audio stream at the indicated index (the index is zero based, so the first value is 0!...
Definition: AudioSourceSDFAT.h:98
virtual Stream * selectStream(const char *path) override
Returns audio stream by path.
Definition: LegacyAudioSourceSDFAT.h:107
virtual Stream * nextStream(int offset) override
Returns next audio stream.
Definition: LegacyAudioSourceSDFAT.h:93
Str which keeps the data on the heap. We grow the allocated memory only if the copy source is not fit...
Definition: Str.h:23
A simple wrapper to provide string functions on existing allocated char*. If the underlying char* is ...
Definition: StrView.h:28
virtual int lastIndexOf(const char *cont)
provides the position of the last occurrence of the indicated substring
Definition: StrView.h:297
virtual const char * c_str()
provides the string value as const char*
Definition: StrView.h:379
virtual bool matches(const char *pattern)
Definition: StrView.h:193
virtual void substring(StrView &from, int start, int end)
copies a substring into the current string
Definition: StrView.h:477
virtual bool endsWithIgnoreCase(const char *str)
checks if the string ends with the indicated substring
Definition: StrView.h:185
Definition: NoArduino.h:125
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:868