Arduino LibHelix

CommonHelix.h
1 #pragma once
2 #if defined(ARDUINO)
3 # include "Arduino.h"
4 #else
5 // remove delay statment if used outside of arduino
6 #include <stdint.h>
7 # define delay(ms)
8 #endif
9
10 // Not all processors support assert
11 #ifndef assert
12 # ifdef NDEBUG
13 # define assert(condition) ((void)0)
14 # else
15 # define assert(condition) /*implementation defined*/
16 # endif
17 #endif
18
19 #include "ConfigHelix.h"
20 #include "utils/Allocator.h"
21 #include "utils/Buffers.h"
22 #include "utils/Vector.h"
23 #include "utils/helix_log.h"
24
25 namespace libhelix {
26
33 class CommonHelix {
34 public:
35 #if defined(ARDUINO) || defined(HELIX_PRINT)
36 void setOutput(Print &output) { this->out = &output; }
37 #endif
38
43 virtual bool begin() {
44 frame_buffer.reset();
45 frame_counter = 0;
46
47 if (active) {
48 end();
49 }
50
51 if (!allocateDecoder()) {
52 return false;
53 }
54 frame_buffer.resize(maxFrameSize());
55 pcm_buffer.resize(maxPCMSize());
56 memset(pcm_buffer.data(), 0, maxPCMSize());
57 memset(frame_buffer.data(), 0, maxFrameSize());
58 active = true;
59 return true;
60 }
61
63 virtual void end() {
64 frame_buffer.resize(0);
65 pcm_buffer.resize(0);
66
67 active = false;
68 }
69
76 virtual size_t write(const void *in_ptr, size_t in_size) {
77 LOGI_HELIX( "write %zu", in_size);
78 int open = in_size;
79 size_t processed = 0;
80 uint8_t *data = (uint8_t *)in_ptr;
81 while (open > 0) {
82 int bytes = writeChunk(data, MIN(open, HELIX_CHUNK_SIZE));
83 // if we did not advance we leave the loop
84 if (bytes == 0) break;
85 open -= bytes;
86 data += bytes;
87 processed += bytes;
88 }
89 return processed;
90 }
91
93 operator bool() { return active; }
94
95 #ifdef ARDUINO
97 uint64_t timeOfLastWrite() { return time_last_write; }
98
100 uint64_t timeOfLastResult() { return time_last_result; }
101 #endif
102
104 void flush() {
105 int rc = 1;
106 while (rc >= 0) {
107 // we must start with sych word
108 if (!presync()) break;
109 // we must end with synch world
110 if (findSynchWord(3) < 0) break;
111 rc = decode();
112 if (!resynch(rc)) break;
113 // remove processed data
114 if (rc > 0) frame_buffer.clearArray(rc);
115 }
116 }
117
120 virtual size_t maxFrameSize() = 0;
121
123 void setMaxFrameSize(size_t len) { max_frame_size = len; }
124
127 virtual size_t maxPCMSize() = 0;
128
130 void setMaxPCMSize(size_t len) { max_pcm_size = len; }
131
133 void setReference(void* ref){
134 p_caller_ref = ref;
135 }
136
137 protected:
138 bool active = false;
139 bool is_raw = false;
140 Vector<uint8_t> pcm_buffer{0};
141 SingleBuffer<uint8_t> frame_buffer{0};
142 size_t max_frame_size = 0;
143 size_t max_pcm_size = 0;
144 size_t frame_counter = 0;
145 int delay_ms = -1;
146 int parse_0_count = 0; // keep track of parser returning 0
147 int min_frame_buffer_size = 0;
148 uint64_t time_last_write = 0;
149 uint64_t time_last_result = 0;
150 void *p_caller_ref = nullptr;
151
152
153 #if defined(ARDUINO) || defined(HELIX_PRINT)
154 Print *out = nullptr;
155 #endif
156
158 bool presync() {
159 LOGD_HELIX( "presynch");
160 bool rc = true;
161 int pos = findSynchWord();
162 if (pos > 3) rc = removeInvalidData(pos);
163
164 return rc;
165 }
166
169 bool resynch(int rc) {
170 LOGD_HELIX( "resynch: %d" , rc);
171 // reset 0 result counter
172 if (rc != 0) parse_0_count = 0;
173 if (rc <= 0) {
174 if (rc == 0) {
175 parse_0_count++;
176 int pos = findSynchWord(SYNCH_WORD_LEN);
177 LOGD_HELIX( "rc: %d - available %d - pos %d", rc,
178 frame_buffer.available(), pos);
179 // if we are stuck, request more data and if this does not help we
180 // remove the invalid data
181 if (parse_0_count > 2) {
182 return removeInvalidData(pos);
183 }
184 return false;
185 } else if (rc == -1) {
186 // underflow
187 LOGD_HELIX( "rc: %d - available %d", rc,
188 frame_buffer.available());
189 return false;
190 } else {
191 // generic error handling: remove the data until the next synch word
192 int pos = findSynchWord(SYNCH_WORD_LEN + 1);
193 removeInvalidData(pos);
194 }
195 }
196 return true;
197 }
198
201 bool removeInvalidData(int pos) {
202 LOGD_HELIX( "removeInvalidData: %d", pos);
203 if (pos > 0) {
204 LOGI_HELIX( "removing: %d bytes", pos);
205 frame_buffer.clearArray(pos);
206 return true;
207 } else if (pos <= 0) {
208 frame_buffer.reset();
209 return false;
210 }
211 return true;
212 }
213
215 virtual size_t writeChunk(const void *in_ptr, size_t in_size) {
216 LOGI_HELIX( "writeChunk %zu", in_size);
217 #ifdef ARDUINO
218 time_last_write = millis();
219 #endif
220 size_t result = frame_buffer.writeArray((uint8_t *)in_ptr, in_size);
221
222 while (frame_buffer.available() >= minFrameBufferSize()) {
223
224 if (!presync()) break;
225 int rc = decode();
226 if (!resynch(rc)) break;
227 // remove processed data
228 frame_buffer.clearArray(rc);
229
230 LOGI_HELIX( "rc: %d - available %d", rc,
231 frame_buffer.available());
232
233 }
234
235 return result;
236 }
237
239 virtual int decode() = 0;
240
242 virtual bool allocateDecoder() = 0;
243
246 virtual int findSynchWord(int offset = 0) = 0;
247
249 virtual int minFrameBufferSize() { return min_frame_buffer_size; }
251 virtual void setMinFrameBufferSize(int size) { min_frame_buffer_size = size; }
252 };
253
254 } // namespace libhelix
libhelix::CommonHelix
Common Simple Arduino API.
Definition: CommonHelix.h:33
libhelix::CommonHelix::removeInvalidData
bool removeInvalidData(int pos)
Definition: CommonHelix.h:201
libhelix::CommonHelix::maxPCMSize
virtual size_t maxPCMSize()=0
libhelix::CommonHelix::writeChunk
virtual size_t writeChunk(const void *in_ptr, size_t in_size)
Decoding Loop: We decode the procided data until we run out of data.
Definition: CommonHelix.h:215
libhelix::CommonHelix::setMaxFrameSize
void setMaxFrameSize(size_t len)
Define your optimized maximum frame size in bytes.
Definition: CommonHelix.h:123
libhelix::CommonHelix::flush
void flush()
Decode all open packets.
Definition: CommonHelix.h:104
libhelix::CommonHelix::findSynchWord
virtual int findSynchWord(int offset=0)=0
libhelix::CommonHelix::setReference
void setReference(void *ref)
Define some additional information which will be provided back in the callbacks.
Definition: CommonHelix.h:133
libhelix::CommonHelix::maxFrameSize
virtual size_t maxFrameSize()=0
libhelix::CommonHelix::end
virtual void end()
Releases the reserved memory.
Definition: CommonHelix.h:63
libhelix::CommonHelix::write
virtual size_t write(const void *in_ptr, size_t in_size)
decodes the next segments from the input. The data can be provided in one big or in small incremental...
Definition: CommonHelix.h:76
libhelix::CommonHelix::setMinFrameBufferSize
virtual void setMinFrameBufferSize(int size)
Defines the minimum frame buffer size which is required before starting the decoding.
Definition: CommonHelix.h:251
libhelix::CommonHelix::timeOfLastWrite
uint64_t timeOfLastWrite()
Provides the timestamp in ms of last write.
Definition: CommonHelix.h:97
libhelix::CommonHelix::timeOfLastResult
uint64_t timeOfLastResult()
Provides the timestamp in ms of last decoded result.
Definition: CommonHelix.h:100
libhelix::CommonHelix::begin
virtual bool begin()
Starts the processing.
Definition: CommonHelix.h:43
libhelix::CommonHelix::allocateDecoder
virtual bool allocateDecoder()=0
Allocate the decoder.
libhelix::CommonHelix::setMaxPCMSize
void setMaxPCMSize(size_t len)
Define your optimized maximum pcm buffer size in bytes.
Definition: CommonHelix.h:130
libhelix::CommonHelix::minFrameBufferSize
virtual int minFrameBufferSize()
Provides the actual minimum frame buffer size.
Definition: CommonHelix.h:249
libhelix::CommonHelix::resynch
bool resynch(int rc)
Definition: CommonHelix.h:169
libhelix::CommonHelix::decode
virtual int decode()=0
Decode w/o parsing.
libhelix::CommonHelix::presync
bool presync()
make sure that we start with a valid sync: remove ID3 data
Definition: CommonHelix.h:158