Arduino MIDI File Parser
MidiFileParserMultiTrack.h
1 #pragma once
2 #include "MidiFileParser.h"
3 #include <iterator>
4 #include <list>
5 #include <vector>
6 
7 namespace midi {
8 
10 enum ProcessingStatus { Inserting, Parsing, EndOfData };
11 
16 public:
18  void add(int track, midi_time_event event) {
19  // add new track
20  addTrack(track);
21  // add event
22  if (event.status!=0){
23  tracks[track].push_back(event);
24  }
25  }
26 
28  int trackCount() { return tracks.size(); }
29 
31  void begin() {
32  // set iterators to first element
33  process_status = EndOfData;
34  for (int track = 0; track < tracks.size(); track++) {
35  if (iterators.size() < track + 1) {
36  iterators.push_back(tracks[track].begin());
37  } else {
38  iterators[track] = tracks[track].begin();
39  }
40  // check if there is any ndata
41  if (iterators[0] != tracks[0].end()) {
42  process_status = Parsing;
43  }
44  }
45  }
46 
49  if (process_status == Inserting) {
50  return no_data;
51  }
52 
53  int min_idx = 0;
54  process_status = EndOfData;
55  // find entry with min time
56  uint64_t min_time = 0xFFFFFFFFFFFFFFFF;
57  for (int j = 0; j < trackCount(); j++) {
58  if (iterators[j] != tracks[j].end() &&
59  (*iterators[j]).time_ms < min_time) {
60  min_idx = j;
61  process_status = Parsing;
62  }
63  }
64 
65  if (process_status == EndOfData) {
66  return no_data;
67  }
68 
69  // increment iterator and return result
70  midi_time_event *p_result = &(*iterators[min_idx]);
71  iterators[min_idx]++;
72  return *p_result;
73  }
74 
76  size_t size() {
77  size_t result = 0;
78  for (int j = 0; j < trackCount(); j++) {
79  result += tracks[j].size();
80  }
81  return result;
82  }
83 
85  bool isEmpty() { return size() == 0; }
86 
88  uint64_t lastTime(int track) {
89  addTrack(track);
90  auto &events = tracks[track];
91  if (events.empty())
92  return 0;
93  return events.back().time_ms;
94  }
95 
97  void end() {
98  tracks.clear();
99  iterators.clear();
100  }
101 
103  ProcessingStatus status() { return process_status; }
104 
106  midi_time_event &getNoDataEvent() { return no_data; }
107 
108 protected:
109  std::vector<std::list<midi_time_event>> tracks;
110  std::vector<std::list<midi_time_event>::iterator> iterators;
111  midi_time_event no_data;
112  ProcessingStatus process_status = Inserting;
113 
114  void addTrack(int track) {
115  if (track >= tracks.size()) {
116  std::list<midi_time_event> event_list;
117  tracks.push_back(event_list);
118  }
119  }
120 };
121 
131 public:
133  bool begin(bool log = true, int bufferSize = MIDI_BUFFER_SIZE) {
134  restart();
135  return parser.begin(log, bufferSize);
136  }
137 
139  void restart() {
140  is_iterators_available = false;
141  start_time = millis();
142  p_current_state = nullptr;
143  }
144 
146  size_t write(uint8_t c) override { return write(&c, 1); }
147 
149  virtual size_t write(const uint8_t *data, size_t len) {
150  int result = parser.write(data, len);
151  // Parse and process the next event
152  parse(20);
153  return result;
154  }
155 
157  int availableForWrite() { return parser.availableForWrite(); }
158 
160  int trackCount() { return data.trackCount(); }
161 
164  // we wait for the parser to complete the processing
165  midi_time_event &event = parseTimeEvent();
166  if (event == data.getNoDataEvent()) {
167  switch (data.status()) {
168  case Inserting:
169  return not_ready;
170  case EndOfData:
171  return eob;
172  default:
173  return not_ready;
174  }
175  }
176 
177  // provide next element
178  parser_state.status = MIDI_PARSER_TRACK_MIDI;
179  parser_state.vtime_ms = event.time_ms;
180  parser_state.midi.channel = event.channel;
181  parser_state.midi.status = event.status;
182  parser_state.midi.param1 = event.param1;
183  parser_state.midi.param2 = event.param2;
184  return parser_state;
185  }
186 
189 
190  if (p_current_state == nullptr) {
191  p_current_state = &parseTimeEvent();
192  }
193 
194  // check if data is relevant
195  if (p_current_state == &data.getNoDataEvent()) {
196  switch (data.status()) {
197  case Inserting:
198  p_current_state = nullptr;
199  return not_ready;
200  case EndOfData:
201  return eob;
202  default:
203  break;
204  }
205  }
206 
207  // Wait for event to become relevant
208  if ((millis() - start_time) < p_current_state->time_ms) {
209  return not_ready;
210  }
211 
212  // return result
213  parser_state.status = MIDI_PARSER_TRACK_MIDI;
214  parser_state.vtime_ms = p_current_state->time_ms;
215  parser_state.midi.channel = p_current_state->channel;
216  parser_state.midi.status = p_current_state->status;
217  parser_state.midi.param1 = p_current_state->param1;
218  parser_state.midi.param2 = p_current_state->param2;
219  p_current_state = nullptr;
220  return parser_state;
221  }
222 
224  operator bool() { return parser || data.status() == Parsing; }
225 
227  bool isEmpty() { return data.isEmpty(); }
228 
230  size_t size() { return data.size(); }
231 
233  void end() { data.end(); }
234 
237  void endWrite() { parser.setState(MIDI_PARSER_EOB); }
238 
240  const char *midi_status_name(int status) {
241  return parser.midi_status_name(status);
242  }
243 
245  const char *midi_file_format_name(int fmt) {
246  return parser.midi_file_format_name(fmt);
247  }
248 
250  const char *midi_meta_name(int type) { return parser.midi_meta_name(type); }
251 
252 protected:
253  MidiFileParser parser;
255  midi_parser_state parser_state;
256  midi_parser_state not_ready{MIDI_PARSER_DELAY};
257  midi_parser_state eob{MIDI_PARSER_EOB};
258  midi_time_event *p_current_state = nullptr;
259  uint64_t start_time = 0l;
260  bool is_iterators_available = false;
261 
262  void parse(int limit) {
263  while (parser.parser_state.in.available() > limit) {
264  midi_parser_state &state = parser.parse();
265  addEvent(state);
266  }
267  }
268 
271  int track = state.track.number;
272  // add new midi_time_event
273  midi_time_event event;
274  event.status = state.midi.status;
275  event.channel = state.midi.channel;
276  event.param1 = state.midi.param1;
277  event.param2 = state.midi.param2;
278  event.time_ms = state.timeInMs() + data.lastTime(track);
279  data.add(track, event);
280 
281  }
282 
285  // create iterators to point at beginning
286  if (!is_iterators_available) {
287  // complete parsing
288  parse(0);
289  // setup iterators
290  is_iterators_available = true;
291  data.begin();
292  }
293 
294  // provide next element
295  return data.nextEvent();
296  }
297 };
298 
299 } // namespace midi
A simple midi parser based on the following project https://github.com/abique/midi-parser.
Midi file parser which stores the data in RAM before making them available for parsing....
Definition: MidiFileParserMultiTrack.h:130
size_t size()
Returns the number of available midi events (after writing)
Definition: MidiFileParserMultiTrack.h:230
const char * midi_status_name(int status)
Provides the string description for the midi_status value.
Definition: MidiFileParserMultiTrack.h:240
bool isEmpty()
Returns true if there are no midi events.
Definition: MidiFileParserMultiTrack.h:227
size_t write(uint8_t c) override
Single character write.
Definition: MidiFileParserMultiTrack.h:146
int availableForWrite()
Max number of bytes that we can write.
Definition: MidiFileParserMultiTrack.h:157
int trackCount()
Returns the number of recorded tracks.
Definition: MidiFileParserMultiTrack.h:160
midi_parser_state & parse()
Provide the next midi element.
Definition: MidiFileParserMultiTrack.h:163
midi_time_event & parseTimeEvent()
Provide the next midi element using the iterators.
Definition: MidiFileParserMultiTrack.h:284
const char * midi_file_format_name(int fmt)
Provides the string description for the file format.
Definition: MidiFileParserMultiTrack.h:245
midi_parser_state & parseTimed()
Provide the next midi element considering the times.
Definition: MidiFileParserMultiTrack.h:188
void addEvent(midi_parser_state &state)
Adds an new midi event with the corresponding time.
Definition: MidiFileParserMultiTrack.h:270
void endWrite()
Definition: MidiFileParserMultiTrack.h:237
void end()
Ends the processing: releases all memory.
Definition: MidiFileParserMultiTrack.h:233
const char * midi_meta_name(int type)
Provides the string description for the midi_meta value.
Definition: MidiFileParserMultiTrack.h:250
virtual size_t write(const uint8_t *data, size_t len)
Feed/Provide the midi data to the parser.
Definition: MidiFileParserMultiTrack.h:149
void restart()
Call this method to restart the parsing with the available data.
Definition: MidiFileParserMultiTrack.h:139
bool begin(bool log=true, int bufferSize=MIDI_BUFFER_SIZE)
Initializes & starts the processing.
Definition: MidiFileParserMultiTrack.h:133
Midi File parser. Provide the data via write: You should try to keep the buffer as full as possible w...
Definition: MidiFileParser.h:61
midi_parser_state & parse()
Parse data in order to provide the next midi element.
Definition: MidiFileParser.h:92
int availableForWrite()
Max number of bytes that we can write.
Definition: MidiFileParser.h:89
const char * midi_meta_name(int type)
Provides the string description for the midi_meta value.
Definition: MidiFileParser.h:191
const char * midi_status_name(int status)
Provides the string description for the midi_status value.
Definition: MidiFileParser.h:153
const char * midi_file_format_name(int fmt)
Provides the string description for the file format.
Definition: MidiFileParser.h:176
bool begin(bool log=true, int bufferSize=MIDI_BUFFER_SIZE)
Initializes & starts the processing.
Definition: MidiFileParser.h:64
Definition: MidiFileParser.h:46
Definition: MidiFileParserMultiTrack.h:15
void end()
Releases all data.
Definition: MidiFileParserMultiTrack.h:97
void add(int track, midi_time_event event)
Adds an midi event to the indicated track.
Definition: MidiFileParserMultiTrack.h:18
ProcessingStatus status()
Provides the current processing status.
Definition: MidiFileParserMultiTrack.h:103
size_t size()
Count number for midi events.
Definition: MidiFileParserMultiTrack.h:76
void begin()
Moves the iterators to the first element.
Definition: MidiFileParserMultiTrack.h:31
bool isEmpty()
Returns true if there are no midi events.
Definition: MidiFileParserMultiTrack.h:85
midi_time_event & getNoDataEvent()
Provides the reference to the nodata time event which signals that we provided no data.
Definition: MidiFileParserMultiTrack.h:106
midi_time_event & nextEvent()
Provides the next event.
Definition: MidiFileParserMultiTrack.h:48
int trackCount()
Provides the number of tracks.
Definition: MidiFileParserMultiTrack.h:28
uint64_t lastTime(int track)
Provides the last recorded time for the indicated track.
Definition: MidiFileParserMultiTrack.h:88
MIDI Parser State Information.
Definition: MidiFileParserState.h:119
Definition: MidiFileParserState.h:90
uint64_t time_ms
cummulated time in milliseconds
Definition: MidiFileParserState.h:92