summaryrefslogtreecommitdiffstats
path: root/spa/plugins/bluez5/test-midi.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--spa/plugins/bluez5/test-midi.c299
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;
+}