我的目标是将从Alesis合成器发送的字节码流转换为人类可读的格式。我需要能够获取“程序转储”,并读取组成补丁名称的10个字符的字符串。
为了从synth接收"Program Dump“,我通过MIDI-OX向synth发送了以下命令:
F0 00 00 0E 0E 01 73 F7我要求它给我发送73号程序的转储文件。
我收到了这个:
F0 00 00 0E 0E 00 73 00 60 24 0B 27 27 01 64 1E 19 19 05 23 19 1E 2A 41 0D 23 46 19 1E 06 00 47 0D 23 30 6C 18 63 30 6C 18 40 3F 0A 67 1B 16 20 40 00 60 18 00 18 06 05 0C 2B 41 13 70 05 30 40 31 63 70 05 00 40 31 63 70 05 00 40 31 63 00 4C 2A 51 00 46 7F 78 18 40 0F 40 31 40 31 04 30 0C 00 30 6C 03 30 3C 0F 00 00 05 0A 0F 14 19 1E 23 28 2D 72 00 76 34 3C 54 42 19 46 0C 33 3C 0C 00 0E 1B 46 60 58 31 46 61 58 31 00 7F 14 4E 37 6C 74 13 00 40 31 00 30 0C 0A 18 56 02 27 60 0B 60 00 63 46 61 0B 00 00 63 46 61 0B 00 00 63 46 01 18 55 22 01 0C 7F 71 31 00 1F 00 63 00 63 08 60 18 00 60 58 07 60 18 1E 00 00 0A 14 1E 28 32 3C 46 50 5A 64 01 0C 2D 15 29 05 36 0C 19 66 78 18 00 1C 36 0C 41 31 63 0C 43 31 63 00 7E 29 1C 6F 58 00 01 02 00 63 00 60 18 14 30 2C 05 4E 40 17 40 01 46 0D 43 17 00 00 46 0D 43 17 00 00 46 0D 03 30 2A 45 02 18 7E 63 63 00 3E 00 46 01 46 11 40 31 00 40 31 0F 40 71 3D 00 00 14 28 3C 50 64 78 0C 21 35 49 03 58 4C 71 31 1C 6C 18 32 4C 71 31 00 38 6C 18 02 63 46 19 06 63 46 01 7C 53 00 60 18 53 37 6C 70 0D 03 40 31 28 60 58 0A 1C 01 2F 00 03 0C 1B 06 2F 00 00 0C 1B 06 2F 00 00 0C 1B 06 60 54 0A 05 30 7C 47 47 01 7C 00 0C 03 0C 23 00 63 00 00 63 1E 3C 63 18 00 00 28 50 78 20 49 71 19 42 6A 12 07 F7 MIDI-OX告诉我它收到了408个字节。
这与规范不符:
单个程序转储发送了400个数据字节,相当于350个字节的程序数据。加上报头,与程序转储一起传输的总字节数为408。程序转储中每个参数的位置将在下一节中显示。
“程序转储”应由以下值组成:
F0 00 00 0E 0E 00 <program#> <data> F7这意味着数据应该以"00 60“开始,以"07 F7”结束。
现在,我应该能够将这400字节转换为这个程序的"350字节的压缩参数数据“。遵循“程序数据格式”,程序名称的10位数应位于打包数据中的某处。补丁73被称为"BlowDeTune“或"PanBristle",不完全确定它是从0开始还是从1开始。
那么如何进行转换呢?规范的第一页给出了传输格式,但我不知道如何解包。
有人能帮上忙吗?
Alesis QS MIDI Sysex规范在这里:
http://www.midiworld.com/quadrasynth/qs_swlib/qs678r.pdf
MIDI-OX可以在这里找到:
http://www.midiox.com/
发布于 2008-12-01 16:23:19
你很幸运,因为几年前我玩过Midi (用我的Atari ST 520),所以我对这个话题有足够的兴趣来调查一下……
为了记录,我找到了MIDI System Exclusive Message格式,这与您为synth提供的参考信息一致。
我最初以为打包算法是用this page描述的,但在实现它的解码和创建垃圾之后,我发现我错了……我会给你这段代码,以防它对你有用……
第一次尝试很有用,因为当我重新阅读PDF文件中的规范时,我理解了A7到G0的符号实际上是位……
数据是“打包”的,因为Midi非控制字必须是7位干净的(高位始终未设置)。
它们获取7字节的原始数据,将其视为56位的大字,并将该字每7位拆分一次,使高位始终为0:
原始数据(使用不同的表示法):
0 - b07 b06 b05 b04 b03 b02 b01 b00
1 - b17 b16 b15 b14 b13 b12 b11 b10
2 - b27 b26 b25 b24 b23 b22 b21 b20
3 - b37 b36 b35 b34 b33 b32 b31 b30
4 - b47 b46 b45 b44 b43 b42 b41 b40
5 - b57 b56 b55 b54 b53 b52 b51 b50
6 - b67 b66 b65 b64 b63 b62 b61 b60传输/编码的数据:
0 - 0 b06 b05 b04 b03 b02 b01 b00
1 - 0 b15 b14 b13 b12 b11 b10 b07
2 - 0 b24 b23 b22 b21 b20 b17 b16
3 - 0 b33 b32 b31 b30 b27 b26 b25
4 - 0 b42 b41 b40 b37 b36 b35 b34
5 - 0 b51 b50 b47 b46 b45 b44 b43
6 - 0 b60 b57 b56 b55 b54 b53 b52
7 - 0 b67 b66 b65 b64 b63 b62 b61所以我们有:
0 - 00000000 0x00
1 - 01100000 0x60
2 - 00100100 0x24
3 - 00001011 0x0B
4 - 00100111 0x27
5 - 00100111 0x27
6 - 00000001 0x01
7 - 01100100 0x64
0 - 00011110 0x1E
1 - 00011001 0x19
2 - 00011001 0x19
3 - 00000101 0x05
4 - 00100011 0x23
5 - 00011001 0x19
6 - 00011110 0x1E
7 - 00101010 0x2A一旦解码,我们应该有:
0 - 00000000 0x00
1 - 00110000 0x30
2 - 01101001 0x69
3 - 01110001 0x71
4 - 00111010 0x3A
5 - 00000101 0x05
6 - 11001000 0xC8
0 - 10011110 0x9E
1 - 01001100 0x4C
2 - 10100110 0xA6
3 - 00110000 0x30
4 - 11001010 0xCA
5 - 01111000 0x78
6 - 01010100 0x54我相信我正确地解码了数据,但仍然有垃圾(即。不可读的字符串)...
也许您会在我的代码中看到一个逻辑错误,这可能是一个起点。
我看到MIDI-OX可以用WSH编写脚本,所以我写了一个JS脚本,我用WSH运行它,并在控制台上输出:
var midiData =
[
0xF0, 0x00, 0x00, 0x0E, 0x0E, 0x00, 0x73,
0x00, 0x60, 0x24, 0x0B, 0x27, 0x27, 0x01, 0x64, 0x1E, 0x19, 0x19, 0x05, 0x23, 0x19, 0x1E, 0x2A,
0x41, 0x0D, 0x23, 0x46, 0x19, 0x1E, 0x06, 0x00, 0x47, 0x0D, 0x23, 0x30, 0x6C, 0x18, 0x63, 0x30,
0x6C, 0x18, 0x40, 0x3F, 0x0A, 0x67, 0x1B, 0x16, 0x20, 0x40, 0x00, 0x60, 0x18, 0x00, 0x18, 0x06,
0x05, 0x0C, 0x2B, 0x41, 0x13, 0x70, 0x05, 0x30, 0x40, 0x31, 0x63, 0x70, 0x05, 0x00, 0x40, 0x31,
0x63, 0x70, 0x05, 0x00, 0x40, 0x31, 0x63, 0x00, 0x4C, 0x2A, 0x51, 0x00, 0x46, 0x7F, 0x78, 0x18,
0x40, 0x0F, 0x40, 0x31, 0x40, 0x31, 0x04, 0x30, 0x0C, 0x00, 0x30, 0x6C, 0x03, 0x30, 0x3C, 0x0F,
0x00, 0x00, 0x05, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x23, 0x28, 0x2D, 0x72, 0x00, 0x76, 0x34, 0x3C,
0x54, 0x42, 0x19, 0x46, 0x0C, 0x33, 0x3C, 0x0C, 0x00, 0x0E, 0x1B, 0x46, 0x60, 0x58, 0x31, 0x46,
0x61, 0x58, 0x31, 0x00, 0x7F, 0x14, 0x4E, 0x37, 0x6C, 0x74, 0x13, 0x00, 0x40, 0x31, 0x00, 0x30,
0x0C, 0x0A, 0x18, 0x56, 0x02, 0x27, 0x60, 0x0B, 0x60, 0x00, 0x63, 0x46, 0x61, 0x0B, 0x00, 0x00,
0x63, 0x46, 0x61, 0x0B, 0x00, 0x00, 0x63, 0x46, 0x01, 0x18, 0x55, 0x22, 0x01, 0x0C, 0x7F, 0x71,
0x31, 0x00, 0x1F, 0x00, 0x63, 0x00, 0x63, 0x08, 0x60, 0x18, 0x00, 0x60, 0x58, 0x07, 0x60, 0x18,
0x1E, 0x00, 0x00, 0x0A, 0x14, 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x64, 0x01, 0x0C, 0x2D,
0x15, 0x29, 0x05, 0x36, 0x0C, 0x19, 0x66, 0x78, 0x18, 0x00, 0x1C, 0x36, 0x0C, 0x41, 0x31, 0x63,
0x0C, 0x43, 0x31, 0x63, 0x00, 0x7E, 0x29, 0x1C, 0x6F, 0x58, 0x00, 0x01, 0x02, 0x00, 0x63, 0x00,
0x60, 0x18, 0x14, 0x30, 0x2C, 0x05, 0x4E, 0x40, 0x17, 0x40, 0x01, 0x46, 0x0D, 0x43, 0x17, 0x00,
0x00, 0x46, 0x0D, 0x43, 0x17, 0x00, 0x00, 0x46, 0x0D, 0x03, 0x30, 0x2A, 0x45, 0x02, 0x18, 0x7E,
0x63, 0x63, 0x00, 0x3E, 0x00, 0x46, 0x01, 0x46, 0x11, 0x40, 0x31, 0x00, 0x40, 0x31, 0x0F, 0x40,
0x71, 0x3D, 0x00, 0x00, 0x14, 0x28, 0x3C, 0x50, 0x64, 0x78, 0x0C, 0x21, 0x35, 0x49, 0x03, 0x58,
0x4C, 0x71, 0x31, 0x1C, 0x6C, 0x18, 0x32, 0x4C, 0x71, 0x31, 0x00, 0x38, 0x6C, 0x18, 0x02, 0x63,
0x46, 0x19, 0x06, 0x63, 0x46, 0x01, 0x7C, 0x53, 0x00, 0x60, 0x18, 0x53, 0x37, 0x6C, 0x70, 0x0D,
0x03, 0x40, 0x31, 0x28, 0x60, 0x58, 0x0A, 0x1C, 0x01, 0x2F, 0x00, 0x03, 0x0C, 0x1B, 0x06, 0x2F,
0x00, 0x00, 0x0C, 0x1B, 0x06, 0x2F, 0x00, 0x00, 0x0C, 0x1B, 0x06, 0x60, 0x54, 0x0A, 0x05, 0x30,
0x7C, 0x47, 0x47, 0x01, 0x7C, 0x00, 0x0C, 0x03, 0x0C, 0x23, 0x00, 0x63, 0x00, 0x00, 0x63, 0x1E,
0x3C, 0x63, 0x18, 0x00, 0x00, 0x28, 0x50, 0x78, 0x20, 0x49, 0x71, 0x19, 0x42, 0x6A, 0x12, 0x07,
0xF7
];
// Show original data
DumpData(midiData, 16);
var headerLength = 7; // Bytes to skip
var resultData = new Array();
var decodedByteCount = 0; // Number of expanded bytes in result
var cumulator = 0;
var bitCount = 0;
for (var i = headerLength; // Skip header
i < midiData.length - 1; // Omit EOF
i++)
{
var rank = (i - headerLength) % 8; // We split the data in runs of 8 bytes
// We cumulate the bits of these runs (less the high bit) to make a big word of 56 bits
/*
cumulator |= midiData[i] << (7 * rank);
if (rank == 7) // End of the run
{
// Split the cumulator in 7 bytes
for (var j = 0; j < 7; j++)
{
var shift = 8 * j;
var byte = (cumulator & (0xFF << shift)) >> shift;
WScript.StdOut.Write(ByteToHex(byte) + ' ');
resultData[decodedByteCount++] = byte;
}
cumulator = 0; // Reset the buffer
}
*/
// Actually, we cannot do that, because JS' bit arithmetic seems to be limited to signed 32 bits!
// So I get the bytes out as soon as they are complete.
// Somehow, it is more elegant anyway (but reflects less the original algorithm).
cumulator |= midiData[i] << bitCount;
bitCount += 7;
//~ WScript.StdOut.Write((i - 7) + ':' + ByteToHex(midiData[i]) + ' (' + bitCount + ') ' + DecimalToHex(cumulator) + '\n');
if (bitCount >= 8)
{
var byte = cumulator & 0xFF;
bitCount -= 8;
cumulator >>= 8;
resultData[decodedByteCount++] = byte;
//~ WScript.StdOut.Write((i - 7) + ':' + ByteToHex(midiData[i]) + ' (' + bitCount + ') ' + DecimalToHex(cumulator) + ' > ' + ByteToHex(byte) + '\n');
}
}
DumpData(resultData, 14);实用程序例程:
function DumpData(data, lineLength)
{
WScript.StdOut.Write("Found " + data.length + " bytes\n");
var txt = '';
for (var i = 0; i < data.length; i++)
{
var rd = data[i];
if (rd > 31)
{
txt += String.fromCharCode(rd);
}
else
{
txt += '.';
}
WScript.StdOut.Write(ByteToHex(rd) + ' ');
if ((i+1) % lineLength == 0)
{
WScript.StdOut.Write(' ' + txt + '\n');
txt = '';
}
}
WScript.StdOut.Write(' ' + txt + '\n');
}
function NibbleToHex(halfByte)
{
return String.fromCharCode(halfByte < 10 ?
halfByte + 48 : // 0 to 9
halfByte + 55); // A to F
}
function ByteToHex(dec)
{
var h = (dec & 0xF0) >> 4;
var l = dec & 0x0F;
return NibbleToHex(h) + NibbleToHex(l);
}
function DecimalToHex(dec)
{
var result = '';
do
{
result = ByteToHex(dec & 0xFF) + result;
dec >>= 8;
} while (dec > 0);
return result;
}输出:
Found 350 bytes
00 30 69 71 3A 05 C8 9E 4C A6 30 CA 78 54 .0iq:.ÈL¦0ÊxT
C1 C6 C8 98 F1 18 00 C7 C6 08 C6 C6 8C 61 ÁÆÈñ..ÇÆ.ÆÆa
6C 0C F0 A7 38 6F 2C 20 20 00 8C 01 60 0C l.ð§8o, ..`.
05 C6 2A 38 81 17 60 C0 D8 18 5E 00 00 63 .Æ*8.`ÀØ.^..c
63 78 01 00 8C 8D 01 4C 55 14 60 FC E3 31 cx...LU.`üã1
C0 07 30 06 8C 11 60 0C 00 8C 3D 80 F1 1E À.0..`..=ñ.
00 40 41 F1 A0 64 3C 23 54 4B 0E B0 D3 78 .@Añ d<#TK.°Óx
54 61 C6 C8 98 F1 18 00 C7 C6 08 C6 C6 8C TaÆÈñ..ÇÆ.ÆÆ
61 6C 0C F0 A7 38 6F 6C FA 04 00 8C 01 60 al.ð§8olú...`
0C 05 C6 2A 38 81 17 60 C0 D8 18 5E 00 00 ..Æ*8.`ÀØ.^..
63 63 78 01 00 8C 8D 01 4C 55 14 60 FC E3 ccx...LU.`üã
31 C0 07 30 06 8C 11 60 0C 00 8C 3D 80 31 1À.0..`..=1
1E 00 40 41 F1 A0 64 3C 23 54 4B 0E 30 5A ..@Añ d<#TK.0Z
95 54 C1 C6 C8 98 F1 18 00 C7 C6 08 C6 C6 TÁÆÈñ..ÇÆ.ÆÆ
8C 61 6C 0C F0 A7 38 6F 2C 20 20 00 8C 01 al.ð§8o, ..
60 0C 05 C6 2A 38 81 17 60 C0 D8 18 5E 00 `..Æ*8.`ÀØ.^.
00 63 63 78 01 00 8C 8D 01 4C 55 14 60 FC .ccx...LU.`ü
E3 31 C0 07 30 06 8C 11 60 0C 00 8C 3D 80 ã1À.0..`..=
F1 1E 00 40 41 F1 A0 64 3C 23 54 4B 0E B0 ñ..@Añ d<#TK.°
CC 78 8C C3 C6 C8 98 F1 18 00 C7 C6 08 C6 ÌxÃÆÈñ..ÇÆ.Æ
C6 8C 61 6C 0C F0 A7 00 30 66 7A 63 C3 1B Æal.ð§.0fzcÃ.
03 60 0C 05 C6 2A 38 81 17 60 C0 D8 18 5E .`..Æ*8.`ÀØ.^
00 00 63 63 78 01 00 8C 8D 01 4C 55 14 60 ..ccx...LU.`
FC E3 31 C0 07 30 06 8C 11 60 0C 00 8C 3D üã1À.0..`..=
BC 31 06 00 40 41 F1 A0 64 3C 23 54 4B 0E ¼1..@Añ d<#TK.以防万一,另一种解包算法:
// Here the 8 bits of 7 bytes of raw data are coded as 7 bytes of data stripped off of the high bit,
// while the stripped bits are grouped in the first byte of the data run.
// In other words, when we have a run of 8 bytes, the first one groups the high bits of the 7 next bytes.
// Information found at http://crystal.apana.org.au/ghansper/midi_introduction/file_dump.html
var headerLength = 7;
var resultData = new Array();
var decodedByteCount = 0; // Number of expanded bytes in result
var runCount = -1; // Number of runs in the encoded data
for (var i = headerLength; // Skip header
i < midiData.length - 1; // Omit EOF
i++)
{
var rank = (i - headerLength) % 8; // We split the data in runs of 8 bytes
if (rank == 0) // Start of the run
{
// Get the high bits
var highBits = midiData[i];
runCount++;
//~ WScript.StdOut.Write(runCount + ' > ' + (i - 7) + ' >> ' + ByteToHex(highBits) + '\n');
}
else
{
resultData[decodedByteCount++] = midiData[i] |
((highBits & (1 << (7 - rank))) << rank);
//~ WScript.StdOut.Write((i - 7) + ' >> ' + ByteToHex(midiData[i]) + ' > ' +
//~ ByteToHex(midiData[i] | ((highBits & (1 << (7 - rank))) << rank)) + '\n');
}
}发布于 2016-04-30 22:32:56
多亏了你的出色工作,我想出了这个pack算法。看起来Alesis和Moog Voyager使用了相同的模式。
packSysex : function(midiData) {
var header = [0xF0, 0x04, 0x01, 0x00, 0x03, 0x00]; //Voyager Single Preset Dump.
var resultData = new Array();
var packedByteCount = 0;
var bitCount = 0;
var thisByte;
var packedByte;
var nextByte = 0x0;
for (var i = 0; i <= midiData.length; i++)
{
thisByte = midiData[i];
packedByte = ((thisByte << bitCount) | nextByte) & 0x7F;
nextByte = midiData[i] >> (7-bitCount);
resultData[packedByteCount++] = packedByte;
bitCount++;
if(bitCount >= 7) {
bitCount = 0;
//Fill last byte
packedByte = nextByte & 0x7F;
resultData[packedByteCount++] = packedByte;
nextByte = 0x0;
}
}
resultData[packedByteCount++] = 0xF7;
resultData = header.concat(resultData);
return resultData;
},https://stackoverflow.com/questions/330085
复制相似问题