mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	bluez5: Add BLE MIDI parser/writer tests
This commit is contained in:
		
							parent
							
								
									5d6f25e8f0
								
							
						
					
					
						commit
						fe3ca50818
					
				
					 2 changed files with 334 additions and 0 deletions
				
			
		| 
						 | 
					@ -150,3 +150,38 @@ if get_option('bluez5-codec-lc3').allowed() and lc3_dep.found()
 | 
				
			||||||
    install : true,
 | 
					    install : true,
 | 
				
			||||||
    install_dir : spa_plugindir / 'bluez5')
 | 
					    install_dir : spa_plugindir / 'bluez5')
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test_apps = [
 | 
				
			||||||
 | 
					  'test-midi',
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					bluez5_test_lib = static_library('bluez5_test_lib',
 | 
				
			||||||
 | 
					  [ 'midi-parser.c' ],
 | 
				
			||||||
 | 
					  include_directories : [ configinc ],
 | 
				
			||||||
 | 
					  dependencies : [ spa_dep ],
 | 
				
			||||||
 | 
					  install : false
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					foreach a : test_apps
 | 
				
			||||||
 | 
					  test(a,
 | 
				
			||||||
 | 
					    executable(a, a + '.c',
 | 
				
			||||||
 | 
					      dependencies : [ spa_dep, dl_lib, pthread_lib, mathlib, bluez5_deps ],
 | 
				
			||||||
 | 
					      include_directories : [ configinc ],
 | 
				
			||||||
 | 
					      link_with : [ bluez5_test_lib ],
 | 
				
			||||||
 | 
					      install_rpath : spa_plugindir / 'bluez5',
 | 
				
			||||||
 | 
					      install : installed_tests_enabled,
 | 
				
			||||||
 | 
					      install_dir : installed_tests_execdir / 'bluez5'),
 | 
				
			||||||
 | 
					      env : [
 | 
				
			||||||
 | 
					        'SPA_PLUGIN_DIR=@0@'.format(spa_dep.get_variable('plugindir')),
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if installed_tests_enabled
 | 
				
			||||||
 | 
					      test_conf = configuration_data()
 | 
				
			||||||
 | 
					      test_conf.set('exec', installed_tests_execdir / 'bluez5' / a)
 | 
				
			||||||
 | 
					      configure_file(
 | 
				
			||||||
 | 
					        input: installed_tests_template,
 | 
				
			||||||
 | 
					        output: a + '.test',
 | 
				
			||||||
 | 
					        install_dir: installed_tests_metadir / 'bluez5',
 | 
				
			||||||
 | 
					        configuration: test_conf
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					  endif
 | 
				
			||||||
 | 
					endforeach
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										299
									
								
								spa/plugins/bluez5/test-midi.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										299
									
								
								spa/plugins/bluez5/test-midi.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct test_info info = {
 | 
				
			||||||
 | 
							.packets = midi_1_packets,
 | 
				
			||||||
 | 
							.events = midi_1_events,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						check_parser(&info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void test_midi_parser_2()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct test_info info = {
 | 
				
			||||||
 | 
							.packets = midi_2_packets,
 | 
				
			||||||
 | 
							.events = midi_2_events,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						check_parser(&info);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void test_midi_writer_1()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct test_info info = {
 | 
				
			||||||
 | 
							.packets = midi_1_packets_mtu14,
 | 
				
			||||||
 | 
							.events = midi_1_events,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						check_writer(&info, 14);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void test_midi_writer_2()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct test_info info = {
 | 
				
			||||||
 | 
							.packets = midi_2_packets_mtu11,
 | 
				
			||||||
 | 
							.events = midi_2_events,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						check_writer(&info, 11);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						test_midi_parser_1();
 | 
				
			||||||
 | 
						test_midi_parser_2();
 | 
				
			||||||
 | 
						test_midi_writer_1();
 | 
				
			||||||
 | 
						test_midi_writer_2();
 | 
				
			||||||
 | 
						test_midi_writer_3();
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue