mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
ump: handle f0 .. f0 f7 .. f0 f7 .. f7 packets
Support the RFC 4695 sysex segmentation rules where a sysex packet can be split into multiple chunks using the f0 and f7 patterns like: begin f0 ... f0 continue f7 ... f0 end f7 ... f7 Add a unit test for the sysex UMP conversion.
This commit is contained in:
parent
e507c03ad3
commit
802972f1ae
3 changed files with 216 additions and 29 deletions
|
|
@ -106,8 +106,8 @@ static inline int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
|
||||||
uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
|
uint32_t *ump, size_t ump_maxsize, uint8_t group, uint64_t *state)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
uint32_t prefix = group << 24, to_consume = 0, bytes;
|
uint32_t i, prefix = group << 24, to_consume = 0, bytes;
|
||||||
uint8_t status, *m = (*midi);
|
uint8_t status, *m = (*midi), end;
|
||||||
|
|
||||||
if (*midi_size < 1)
|
if (*midi_size < 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -117,41 +117,54 @@ static inline int spa_ump_from_midi(uint8_t **midi, size_t *midi_size,
|
||||||
status = m[0];
|
status = m[0];
|
||||||
|
|
||||||
/* SysEx */
|
/* SysEx */
|
||||||
if (*state == 0 && status == 0xf0)
|
if (*state == 0) {
|
||||||
*state = 1; /* sysex start */
|
if (status == 0xf0)
|
||||||
|
*state = 1; /* sysex start */
|
||||||
|
else if (status == 0xf7)
|
||||||
|
*state = 2; /* sysex continue */
|
||||||
|
}
|
||||||
if (*state & 3) {
|
if (*state & 3) {
|
||||||
prefix |= 0x30000000;
|
prefix |= 0x30000000;
|
||||||
if (*state == 1) {
|
if (status & 0x80) {
|
||||||
m++;
|
m++;
|
||||||
to_consume++;
|
to_consume++;
|
||||||
}
|
}
|
||||||
bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 6u);
|
bytes = SPA_CLAMP(*midi_size - to_consume, 0u, 7u);
|
||||||
to_consume += bytes;
|
|
||||||
if (bytes > 0 && m[bytes-1] == 0xf7) {
|
|
||||||
bytes--;
|
|
||||||
if (*state == 2)
|
|
||||||
prefix |= 0x3 << 20;
|
|
||||||
*state = 0;
|
|
||||||
} else if (*state == 1) {
|
|
||||||
prefix |= 0x1 << 20;
|
|
||||||
*state = 2; /* sysex continue */
|
|
||||||
} else {
|
|
||||||
prefix |= 0x2 << 20;
|
|
||||||
}
|
|
||||||
if (bytes > 0) {
|
if (bytes > 0) {
|
||||||
ump[size++] = prefix | bytes << 16 | (m[0] & 0x7f) << 8;
|
end = m[bytes-1];
|
||||||
|
if (end & 0x80) {
|
||||||
|
bytes--; /* skip terminator */
|
||||||
|
to_consume++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
end = 0xf0; /* pretend there is a continue terminator */
|
||||||
|
|
||||||
|
bytes = SPA_CLAMP(bytes, 0u, 6u);
|
||||||
|
to_consume += bytes;
|
||||||
|
|
||||||
|
if (end == 0xf7) {
|
||||||
|
if (*state == 2) {
|
||||||
|
/* continue and done */
|
||||||
|
prefix |= 0x3 << 20;
|
||||||
|
*state = 0;
|
||||||
|
}
|
||||||
|
} else if (*state == 1) {
|
||||||
|
/* first packet but not finished */
|
||||||
|
prefix |= 0x1 << 20;
|
||||||
|
*state = 2; /* sysex continue */
|
||||||
|
} else {
|
||||||
|
/* continue and not finished */
|
||||||
|
prefix |= 0x2 << 20;
|
||||||
|
}
|
||||||
|
ump[size++] = prefix | bytes << 16;
|
||||||
ump[size++] = 0;
|
ump[size++] = 0;
|
||||||
|
for (i = 0 ; i < bytes; i++)
|
||||||
|
/* ump[0] |= (m[0] & 0x7f) << 8
|
||||||
|
* ump[0] |= (m[1] & 0x7f)
|
||||||
|
* ump[1] |= (m[2] & 0x7f) << 24
|
||||||
|
* ... */
|
||||||
|
ump[(i+2)/4] |= (m[i] & 0x7f) << ((5-i)%4 * 8);
|
||||||
}
|
}
|
||||||
if (bytes > 1)
|
|
||||||
ump[0] |= (m[1] & 0x7f);
|
|
||||||
if (bytes > 2)
|
|
||||||
ump[1] |= (m[2] & 0x7f) << 24;
|
|
||||||
if (bytes > 3)
|
|
||||||
ump[1] |= (m[3] & 0x7f) << 16;
|
|
||||||
if (bytes > 4)
|
|
||||||
ump[1] |= (m[4] & 0x7f) << 8;
|
|
||||||
if (bytes > 5)
|
|
||||||
ump[1] |= (m[5] & 0x7f);
|
|
||||||
} else {
|
} else {
|
||||||
/* regular messages */
|
/* regular messages */
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ endif
|
||||||
test('test-spa',
|
test('test-spa',
|
||||||
executable('test-spa',
|
executable('test-spa',
|
||||||
'test-spa-buffer.c',
|
'test-spa-buffer.c',
|
||||||
|
'test-spa-control.c',
|
||||||
'test-spa-json.c',
|
'test-spa-json.c',
|
||||||
'test-spa-utils.c',
|
'test-spa-utils.c',
|
||||||
'test-spa-log.c',
|
'test-spa-log.c',
|
||||||
|
|
|
||||||
173
test/test-spa-control.c
Normal file
173
test/test-spa-control.c
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
/* Simple Plugin API */
|
||||||
|
/* SPDX-FileCopyrightText: Copyright © 2024 Wim Taymans. */
|
||||||
|
/* SPDX-License-Identifier: MIT */
|
||||||
|
|
||||||
|
#include "pwtest.h"
|
||||||
|
|
||||||
|
#include <spa/control/control.h>
|
||||||
|
#include <spa/control/ump-utils.h>
|
||||||
|
|
||||||
|
PWTEST(control_abi_types)
|
||||||
|
{
|
||||||
|
/* contol */
|
||||||
|
pwtest_int_eq(SPA_CONTROL_Invalid, 0);
|
||||||
|
pwtest_int_eq(SPA_CONTROL_Properties, 1);
|
||||||
|
pwtest_int_eq(SPA_CONTROL_Midi, 2);
|
||||||
|
pwtest_int_eq(SPA_CONTROL_OSC, 3);
|
||||||
|
pwtest_int_eq(SPA_CONTROL_UMP, 4);
|
||||||
|
pwtest_int_eq(_SPA_CONTROL_LAST, 5);
|
||||||
|
|
||||||
|
return PWTEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tohex(char v)
|
||||||
|
{
|
||||||
|
if (v >= '0' && v <= '9')
|
||||||
|
return v - '0';
|
||||||
|
if (v >= 'a' && v <= 'f')
|
||||||
|
return v - 'a' + 10;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t parse_midi(const char *midi, uint8_t *data, size_t max_size)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
while (*midi) {
|
||||||
|
while (*midi == ' ')
|
||||||
|
midi++;
|
||||||
|
data[size++] = tohex(*(midi+0)) << 4 |
|
||||||
|
tohex(*(midi+1));
|
||||||
|
midi+=2;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t parse_ump(const char *ump, uint32_t *data, size_t max_size)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
while (*ump) {
|
||||||
|
while (*ump == ' ')
|
||||||
|
ump++;
|
||||||
|
data[size++] = tohex(*(ump+0)) << 28 |
|
||||||
|
tohex(*(ump+1)) << 24 |
|
||||||
|
tohex(*(ump+2)) << 20 |
|
||||||
|
tohex(*(ump+3)) << 16 |
|
||||||
|
tohex(*(ump+4)) << 12 |
|
||||||
|
tohex(*(ump+5)) << 8 |
|
||||||
|
tohex(*(ump+6)) << 4 |
|
||||||
|
tohex(*(ump+7));
|
||||||
|
ump+=8;
|
||||||
|
}
|
||||||
|
return size * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_midi_to_ump_test(char *midi, char *ump)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
size_t m_size, u_size, u_offs = 0;
|
||||||
|
uint8_t *m_data = alloca(strlen(midi) / 2);
|
||||||
|
uint32_t *u_data = alloca(strlen(ump) / 2);
|
||||||
|
uint64_t state = 0;
|
||||||
|
|
||||||
|
m_size = parse_midi(midi, m_data, sizeof(m_data));
|
||||||
|
u_size = parse_ump(ump, u_data, sizeof(u_data));
|
||||||
|
|
||||||
|
while (m_size > 0) {
|
||||||
|
uint32_t ump[4];
|
||||||
|
fprintf(stdout, "%zd %08x\n", m_size, *m_data);
|
||||||
|
int ump_size = spa_ump_from_midi(&m_data, &m_size,
|
||||||
|
ump, sizeof(ump), 0, &state);
|
||||||
|
if (ump_size <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (u_size <= u_offs)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < ump_size / 4; i++) {
|
||||||
|
fprintf(stdout, "%08x %08x\n", u_data[u_offs], ump[i]);
|
||||||
|
spa_assert(u_data[u_offs++] == ump[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PWTEST(control_midi_to_ump)
|
||||||
|
{
|
||||||
|
/* sysex */
|
||||||
|
do_midi_to_ump_test("f0 f7",
|
||||||
|
"30000000 00000000");
|
||||||
|
|
||||||
|
do_midi_to_ump_test("f0 01 02 03 04 05 f7",
|
||||||
|
"30050102 03040500");
|
||||||
|
|
||||||
|
do_midi_to_ump_test("f0 01 02 03 04 05 06 f7",
|
||||||
|
"30060102 03040506");
|
||||||
|
do_midi_to_ump_test("f0 01 02 03 04 05 06 07 f7",
|
||||||
|
"30160102 03040506 30310700 00000000");
|
||||||
|
do_midi_to_ump_test("f0 01 02 03 04 05 06 07 08 09 10 11 12 13 f7",
|
||||||
|
"30160102 03040506 30260708 09101112 30311300 00000000");
|
||||||
|
|
||||||
|
do_midi_to_ump_test("f0 01 02 03 04 05 06 f0",
|
||||||
|
"30160102 03040506");
|
||||||
|
do_midi_to_ump_test("f7 01 02 03 04 05 06 07 08 f0",
|
||||||
|
"30260102 03040506 30220708 00000000");
|
||||||
|
do_midi_to_ump_test("f7 01 02 03 04 05 06 07 08 09 f7",
|
||||||
|
"30260102 03040506 30330708 09000000");
|
||||||
|
|
||||||
|
return PWTEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_ump_to_midi_test(char *ump, char *midi)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
size_t m_size, u_size, m_offs = 0;
|
||||||
|
uint8_t *m_data = alloca(strlen(midi) / 2);
|
||||||
|
uint32_t *u_data = alloca(strlen(ump) / 2);
|
||||||
|
|
||||||
|
u_size = parse_ump(ump, u_data, sizeof(u_data));
|
||||||
|
m_size = parse_midi(midi, m_data, sizeof(m_data));
|
||||||
|
|
||||||
|
spa_assert(u_size > 0);
|
||||||
|
spa_assert(m_size > 0);
|
||||||
|
|
||||||
|
while (u_size > 0) {
|
||||||
|
uint8_t midi[32];
|
||||||
|
fprintf(stdout, "%zd %08x\n", u_size, *u_data);
|
||||||
|
int midi_size = spa_ump_to_midi(u_data, u_size,
|
||||||
|
midi, sizeof(midi));
|
||||||
|
if (midi_size <= 0)
|
||||||
|
return midi_size;
|
||||||
|
|
||||||
|
if (m_size <= m_offs)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < midi_size; i++) {
|
||||||
|
fprintf(stdout, "%08x %08x\n", m_data[m_offs], midi[i]);
|
||||||
|
spa_assert(m_data[m_offs++] == midi[i]);
|
||||||
|
}
|
||||||
|
u_size -= spa_ump_message_size(*u_data >> 28) * 4;
|
||||||
|
u_data += spa_ump_message_size(*u_data >> 28);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PWTEST(control_ump_to_midi)
|
||||||
|
{
|
||||||
|
spa_assert(do_ump_to_midi_test("30000000 00000000",
|
||||||
|
"f0 f7") >= 0);
|
||||||
|
spa_assert(do_ump_to_midi_test("30050102 03040500",
|
||||||
|
"f0 01 02 03 04 05 f7") >= 0);
|
||||||
|
|
||||||
|
spa_assert(do_ump_to_midi_test("30160102 03040506 30260708 09101112 30311300 00000000",
|
||||||
|
"f0 01 02 03 04 05 06 07 08 09 10 11 12 13 f7") >= 0);
|
||||||
|
return PWTEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
PWTEST_SUITE(spa_buffer)
|
||||||
|
{
|
||||||
|
pwtest_add(control_abi_types, PWTEST_NOARG);
|
||||||
|
pwtest_add(control_midi_to_ump, PWTEST_NOARG);
|
||||||
|
pwtest_add(control_ump_to_midi, PWTEST_NOARG);
|
||||||
|
|
||||||
|
return PWTEST_PASS;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue