Skip to content

Commit a637307

Browse files
committed
FileLoop
1 parent 2276cf4 commit a637307

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @file streams-sd-audiokit.ino
3+
* @author Phil Schatzmann
4+
* @brief Just a small demo, how to use files with the SD library
5+
* @version 0.1
6+
* @date 2022-10-09
7+
*
8+
* @copyright Copyright (c) 2022
9+
*
10+
*/
11+
#include <SPI.h>
12+
#include <SD.h>
13+
#include "AudioTools.h"
14+
#include "AudioLibs/AudioKit.h"
15+
#include "AudioLibs/FileLoop.h"
16+
#include "AudioCodecs/CodecMP3Helix.h"
17+
18+
19+
const int chipSelect=PIN_AUDIO_KIT_SD_CARD_CS;
20+
AudioKitStream i2s; // final output of decoded stream
21+
EncodedAudioStream decoder(&i2s, new MP3DecoderHelix()); // Decoding stream
22+
FileLoop loopingFile;
23+
StreamCopy copier(decoder, audioFile);
24+
25+
void setup(){
26+
Serial.begin(115200);
27+
AudioLogger::instance().begin(Serial, AudioLogger::Info);
28+
29+
// setup audiokit before SD!
30+
auto config = i2s.defaultConfig(TX_MODE);
31+
config.sd_active = true;
32+
i2s.begin(config);
33+
34+
// setup file
35+
SD.begin(chipSelect);
36+
loopingFile.setFile(SD.open("/ZZ Top/Unknown Album/Lowrider.mp3"));
37+
//loopingFile.setLoopCount(-1); // define loop count
38+
//audioFile.setStartPos(44); // restart from pos 44
39+
//if ((audioFile.size()-44) % 1024!=0) audioFile.setSize((((audioFile.size()-44)/1024)+1)*1024+44);
40+
41+
// setup I2S based on sampling rate provided by decoder
42+
decoder.begin();
43+
}
44+
45+
void loop(){
46+
copier.copy();
47+
}

src/AudioLibs/FileLoop.h

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#pragma once
2+
3+
#include "AudioTools/AudioStreams.h"
4+
#include "FS.h"
5+
6+
namespace audio_tools {
7+
8+
/**
9+
* @brief A simple class which implements a automatic looping file.
10+
* In order to support different file implementation the file class
11+
* is a template parameter. The number of loops can be defined by
12+
* calling setLoopCount().
13+
* You can also optinally limit the total looping file size by calling
14+
* setSize();
15+
* @ingroup io
16+
* @author Phil Schatzmann
17+
* @copyright GPLv3
18+
*/
19+
template <class FileType> class FileLoopT : public AudioStream {
20+
public:
21+
FileLoopT() = default;
22+
FileLoopT(FileType file, int count = -1, int rewindPos = 0) {
23+
setFile(file);
24+
setLoopCount(count);
25+
setStartPos(rewindPos);
26+
}
27+
28+
// restarts the file from the beginning
29+
bool begin() override {
30+
TRACEI();
31+
current_file.seek(start_pos);
32+
size_open = total_size;
33+
return current_file;
34+
}
35+
36+
// closes the file
37+
void end() override {
38+
TRACEI();
39+
current_file.close();
40+
}
41+
42+
/// defines the file that is used for looping
43+
void setFile(FileType file) { this->current_file = file; }
44+
45+
/// Returns the file
46+
FileType &file(){
47+
return current_file;
48+
}
49+
50+
/// defines the start position after the rewind. E.g. for wav files this should be 44
51+
void setStartPos(int pos) { start_pos = pos; }
52+
53+
/// defines the requested playing size in bytes
54+
void setSize(size_t len) {
55+
total_size = len;
56+
}
57+
58+
/// You can be notified about a rewind
59+
void setCallback(void (*cb)(FileLoopT &loop)){
60+
callback = cb;
61+
}
62+
63+
/// count values: 0) do not loop, 1) loop once, n) loop n times, -1) loop
64+
/// endless
65+
void setLoopCount(int count) { loop_count = count; }
66+
67+
int available() override {
68+
// if we manage the size, we return the open amount
69+
if (total_size!=-1) return size_open;
70+
// otherwise we return DEFAULT_BUFFER_SIZE if looping is active
71+
return isLoopActive() ? DEFAULT_BUFFER_SIZE : current_file.available();
72+
}
73+
74+
size_t readBytes(uint8_t *data, size_t len) {
75+
LOGD("FileLoopT::readBytes %d at %d", len, current_file.position());
76+
if (!current_file)
77+
return 0;
78+
79+
// limit the copy size if necessary
80+
int copy_len = len;
81+
if (total_size!=-1){
82+
copy_len = min((int)len, size_open);
83+
}
84+
85+
// read step 1;
86+
int result1 = current_file.readBytes((char *)data, copy_len);
87+
int result2 = 0;
88+
int open = copy_len - result1;
89+
if (isLoopActive() && open>0) {
90+
LOGI("seek 0");
91+
// looping logic -> rewind to beginning: read step 2
92+
current_file.seek(start_pos);
93+
// notify user
94+
if (callback!=nullptr){
95+
callback(*this);
96+
}
97+
result1 = current_file.readBytes((char *)data + result1, open);
98+
if (loop_count>0)
99+
loop_count--;
100+
}
101+
// determine the result size
102+
int result = result1 + result2;
103+
// calculate the size_open if necessary
104+
if (total_size!=-1){
105+
size_open -= result;
106+
}
107+
return result;
108+
}
109+
110+
operator bool() {
111+
return current_file;
112+
}
113+
114+
/// Returns the (requested) file size
115+
size_t size() {
116+
return total_size == -1 ? current_file.size() : total_size;
117+
}
118+
119+
/// @return true as long as we are looping
120+
bool isLoopActive() { return loop_count > 0 || loop_count == -1; }
121+
122+
protected:
123+
int start_pos = 0;
124+
int loop_count = -1;
125+
int size_open = -1;
126+
int total_size = -1;
127+
void (*callback)(FileLoopT &loop) = nullptr;
128+
FileType current_file;
129+
};
130+
131+
/**
132+
* @brief A simple class which implements a automatic looping file.
133+
* The file needs to be of the class File from FS.h. The number of loops can be
134+
* defined by calling setLoopCount().
135+
* You can also optinally limit the total looping file size by calling
136+
* setSize();
137+
* @ingroup io
138+
* @author Phil Schatzmann
139+
* @copyright GPLv3
140+
*/
141+
142+
class FileLoop : public FileLoopT<File> {
143+
public:
144+
FileLoop() = default;
145+
FileLoop(File file, int count = -1, int rewindPos = 0)
146+
: FileLoopT<File>(file, count, rewindPos) {}
147+
};
148+
149+
} // namespace audio_tools

0 commit comments

Comments
 (0)