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  LOG_HELIX(LogLevelHelix::Info, "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 
96  uint64_t timeOfLastWrite() { return time_last_write; }
97 
99  uint64_t timeOfLastResult() { return time_last_result; }
100 
102  void flush() {
103  int rc = 1;
104  while (rc >= 0) {
105  if (!presync()) break;
106  rc = decode();
107  if (!resynch(rc)) break;
108  // remove processed data
109  frame_buffer.clearArray(rc);
110  }
111  }
112 
115  virtual size_t maxFrameSize() = 0;
116 
118  void setMaxFrameSize(size_t len) { max_frame_size = len; }
119 
122  virtual size_t maxPCMSize() = 0;
123 
125  void setMaxPCMSize(size_t len) { max_pcm_size = len; }
126 
128  void setReference(void* ref){
129  p_caller_ref = ref;
130  }
131 
132  protected:
133  bool active = false;
134  bool is_raw = false;
135  Vector<uint8_t> pcm_buffer{0};
136  SingleBuffer<uint8_t> frame_buffer{0};
137  size_t max_frame_size = 0;
138  size_t max_pcm_size = 0;
139  size_t frame_counter = 0;
140  int delay_ms = -1;
141  int parse_0_count = 0; // keep track of parser returning 0
142  int min_frame_buffer_size = 0;
143  uint64_t time_last_write = 0;
144  uint64_t time_last_result = 0;
145  void *p_caller_ref = nullptr;
146 
147 
148 #if defined(ARDUINO) || defined(HELIX_PRINT)
149  Print *out = nullptr;
150 #endif
151 
153  bool presync() {
154  LOG_HELIX(LogLevelHelix::Debug, "presynch");
155  bool rc = true;
156  int pos = findSynchWord();
157  if (pos > 3) rc = removeInvalidData(pos);
158  return rc;
159  }
160 
163  bool resynch(int rc) {
164  LOG_HELIX(LogLevelHelix::Debug, "resynch: %d" , rc);
165  // reset 0 result counter
166  if (rc != 0) parse_0_count = 0;
167  if (rc <= 0) {
168  if (rc == 0) {
169  parse_0_count++;
170  int pos = findSynchWord(SYNCH_WORD_LEN);
171  LOG_HELIX(LogLevelHelix::Debug, "rc: %d - available %d - pos %d", rc,
172  frame_buffer.available(), pos);
173  // if we are stuck, request more data and if this does not help we
174  // remove the invalid data
175  if (parse_0_count > 2) {
176  return removeInvalidData(pos);
177  }
178  return false;
179  } else if (rc == -1) {
180  // underflow
181  LOG_HELIX(LogLevelHelix::Debug, "rc: %d - available %d", rc,
182  frame_buffer.available());
183  return false;
184  } else {
185  // generic error handling: remove the data until the next synch word
186  int pos = findSynchWord(SYNCH_WORD_LEN + 1);
187  removeInvalidData(pos);
188  }
189  }
190  return true;
191  }
192 
195  bool removeInvalidData(int pos) {
196  LOG_HELIX(LogLevelHelix::Debug, "removeInvalidData: %d", pos);
197  if (pos > 0) {
198  LOG_HELIX(LogLevelHelix::Info, "removing: %d bytes", pos);
199  frame_buffer.clearArray(pos);
200  return true;
201  } else if (pos <= 0) {
202  frame_buffer.reset();
203  return false;
204  }
205  return true;
206  }
207 
209  virtual size_t writeChunk(const void *in_ptr, size_t in_size) {
210  LOG_HELIX(LogLevelHelix::Info, "writeChunk %zu", in_size);
211  time_last_write = millis();
212  size_t result = frame_buffer.writeArray((uint8_t *)in_ptr, in_size);
213 
214  while (frame_buffer.available() >= minFrameBufferSize()) {
215 
216  if (!presync()) break;
217  int rc = decode();
218  if (!resynch(rc)) break;
219  // remove processed data
220  frame_buffer.clearArray(rc);
221 
222  LOG_HELIX(LogLevelHelix::Info, "rc: %d - available %d", rc,
223  frame_buffer.available());
224 
225  }
226 
227  return result;
228  }
229 
231  virtual int decode() = 0;
232 
234  virtual bool allocateDecoder() = 0;
235 
238  virtual int findSynchWord(int offset = 0) = 0;
239 
241  virtual int minFrameBufferSize() { return min_frame_buffer_size; }
243  virtual void setMinFrameBufferSize(int size) { min_frame_buffer_size = size; }
244 };
245 
246 } // namespace libhelix
Common Simple Arduino API.
Definition: CommonHelix.h:33
bool removeInvalidData(int pos)
Definition: CommonHelix.h:195
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:209
void setMaxFrameSize(size_t len)
Define your optimized maximum frame size in bytes.
Definition: CommonHelix.h:118
void flush()
Decode all open packets.
Definition: CommonHelix.h:102
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:128
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:243
uint64_t timeOfLastWrite()
Provides the timestamp in ms of last write.
Definition: CommonHelix.h:96
uint64_t timeOfLastResult()
Provides the timestamp in ms of last decoded result.
Definition: CommonHelix.h:99
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:125
virtual int minFrameBufferSize()
Provides the actual minimum frame buffer size.
Definition: CommonHelix.h:241
bool resynch(int rc)
Definition: CommonHelix.h:163
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:153