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
Common Simple Arduino API.
Definition: CommonHelix.h:33
bool removeInvalidData(int pos)
Definition: CommonHelix.h:201
virtual size_t maxPCMSize()=0
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
void setMaxFrameSize(size_t len)
Define your optimized maximum frame size in bytes.
Definition: CommonHelix.h:123
void flush()
Decode all open packets.
Definition: CommonHelix.h:104
virtual int findSynchWord(int offset=0)=0
void setReference(void *ref)
Define some additional information which will be provided back in the callbacks.
Definition: CommonHelix.h:133
virtual size_t maxFrameSize()=0
virtual void end()
Releases the reserved memory.
Definition: CommonHelix.h:63
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
virtual void setMinFrameBufferSize(int size)
Defines the minimum frame buffer size which is required before starting the decoding.
Definition: CommonHelix.h:251
uint64_t timeOfLastWrite()
Provides the timestamp in ms of last write.
Definition: CommonHelix.h:97
uint64_t timeOfLastResult()
Provides the timestamp in ms of last decoded result.
Definition: CommonHelix.h:100
virtual bool begin()
Starts the processing.
Definition: CommonHelix.h:43
virtual bool allocateDecoder()=0
Allocate the decoder.
void setMaxPCMSize(size_t len)
Define your optimized maximum pcm buffer size in bytes.
Definition: CommonHelix.h:130
virtual int minFrameBufferSize()
Provides the actual minimum frame buffer size.
Definition: CommonHelix.h:249
bool resynch(int rc)
Definition: CommonHelix.h:169
virtual int decode()=0
Decode w/o parsing.
bool presync()
make sure that we start with a valid sync: remove ID3 data
Definition: CommonHelix.h:158