diff --git a/src/lib/transformers/ableton.ts b/src/lib/transformers/ableton.ts index 5a616f4..b4ca577 100644 --- a/src/lib/transformers/ableton.ts +++ b/src/lib/transformers/ableton.ts @@ -298,6 +298,7 @@ function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams const groupTracksForDrumRack = tracks .filter((track) => track.group === group && track.sampleName) .filter((track) => getTrackNoteValues(track).length <= 1) + .filter((track) => !track.lane?.clips.some((clip) => clip.midiEvents.length > 0)) .map((track) => ({ ...track, drumRackSendingNote: getTrackNoteValues(track)[0] ?? track.rootNote, diff --git a/src/routes/home/Export/ExportProjectDialog.tsx b/src/routes/home/Export/ExportProjectDialog.tsx index fb6a65a..574fa74 100644 --- a/src/routes/home/Export/ExportProjectDialog.tsx +++ b/src/routes/home/Export/ExportProjectDialog.tsx @@ -23,7 +23,7 @@ function sanitizeFileName(value: string): string { } const NOTES: Record = { - ableton: `When samples are included, pads used at one consistent pitch are automatically organized into Drum Racks, while chromatic pads remain on separate MIDI tracks. Recorded fader automation is exported for LVL, PTC, LPF, HPF, FX, ATK, REL, PAN, TUNE, VEL and MOD. Choke groups work between pads in the same Drum Rack. TIM automation, the EP effects and stretching algorithms remain approximations.`, + ableton: `When samples are included, suitable single-pitch pads are automatically organized into Drum Racks, while chromatic or expressive pads remain on separate MIDI tracks. Recorded fader automation is exported for LVL, PTC, LPF, HPF, FX, ATK, REL, PAN, TUNE, VEL and MOD. Choke groups work between pads in the same Drum Rack. TIM automation, the EP effects and stretching algorithms remain approximations.`, dawproject: `Unfortunately, the DAWproject format does not currently support the "Sampler" instrument, so you will need to manually assign the samples in your DAW.`, midi: `The simplest format supported by any DAW. But you have to assign the samples manually.`, reaper: `Only basic sampler features are supported`, diff --git a/tests/ableton-hybrid.test.ts b/tests/ableton-hybrid.test.ts index 39f5d0d..2c4b26e 100644 --- a/tests/ableton-hybrid.test.ts +++ b/tests/ableton-hybrid.test.ts @@ -6,9 +6,11 @@ import { FaderParam, Group, GroupFaderParam, + MidiPatternEvent, Note, Pad, PadCode, + PatternEventKind, ProjectRawData, } from '../src/types/types'; @@ -222,6 +224,26 @@ test('classifies a pad from its distinct pitches across every exported scene', ( assert.ok(result.tracks.some((track) => track.padCode === 'a1' && !track.drumRack)); }); +test('keeps pad-specific MIDI expression on an individual track', () => { + const data = project(); + const rawData = new Uint8Array([0, 0, PatternEventKind.Midi, 0xd0, 100, 0, 0, 0]); + const event: MidiPatternEvent = { + type: 'midi', + kind: PatternEventKind.Midi, + position: 0, + source: 0, + flags: 0, + rawData, + data: rawData.slice(3, 7), + }; + data.scenes[0].patternEvents.a?.events.push(event); + const result = abletonTransformer(data, { includeArchivedSamples: true }); + const a0 = result.tracks.find((track) => track.padCode === 'a0' && !track.drumRack); + + assert.ok(a0); + assert.equal(a0.lane?.clips[0].midiEvents.length, 1); +}); + test('does not create selector-note racks when samples are excluded', () => { const result = abletonTransformer(project(), { includeArchivedSamples: false,