diff options
Diffstat (limited to '')
-rw-r--r-- | spa/plugins/bluez5/test-midi.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/spa/plugins/bluez5/test-midi.c b/spa/plugins/bluez5/test-midi.c new file mode 100644 index 0000000..8e517aa --- /dev/null +++ b/spa/plugins/bluez5/test-midi.c @@ -0,0 +1,299 @@ +#include <spa/utils/defs.h> + +#include "midi.h" + +#define TIME_HI(v) (0x80 | ((v >> 7) & 0x3f)) +#define TIME_LO(v) (0x80 | (v & 0x7f)) + +struct event { + uint16_t time_msec; + size_t size; + const uint8_t *data; +}; + +struct packet { + size_t size; + const uint8_t *data; +}; + +struct test_info { + const struct packet *packets; + const struct event *events; + unsigned int i; +}; + +static const struct packet midi_1_packets[] = { + { + .size = 27, + .data = (uint8_t[]) { + TIME_HI(0x1234), + /* event 1 */ + TIME_LO(0x1234), 0xa0, 0x01, 0x02, + /* event 2: running status */ + 0x03, 0x04, + /* event 3: running status with timestamp */ + TIME_LO(0x1235), 0x05, 0x06, + /* event 4 */ + TIME_LO(0x1236), 0xf8, + /* event 5: sysex */ + TIME_LO(0x1237), 0xf0, 0x0a, 0x0b, 0x0c, + /* event 6: realtime event inside sysex */ + TIME_LO(0x1238), 0xff, + /* event 5 continues */ + 0x0d, 0x0e, TIME_LO(0x1239), 0xf7, + /* event 6: sysex */ + TIME_LO(0x1240), 0xf0, 0x10, 0x11, + /* packet end in middle of sysex */ + }, + }, + { + .size = 7, + .data = (uint8_t[]) { + TIME_HI(0x1241), + /* event 6: continued from previous packet */ + 0x12, TIME_LO(0x1241), 0xf7, + /* event 7 */ + TIME_LO(0x1242), 0xf1, 0x13, + } + }, + {0} +}; + +static const struct event midi_1_events[] = { + { 0x1234, 3, (uint8_t[]) { 0xa0, 0x01, 0x02 } }, + { 0x1234, 3, (uint8_t[]) { 0xa0, 0x03, 0x04 } }, + { 0x1235, 3, (uint8_t[]) { 0xa0, 0x05, 0x06 } }, + { 0x1236, 1, (uint8_t[]) { 0xf8 } }, + /* realtime event inside sysex come before it */ + { 0x1238, 1, (uint8_t[]) { 0xff } }, + /* sysex timestamp indicates the end time; sysex contains the end marker */ + { 0x1239, 7, (uint8_t[]) { 0xf0, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0xf7 } }, + { 0x1241, 5, (uint8_t[]) { 0xf0, 0x10, 0x11, 0x12, 0xf7 } }, + { 0x1242, 2, (uint8_t[]) { 0xf1, 0x13 } }, + {0} +}; + +static const struct packet midi_1_packets_mtu14[] = { + { + .size = 11, + .data = (uint8_t[]) { + TIME_HI(0x1234), + TIME_LO(0x1234), 0xa0, 0x01, 0x02, + 0x03, 0x04, + /* output Apple-style BLE; running status only for coincident time */ + TIME_LO(0x1235), 0xa0, 0x05, 0x06, + }, + }, + { + .size = 11, + .data = (uint8_t[]) { + TIME_HI(0x1236), + TIME_LO(0x1236), 0xf8, + TIME_LO(0x1238), 0xff, + TIME_LO(0x1239), 0xf0, 0x0a, 0x0b, 0x0c, 0x0d, + }, + }, + { + .size = 11, + .data = (uint8_t[]) { + TIME_HI(0x1239), + 0x0e, TIME_LO(0x1239), 0xf7, + TIME_LO(0x1241), 0xf0, 0x10, 0x11, 0x12, TIME_LO(0x1241), 0xf7 + }, + }, + { + .size = 4, + .data = (uint8_t[]) { + TIME_HI(0x1242), + TIME_LO(0x1242), 0xf1, 0x13 + }, + }, + {0} +}; + +static const struct packet midi_2_packets[] = { + { + .size = 9, + .data = (uint8_t[]) { + TIME_HI(0x1234), + /* event 1 */ + TIME_LO(0x1234), 0xa0, 0x01, 0x02, + /* event 2: timestamp low bits rollover */ + TIME_LO(0x12b3), 0xa0, 0x03, 0x04, + }, + }, + { + .size = 5, + .data = (uint8_t[]) { + TIME_HI(0x18b3), + /* event 3: timestamp high bits jump */ + TIME_LO(0x18b3), 0xa0, 0x05, 0x06, + }, + }, + {0} +}; + +static const struct event midi_2_events[] = { + { 0x1234, 3, (uint8_t[]) { 0xa0, 0x01, 0x02 } }, + { 0x12b3, 3, (uint8_t[]) { 0xa0, 0x03, 0x04 } }, + { 0x18b3, 3, (uint8_t[]) { 0xa0, 0x05, 0x06 } }, + {0} +}; + +static const struct packet midi_2_packets_mtu11[] = { + /* Small MTU: only room for one event per packet */ + { + .size = 5, + .data = (uint8_t[]) { + TIME_HI(0x1234), TIME_LO(0x1234), 0xa0, 0x01, 0x02, + }, + }, + { + .size = 5, + .data = (uint8_t[]) { + TIME_HI(0x12b3), TIME_LO(0x12b3), 0xa0, 0x03, 0x04, + }, + }, + { + .size = 5, + .data = (uint8_t[]) { + TIME_HI(0x18b3), TIME_LO(0x18b3), 0xa0, 0x05, 0x06, + }, + }, + {0} +}; + + +static void check_event(void *user_data, uint16_t time, uint8_t *event, size_t event_size) +{ + struct test_info *info = user_data; + const struct event *ev = &info->events[info->i]; + + spa_assert_se(ev->size > 0); + spa_assert_se(ev->time_msec == time); + spa_assert_se(ev->size == event_size); + spa_assert_se(memcmp(event, ev->data, ev->size) == 0); + + ++info->i; +} + +static void check_parser(struct test_info *info) +{ + struct spa_bt_midi_parser parser; + int res; + int i; + + info->i = 0; + + spa_bt_midi_parser_init(&parser); + for (i = 0; info->packets[i].size > 0; ++i) { + res = spa_bt_midi_parser_parse(&parser, + info->packets[i].data, info->packets[i].size, + false, check_event, info); + spa_assert_se(res == 0); + } + spa_assert_se(info->events[info->i].size == 0); +} + +static void check_writer(struct test_info *info, unsigned int mtu) +{ + struct spa_bt_midi_writer writer; + struct spa_bt_midi_parser parser; + unsigned int i, packet; + void SPA_UNUSED *buf = writer.buf; + + spa_bt_midi_parser_init(&parser); + spa_bt_midi_writer_init(&writer, mtu); + + packet = 0; + info->i = 0; + + for (i = 0; info->events[i].size > 0; ++i) { + const struct event *ev = &info->events[i]; + bool last = (info->events[i+1].size == 0); + int res; + + do { + res = spa_bt_midi_writer_write(&writer, + ev->time_msec * SPA_NSEC_PER_MSEC, ev->data, ev->size); + spa_assert_se(res >= 0); + if (res || last) { + int r; + + spa_assert_se(info->packets[packet].size > 0); + spa_assert_se(writer.size == info->packets[packet].size); + spa_assert_se(memcmp(writer.buf, info->packets[packet].data, writer.size) == 0); + ++packet; + + /* Test roundtrip */ + r = spa_bt_midi_parser_parse(&parser, writer.buf, writer.size, + false, check_event, info); + spa_assert_se(r == 0); + } + } while (res); + } + + spa_assert_se(info->packets[packet].size == 0); + spa_assert_se(info->events[info->i].size == 0); +} + +static void test_midi_parser_1(void) +{ + struct test_info info = { + .packets = midi_1_packets, + .events = midi_1_events, + }; + + check_parser(&info); +} + +static void test_midi_parser_2(void) +{ + struct test_info info = { + .packets = midi_2_packets, + .events = midi_2_events, + }; + + check_parser(&info); +} + +static void test_midi_writer_1(void) +{ + struct test_info info = { + .packets = midi_1_packets_mtu14, + .events = midi_1_events, + }; + + check_writer(&info, 14); +} + +static void test_midi_writer_2(void) +{ + struct test_info info = { + .packets = midi_2_packets, + .events = midi_2_events, + }; + + check_writer(&info, 23); + check_writer(&info, 12); +} + +static void test_midi_writer_3(void) +{ + struct test_info info = { + .packets = midi_2_packets_mtu11, + .events = midi_2_events, + }; + + check_writer(&info, 11); +} + +int main(void) +{ + test_midi_parser_1(); + test_midi_parser_2(); + test_midi_writer_1(); + test_midi_writer_2(); + test_midi_writer_3(); + return 0; +} |