clap

CLAP Audio Plugin API
Log | Files | Refs | README | LICENSE

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:
MCMakeLists.txt | 1+
Minclude/clap/clap-midi-parser.c | 67++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Minclude/clap/clap-midi-parser.h | 22+++++++++++++---------
Atests/CMakeLists.txt | 2++
Atests/midi-parser/CMakeLists.txt | 2++
Atests/midi-parser/midi-parser.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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]); +}