commit f4b0b12ac09fa50bca1b45211346006aa4e59e61
parent a3246e830341053d9750e25ff97a64b5584f44c2
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date: Wed, 12 Nov 2014 23:20:50 +0100
Update the midi parser
Diffstat:
6 files changed, 186 insertions(+), 16 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -7,3 +7,4 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
include_directories(include)
add_subdirectory(tools)
add_subdirectory(examples)
+add_subdirectory(tests)
diff --git a/include/clap/clap-midi-parser.c b/include/clap/clap-midi-parser.c
@@ -1,3 +1,5 @@
+#include <assert.h>
+
static inline uint16_t
clap_midi_parse_be16(const uint8_t *in)
{
@@ -10,13 +12,28 @@ clap_midi_parse_be32(const uint8_t *in)
return (in[0] << 24) | (in[1] << 16) | (in[2] << 8) | in[3];
}
+static inline uint32_t
+clap_midi_parse_variable_length(struct clap_midi_parser *parser, uint32_t *offset)
+{
+ uint32_t value = 0;
+ uint32_t i = *offset;
+
+ for (; i < parser->size; ++i) {
+ value = (value << 7) | (parser->in[i] & 0x7f);
+ if (!(parser->in[i] & 0x8f))
+ break;
+ }
+ *offset = i + 1;
+ return value;
+}
+
static inline enum clap_midi_parser_status
clap_midi_parse_header(struct clap_midi_parser *parser)
{
- if (paser->size < 14)
+ if (parser->size < 14)
return CLAP_MIDI_PARSER_EOB;
- if (memcmp(parser->in, "MThd"))
+ if (memcmp(parser->in, "MThd", 4))
return CLAP_MIDI_PARSER_ERROR;
parser->header.size = clap_midi_parse_be32(parser->in + 4);
@@ -33,14 +50,45 @@ clap_midi_parse_header(struct clap_midi_parser *parser)
static inline enum clap_midi_parser_status
clap_midi_parse_track(struct clap_midi_parser *parser)
{
- if (paser->size < 8)
+ if (parser->size < 8)
return CLAP_MIDI_PARSER_EOB;
- parser->track.size = clap_midi_parse_be32(parser->in + 4);
+ parser->track.size = clap_midi_parse_be32(parser->in + 4);
+ parser->state = CLAP_MIDI_PARSER_TRACK;
+ parser->in += 8;
+ parser->size -= 8;
+ return CLAP_MIDI_PARSER_TRACK;
+}
- parser->in += 8;
- parser->size -= 8;
- return CLAP_MIDI_PARSER_HEADER;
+static inline enum clap_midi_parser_status
+clap_midi_parse_meta_event(struct clap_midi_parser *parser)
+{
+ assert(parser->in[0] == 0xff);
+
+ if (parser->size < 2)
+ return CLAP_MIDI_PARSER_EOB;
+
+ parser->meta.type = parser->in[1];
+ uint32_t offset = 2;
+ parser->meta.length = clap_midi_parse_variable_length(parser, &offset);
+
+ // check buffer size
+ if (parser->size < offset + parser->meta.length)
+ return CLAP_MIDI_PARSER_EOB;
+
+ offset += parser->meta.length;
+ parser->in += offset;
+ parser->size -= offset;
+ parser->track.size -= offset;
+ return CLAP_MIDI_PARSER_META;
+}
+
+static inline enum clap_midi_parser_status
+clap_midi_parse_event(struct clap_midi_parser *parser)
+{
+ if (parser->in[0] == 0xff)
+ return clap_midi_parse_meta_event(parser);
+ return CLAP_MIDI_PARSER_ERROR;
}
static inline enum clap_midi_parser_status
@@ -57,6 +105,11 @@ clap_midi_parse(struct clap_midi_parser *parser)
return clap_midi_parse_track(parser);
case CLAP_MIDI_PARSER_TRACK:
+ if (parser->track.size == 0) {
+ // we reached the end of the track
+ parser->state = CLAP_MIDI_PARSER_HEADER;
+ return clap_midi_parse(parser);
+ }
return clap_midi_parse_event(parser);
default:
diff --git a/include/clap/clap-midi-parser.h b/include/clap/clap-midi-parser.h
@@ -1,3 +1,9 @@
+/*
+ * Simple MIDI parser implementation.
+ * I used the following reference:
+ * http://www.sonicspot.com/guide/midifiles.html
+ */
+
#ifndef CLAP_MIDI_PARSER_H
# define CLAP_MIDI_PARSER_H
@@ -52,7 +58,7 @@ struct clap_midi_channel_event
struct clap_midi_meta_event
{
uint8_t type;
- uint32_t lenght;
+ uint32_t length;
const uint8_t *bytes; // reference to the input buffer
};
@@ -60,7 +66,7 @@ struct clap_midi_sysex_event
{
uint8_t sysex;
uint8_t type;
- uint32_t lenght;
+ uint32_t length;
const uint8_t *bytes; // reference to the input buffer
};
@@ -72,13 +78,11 @@ struct clap_midi_parser
const uint8_t *in;
uint32_t size;
- union {
- struct clap_midi_header header;
- struct clap_midi_track track;
- struct clap_midi_channel_event channel;
- struct clap_midi_meta_event meta;
- struct clap_midi_sysex_event sysex;
- };
+ struct clap_midi_header header;
+ struct clap_midi_track track;
+ struct clap_midi_channel_event channel;
+ struct clap_midi_meta_event meta;
+ struct clap_midi_sysex_event sysex;
};
static inline enum clap_midi_parser_status
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(midi-parser)
+\ No newline at end of file
diff --git a/tests/midi-parser/CMakeLists.txt b/tests/midi-parser/CMakeLists.txt
@@ -0,0 +1 @@
+add_executable(midi-parser midi-parser.c)
+\ No newline at end of file
diff --git a/tests/midi-parser/midi-parser.c b/tests/midi-parser/midi-parser.c
@@ -0,0 +1,108 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <clap/clap-midi-parser.h>
+
+void usage(const char *prog)
+{
+ printf("usage: %s <file.midi>\n", prog);
+}
+
+void parse_and_dump(struct clap_midi_parser *parser)
+{
+ enum clap_midi_channel_event_type status;
+
+ while (1) {
+ status = clap_midi_parse(parser);
+ switch (status) {
+ case CLAP_MIDI_PARSER_EOB:
+ puts("eob");
+ return;
+
+ case CLAP_MIDI_PARSER_ERROR:
+ puts("error");
+ return;
+
+ case CLAP_MIDI_PARSER_INIT:
+ puts("init");
+ break;
+
+ case CLAP_MIDI_PARSER_HEADER:
+ printf("header\n");
+ printf(" size: %d\n", parser->header.size);
+ printf(" format: %d\n", parser->header.format);
+ printf(" tracks count: %d\n", parser->header.tracks_count);
+ printf(" time division: %d\n", parser->header.time_division);
+ break;
+
+ case CLAP_MIDI_PARSER_TRACK:
+ puts("track");
+ break;
+
+ case CLAP_MIDI_PARSER_CHANNEL:
+ puts("channel");
+ break;
+
+ case CLAP_MIDI_PARSER_META:
+ printf("meta\n");
+ printf(" type: %d\n", parser->meta.type);
+ printf(" length: %d\n", parser->meta.length);
+ break;
+
+ case CLAP_MIDI_PARSER_SYSEX:
+ puts("sysex");
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+int parse_file(const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st)) {
+ printf("stat(%s): %m\n", path);
+ return 1;
+ }
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ printf("open(%s): %m\n", path);
+ return 1;
+ }
+
+ void *mem = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED) {
+ printf("mmap(%s): %m\n", path);
+ close(fd);
+ return 1;
+ }
+
+ struct clap_midi_parser parser;
+ parser.state = CLAP_MIDI_PARSER_INIT;
+ parser.size = st.st_size;
+ parser.in = mem;
+
+ parse_and_dump(&parser);
+
+ munmap(mem, st.st_size);
+ close(fd);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ return parse_file(argv[1]);
+}