117 lines
3.3 KiB
TypeScript
117 lines
3.3 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { collectScenesAndPatterns, parsePatternEvents } from '../src/lib/parsers';
|
|
import { TarFile } from '../src/lib/untar';
|
|
|
|
function buildPattern(records: number[][], bars: number = 4, declaredCount = records.length) {
|
|
const data = new Uint8Array(4 + records.length * 8);
|
|
data[1] = bars;
|
|
data[2] = declaredCount & 0xff;
|
|
data[3] = declaredCount >> 8;
|
|
|
|
records.forEach((record, index) => {
|
|
data.set(record, 4 + index * 8);
|
|
});
|
|
|
|
return data;
|
|
}
|
|
|
|
test('parses note records without changing the derived note shape', () => {
|
|
const result = parsePatternEvents(buildPattern([[0x34, 0x12, 3 << 3, 64, 99, 0x78, 0x56, 7]]));
|
|
|
|
assert.equal(result.bars, 4);
|
|
assert.equal(result.recordCount, 1);
|
|
assert.deepEqual(result.notes, {
|
|
3: [{ note: 64, position: 0x1234, duration: 0x5678, velocity: 99 }],
|
|
});
|
|
assert.deepEqual(result.events[0], {
|
|
type: 'note',
|
|
kind: 0,
|
|
position: 0x1234,
|
|
source: 3,
|
|
flags: 7,
|
|
rawData: new Uint8Array([0x34, 0x12, 3 << 3, 64, 99, 0x78, 0x56, 7]),
|
|
pad: 3,
|
|
note: 64,
|
|
velocity: 99,
|
|
duration: 0x5678,
|
|
});
|
|
});
|
|
|
|
test('parses and normalizes fader records', () => {
|
|
const result = parsePatternEvents(buildPattern([[0x60, 0, 1, 7, 9, 0xff, 0x7f, 128]]));
|
|
|
|
assert.deepEqual(result.notes, {});
|
|
assert.deepEqual(result.events[0], {
|
|
type: 'fader',
|
|
kind: 1,
|
|
position: 96,
|
|
source: 0,
|
|
flags: 128,
|
|
rawData: new Uint8Array([0x60, 0, 1, 7, 9, 0xff, 0x7f, 128]),
|
|
parameter: 7,
|
|
reserved: 9,
|
|
rawValue: 32767,
|
|
value: 1,
|
|
});
|
|
});
|
|
|
|
test('preserves MIDI and unknown records', () => {
|
|
const midi = [0x27, 0, (2 << 3) | 2, 0xd0, 0x62, 0xff, 0, 0x3d];
|
|
const unknown = [2, 0, (5 << 3) | 6, 10, 11, 12, 13, 14];
|
|
const result = parsePatternEvents(buildPattern([midi, unknown]));
|
|
|
|
assert.deepEqual(result.events[0], {
|
|
type: 'midi',
|
|
kind: 2,
|
|
position: 0x27,
|
|
source: 2,
|
|
flags: 0x3d,
|
|
rawData: new Uint8Array(midi),
|
|
data: new Uint8Array([0xd0, 0x62, 0xff, 0]),
|
|
});
|
|
assert.deepEqual(result.events[1], {
|
|
type: 'unknown',
|
|
kind: 6,
|
|
position: 2,
|
|
source: 5,
|
|
flags: 14,
|
|
rawData: new Uint8Array(unknown),
|
|
data: new Uint8Array([10, 11, 12, 13]),
|
|
});
|
|
});
|
|
|
|
test('retains MIDI-only sources as empty-note scene patterns', () => {
|
|
const patternData = buildPattern([[0x27, 0, (2 << 3) | 2, 0xd0, 0x62, 0xff, 0, 0x3d]]);
|
|
const scenes = collectScenesAndPatterns(
|
|
[new TarFile('patterns/a01', patternData.length, patternData)],
|
|
'TE032AS001',
|
|
);
|
|
|
|
assert.equal(scenes.length, 1);
|
|
assert.equal(scenes[0].patterns.length, 1);
|
|
assert.equal(scenes[0].patterns[0].pad, 'a2');
|
|
assert.deepEqual(scenes[0].patterns[0].notes, []);
|
|
assert.equal(scenes[0].patternEvents.a?.events.length, 1);
|
|
});
|
|
|
|
test('uses the uint16 header count and ignores undeclared or partial records', () => {
|
|
const records = Array.from({ length: 300 }, (_, index) => [
|
|
index & 0xff,
|
|
index >> 8,
|
|
0,
|
|
60,
|
|
100,
|
|
1,
|
|
0,
|
|
0,
|
|
]);
|
|
const data = buildPattern([...records, [0, 0, 1, 0, 0, 0, 0, 0]], 8, 300);
|
|
const truncated = data.slice(0, data.length - 3);
|
|
const result = parsePatternEvents(truncated);
|
|
|
|
assert.equal(result.recordCount, 300);
|
|
assert.equal(result.events.length, 300);
|
|
assert.equal(result.events[299].position, 299);
|
|
});
|