Add boilerplate files and licenses
This commit is contained in:
83
src/lib/constants.ts
Normal file
83
src/lib/constants.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
export const SKU_EP133 = 'TE032AS001';
|
||||
export const SKU_EP1320 = 'TE032AS005';
|
||||
export const SKU_EP40 = 'TE032AS006';
|
||||
|
||||
export const SKU_TO_NAME: Record<string, { id: string; name: string }> = {
|
||||
[SKU_EP133]: { id: 'ep133', name: 'EP-133' },
|
||||
[SKU_EP1320]: { id: 'ep1320', name: 'EP-1320' },
|
||||
[SKU_EP40]: { id: 'ep40', name: 'EP-40' },
|
||||
};
|
||||
|
||||
export const GROUPS = [
|
||||
{
|
||||
name: 'A',
|
||||
id: 'a',
|
||||
},
|
||||
{
|
||||
name: 'B',
|
||||
id: 'b',
|
||||
},
|
||||
{
|
||||
name: 'C',
|
||||
id: 'c',
|
||||
},
|
||||
{
|
||||
name: 'D',
|
||||
id: 'd',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const PADS = ['9', '8', '7', '6', '5', '4', '3', '2', '1', '.', '0', 'E'];
|
||||
|
||||
export const PAD_DISPLAY_FROM_INDEX: Record<number, string> = {
|
||||
0: '7',
|
||||
1: '8',
|
||||
2: '9',
|
||||
3: '4',
|
||||
4: '5',
|
||||
5: '6',
|
||||
6: '1',
|
||||
7: '2',
|
||||
8: '3',
|
||||
9: '.',
|
||||
10: '0',
|
||||
11: 'E',
|
||||
};
|
||||
|
||||
export const EFFECTS = {
|
||||
0: 'None',
|
||||
1: 'Delay',
|
||||
2: 'Reverb',
|
||||
3: 'Distortion',
|
||||
4: 'Chorus',
|
||||
5: 'Filter',
|
||||
6: 'Compressor',
|
||||
};
|
||||
|
||||
export const EFFECTS_SHORT = {
|
||||
0: 'OFF',
|
||||
1: 'DLY',
|
||||
2: 'RVB',
|
||||
3: 'DST',
|
||||
4: 'CHO',
|
||||
5: 'FLT',
|
||||
6: 'CMP',
|
||||
};
|
||||
|
||||
export const SCALES_SHORT: Record<number, string> = {
|
||||
0: '2T',
|
||||
1: 'MAJ',
|
||||
2: 'MIN',
|
||||
3: 'DOR',
|
||||
4: 'PHR',
|
||||
5: 'LYD',
|
||||
6: 'MIX',
|
||||
7: 'LOC',
|
||||
8: 'MA.P',
|
||||
9: 'MI.P',
|
||||
10: 'BLU',
|
||||
11: 'H.MI',
|
||||
12: 'M.MI',
|
||||
};
|
||||
|
||||
export const NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
687
src/lib/exporters/ableton/builders.ts
Normal file
687
src/lib/exporters/ableton/builders.ts
Normal file
@@ -0,0 +1,687 @@
|
||||
import { create } from 'xmlbuilder2';
|
||||
import {
|
||||
EffectType,
|
||||
ExporterParams,
|
||||
FaderParam,
|
||||
ProjectRawData,
|
||||
ProjectSettings,
|
||||
} from '../../../types/types';
|
||||
import { EFFECTS } from '../../constants';
|
||||
import abletonTransformer, { AblClip, AblScene, AblTrack } from '../../transformers/ableton';
|
||||
import { getQuarterNotesPerBar } from '../utils';
|
||||
import { ALSDrumBranch } from './types/drumBranch';
|
||||
import { ALSDrumRack } from './types/drumRack';
|
||||
import { ALSChorus } from './types/effectChorus';
|
||||
import { ALSCompressor } from './types/effectCompressor';
|
||||
import { ALSDelay } from './types/effectDelay';
|
||||
import { ALSDistortion } from './types/effectDistortion';
|
||||
import { ALSFilter } from './types/effectFilter';
|
||||
import { ALSReverb } from './types/effectReverb';
|
||||
import { ALSGroupTrack } from './types/groupTrack';
|
||||
import { ALSMidiClip, ALSMidiClipContent } from './types/midiClip';
|
||||
import { ALSMidiTrack } from './types/midiTrack';
|
||||
import { ALSProject } from './types/project';
|
||||
import { ALSReturnTrack } from './types/returnTrack';
|
||||
import { ALSScene, ALSSceneContent } from './types/scene';
|
||||
import { ALSSimpler } from './types/simpler';
|
||||
import { ALSTrackSendHolder } from './types/trackSendHolder';
|
||||
import {
|
||||
filterFreqFromNormalized,
|
||||
fixIds,
|
||||
getId,
|
||||
gzipString,
|
||||
koEnvRangeToSeconds,
|
||||
loadTemplate,
|
||||
TIME_SIGNATURES,
|
||||
toNativePath,
|
||||
} from './utils';
|
||||
|
||||
let _localId = -1;
|
||||
let _localGroupId = -1;
|
||||
let _localTrackColor = -1;
|
||||
let _localGroupTrackColor = -1;
|
||||
|
||||
const MIN_CLIP_LAUNCHER_SCENES = 8;
|
||||
|
||||
async function buildMidiClip(
|
||||
koClip: AblClip,
|
||||
clipIdx: number,
|
||||
color: number,
|
||||
clipForLauncher: boolean = false,
|
||||
): Promise<ALSMidiClipContent> {
|
||||
const midiClipTemplate = await loadTemplate<ALSMidiClip>('midiClip');
|
||||
const midiClip = structuredClone(midiClipTemplate.MidiClip);
|
||||
|
||||
const beats = getQuarterNotesPerBar(
|
||||
koClip.timeSignature.numerator,
|
||||
koClip.timeSignature.denominator,
|
||||
);
|
||||
const time = koClip.offset * beats;
|
||||
const start = time;
|
||||
const end = time + koClip.sceneBars * beats;
|
||||
|
||||
midiClip['@Id'] = clipIdx;
|
||||
midiClip['@Time'] = time;
|
||||
midiClip.CurrentStart['@Value'] = start;
|
||||
midiClip.CurrentEnd['@Value'] = end;
|
||||
midiClip.Loop.LoopOn['@Value'] = 'true';
|
||||
midiClip.Loop.LoopStart['@Value'] = 0;
|
||||
midiClip.Loop.LoopEnd['@Value'] = koClip.bars * beats;
|
||||
midiClip.Loop.HiddenLoopStart['@Value'] = 0;
|
||||
midiClip.Loop.HiddenLoopEnd['@Value'] = koClip.bars * beats;
|
||||
midiClip.Color['@Value'] = color;
|
||||
midiClip.Name['@Value'] = koClip.sceneName;
|
||||
midiClip.TimeSignature.TimeSignatures.RemoteableTimeSignature.Numerator['@Value'] =
|
||||
koClip.timeSignature.numerator;
|
||||
midiClip.TimeSignature.TimeSignatures.RemoteableTimeSignature.Denominator['@Value'] =
|
||||
koClip.timeSignature.denominator;
|
||||
|
||||
if (clipForLauncher) {
|
||||
midiClip['@Time'] = 0;
|
||||
midiClip.CurrentStart['@Value'] = 0;
|
||||
midiClip.CurrentEnd['@Value'] = koClip.bars * beats;
|
||||
}
|
||||
|
||||
// ableton group notes
|
||||
const grouppedNotes: Record<string, typeof koClip.notes> = koClip.notes.reduce(
|
||||
(acc, note) => {
|
||||
const group = note.note.toString();
|
||||
acc[group] = acc[group] || [];
|
||||
acc[group].push(note);
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof koClip.notes>,
|
||||
);
|
||||
|
||||
midiClip.Notes.KeyTracks.KeyTrack = [];
|
||||
let _noteId = 0;
|
||||
|
||||
Object.entries(grouppedNotes).forEach(([noteValueStr, notes], groupIndex) => {
|
||||
const noteValue = parseInt(noteValueStr, 10);
|
||||
|
||||
midiClip.Notes.KeyTracks.KeyTrack.push({
|
||||
'@Id': groupIndex,
|
||||
Notes: {
|
||||
MidiNoteEvent: notes.map((noteData, noteIndex) => {
|
||||
let dur = noteData.duration / 96;
|
||||
const nextNote = notes[noteIndex + 1];
|
||||
|
||||
// making sure same notes are not overlapping
|
||||
if (nextNote && noteData.position / 96 + dur > nextNote.position / 96) {
|
||||
dur = nextNote.position / 96 - noteData.position / 96;
|
||||
}
|
||||
|
||||
let velocity = noteData.velocity;
|
||||
|
||||
if (koClip.faderParams[FaderParam.VEL] !== -1) {
|
||||
velocity = koClip.faderParams[FaderParam.VEL] * 127;
|
||||
}
|
||||
|
||||
return {
|
||||
'@Time': noteData.position / 96,
|
||||
'@Duration': dur,
|
||||
'@Velocity': velocity,
|
||||
'@OffVelocity': 64,
|
||||
'@NoteId': _noteId++,
|
||||
};
|
||||
}),
|
||||
},
|
||||
MidiKey: {
|
||||
'@Value': noteValue,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return midiClip;
|
||||
}
|
||||
|
||||
async function buildSimplerDevice(koTrack: AblTrack) {
|
||||
const simplerTemplate = await loadTemplate<ALSSimpler>('simpler');
|
||||
const device = structuredClone(simplerTemplate.OriginalSimpler);
|
||||
|
||||
device.LastPresetRef.Value = {};
|
||||
switch (koTrack.playMode) {
|
||||
case 'legato':
|
||||
case 'oneshot':
|
||||
device.Globals.PlaybackMode['@Value'] = 1;
|
||||
break;
|
||||
case 'key':
|
||||
device.Globals.PlaybackMode['@Value'] = 0;
|
||||
break;
|
||||
}
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleRef.FileRef.RelativePath[
|
||||
'@Value'
|
||||
] = toNativePath(`Samples/Imported/${koTrack.sampleName}`);
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.Name['@Value'] = koTrack.name;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.RootKey['@Value'] = koTrack.rootNote;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleStart['@Value'] = koTrack.trimLeft;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleEnd['@Value'] = koTrack.trimRight;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.TimeSignature.TimeSignatures.RemoteableTimeSignature.Numerator[
|
||||
'@Value'
|
||||
] = koTrack.timeSignature.numerator;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.TimeSignature.TimeSignatures.RemoteableTimeSignature.Denominator[
|
||||
'@Value'
|
||||
] = koTrack.timeSignature.denominator;
|
||||
|
||||
// setting velocity scaling to 20% to match KO behavior
|
||||
device.VolumeAndPan.VolumeVelScale.Manual['@Value'] = 0.2;
|
||||
|
||||
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.release, koTrack.soundLength) * 1000;
|
||||
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.attack, koTrack.soundLength) * 1000;
|
||||
device.VolumeAndPan.Panorama.Manual['@Value'] = koTrack.pan;
|
||||
|
||||
// overriding attack time if ATK fader param is defined
|
||||
if (koTrack.faderParams[FaderParam.ATK] !== -1) {
|
||||
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.faderParams[FaderParam.ATK] * 255, koTrack.soundLength) * 1000;
|
||||
}
|
||||
|
||||
// overriding release time if REL fader param is defined
|
||||
if (koTrack.faderParams[FaderParam.REL] !== -1) {
|
||||
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.faderParams[FaderParam.REL] * 255, koTrack.soundLength) * 1000;
|
||||
}
|
||||
|
||||
let pitch = koTrack.pitch || 0;
|
||||
if (koTrack.samplePitch) {
|
||||
pitch += koTrack.samplePitch;
|
||||
}
|
||||
|
||||
// adding PITCH fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.PTC] !== -1) {
|
||||
const normalized = (koTrack.faderParams[FaderParam.PTC] - 0.5) * 2; // from 0-1 to -1 to +1
|
||||
pitch += Math.round(normalized * 5);
|
||||
}
|
||||
|
||||
// adding TUNE fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.TUNE] !== -1) {
|
||||
pitch += Math.round(koTrack.faderParams[FaderParam.TUNE] * 23.5 - 12); // the range is slightly less than +/-12 cause that's how KO does it
|
||||
}
|
||||
|
||||
device.Pitch.TransposeKey.Manual['@Value'] = pitch;
|
||||
|
||||
// adding vibrato from MOD fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.MOD] !== -1) {
|
||||
device.Lfo.IsOn.Manual['@Value'] = 'true';
|
||||
// KO's MOD wheel on 100% is roughly equal to 5% LFO pitch modulation
|
||||
device.Pitch.PitchLfoAmount.Manual['@Value'] = koTrack.faderParams[FaderParam.MOD] / 20;
|
||||
device.Lfo.Slot.Value.SimplerLfo.RateType.Manual['@Value'] = 1; // tempo synced 1/16
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.LPF] !== -1 && koTrack.faderParams[FaderParam.LPF] !== 1) {
|
||||
device.Filter.IsOn.Manual['@Value'] = 'true';
|
||||
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 0; // Low Pass
|
||||
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'true'; // 24db/oct
|
||||
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
|
||||
koTrack.faderParams[FaderParam.LPF] * 0.9, // lowering max freq to match KO behavior
|
||||
);
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.HPF] !== -1 && koTrack.faderParams[FaderParam.HPF] !== 0) {
|
||||
device.Filter.IsOn.Manual['@Value'] = 'true';
|
||||
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 1; // High Pass
|
||||
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'false'; // 12db/oct
|
||||
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
|
||||
koTrack.faderParams[FaderParam.HPF] * 0.7, // lowering max freq to match KO behavior
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: add bandpass filter
|
||||
|
||||
if (koTrack.timeStretch === 'bars') {
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
|
||||
'@Value'
|
||||
] = 'true';
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMode[
|
||||
'@Value'
|
||||
] = 0; // warp in 'Beats' mode
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMarkers.WarpMarker =
|
||||
[
|
||||
{
|
||||
'@Id': 0,
|
||||
'@SecTime': 0,
|
||||
'@BeatTime': 0,
|
||||
},
|
||||
{
|
||||
'@Id': 1,
|
||||
'@SecTime': koTrack.soundLength,
|
||||
'@BeatTime': koTrack.timeStretchBars * koTrack.timeSignature.numerator, // convert bars to beats
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (koTrack.timeStretch === 'bpm') {
|
||||
// calculating beat length using koTrack.timeStretchBpm
|
||||
const beatTime = koTrack.soundLength / (60 / koTrack.timeStretchBpm);
|
||||
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
|
||||
'@Value'
|
||||
] = 'true';
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMode[
|
||||
'@Value'
|
||||
] = 0; // warp in 'Beats' mode
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMarkers.WarpMarker =
|
||||
[
|
||||
{
|
||||
'@Id': 0,
|
||||
'@SecTime': 0,
|
||||
'@BeatTime': 0,
|
||||
},
|
||||
{
|
||||
'@Id': 1,
|
||||
'@SecTime': koTrack.soundLength,
|
||||
'@BeatTime': beatTime,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (koTrack.timeStretch === 'rev') {
|
||||
device.Player.Reverse.Manual['@Value'] = 'true';
|
||||
}
|
||||
|
||||
return {
|
||||
OriginalSimpler: device,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildDrumRackDevice(koTrack: AblTrack) {
|
||||
const [drumRackTemplate, drumBranchTemplate] = await Promise.all([
|
||||
loadTemplate<ALSDrumRack>('drumRack'),
|
||||
loadTemplate<ALSDrumBranch>('drumBranch'),
|
||||
]);
|
||||
|
||||
const device = structuredClone(drumRackTemplate.DrumGroupDevice);
|
||||
|
||||
device.Branches.DrumBranch = [];
|
||||
|
||||
for (const [idx, subtrack] of koTrack.tracks.entries()) {
|
||||
if (!subtrack.sampleName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const padNumber = parseInt(subtrack.padCode.slice(1), 10);
|
||||
// EP133 is 3x4 (top→bottom), Ableton Drum Rack is 4x4 (bottom→top)
|
||||
// flip vertically: row 3 → row 0, row 0 → row 3
|
||||
const row = 3 - Math.floor(padNumber / 3);
|
||||
const col = padNumber % 3;
|
||||
const drumPad = row * 4 + col;
|
||||
|
||||
const drumBranch = structuredClone(drumBranchTemplate.DrumBranch);
|
||||
const note = 92 - drumPad; // 92 is the number of slot for C1 in the drum rack and it goes down
|
||||
|
||||
drumBranch['@Id'] = idx;
|
||||
drumBranch.Name.EffectiveName['@Value'] = subtrack.name;
|
||||
drumBranch.Name.UserName['@Value'] = subtrack.name;
|
||||
drumBranch.BranchInfo.ReceivingNote['@Value'] = note;
|
||||
drumBranch.BranchInfo.SendingNote['@Value'] = 60; // not sure if this matters
|
||||
drumBranch.BranchInfo.ChokeGroup['@Value'] = subtrack.inChokeGroup ? 1 : 0;
|
||||
drumBranch.DeviceChain.MidiToAudioDeviceChain.Devices = await buildSimplerDevice(subtrack);
|
||||
|
||||
device.Branches.DrumBranch.push(drumBranch);
|
||||
}
|
||||
|
||||
return {
|
||||
DrumGroupDevice: device,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildTrack(
|
||||
koTrack: AblTrack,
|
||||
maxScenes: number,
|
||||
trackGroupId: number,
|
||||
exporterParams: ExporterParams,
|
||||
): Promise<ALSMidiTrack> {
|
||||
const midiTrackTemplate = await loadTemplate<ALSMidiTrack>('midiTrack');
|
||||
const midiTrack = structuredClone(midiTrackTemplate.MidiTrack);
|
||||
|
||||
midiTrack['@Id'] = _localId++;
|
||||
midiTrack.Name.EffectiveName['@Value'] = koTrack.soundId
|
||||
? `${String(koTrack.soundId).padStart(3, '0')} ${koTrack.name}`
|
||||
: koTrack.name;
|
||||
midiTrack.Name.UserName['@Value'] = midiTrack.Name.EffectiveName['@Value'];
|
||||
midiTrack.Color['@Value'] = 8 + _localTrackColor++; // started with 8th color in the Ableton palette, why not
|
||||
midiTrack.DeviceChain.MainSequencer.ClipTimeable.ArrangerAutomation.Events.MidiClip = [];
|
||||
midiTrack.TrackGroupId['@Value'] = trackGroupId;
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'] = [];
|
||||
midiTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = koTrack.volume;
|
||||
|
||||
if (koTrack.faderParams[FaderParam.PAN] !== -1) {
|
||||
midiTrack.DeviceChain.Mixer.Pan.Manual['@Value'] =
|
||||
(koTrack.faderParams[FaderParam.PAN] - 0.5) * 2;
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.LVL] !== -1) {
|
||||
// setting volume to the minimum of track volume and fader param
|
||||
midiTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = Math.min(
|
||||
koTrack.volume,
|
||||
koTrack.faderParams[FaderParam.LVL],
|
||||
);
|
||||
}
|
||||
|
||||
if (koTrack.drumRack && exporterParams.includeArchivedSamples) {
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].push(await buildDrumRackDevice(koTrack));
|
||||
}
|
||||
|
||||
if (!koTrack.drumRack && koTrack.sampleName && exporterParams.includeArchivedSamples) {
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].push(await buildSimplerDevice(koTrack));
|
||||
}
|
||||
|
||||
// make sure id's are sequential in device chain
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].forEach((device, deviceIdx) => {
|
||||
Object.values(device).forEach((devContent: any) => {
|
||||
devContent['@Id'] = deviceIdx;
|
||||
});
|
||||
});
|
||||
|
||||
// make sure each tracks has the same empty slots for clips
|
||||
// must be equeal to GroupTrackSlot if this track is in a group
|
||||
// must be at least 8 slots to avoid Ableton crashes
|
||||
for (let sc = 0; sc < Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes); sc++) {
|
||||
midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[sc] = {
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
ClipSlot: {
|
||||
Value: {},
|
||||
},
|
||||
};
|
||||
midiTrack.DeviceChain.FreezeSequencer.ClipSlotList.ClipSlot[sc] = {
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
ClipSlot: {
|
||||
Value: {},
|
||||
},
|
||||
HasStop: {
|
||||
'@Value': 'true',
|
||||
},
|
||||
NeedRefreeze: {
|
||||
'@Value': 'true',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (koTrack.lane) {
|
||||
if (!exporterParams.clips) {
|
||||
for (const [clipIdx, koClip] of koTrack.lane.clips.entries()) {
|
||||
const midiClip = await buildMidiClip(koClip, clipIdx, midiTrack.Color['@Value']);
|
||||
|
||||
midiTrack.DeviceChain.MainSequencer.ClipTimeable.ArrangerAutomation.Events.MidiClip.push(
|
||||
midiClip,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (const koClip of koTrack.lane.clips.values()) {
|
||||
const midiClip = await buildMidiClip(koClip, 0, midiTrack.Color['@Value'], true);
|
||||
|
||||
midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[koClip.sceneIndex] = {
|
||||
...midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[koClip.sceneIndex],
|
||||
ClipSlot: {
|
||||
Value: {
|
||||
MidiClip: midiClip,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exporterParams.groupTracks) {
|
||||
midiTrack.DeviceChain.AudioOutputRouting.Target['@Value'] = 'AudioOut/GroupTrack';
|
||||
midiTrack.DeviceChain.AudioOutputRouting.UpperDisplayString['@Value'] = 'Group';
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects && !exporterParams.groupTracks) {
|
||||
const trackSendHolderTemplate = await loadTemplate<ALSTrackSendHolder>('trackSendHolder');
|
||||
const trackSendHolder = structuredClone(trackSendHolderTemplate.TrackSendHolder);
|
||||
|
||||
trackSendHolder.Send.Manual['@Value'] = koTrack.faderParams[FaderParam.FX];
|
||||
|
||||
midiTrack.DeviceChain.Mixer.Sends.TrackSendHolder = trackSendHolder;
|
||||
}
|
||||
|
||||
return { MidiTrack: midiTrack };
|
||||
}
|
||||
|
||||
async function buildGroupTrack(
|
||||
koTrack: AblTrack,
|
||||
exporterParams: ExporterParams,
|
||||
maxScenes: number,
|
||||
) {
|
||||
const groupTrackTemplate = await loadTemplate<ALSGroupTrack>('groupTrack');
|
||||
const groupTrack = structuredClone(groupTrackTemplate.GroupTrack);
|
||||
|
||||
groupTrack['@Id'] = _localGroupId++;
|
||||
groupTrack.Name.EffectiveName['@Value'] = koTrack.group.toLocaleUpperCase();
|
||||
groupTrack.Name.UserName['@Value'] = koTrack.group.toLocaleUpperCase();
|
||||
groupTrack.Color['@Value'] = 5 + _localGroupTrackColor++;
|
||||
groupTrack.Slots.GroupTrackSlot = [];
|
||||
|
||||
// adding empty slots for clips (or Ableton will crash)
|
||||
// must be at least 8 slots to avoid Ableton crashes
|
||||
for (let sc = 0; sc < Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes); sc++) {
|
||||
groupTrack.Slots.GroupTrackSlot.push({
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects && koTrack.faderParams[FaderParam.FX] !== -1) {
|
||||
const trackSendHolderTemplate = await loadTemplate<ALSTrackSendHolder>('trackSendHolder');
|
||||
const trackSendHolder = structuredClone(trackSendHolderTemplate.TrackSendHolder);
|
||||
|
||||
trackSendHolder.Send.Manual['@Value'] = koTrack.faderParams[FaderParam.FX];
|
||||
|
||||
groupTrack.DeviceChain.Mixer.Sends.TrackSendHolder = trackSendHolder;
|
||||
}
|
||||
|
||||
return { GroupTrack: groupTrack };
|
||||
}
|
||||
|
||||
async function buildScenes(scenes: AblScene[], settings: ProjectSettings, maxScenes: number) {
|
||||
const sceneTemplate = await loadTemplate<ALSScene>('scene');
|
||||
const result: ALSSceneContent[] = [];
|
||||
const sceneCount = Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes);
|
||||
|
||||
for (let index = 0; index < sceneCount; index++) {
|
||||
const scene = scenes[index];
|
||||
const sceneContent = structuredClone(sceneTemplate.Scene);
|
||||
|
||||
sceneContent['@Id'] = _localId++;
|
||||
sceneContent.Name['@Value'] = scene?.name || '';
|
||||
sceneContent.Tempo['@Value'] = settings.bpm;
|
||||
|
||||
result.push(sceneContent);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function buildTracks(tracks: AblTrack[], maxScenes: number, exporterParams: ExporterParams) {
|
||||
const result = [];
|
||||
let trackGroupId = -1;
|
||||
let currentGroup = '';
|
||||
|
||||
// if the tracks are grouped, we need to create a group track for each group BEFORE the children midi tracks
|
||||
// this took me around 3 hours to figure out
|
||||
for (const koTrack of tracks.sort((a, b) => a.group.localeCompare(b.group))) {
|
||||
if (exporterParams.groupTracks && koTrack.group !== currentGroup[0]) {
|
||||
currentGroup = koTrack.group;
|
||||
const groupTrack = await buildGroupTrack(koTrack, exporterParams, maxScenes);
|
||||
trackGroupId = groupTrack.GroupTrack['@Id'];
|
||||
result.push(groupTrack);
|
||||
}
|
||||
|
||||
const midiTrack = await buildTrack(koTrack, maxScenes, trackGroupId, exporterParams);
|
||||
|
||||
result.push(midiTrack);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function buildReturnTrack(projectData: ProjectRawData, trackId: number = 0) {
|
||||
const returnTrackTemplate = await loadTemplate<ALSReturnTrack>('returnTrack');
|
||||
const returnTrack = structuredClone(returnTrackTemplate.ReturnTrack);
|
||||
|
||||
returnTrack['@Id'] = trackId;
|
||||
returnTrack.Name.EffectiveName['@Value'] = EFFECTS[projectData.effects.effectType] || '';
|
||||
returnTrack.Name.UserName['@Value'] = EFFECTS[projectData.effects.effectType] || '';
|
||||
returnTrack.Color['@Value'] = 28;
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Reverb) {
|
||||
const effectReverbTemplate = await loadTemplate<ALSReverb>('effectReverb');
|
||||
const effectReverb = structuredClone(effectReverbTemplate.Reverb);
|
||||
|
||||
// these params are very rough approximation of the KO's reverb
|
||||
// it's not exact, but close enough
|
||||
effectReverb.PreDelay.Manual['@Value'] = 0.5;
|
||||
effectReverb.RoomSize.Manual['@Value'] = 300;
|
||||
effectReverb.ShelfHiFreq.Manual['@Value'] = 1000;
|
||||
effectReverb.DecayTime.Manual['@Value'] = projectData.effects.param1 * 8000; // max 8 seconds which is kinda close to how it sounds in KO
|
||||
effectReverb.ShelfHiGain.Manual['@Value'] = projectData.effects.param2; // param2 is "color" which roughly matches the high shelf gain cut amount
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Reverb: effectReverb };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Delay) {
|
||||
const effectDelayTemplate = await loadTemplate<ALSDelay>('effectDelay');
|
||||
const effectDelay = structuredClone(effectDelayTemplate.Delay);
|
||||
|
||||
effectDelay.DelayLine_SyncL.Manual['@Value'] = 'false';
|
||||
effectDelay.DelayLine_SyncR.Manual['@Value'] = 'false';
|
||||
// don't even ask how I came up with this formula
|
||||
effectDelay.DelayLine_TimeL.Manual['@Value'] =
|
||||
0.017397 * (287.725 ** projectData.effects.param1 - 1);
|
||||
effectDelay.DelayLine_TimeR.Manual['@Value'] =
|
||||
0.017397 * (287.725 ** projectData.effects.param1 - 1);
|
||||
effectDelay.Feedback.Manual['@Value'] = projectData.effects.param2;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Delay: effectDelay };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Distortion) {
|
||||
const effectDistortionTemplate = await loadTemplate<ALSDistortion>('effectDistortion');
|
||||
const effectDistortion = structuredClone(effectDistortionTemplate.Overdrive);
|
||||
|
||||
effectDistortion.Drive.Manual['@Value'] = projectData.effects.param1 * 50;
|
||||
effectDistortion.Tone.Manual['@Value'] = projectData.effects.param2 * 100;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Overdrive: effectDistortion };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Chorus) {
|
||||
const effectChorusTemplate = await loadTemplate<ALSChorus>('effectChorus');
|
||||
const effectChorus = structuredClone(effectChorusTemplate.Chorus2);
|
||||
|
||||
effectChorus.Amount.Manual['@Value'] = projectData.effects.param1;
|
||||
effectChorus.Feedback.Manual['@Value'] = projectData.effects.param2 * 0.8;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Chorus2: effectChorus };
|
||||
}
|
||||
|
||||
// putting a filter in return track is absolutely pointless, but hey, why not
|
||||
if (projectData.effects.effectType === EffectType.Filter) {
|
||||
const effectFilterTemplate = await loadTemplate<ALSFilter>('effectFilter');
|
||||
const effectFilter = structuredClone(effectFilterTemplate.AutoFilter);
|
||||
|
||||
effectFilter.Cutoff.Manual['@Value'] = 20 + projectData.effects.param1 * (135 - 20);
|
||||
effectFilter.Resonance.Manual['@Value'] = projectData.effects.param2 * 1.25;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { AutoFilter: effectFilter };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Compressor) {
|
||||
const effectCompressorTemplate = await loadTemplate<ALSCompressor>('effectCompressor');
|
||||
const effectCompressor = structuredClone(effectCompressorTemplate.Compressor2);
|
||||
|
||||
// params are ignored for now, just putting a basic compressor
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Compressor2: effectCompressor };
|
||||
}
|
||||
|
||||
return { ReturnTrack: returnTrack };
|
||||
}
|
||||
|
||||
export async function buildProject(projectData: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const transformedData = abletonTransformer(projectData, exporterParams);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('projectData', projectData);
|
||||
console.log('transformedData', transformedData);
|
||||
}
|
||||
|
||||
// reset local ids
|
||||
_localId = 1;
|
||||
_localGroupId = 50;
|
||||
_localTrackColor = 1;
|
||||
_localGroupTrackColor = 1;
|
||||
|
||||
const projectTemplate = await loadTemplate<ALSProject>('project');
|
||||
const project = structuredClone(projectTemplate);
|
||||
|
||||
// setting up tempo
|
||||
project.Ableton.LiveSet.MasterTrack.DeviceChain.Mixer.Tempo.Manual['@Value'] =
|
||||
projectData.settings.bpm;
|
||||
project.Ableton.LiveSet.MasterTrack.AutomationEnvelopes.Envelopes.AutomationEnvelope[1].Automation.Events.FloatEvent[
|
||||
'@Value'
|
||||
] = projectData.settings.bpm;
|
||||
// setting up time signature
|
||||
project.Ableton.LiveSet.MasterTrack.AutomationEnvelopes.Envelopes.AutomationEnvelope[0].Automation.Events.EnumEvent[
|
||||
'@Value'
|
||||
] =
|
||||
TIME_SIGNATURES[
|
||||
`${projectData.scenesSettings.timeSignature.numerator}/${projectData.scenesSettings.timeSignature.denominator}`
|
||||
] || TIME_SIGNATURES['4/4'];
|
||||
|
||||
project.Ableton.LiveSet.Tracks['#'] = await buildTracks(
|
||||
transformedData.tracks,
|
||||
transformedData.scenes.length,
|
||||
exporterParams,
|
||||
);
|
||||
|
||||
if (exporterParams.sendEffects) {
|
||||
const returnTrack = await buildReturnTrack(
|
||||
projectData,
|
||||
project.Ableton.LiveSet.Tracks['#'].length + 1,
|
||||
);
|
||||
|
||||
project.Ableton.LiveSet.Tracks['#'].push(returnTrack);
|
||||
}
|
||||
|
||||
if (exporterParams.clips) {
|
||||
project.Ableton.LiveSet.Scenes.Scene = await buildScenes(
|
||||
transformedData.scenes,
|
||||
projectData.settings,
|
||||
transformedData.scenes.length,
|
||||
);
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects) {
|
||||
project.Ableton.LiveSet.SendsPre = {
|
||||
SendPreBool: {
|
||||
'@Id': 1,
|
||||
'@Value': 'false',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const fixedRoot = fixIds(project);
|
||||
fixedRoot.Ableton.LiveSet.NextPointeeId['@Value'] = getId() + 1;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('ROOT', fixedRoot);
|
||||
}
|
||||
|
||||
const newXml = create(fixedRoot).end({ prettyPrint: true, indent: '\t' });
|
||||
const gzipped = gzipString(newXml);
|
||||
|
||||
return gzipped;
|
||||
}
|
||||
73
src/lib/exporters/ableton/index.ts
Normal file
73
src/lib/exporters/ableton/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportResultFile,
|
||||
ExportStatus,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../../types/types';
|
||||
import { AbortError } from '../../utils';
|
||||
import { collectSamples } from '../utils';
|
||||
import { buildProject } from './builders';
|
||||
|
||||
async function exportAbleton(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ExportResult> {
|
||||
const files: ExportResultFile[] = [];
|
||||
const projectName = exporterParams.projectName || `Project${projectId}`;
|
||||
const zippedProject = new JSZip();
|
||||
|
||||
progressCallback({ progress: 1, status: 'Building project...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const alsFile = await buildProject(data, exporterParams);
|
||||
|
||||
zippedProject.file(`${projectName} Project/${projectName}.als`, alsFile);
|
||||
zippedProject.file(`${projectName} Project/Ableton Project Info/.dummy`, '');
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
samples.forEach((s) => {
|
||||
zippedProject.file(`${projectName} Project/Samples/Imported/${s.name}`, s.data);
|
||||
});
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle everything...' });
|
||||
|
||||
const zippedProjectFile = await zippedProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
files.push({
|
||||
name: `${projectName}.zip`,
|
||||
url: URL.createObjectURL(zippedProjectFile),
|
||||
type: 'archive',
|
||||
size: zippedProjectFile.size,
|
||||
});
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
};
|
||||
}
|
||||
|
||||
export default exportAbleton;
|
||||
127
src/lib/exporters/ableton/templates/drumBranch.xml
Normal file
127
src/lib/exporters/ableton/templates/drumBranch.xml
Normal file
@@ -0,0 +1,127 @@
|
||||
<DrumBranch Id="0">
|
||||
<LomId Value="0" />
|
||||
<Name>
|
||||
<EffectiveName Value="005 nt kick d" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<IsSelected Value="false" />
|
||||
<DeviceChain>
|
||||
<MidiToAudioDeviceChain Id="0">
|
||||
<Devices>
|
||||
|
||||
</Devices>
|
||||
<SignalModulations />
|
||||
</MidiToAudioDeviceChain>
|
||||
</DeviceChain>
|
||||
<BranchSelectorRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0" />
|
||||
<CrossfadeMin Value="0" />
|
||||
<CrossfadeMax Value="0" />
|
||||
</BranchSelectorRange>
|
||||
<IsSoloed Value="false" />
|
||||
<SessionViewBranchWidth Value="55" />
|
||||
<IsHighlightedInSessionView Value="false" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Color Value="1" />
|
||||
<AutoColored Value="true" />
|
||||
<AutoColorScheme Value="0" />
|
||||
<SoloActivatedInSessionMixer Value="false" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<MixerDevice>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22191">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22192" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22193">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22194">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22195">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<Panorama>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22196">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22197">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Panorama>
|
||||
<SendInfos />
|
||||
<RoutingHelper>
|
||||
<Routable>
|
||||
<Target Value="AudioOut/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<TargetEnum Value="0" />
|
||||
</RoutingHelper>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</MixerDevice>
|
||||
<BranchInfo>
|
||||
<ReceivingNote Value="92" />
|
||||
<SendingNote Value="60" />
|
||||
<ChokeGroup Value="0" />
|
||||
</BranchInfo>
|
||||
</DrumBranch>
|
||||
460
src/lib/exporters/ableton/templates/drumRack.xml
Normal file
460
src/lib/exporters/ableton/templates/drumRack.xml
Normal file
@@ -0,0 +1,460 @@
|
||||
<DrumGroupDevice Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22155">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22156" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="DrumGroupDevice" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:Synths#Drum%20Rack" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="DrumGroupDevice" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:instr:DrumGroupDevice" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Branches>
|
||||
</Branches>
|
||||
<IsBranchesListVisible Value="false" />
|
||||
<IsReturnBranchesListVisible Value="false" />
|
||||
<IsRangesEditorVisible Value="false" />
|
||||
<AreDevicesVisible Value="false" />
|
||||
<NumVisibleMacroControls Value="8" />
|
||||
<MacroControls.0>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22157">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22158">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.0>
|
||||
<MacroControls.1>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22159">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22160">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.1>
|
||||
<MacroControls.2>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22161">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22162">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.2>
|
||||
<MacroControls.3>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22163">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22164">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.3>
|
||||
<MacroControls.4>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22165">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22166">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.4>
|
||||
<MacroControls.5>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22167">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22168">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.5>
|
||||
<MacroControls.6>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22169">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22170">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.6>
|
||||
<MacroControls.7>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22171">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22172">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.7>
|
||||
<MacroControls.8>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22173">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22174">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.8>
|
||||
<MacroControls.9>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22175">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22176">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.9>
|
||||
<MacroControls.10>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22177">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22178">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.10>
|
||||
<MacroControls.11>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22179">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22180">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.11>
|
||||
<MacroControls.12>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22181">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22182">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.12>
|
||||
<MacroControls.13>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22183">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22184">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.13>
|
||||
<MacroControls.14>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22185">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22186">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.14>
|
||||
<MacroControls.15>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22187">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22188">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.15>
|
||||
<MacroDisplayNames.0 Value="Macro 1" />
|
||||
<MacroDisplayNames.1 Value="Macro 2" />
|
||||
<MacroDisplayNames.2 Value="Macro 3" />
|
||||
<MacroDisplayNames.3 Value="Macro 4" />
|
||||
<MacroDisplayNames.4 Value="Macro 5" />
|
||||
<MacroDisplayNames.5 Value="Macro 6" />
|
||||
<MacroDisplayNames.6 Value="Macro 7" />
|
||||
<MacroDisplayNames.7 Value="Macro 8" />
|
||||
<MacroDisplayNames.8 Value="Macro 9" />
|
||||
<MacroDisplayNames.9 Value="Macro 10" />
|
||||
<MacroDisplayNames.10 Value="Macro 11" />
|
||||
<MacroDisplayNames.11 Value="Macro 12" />
|
||||
<MacroDisplayNames.12 Value="Macro 13" />
|
||||
<MacroDisplayNames.13 Value="Macro 14" />
|
||||
<MacroDisplayNames.14 Value="Macro 15" />
|
||||
<MacroDisplayNames.15 Value="Macro 16" />
|
||||
<MacroDefaults.0 Value="-1" />
|
||||
<MacroDefaults.1 Value="-1" />
|
||||
<MacroDefaults.2 Value="-1" />
|
||||
<MacroDefaults.3 Value="-1" />
|
||||
<MacroDefaults.4 Value="-1" />
|
||||
<MacroDefaults.5 Value="-1" />
|
||||
<MacroDefaults.6 Value="-1" />
|
||||
<MacroDefaults.7 Value="-1" />
|
||||
<MacroDefaults.8 Value="-1" />
|
||||
<MacroDefaults.9 Value="-1" />
|
||||
<MacroDefaults.10 Value="-1" />
|
||||
<MacroDefaults.11 Value="-1" />
|
||||
<MacroDefaults.12 Value="-1" />
|
||||
<MacroDefaults.13 Value="-1" />
|
||||
<MacroDefaults.14 Value="-1" />
|
||||
<MacroDefaults.15 Value="-1" />
|
||||
<MacroAnnotations.0 Value="" />
|
||||
<MacroAnnotations.1 Value="" />
|
||||
<MacroAnnotations.2 Value="" />
|
||||
<MacroAnnotations.3 Value="" />
|
||||
<MacroAnnotations.4 Value="" />
|
||||
<MacroAnnotations.5 Value="" />
|
||||
<MacroAnnotations.6 Value="" />
|
||||
<MacroAnnotations.7 Value="" />
|
||||
<MacroAnnotations.8 Value="" />
|
||||
<MacroAnnotations.9 Value="" />
|
||||
<MacroAnnotations.10 Value="" />
|
||||
<MacroAnnotations.11 Value="" />
|
||||
<MacroAnnotations.12 Value="" />
|
||||
<MacroAnnotations.13 Value="" />
|
||||
<MacroAnnotations.14 Value="" />
|
||||
<MacroAnnotations.15 Value="" />
|
||||
<ForceDisplayGenericValue.0 Value="false" />
|
||||
<ForceDisplayGenericValue.1 Value="false" />
|
||||
<ForceDisplayGenericValue.2 Value="false" />
|
||||
<ForceDisplayGenericValue.3 Value="false" />
|
||||
<ForceDisplayGenericValue.4 Value="false" />
|
||||
<ForceDisplayGenericValue.5 Value="false" />
|
||||
<ForceDisplayGenericValue.6 Value="false" />
|
||||
<ForceDisplayGenericValue.7 Value="false" />
|
||||
<ForceDisplayGenericValue.8 Value="false" />
|
||||
<ForceDisplayGenericValue.9 Value="false" />
|
||||
<ForceDisplayGenericValue.10 Value="false" />
|
||||
<ForceDisplayGenericValue.11 Value="false" />
|
||||
<ForceDisplayGenericValue.12 Value="false" />
|
||||
<ForceDisplayGenericValue.13 Value="false" />
|
||||
<ForceDisplayGenericValue.14 Value="false" />
|
||||
<ForceDisplayGenericValue.15 Value="false" />
|
||||
<AreMacroControlsVisible Value="false" />
|
||||
<IsAutoSelectEnabled Value="true" />
|
||||
<ChainSelector>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22189">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22190">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ChainSelector>
|
||||
<ChainSelectorRelativePosition Value="-1073741824" />
|
||||
<ViewsToRestoreWhenUnfolding Value="0" />
|
||||
<ReturnBranches />
|
||||
<BranchesSplitterProportion Value="0.5" />
|
||||
<ShowBranchesInSessionMixer Value="false" />
|
||||
<MacroColor.0 Value="-1" />
|
||||
<MacroColor.1 Value="-1" />
|
||||
<MacroColor.2 Value="-1" />
|
||||
<MacroColor.3 Value="-1" />
|
||||
<MacroColor.4 Value="-1" />
|
||||
<MacroColor.5 Value="-1" />
|
||||
<MacroColor.6 Value="-1" />
|
||||
<MacroColor.7 Value="-1" />
|
||||
<MacroColor.8 Value="-1" />
|
||||
<MacroColor.9 Value="-1" />
|
||||
<MacroColor.10 Value="-1" />
|
||||
<MacroColor.11 Value="-1" />
|
||||
<MacroColor.12 Value="-1" />
|
||||
<MacroColor.13 Value="-1" />
|
||||
<MacroColor.14 Value="-1" />
|
||||
<MacroColor.15 Value="-1" />
|
||||
<LockId Value="0" />
|
||||
<LockSeal Value="0" />
|
||||
<ChainsListWrapper LomId="0" />
|
||||
<ReturnChainsListWrapper LomId="0" />
|
||||
<MacroVariations>
|
||||
<MacroSnapshots />
|
||||
</MacroVariations>
|
||||
<ExcludeMacroFromRandomization.0 Value="false" />
|
||||
<ExcludeMacroFromRandomization.1 Value="false" />
|
||||
<ExcludeMacroFromRandomization.2 Value="false" />
|
||||
<ExcludeMacroFromRandomization.3 Value="false" />
|
||||
<ExcludeMacroFromRandomization.4 Value="false" />
|
||||
<ExcludeMacroFromRandomization.5 Value="false" />
|
||||
<ExcludeMacroFromRandomization.6 Value="false" />
|
||||
<ExcludeMacroFromRandomization.7 Value="false" />
|
||||
<ExcludeMacroFromRandomization.8 Value="false" />
|
||||
<ExcludeMacroFromRandomization.9 Value="false" />
|
||||
<ExcludeMacroFromRandomization.10 Value="false" />
|
||||
<ExcludeMacroFromRandomization.11 Value="false" />
|
||||
<ExcludeMacroFromRandomization.12 Value="false" />
|
||||
<ExcludeMacroFromRandomization.13 Value="false" />
|
||||
<ExcludeMacroFromRandomization.14 Value="false" />
|
||||
<ExcludeMacroFromRandomization.15 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.0 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.1 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.2 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.3 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.4 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.5 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.6 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.7 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.8 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.9 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.10 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.11 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.12 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.13 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.14 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.15 Value="false" />
|
||||
<AreMacroVariationsControlsVisible Value="false" />
|
||||
<ChainSelectorFilterMidiCtrl Value="false" />
|
||||
<RangeTypeIndex Value="1" />
|
||||
<ShowsZonesInsteadOfNoteNames Value="true" />
|
||||
<IsMidiSectionVisible Value="false" />
|
||||
<AreSendsVisible Value="false" />
|
||||
<ArePadsVisible Value="true" />
|
||||
<PadScrollPosition Value="19" />
|
||||
<DrumPadsListWrapper LomId="0" />
|
||||
<VisibleDrumPadsListWrapper LomId="0" />
|
||||
</DrumGroupDevice>
|
||||
248
src/lib/exporters/ableton/templates/effectChorus.xml
Normal file
248
src/lib/exporters/ableton/templates/effectChorus.xml
Normal file
@@ -0,0 +1,248 @@
|
||||
<Chorus2 Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23870">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23871" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Chorus2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Pitch%20&%20Modulation:Chorus-Ensemble" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Chorus2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Chorus2" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Mode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23872">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Mode>
|
||||
<Shaping>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23873">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23874">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Shaping>
|
||||
<Rate>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.9000000358" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="15" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23875">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23876">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Rate>
|
||||
<Amount>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23877">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23878">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Amount>
|
||||
<Feedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.9900000095" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23879">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23880">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Feedback>
|
||||
<InvertFeedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23881">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</InvertFeedback>
|
||||
<VibratoOffset>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="180" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23882">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23883">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</VibratoOffset>
|
||||
<HighpassEnabled>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23884">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</HighpassEnabled>
|
||||
<HighpassFrequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50.0000076" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="20" />
|
||||
<Max Value="2000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23885">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23886">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</HighpassFrequency>
|
||||
<Width>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23887">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23888">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Width>
|
||||
<Warmth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23889">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23890">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Warmth>
|
||||
<OutputGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.9999999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23891">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23892">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</OutputGain>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23893">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23894">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</Chorus2>
|
||||
386
src/lib/exporters/ableton/templates/effectCompressor.xml
Normal file
386
src/lib/exporters/ableton/templates/effectCompressor.xml
Normal file
@@ -0,0 +1,386 @@
|
||||
<Compressor2 Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23943">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23944" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Compressor2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Dynamics:Compressor" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Compressor2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Compressor2" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Threshold>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23945">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23946">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Threshold>
|
||||
<Ratio>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="340282326356119256160033759537265639424" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23947">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23948">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Ratio>
|
||||
<ExpansionRatio>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1.14999998" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23949">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23950">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ExpansionRatio>
|
||||
<Attack>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="1000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23951">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23952">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Attack>
|
||||
<Release>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="30" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="3000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23953">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23954">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Release>
|
||||
<AutoReleaseControlOnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23955">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</AutoReleaseControlOnOff>
|
||||
<Gain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-36" />
|
||||
<Max Value="36" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23956">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23957">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Gain>
|
||||
<GainCompensation>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23958">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</GainCompensation>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23959">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23960">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<Model>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23961">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Model>
|
||||
<LegacyModel>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23962">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyModel>
|
||||
<LogEnvelope>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23963">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</LogEnvelope>
|
||||
<LegacyEnvFollowerMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23964">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyEnvFollowerMode>
|
||||
<Knee>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="18" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23965">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23966">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Knee>
|
||||
<LookAhead>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23967">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LookAhead>
|
||||
<SideListen>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23968">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</SideListen>
|
||||
<SideChain>
|
||||
<OnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23969">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</OnOff>
|
||||
<RoutedInput>
|
||||
<Routable>
|
||||
<Target Value="AudioIn/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="15.8489332" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23970">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23971">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
</RoutedInput>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23972">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23973">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</SideChain>
|
||||
<SideChainEq>
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23974">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<Mode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="5" />
|
||||
<AutomationTarget Id="23975">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Mode>
|
||||
<Freq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="80" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="30" />
|
||||
<Max Value="15000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23976">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23977">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Freq>
|
||||
<Q>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.7071067691" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="12" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23978">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23979">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Q>
|
||||
<Gain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-15" />
|
||||
<Max Value="15" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23980">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23981">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Gain>
|
||||
</SideChainEq>
|
||||
<Live8LegacyMode Value="false" />
|
||||
<ViewMode Value="2" />
|
||||
<IsOutputCurveVisible Value="false" />
|
||||
<RmsTimeShort Value="8" />
|
||||
<RmsTimeLong Value="250" />
|
||||
<ReleaseTimeShort Value="15" />
|
||||
<ReleaseTimeLong Value="1500" />
|
||||
<CrossfaderSmoothingTime Value="10" />
|
||||
</Compressor2>
|
||||
379
src/lib/exporters/ableton/templates/effectDelay.xml
Normal file
379
src/lib/exporters/ableton/templates/effectDelay.xml
Normal file
@@ -0,0 +1,379 @@
|
||||
<Delay Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23801">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23802" />
|
||||
<LastSelectedTimeableIndex Value="4" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Delay" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Delay%20&%20Loop:Delay" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Delay" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Delay" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<DelayLine_SmoothingMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23803">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SmoothingMode>
|
||||
<DelayLine_Link>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23804">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_Link>
|
||||
<DelayLine_PingPong>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23805">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_PingPong>
|
||||
<DelayLine_SyncL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23806">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_SyncL>
|
||||
<DelayLine_SyncR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23807">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_SyncR>
|
||||
<DelayLine_TimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.3749999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23808">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23809">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_TimeL>
|
||||
<DelayLine_TimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.3749999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23810">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23811">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_TimeR>
|
||||
<DelayLine_SimpleDelayTimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="300" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23812">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23813">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_SimpleDelayTimeL>
|
||||
<DelayLine_SimpleDelayTimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="300" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23814">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23815">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_SimpleDelayTimeR>
|
||||
<DelayLine_PingPongDelayTimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="999" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23816">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23817">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_PingPongDelayTimeL>
|
||||
<DelayLine_PingPongDelayTimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="999" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23818">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23819">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_PingPongDelayTimeR>
|
||||
<DelayLine_SyncedSixteenthL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23820">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SyncedSixteenthL>
|
||||
<DelayLine_SyncedSixteenthR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="3" />
|
||||
<AutomationTarget Id="23821">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SyncedSixteenthR>
|
||||
<DelayLine_OffsetL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-0.3330000043" />
|
||||
<Max Value="0.3330000043" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23822">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23823">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_OffsetL>
|
||||
<DelayLine_OffsetR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-0.3330000043" />
|
||||
<Max Value="0.3330000043" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23824">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23825">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_OffsetR>
|
||||
<DelayLine_CompatibilityMode Value="0" />
|
||||
<Feedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.9499999881" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23826">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23827">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Feedback>
|
||||
<Freeze>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23828">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Freeze>
|
||||
<Filter_On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23829">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Filter_On>
|
||||
<Filter_Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="999.999878" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="49.9999962" />
|
||||
<Max Value="18000.0059" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23830">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23831">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Filter_Frequency>
|
||||
<Filter_Bandwidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="8" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23832">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23833">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Filter_Bandwidth>
|
||||
<Modulation_Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.01000000071" />
|
||||
<Max Value="39.9999962" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23834">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23835">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_Frequency>
|
||||
<Modulation_AmountTime>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23836">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23837">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_AmountTime>
|
||||
<Modulation_AmountFilter>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23838">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23839">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_AmountFilter>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23840">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23841">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<DryWetMode Value="1" />
|
||||
<EcoProcessing Value="false" />
|
||||
</Delay>
|
||||
163
src/lib/exporters/ableton/templates/effectDistortion.xml
Normal file
163
src/lib/exporters/ableton/templates/effectDistortion.xml
Normal file
@@ -0,0 +1,163 @@
|
||||
<Overdrive Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23856">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23857" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Overdrive" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Drive%20&%20Color:Overdrive" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Overdrive" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Overdrive" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<MidFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1250" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="50" />
|
||||
<Max Value="20000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23858">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23859">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MidFreq>
|
||||
<BandWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23860">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23861">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandWidth>
|
||||
<Drive>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23862">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23863">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Drive>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23864">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23865">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<Tone>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23866">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23867">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Tone>
|
||||
<PreserveDynamics>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23868">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23869">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</PreserveDynamics>
|
||||
</Overdrive>
|
||||
435
src/lib/exporters/ableton/templates/effectFilter.xml
Normal file
435
src/lib/exporters/ableton/templates/effectFilter.xml
Normal file
@@ -0,0 +1,435 @@
|
||||
<AutoFilter Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23895">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23896" />
|
||||
<LastSelectedTimeableIndex Value="14" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="AutoFilter" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#EQ%20&%20Filters:Auto%20Filter" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="AutoFilter" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:AutoFilter" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<LegacyMode Value="false" />
|
||||
<LegacyFilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23897">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyFilterType>
|
||||
<FilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23898">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</FilterType>
|
||||
<CircuitLpHp>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23899">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CircuitLpHp>
|
||||
<CircuitBpNoMo>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23900">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CircuitBpNoMo>
|
||||
<Slope>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23901">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Slope>
|
||||
<Cutoff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="127" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="20" />
|
||||
<Max Value="135" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23902">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23903">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Cutoff>
|
||||
<CutoffLimit Value="135" />
|
||||
<LegacyQ>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.8199999928" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.200000003" />
|
||||
<Max Value="3" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23904">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23905">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</LegacyQ>
|
||||
<Resonance>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.1379310191" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1.25" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23906">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23907">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Resonance>
|
||||
<Morph>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23908">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23909">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Morph>
|
||||
<Drive>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="24" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23910">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23911">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Drive>
|
||||
<ModHub>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-127" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23912">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23913">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ModHub>
|
||||
<Attack>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="30" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23914">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23915">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Attack>
|
||||
<Release>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="200" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="400" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23916">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23917">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Release>
|
||||
<LfoAmount>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="30" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23918">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23919">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</LfoAmount>
|
||||
<Lfo>
|
||||
<Type>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23920">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Type>
|
||||
<Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.1000000089" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="10" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23921">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23922">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Frequency>
|
||||
<RateType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23923">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</RateType>
|
||||
<BeatRate>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="21" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23924">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23925">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BeatRate>
|
||||
<StereoMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23926">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</StereoMode>
|
||||
<Spin>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23927">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23928">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Spin>
|
||||
<Phase>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="360" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23929">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23930">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Phase>
|
||||
<Offset>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="360" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23931">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23932">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Offset>
|
||||
<IsOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23933">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</IsOn>
|
||||
<Quantize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23934">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Quantize>
|
||||
<BeatQuantize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23935">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</BeatQuantize>
|
||||
<NoiseWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23936">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23937">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</NoiseWidth>
|
||||
</Lfo>
|
||||
<SideChain>
|
||||
<OnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23938">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</OnOff>
|
||||
<RoutedInput>
|
||||
<Routable>
|
||||
<Target Value="AudioIn/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="15.8489332" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23939">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23940">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
</RoutedInput>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23941">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23942">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</SideChain>
|
||||
</AutoFilter>
|
||||
480
src/lib/exporters/ableton/templates/effectReverb.xml
Normal file
480
src/lib/exporters/ableton/templates/effectReverb.xml
Normal file
@@ -0,0 +1,480 @@
|
||||
<Reverb Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23747">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23748" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Reverb" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Reverb%20&%20Resonance:Reverb" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Reverb" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Reverb" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<PreDelay>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2.49999976" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="249.999969" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23749">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23750">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</PreDelay>
|
||||
<BandHighOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23751">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</BandHighOn>
|
||||
<BandLowOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23752">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</BandLowOn>
|
||||
<BandFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="829.999756" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="49.9999962" />
|
||||
<Max Value="18000.0059" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23753">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23754">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandFreq>
|
||||
<BandWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="5.8499999" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23755">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23756">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandWidth>
|
||||
<SpinOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23757">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</SpinOn>
|
||||
<EarlyReflectModFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.2977000177" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.07400000095" />
|
||||
<Max Value="1.29999983" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23758">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23759">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</EarlyReflectModFreq>
|
||||
<EarlyReflectModDepth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="17.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="2" />
|
||||
<Max Value="55" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23760">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23761">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</EarlyReflectModDepth>
|
||||
<DiffuseDelay>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23762">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23763">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DiffuseDelay>
|
||||
<ShelfHighOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23764">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ShelfHighOn>
|
||||
<HighFilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23765">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</HighFilterType>
|
||||
<ShelfHiFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4500.00146" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="19.9999981" />
|
||||
<Max Value="15999.998" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23766">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23767">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfHiFreq>
|
||||
<ShelfHiGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.6999999881" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23768">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23769">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfHiGain>
|
||||
<ShelfLowOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23770">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ShelfLowOn>
|
||||
<ShelfLoFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="90.0000076" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="19.9999981" />
|
||||
<Max Value="15000.001" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23771">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23772">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfLoFreq>
|
||||
<ShelfLoGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.75" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.200000003" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23773">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23774">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfLoGain>
|
||||
<ChorusOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23775">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ChorusOn>
|
||||
<SizeModFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.02000000142" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.01000000071" />
|
||||
<Max Value="8" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23776">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23777">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SizeModFreq>
|
||||
<SizeModDepth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.01999999955" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="4" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23778">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23779">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SizeModDepth>
|
||||
<DecayTime>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1200.00012" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="199.999985" />
|
||||
<Max Value="60000.0039" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23780">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23781">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DecayTime>
|
||||
<AllPassGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.6000000238" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="0.9599999785" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23782">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23783">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</AllPassGain>
|
||||
<AllPassSize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.400000006" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.05000000075" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23784">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23785">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</AllPassSize>
|
||||
<FreezeOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23786">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</FreezeOn>
|
||||
<FlatOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23787">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</FlatOn>
|
||||
<CutOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23788">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</CutOn>
|
||||
<RoomSize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="99.9999924" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.2220000029" />
|
||||
<Max Value="499.999939" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23789">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23790">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</RoomSize>
|
||||
<SizeSmoothing>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23791">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</SizeSmoothing>
|
||||
<StereoSeparation>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="120" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23792">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23793">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</StereoSeparation>
|
||||
<RoomType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23794">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</RoomType>
|
||||
<MixReflect>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.02999999933" />
|
||||
<Max Value="1.99530005" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23795">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23796">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixReflect>
|
||||
<MixDiffuse>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.02999999933" />
|
||||
<Max Value="1.99530005" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23797">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23798">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixDiffuse>
|
||||
<MixDirect>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23799">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23800">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixDirect>
|
||||
<StereoSeparationOnDrySignal Value="false" />
|
||||
</Reverb>
|
||||
269
src/lib/exporters/ableton/templates/groupTrack.xml
Normal file
269
src/lib/exporters/ableton/templates/groupTrack.xml
Normal file
@@ -0,0 +1,269 @@
|
||||
<GroupTrack Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="1-Group" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="-1" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="true" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<Slots>
|
||||
</Slots>
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="51" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22311">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22312" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22318">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22319">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22320">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22321">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22322">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22323">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22316">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSesstionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="22326">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22327">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22328" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList />
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="22329">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="22330">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="22331">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="22332">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="22333">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
</DeviceChain>
|
||||
</GroupTrack>
|
||||
119
src/lib/exporters/ableton/templates/midiClip.xml
Normal file
119
src/lib/exporters/ableton/templates/midiClip.xml
Normal file
@@ -0,0 +1,119 @@
|
||||
<MidiClip Id="0" Time="0">
|
||||
<LomId Value="0"/>
|
||||
<LomIdView Value="0"/>
|
||||
<CurrentStart Value="0"/>
|
||||
<CurrentEnd Value="4"/>
|
||||
<Loop>
|
||||
<LoopStart Value="0"/>
|
||||
<LoopEnd Value="4"/>
|
||||
<StartRelative Value="0"/>
|
||||
<LoopOn Value="true"/>
|
||||
<OutMarker Value="4"/>
|
||||
<HiddenLoopStart Value="0"/>
|
||||
<HiddenLoopEnd Value="4"/>
|
||||
</Loop>
|
||||
<Name Value=""/>
|
||||
<Annotation Value=""/>
|
||||
<Color Value="10"/>
|
||||
<LaunchMode Value="0"/>
|
||||
<LaunchQuantisation Value="0"/>
|
||||
<TimeSignature>
|
||||
<TimeSignatures>
|
||||
<RemoteableTimeSignature Id="0">
|
||||
<Numerator Value="4"/>
|
||||
<Denominator Value="4"/>
|
||||
<Time Value="0"/>
|
||||
</RemoteableTimeSignature>
|
||||
</TimeSignatures>
|
||||
</TimeSignature>
|
||||
<Envelopes>
|
||||
<Envelopes/>
|
||||
</Envelopes>
|
||||
<ScrollerTimePreserver>
|
||||
<LeftTime Value="0"/>
|
||||
<RightTime Value="4"/>
|
||||
</ScrollerTimePreserver>
|
||||
<TimeSelection>
|
||||
<AnchorTime Value="0"/>
|
||||
<OtherTime Value="0"/>
|
||||
</TimeSelection>
|
||||
<Legato Value="false"/>
|
||||
<Ram Value="false"/>
|
||||
<GrooveSettings>
|
||||
<GrooveId Value="-1"/>
|
||||
</GrooveSettings>
|
||||
<Disabled Value="false"/>
|
||||
<VelocityAmount Value="0"/>
|
||||
<FollowAction>
|
||||
<FollowTime Value="4"/>
|
||||
<IsLinked Value="true"/>
|
||||
<LoopIterations Value="1"/>
|
||||
<FollowActionA Value="4"/>
|
||||
<FollowActionB Value="0"/>
|
||||
<FollowChanceA Value="100"/>
|
||||
<FollowChanceB Value="0"/>
|
||||
<JumpIndexA Value="1"/>
|
||||
<JumpIndexB Value="1"/>
|
||||
<FollowActionEnabled Value="false"/>
|
||||
</FollowAction>
|
||||
<Grid>
|
||||
<FixedNumerator Value="1"/>
|
||||
<FixedDenominator Value="16"/>
|
||||
<GridIntervalPixel Value="20"/>
|
||||
<Ntoles Value="2"/>
|
||||
<SnapToGrid Value="true"/>
|
||||
<Fixed Value="true"/>
|
||||
</Grid>
|
||||
<FreezeStart Value="0"/>
|
||||
<FreezeEnd Value="0"/>
|
||||
<IsWarped Value="true"/>
|
||||
<TakeId Value="1"/>
|
||||
<IsInKey Value="true"/>
|
||||
<ScaleInformation>
|
||||
<Root Value="0"/>
|
||||
<Name Value="Major"/>
|
||||
</ScaleInformation>
|
||||
<Notes>
|
||||
<KeyTracks>
|
||||
<KeyTrack Id="0">
|
||||
<Notes>
|
||||
<MidiNoteEvent Time="0" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="1"/>
|
||||
<MidiNoteEvent Time="1" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="2"/>
|
||||
<MidiNoteEvent Time="2" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="3"/>
|
||||
<MidiNoteEvent Time="3" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="4"/>
|
||||
</Notes>
|
||||
<MidiKey Value="60"/>
|
||||
</KeyTrack>
|
||||
</KeyTracks>
|
||||
<PerNoteEventStore>
|
||||
<EventLists/>
|
||||
</PerNoteEventStore>
|
||||
<NoteProbabilityGroups/>
|
||||
<ProbabilityGroupIdGenerator>
|
||||
<NextId Value="1"/>
|
||||
</ProbabilityGroupIdGenerator>
|
||||
<NoteIdGenerator>
|
||||
<NextId Value="5"/>
|
||||
</NoteIdGenerator>
|
||||
</Notes>
|
||||
<BankSelectCoarse Value="-1"/>
|
||||
<BankSelectFine Value="-1"/>
|
||||
<ProgramChange Value="-1"/>
|
||||
<NoteEditorFoldInZoom Value="-1"/>
|
||||
<NoteEditorFoldInScroll Value="0"/>
|
||||
<NoteEditorFoldOutZoom Value="867"/>
|
||||
<NoteEditorFoldOutScroll Value="-413"/>
|
||||
<NoteEditorFoldScaleZoom Value="-1"/>
|
||||
<NoteEditorFoldScaleScroll Value="0"/>
|
||||
<NoteSpellingPreference Value="0"/>
|
||||
<AccidentalSpellingPreference Value="3"/>
|
||||
<PreferFlatRootNote Value="false"/>
|
||||
<ExpressionGrid>
|
||||
<FixedNumerator Value="1"/>
|
||||
<FixedDenominator Value="16"/>
|
||||
<GridIntervalPixel Value="20"/>
|
||||
<Ntoles Value="2"/>
|
||||
<SnapToGrid Value="false"/>
|
||||
<Fixed Value="false"/>
|
||||
</ExpressionGrid>
|
||||
</MidiClip>
|
||||
850
src/lib/exporters/ableton/templates/midiTrack.xml
Normal file
850
src/lib/exporters/ableton/templates/midiTrack.xml
Normal file
@@ -0,0 +1,850 @@
|
||||
<MidiTrack Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="TRACK_1" />
|
||||
<UserName Value="TRACK_1" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="10" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="true" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<SavedPlayingSlot Value="-1" />
|
||||
<SavedPlayingOffset Value="0" />
|
||||
<Freeze Value="false" />
|
||||
<NeedArrangerRefreeze Value="true" />
|
||||
<PostProcessFreezeClips Value="0" />
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="68" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22182">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22183" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="false" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22184">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22185">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22186">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22187">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22188">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22189">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22190">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22191">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22192">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSessionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="22193">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<MainSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22194">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22195" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList>
|
||||
<ClipSlot Id="0">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="1">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="2">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="3">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="4">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="5">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="6">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="7">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
</ClipSlotList>
|
||||
<MonitoringEnum Value="1" />
|
||||
<ClipTimeable>
|
||||
<ArrangerAutomation>
|
||||
<Events>
|
||||
<MidiClip />
|
||||
<MidiClip />
|
||||
</Events>
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</ClipTimeable>
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="0" />
|
||||
</Recorder>
|
||||
<MidiControllers>
|
||||
<ControllerTargets.0 Id="22196">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.0>
|
||||
<ControllerTargets.1 Id="22197">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.1>
|
||||
<ControllerTargets.2 Id="22198">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.2>
|
||||
<ControllerTargets.3 Id="22199">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.3>
|
||||
<ControllerTargets.4 Id="22200">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.4>
|
||||
<ControllerTargets.5 Id="22201">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.5>
|
||||
<ControllerTargets.6 Id="22202">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.6>
|
||||
<ControllerTargets.7 Id="22203">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.7>
|
||||
<ControllerTargets.8 Id="22204">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.8>
|
||||
<ControllerTargets.9 Id="22205">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.9>
|
||||
<ControllerTargets.10 Id="22206">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.10>
|
||||
<ControllerTargets.11 Id="22207">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.11>
|
||||
<ControllerTargets.12 Id="22208">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.12>
|
||||
<ControllerTargets.13 Id="22209">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.13>
|
||||
<ControllerTargets.14 Id="22210">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.14>
|
||||
<ControllerTargets.15 Id="22211">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.15>
|
||||
<ControllerTargets.16 Id="22212">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.16>
|
||||
<ControllerTargets.17 Id="22213">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.17>
|
||||
<ControllerTargets.18 Id="22214">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.18>
|
||||
<ControllerTargets.19 Id="22215">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.19>
|
||||
<ControllerTargets.20 Id="22216">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.20>
|
||||
<ControllerTargets.21 Id="22217">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.21>
|
||||
<ControllerTargets.22 Id="22218">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.22>
|
||||
<ControllerTargets.23 Id="22219">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.23>
|
||||
<ControllerTargets.24 Id="22220">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.24>
|
||||
<ControllerTargets.25 Id="22221">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.25>
|
||||
<ControllerTargets.26 Id="22222">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.26>
|
||||
<ControllerTargets.27 Id="22223">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.27>
|
||||
<ControllerTargets.28 Id="22224">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.28>
|
||||
<ControllerTargets.29 Id="22225">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.29>
|
||||
<ControllerTargets.30 Id="22226">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.30>
|
||||
<ControllerTargets.31 Id="22227">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.31>
|
||||
<ControllerTargets.32 Id="22228">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.32>
|
||||
<ControllerTargets.33 Id="22229">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.33>
|
||||
<ControllerTargets.34 Id="22230">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.34>
|
||||
<ControllerTargets.35 Id="22231">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.35>
|
||||
<ControllerTargets.36 Id="22232">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.36>
|
||||
<ControllerTargets.37 Id="22233">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.37>
|
||||
<ControllerTargets.38 Id="22234">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.38>
|
||||
<ControllerTargets.39 Id="22235">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.39>
|
||||
<ControllerTargets.40 Id="22236">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.40>
|
||||
<ControllerTargets.41 Id="22237">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.41>
|
||||
<ControllerTargets.42 Id="22238">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.42>
|
||||
<ControllerTargets.43 Id="22239">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.43>
|
||||
<ControllerTargets.44 Id="22240">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.44>
|
||||
<ControllerTargets.45 Id="22241">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.45>
|
||||
<ControllerTargets.46 Id="22242">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.46>
|
||||
<ControllerTargets.47 Id="22243">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.47>
|
||||
<ControllerTargets.48 Id="22244">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.48>
|
||||
<ControllerTargets.49 Id="22245">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.49>
|
||||
<ControllerTargets.50 Id="22246">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.50>
|
||||
<ControllerTargets.51 Id="22247">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.51>
|
||||
<ControllerTargets.52 Id="22248">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.52>
|
||||
<ControllerTargets.53 Id="22249">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.53>
|
||||
<ControllerTargets.54 Id="22250">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.54>
|
||||
<ControllerTargets.55 Id="22251">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.55>
|
||||
<ControllerTargets.56 Id="22252">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.56>
|
||||
<ControllerTargets.57 Id="22253">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.57>
|
||||
<ControllerTargets.58 Id="22254">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.58>
|
||||
<ControllerTargets.59 Id="22255">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.59>
|
||||
<ControllerTargets.60 Id="22256">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.60>
|
||||
<ControllerTargets.61 Id="22257">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.61>
|
||||
<ControllerTargets.62 Id="22258">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.62>
|
||||
<ControllerTargets.63 Id="22259">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.63>
|
||||
<ControllerTargets.64 Id="22260">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.64>
|
||||
<ControllerTargets.65 Id="22261">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.65>
|
||||
<ControllerTargets.66 Id="22262">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.66>
|
||||
<ControllerTargets.67 Id="22263">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.67>
|
||||
<ControllerTargets.68 Id="22264">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.68>
|
||||
<ControllerTargets.69 Id="22265">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.69>
|
||||
<ControllerTargets.70 Id="22266">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.70>
|
||||
<ControllerTargets.71 Id="22267">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.71>
|
||||
<ControllerTargets.72 Id="22268">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.72>
|
||||
<ControllerTargets.73 Id="22269">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.73>
|
||||
<ControllerTargets.74 Id="22270">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.74>
|
||||
<ControllerTargets.75 Id="22271">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.75>
|
||||
<ControllerTargets.76 Id="22272">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.76>
|
||||
<ControllerTargets.77 Id="22273">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.77>
|
||||
<ControllerTargets.78 Id="22274">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.78>
|
||||
<ControllerTargets.79 Id="22275">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.79>
|
||||
<ControllerTargets.80 Id="22276">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.80>
|
||||
<ControllerTargets.81 Id="22277">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.81>
|
||||
<ControllerTargets.82 Id="22278">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.82>
|
||||
<ControllerTargets.83 Id="22279">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.83>
|
||||
<ControllerTargets.84 Id="22280">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.84>
|
||||
<ControllerTargets.85 Id="22281">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.85>
|
||||
<ControllerTargets.86 Id="22282">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.86>
|
||||
<ControllerTargets.87 Id="22283">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.87>
|
||||
<ControllerTargets.88 Id="22284">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.88>
|
||||
<ControllerTargets.89 Id="22285">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.89>
|
||||
<ControllerTargets.90 Id="22286">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.90>
|
||||
<ControllerTargets.91 Id="22287">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.91>
|
||||
<ControllerTargets.92 Id="22288">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.92>
|
||||
<ControllerTargets.93 Id="22289">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.93>
|
||||
<ControllerTargets.94 Id="22290">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.94>
|
||||
<ControllerTargets.95 Id="22291">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.95>
|
||||
<ControllerTargets.96 Id="22292">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.96>
|
||||
<ControllerTargets.97 Id="22293">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.97>
|
||||
<ControllerTargets.98 Id="22294">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.98>
|
||||
<ControllerTargets.99 Id="22295">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.99>
|
||||
<ControllerTargets.100 Id="22296">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.100>
|
||||
<ControllerTargets.101 Id="22297">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.101>
|
||||
<ControllerTargets.102 Id="22298">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.102>
|
||||
<ControllerTargets.103 Id="22299">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.103>
|
||||
<ControllerTargets.104 Id="22300">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.104>
|
||||
<ControllerTargets.105 Id="22301">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.105>
|
||||
<ControllerTargets.106 Id="22302">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.106>
|
||||
<ControllerTargets.107 Id="22303">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.107>
|
||||
<ControllerTargets.108 Id="22304">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.108>
|
||||
<ControllerTargets.109 Id="22305">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.109>
|
||||
<ControllerTargets.110 Id="22306">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.110>
|
||||
<ControllerTargets.111 Id="22307">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.111>
|
||||
<ControllerTargets.112 Id="22308">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.112>
|
||||
<ControllerTargets.113 Id="22309">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.113>
|
||||
<ControllerTargets.114 Id="22310">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.114>
|
||||
<ControllerTargets.115 Id="22311">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.115>
|
||||
<ControllerTargets.116 Id="22312">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.116>
|
||||
<ControllerTargets.117 Id="22313">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.117>
|
||||
<ControllerTargets.118 Id="22314">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.118>
|
||||
<ControllerTargets.119 Id="22315">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.119>
|
||||
<ControllerTargets.120 Id="22316">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.120>
|
||||
<ControllerTargets.121 Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.121>
|
||||
<ControllerTargets.122 Id="22318">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.122>
|
||||
<ControllerTargets.123 Id="22319">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.123>
|
||||
<ControllerTargets.124 Id="22320">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.124>
|
||||
<ControllerTargets.125 Id="22321">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.125>
|
||||
<ControllerTargets.126 Id="22322">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.126>
|
||||
<ControllerTargets.127 Id="22323">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.127>
|
||||
<ControllerTargets.128 Id="22324">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.128>
|
||||
<ControllerTargets.129 Id="22325">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.129>
|
||||
<ControllerTargets.130 Id="22326">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.130>
|
||||
</MidiControllers>
|
||||
</MainSequencer>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<BreakoutIsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22327">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22328" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList>
|
||||
<ClipSlot Id="0">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="1">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="2">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="3">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="4">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="5">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="6">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="7">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
</ClipSlotList>
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="22329">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="22330">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="22332">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="22333">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="22334">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
</DeviceChain>
|
||||
<ReWireSlaveMidiTargetId Value="0" />
|
||||
<PitchbendRange Value="96" />
|
||||
</MidiTrack>
|
||||
1049
src/lib/exporters/ableton/templates/project.xml
Normal file
1049
src/lib/exporters/ableton/templates/project.xml
Normal file
File diff suppressed because it is too large
Load Diff
285
src/lib/exporters/ableton/templates/returnTrack.xml
Normal file
285
src/lib/exporters/ableton/templates/returnTrack.xml
Normal file
@@ -0,0 +1,285 @@
|
||||
<ReturnTrack Id="-1">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="A-Return" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="16" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="false" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="68" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23801">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23802" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends>
|
||||
<TrackSendHolder Id="0">
|
||||
<Send>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23830">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23831">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Send>
|
||||
<Active Value="false" />
|
||||
</TrackSendHolder>
|
||||
</Sends>
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23803">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23804">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23805">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23806">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23807">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23808">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23809">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23810">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23811">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSesstionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23812">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23813">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23814" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList />
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="23815">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="23816">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="23817">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="23818">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="23819">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
</DeviceChain>
|
||||
</ReturnTrack>
|
||||
23
src/lib/exporters/ableton/templates/scene.xml
Normal file
23
src/lib/exporters/ableton/templates/scene.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<Scene Id="0">
|
||||
<FollowAction>
|
||||
<FollowTime Value="0" />
|
||||
<IsLinked Value="true" />
|
||||
<LoopIterations Value="1" />
|
||||
<FollowActionA Value="4" />
|
||||
<FollowActionB Value="0" />
|
||||
<FollowChanceA Value="100" />
|
||||
<FollowChanceB Value="0" />
|
||||
<JumpIndexA Value="0" />
|
||||
<JumpIndexB Value="0" />
|
||||
<FollowActionEnabled Value="false" />
|
||||
</FollowAction>
|
||||
<Name Value="" />
|
||||
<Annotation Value="" />
|
||||
<Color Value="-1" />
|
||||
<Tempo Value="120" />
|
||||
<IsTempoEnabled Value="false" />
|
||||
<TimeSignatureId Value="201" />
|
||||
<IsTimeSignatureEnabled Value="false" />
|
||||
<LomId Value="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
</Scene>
|
||||
1815
src/lib/exporters/ableton/templates/simpler.xml
Normal file
1815
src/lib/exporters/ableton/templates/simpler.xml
Normal file
File diff suppressed because it is too large
Load Diff
17
src/lib/exporters/ableton/templates/trackSendHolder.xml
Normal file
17
src/lib/exporters/ableton/templates/trackSendHolder.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<TrackSendHolder Id="1">
|
||||
<Send>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23828">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23829">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Send>
|
||||
<Active Value="true" />
|
||||
</TrackSendHolder>
|
||||
77
src/lib/exporters/ableton/types/common.ts
Normal file
77
src/lib/exporters/ableton/types/common.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export interface ValueElement {
|
||||
'@Value': any;
|
||||
}
|
||||
|
||||
export interface LomIdElement {
|
||||
LomId: ValueElement;
|
||||
}
|
||||
|
||||
export interface AutomationTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
export interface MidiCCOnOffThresholds {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
}
|
||||
|
||||
export interface On {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
export interface MidiControllerRange {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
}
|
||||
|
||||
export interface ModulationTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
export interface FileRef {
|
||||
RelativePathType: ValueElement;
|
||||
RelativePath: ValueElement;
|
||||
Path: ValueElement;
|
||||
Type: ValueElement;
|
||||
LivePackName: ValueElement;
|
||||
LivePackId: ValueElement;
|
||||
OriginalFileSize: ValueElement;
|
||||
OriginalCrc: ValueElement;
|
||||
}
|
||||
|
||||
export interface AbletonDefaultPresetRef {
|
||||
'@Id': number;
|
||||
FileRef: FileRef;
|
||||
DeviceId: {
|
||||
'@Name': string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LastPresetRef {
|
||||
Value: AbletonDefaultPresetRef;
|
||||
}
|
||||
|
||||
export interface OriginalFileRef {
|
||||
FileRef: FileRef;
|
||||
}
|
||||
|
||||
export interface PresetRef {
|
||||
AbletonDefaultPresetRef: AbletonDefaultPresetRef;
|
||||
}
|
||||
|
||||
export interface BranchSourceContext {
|
||||
'@Id': number;
|
||||
OriginalFileRef: OriginalFileRef;
|
||||
BrowserContentPath: ValueElement;
|
||||
PresetRef: PresetRef;
|
||||
BranchDeviceId: ValueElement;
|
||||
}
|
||||
|
||||
export interface SourceContext {
|
||||
Value: BranchSourceContext;
|
||||
}
|
||||
127
src/lib/exporters/ableton/types/drumBranch.ts
Normal file
127
src/lib/exporters/ableton/types/drumBranch.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface MidiToAudioDeviceChain {
|
||||
'@Id': number;
|
||||
Devices: {};
|
||||
SignalModulations: {};
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
MidiToAudioDeviceChain: MidiToAudioDeviceChain;
|
||||
}
|
||||
|
||||
interface BranchSelectorRange {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Panorama {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface RoutingHelper {
|
||||
Routable: Routable;
|
||||
TargetEnum: ValueElement;
|
||||
}
|
||||
|
||||
interface MixerDevice {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Speaker: On;
|
||||
Volume: Volume;
|
||||
Panorama: Panorama;
|
||||
SendInfos: {};
|
||||
RoutingHelper: RoutingHelper;
|
||||
SendsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
interface BranchInfo {
|
||||
ReceivingNote: ValueElement;
|
||||
SendingNote: ValueElement;
|
||||
ChokeGroup: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSDrumBranchContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
Name: Name;
|
||||
IsSelected: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
BranchSelectorRange: BranchSelectorRange;
|
||||
IsSoloed: ValueElement;
|
||||
SessionViewBranchWidth: ValueElement;
|
||||
IsHighlightedInSessionView: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
Color: ValueElement;
|
||||
AutoColored: ValueElement;
|
||||
AutoColorScheme: ValueElement;
|
||||
SoloActivatedInSessionMixer: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
MixerDevice: MixerDevice;
|
||||
BranchInfo: BranchInfo;
|
||||
}
|
||||
|
||||
export interface ALSDrumBranch {
|
||||
DrumBranch: ALSDrumBranchContent;
|
||||
}
|
||||
97
src/lib/exporters/ableton/types/drumRack.ts
Normal file
97
src/lib/exporters/ableton/types/drumRack.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import { ALSDrumBranchContent } from './drumBranch';
|
||||
|
||||
interface MacroControl {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ChainSelector {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MacroVariations {
|
||||
MacroSnapshots: {};
|
||||
}
|
||||
|
||||
export interface ALSDrumRackContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Branches: {
|
||||
DrumBranch: ALSDrumBranchContent[];
|
||||
};
|
||||
IsBranchesListVisible: ValueElement;
|
||||
IsReturnBranchesListVisible: ValueElement;
|
||||
IsRangesEditorVisible: ValueElement;
|
||||
AreDevicesVisible: ValueElement;
|
||||
NumVisibleMacroControls: ValueElement;
|
||||
MacroControls: { [key: string]: MacroControl };
|
||||
MacroDisplayNames: { [key: string]: ValueElement };
|
||||
MacroDefaults: { [key: string]: ValueElement };
|
||||
MacroAnnotations: { [key: string]: ValueElement };
|
||||
ForceDisplayGenericValue: { [key: string]: ValueElement };
|
||||
AreMacroControlsVisible: ValueElement;
|
||||
IsAutoSelectEnabled: ValueElement;
|
||||
ChainSelector: ChainSelector;
|
||||
ChainSelectorRelativePosition: ValueElement;
|
||||
ViewsToRestoreWhenUnfolding: ValueElement;
|
||||
ReturnBranches: {};
|
||||
BranchesSplitterProportion: ValueElement;
|
||||
ShowBranchesInSessionMixer: ValueElement;
|
||||
MacroColor: { [key: string]: ValueElement };
|
||||
LockId: ValueElement;
|
||||
LockSeal: ValueElement;
|
||||
ChainsListWrapper: LomIdElement;
|
||||
ReturnChainsListWrapper: LomIdElement;
|
||||
MacroVariations: MacroVariations;
|
||||
ExcludeMacroFromRandomization: { [key: string]: ValueElement };
|
||||
ExcludeMacroFromSnapshots: { [key: string]: ValueElement };
|
||||
AreMacroVariationsControlsVisible: ValueElement;
|
||||
ChainSelectorFilterMidiCtrl: ValueElement;
|
||||
RangeTypeIndex: ValueElement;
|
||||
ShowsZonesInsteadOfNoteNames: ValueElement;
|
||||
IsMidiSectionVisible: ValueElement;
|
||||
AreSendsVisible: ValueElement;
|
||||
ArePadsVisible: ValueElement;
|
||||
PadScrollPosition: ValueElement;
|
||||
DrumPadsListWrapper: LomIdElement;
|
||||
VisibleDrumPadsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
export interface ALSDrumRack {
|
||||
DrumGroupDevice: ALSDrumRackContent;
|
||||
}
|
||||
151
src/lib/exporters/ableton/types/effectChorus.ts
Normal file
151
src/lib/exporters/ableton/types/effectChorus.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Mode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Shaping {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Rate {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Amount {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Feedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface InvertFeedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface VibratoOffset {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface HighpassEnabled {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface HighpassFrequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Width {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Warmth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface OutputGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSChorusContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Mode: Mode;
|
||||
Shaping: Shaping;
|
||||
Rate: Rate;
|
||||
Amount: Amount;
|
||||
Feedback: Feedback;
|
||||
InvertFeedback: InvertFeedback;
|
||||
VibratoOffset: VibratoOffset;
|
||||
HighpassEnabled: HighpassEnabled;
|
||||
HighpassFrequency: HighpassFrequency;
|
||||
Width: Width;
|
||||
Warmth: Warmth;
|
||||
OutputGain: OutputGain;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
export interface ALSChorus {
|
||||
Chorus2: ALSChorusContent;
|
||||
}
|
||||
249
src/lib/exporters/ableton/types/effectCompressor.ts
Normal file
249
src/lib/exporters/ableton/types/effectCompressor.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Threshold {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Ratio {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ExpansionRatio {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Attack {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Release {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AutoReleaseControlOnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Gain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface GainCompensation {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Model {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface LegacyModel {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface LogEnvelope {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface LegacyEnvFollowerMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Knee {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LookAhead {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface SideListen {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoutedInput {
|
||||
Routable: Routable;
|
||||
Volume: Volume;
|
||||
}
|
||||
|
||||
interface OnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SideChain {
|
||||
OnOff: OnOff;
|
||||
RoutedInput: RoutedInput;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
interface Mode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Freq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Q {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SideChainEq {
|
||||
On: On;
|
||||
Mode: Mode;
|
||||
Freq: Freq;
|
||||
Q: Q;
|
||||
Gain: Gain;
|
||||
}
|
||||
|
||||
export interface ALSCompressorContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Threshold: Threshold;
|
||||
Ratio: Ratio;
|
||||
ExpansionRatio: ExpansionRatio;
|
||||
Attack: Attack;
|
||||
Release: Release;
|
||||
AutoReleaseControlOnOff: AutoReleaseControlOnOff;
|
||||
Gain: Gain;
|
||||
GainCompensation: GainCompensation;
|
||||
DryWet: DryWet;
|
||||
Model: Model;
|
||||
LegacyModel: LegacyModel;
|
||||
LogEnvelope: LogEnvelope;
|
||||
LegacyEnvFollowerMode: LegacyEnvFollowerMode;
|
||||
Knee: Knee;
|
||||
LookAhead: LookAhead;
|
||||
SideListen: SideListen;
|
||||
SideChain: SideChain;
|
||||
SideChainEq: SideChainEq;
|
||||
Live8LegacyMode: ValueElement;
|
||||
ViewMode: ValueElement;
|
||||
IsOutputCurveVisible: ValueElement;
|
||||
RmsTimeShort: ValueElement;
|
||||
RmsTimeLong: ValueElement;
|
||||
ReleaseTimeShort: ValueElement;
|
||||
ReleaseTimeLong: ValueElement;
|
||||
CrossfaderSmoothingTime: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSCompressor {
|
||||
Compressor2: ALSCompressorContent;
|
||||
}
|
||||
245
src/lib/exporters/ableton/types/effectDelay.ts
Normal file
245
src/lib/exporters/ableton/types/effectDelay.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface DelayLine_SmoothingMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_Link {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPong {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_TimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_TimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SimpleDelayTimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SimpleDelayTimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPongDelayTimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPongDelayTimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncedSixteenthL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncedSixteenthR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_OffsetL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_OffsetR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Feedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Freeze {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Filter_On {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Filter_Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Filter_Bandwidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_AmountTime {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_AmountFilter {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSDelayContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
DelayLine_SmoothingMode: DelayLine_SmoothingMode;
|
||||
DelayLine_Link: DelayLine_Link;
|
||||
DelayLine_PingPong: DelayLine_PingPong;
|
||||
DelayLine_SyncL: DelayLine_SyncL;
|
||||
DelayLine_SyncR: DelayLine_SyncR;
|
||||
DelayLine_TimeL: DelayLine_TimeL;
|
||||
DelayLine_TimeR: DelayLine_TimeR;
|
||||
DelayLine_SimpleDelayTimeL: DelayLine_SimpleDelayTimeL;
|
||||
DelayLine_SimpleDelayTimeR: DelayLine_SimpleDelayTimeR;
|
||||
DelayLine_PingPongDelayTimeL: DelayLine_PingPongDelayTimeL;
|
||||
DelayLine_PingPongDelayTimeR: DelayLine_PingPongDelayTimeR;
|
||||
DelayLine_SyncedSixteenthL: DelayLine_SyncedSixteenthL;
|
||||
DelayLine_SyncedSixteenthR: DelayLine_SyncedSixteenthR;
|
||||
DelayLine_OffsetL: DelayLine_OffsetL;
|
||||
DelayLine_OffsetR: DelayLine_OffsetR;
|
||||
DelayLine_CompatibilityMode: ValueElement;
|
||||
Feedback: Feedback;
|
||||
Freeze: Freeze;
|
||||
Filter_On: Filter_On;
|
||||
Filter_Frequency: Filter_Frequency;
|
||||
Filter_Bandwidth: Filter_Bandwidth;
|
||||
Modulation_Frequency: Modulation_Frequency;
|
||||
Modulation_AmountTime: Modulation_AmountTime;
|
||||
Modulation_AmountFilter: Modulation_AmountFilter;
|
||||
DryWet: DryWet;
|
||||
DryWetMode: ValueElement;
|
||||
EcoProcessing: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSDelay {
|
||||
Delay: ALSDelayContent;
|
||||
}
|
||||
91
src/lib/exporters/ableton/types/effectDistortion.ts
Normal file
91
src/lib/exporters/ableton/types/effectDistortion.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface MidFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Drive {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Tone {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface PreserveDynamics {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSDistortionContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
MidFreq: MidFreq;
|
||||
BandWidth: BandWidth;
|
||||
Drive: Drive;
|
||||
DryWet: DryWet;
|
||||
Tone: Tone;
|
||||
PreserveDynamics: PreserveDynamics;
|
||||
}
|
||||
|
||||
export interface ALSDistortion {
|
||||
Overdrive: ALSDistortionContent;
|
||||
}
|
||||
307
src/lib/exporters/ableton/types/effectFilter.ts
Normal file
307
src/lib/exporters/ableton/types/effectFilter.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface LegacyFilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface FilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface CircuitLpHp {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface CircuitBpNoMo {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Slope {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Cutoff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LegacyQ {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Resonance {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Morph {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Drive {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ModHub {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Attack {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Release {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LfoAmount {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Type {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RateType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface BeatRate {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface StereoMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Spin {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Phase {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Offset {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface IsOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Quantize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BeatQuantize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface NoiseWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Lfo {
|
||||
Type: Type;
|
||||
Frequency: Frequency;
|
||||
RateType: RateType;
|
||||
BeatRate: BeatRate;
|
||||
StereoMode: StereoMode;
|
||||
Spin: Spin;
|
||||
Phase: Phase;
|
||||
Offset: Offset;
|
||||
IsOn: IsOn;
|
||||
Quantize: Quantize;
|
||||
BeatQuantize: BeatQuantize;
|
||||
NoiseWidth: NoiseWidth;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoutedInput {
|
||||
Routable: Routable;
|
||||
Volume: Volume;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface OnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SideChain {
|
||||
OnOff: OnOff;
|
||||
RoutedInput: RoutedInput;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
export interface ALSFilterContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
LegacyMode: ValueElement;
|
||||
LegacyFilterType: LegacyFilterType;
|
||||
FilterType: FilterType;
|
||||
CircuitLpHp: CircuitLpHp;
|
||||
CircuitBpNoMo: CircuitBpNoMo;
|
||||
Slope: Slope;
|
||||
Cutoff: Cutoff;
|
||||
CutoffLimit: ValueElement;
|
||||
LegacyQ: LegacyQ;
|
||||
Resonance: Resonance;
|
||||
Morph: Morph;
|
||||
Drive: Drive;
|
||||
ModHub: ModHub;
|
||||
Attack: Attack;
|
||||
Release: Release;
|
||||
LfoAmount: LfoAmount;
|
||||
Lfo: Lfo;
|
||||
SideChain: SideChain;
|
||||
}
|
||||
|
||||
export interface ALSFilter {
|
||||
AutoFilter: ALSFilterContent;
|
||||
}
|
||||
312
src/lib/exporters/ableton/types/effectReverb.ts
Normal file
312
src/lib/exporters/ableton/types/effectReverb.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface PreDelay {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandHighOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BandLowOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BandFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SpinOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface EarlyReflectModFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface EarlyReflectModDepth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DiffuseDelay {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHighOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface HighFilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHiFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHiGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfLowOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface ShelfLoFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfLoGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ChorusOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SizeModFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SizeModDepth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DecayTime {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AllPassGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AllPassSize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface FreezeOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface FlatOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface CutOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface RoomSize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SizeSmoothing {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface StereoSeparation {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoomType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface MixReflect {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MixDiffuse {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MixDirect {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSReverbContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
PreDelay: PreDelay;
|
||||
BandHighOn: BandHighOn;
|
||||
BandLowOn: BandLowOn;
|
||||
BandFreq: BandFreq;
|
||||
BandWidth: BandWidth;
|
||||
SpinOn: SpinOn;
|
||||
EarlyReflectModFreq: EarlyReflectModFreq;
|
||||
EarlyReflectModDepth: EarlyReflectModDepth;
|
||||
DiffuseDelay: DiffuseDelay;
|
||||
ShelfHighOn: ShelfHighOn;
|
||||
HighFilterType: HighFilterType;
|
||||
ShelfHiFreq: ShelfHiFreq;
|
||||
ShelfHiGain: ShelfHiGain;
|
||||
ShelfLowOn: ShelfLowOn;
|
||||
ShelfLoFreq: ShelfLoFreq;
|
||||
ShelfLoGain: ShelfLoGain;
|
||||
ChorusOn: ChorusOn;
|
||||
SizeModFreq: SizeModFreq;
|
||||
SizeModDepth: SizeModDepth;
|
||||
DecayTime: DecayTime;
|
||||
AllPassGain: AllPassGain;
|
||||
AllPassSize: AllPassSize;
|
||||
FreezeOn: FreezeOn;
|
||||
FlatOn: FlatOn;
|
||||
CutOn: CutOn;
|
||||
RoomSize: RoomSize;
|
||||
SizeSmoothing: SizeSmoothing;
|
||||
StereoSeparation: StereoSeparation;
|
||||
RoomType: RoomType;
|
||||
MixReflect: MixReflect;
|
||||
MixDiffuse: MixDiffuse;
|
||||
MixDirect: MixDirect;
|
||||
StereoSeparationOnDrySignal: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSReverb {
|
||||
Reverb: ALSReverbContent;
|
||||
}
|
||||
65
src/lib/exporters/ableton/types/groupTrack.ts
Normal file
65
src/lib/exporters/ableton/types/groupTrack.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { LomIdElement, ValueElement } from './common';
|
||||
import { Mixer } from './midiTrack';
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface GroupTrackSlot {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
}
|
||||
|
||||
interface Slots {
|
||||
GroupTrackSlot: GroupTrackSlot[];
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
Mixer: Mixer;
|
||||
DeviceChain: {
|
||||
Devices: {
|
||||
'#': Array<any>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSGroupTrackContent {
|
||||
'@Id': number;
|
||||
'@_internalId'?: string;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
Slots: Slots;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
export interface ALSGroupTrack {
|
||||
GroupTrack: ALSGroupTrackContent;
|
||||
}
|
||||
136
src/lib/exporters/ableton/types/midiClip.ts
Normal file
136
src/lib/exporters/ableton/types/midiClip.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
interface ValueElement {
|
||||
'@Value': any;
|
||||
}
|
||||
|
||||
interface MidiNoteEvent {
|
||||
'@Time': number;
|
||||
'@Duration': number;
|
||||
'@Velocity': number;
|
||||
'@OffVelocity': number;
|
||||
'@NoteId': number;
|
||||
}
|
||||
|
||||
interface KeyTrack {
|
||||
'@Id': number;
|
||||
Notes: {
|
||||
MidiNoteEvent: MidiNoteEvent[];
|
||||
};
|
||||
MidiKey: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSMidiClipContent {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
CurrentStart: ValueElement;
|
||||
CurrentEnd: ValueElement;
|
||||
Loop: {
|
||||
LoopStart: ValueElement;
|
||||
LoopEnd: ValueElement;
|
||||
StartRelative: ValueElement;
|
||||
LoopOn: ValueElement;
|
||||
OutMarker: ValueElement;
|
||||
HiddenLoopStart: ValueElement;
|
||||
HiddenLoopEnd: ValueElement;
|
||||
};
|
||||
Name: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
Color: ValueElement;
|
||||
LaunchMode: ValueElement;
|
||||
LaunchQuantisation: ValueElement;
|
||||
TimeSignature: {
|
||||
TimeSignatures: {
|
||||
RemoteableTimeSignature: {
|
||||
'@Id': number;
|
||||
Numerator: ValueElement;
|
||||
Denominator: ValueElement;
|
||||
Time: ValueElement;
|
||||
};
|
||||
};
|
||||
};
|
||||
Envelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
ScrollerTimePreserver: {
|
||||
LeftTime: ValueElement;
|
||||
RightTime: ValueElement;
|
||||
};
|
||||
TimeSelection: {
|
||||
AnchorTime: ValueElement;
|
||||
OtherTime: ValueElement;
|
||||
};
|
||||
Legato: ValueElement;
|
||||
Ram: ValueElement;
|
||||
GrooveSettings: {
|
||||
GrooveId: ValueElement;
|
||||
};
|
||||
Disabled: ValueElement;
|
||||
VelocityAmount: ValueElement;
|
||||
FollowAction: {
|
||||
FollowTime: ValueElement;
|
||||
IsLinked: ValueElement;
|
||||
LoopIterations: ValueElement;
|
||||
FollowActionA: ValueElement;
|
||||
FollowActionB: ValueElement;
|
||||
FollowChanceA: ValueElement;
|
||||
FollowChanceB: ValueElement;
|
||||
JumpIndexA: ValueElement;
|
||||
JumpIndexB: ValueElement;
|
||||
FollowActionEnabled: ValueElement;
|
||||
};
|
||||
Grid: {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
};
|
||||
FreezeStart: ValueElement;
|
||||
FreezeEnd: ValueElement;
|
||||
IsWarped: ValueElement;
|
||||
TakeId: ValueElement;
|
||||
IsInKey: ValueElement;
|
||||
ScaleInformation: {
|
||||
Root: ValueElement;
|
||||
Name: ValueElement;
|
||||
};
|
||||
Notes: {
|
||||
KeyTracks: {
|
||||
KeyTrack: KeyTrack[];
|
||||
};
|
||||
PerNoteEventStore: {
|
||||
EventLists: {};
|
||||
};
|
||||
NoteProbabilityGroups: {};
|
||||
ProbabilityGroupIdGenerator: {
|
||||
NextId: ValueElement;
|
||||
};
|
||||
NoteIdGenerator: {
|
||||
NextId: ValueElement;
|
||||
};
|
||||
};
|
||||
BankSelectCoarse: ValueElement;
|
||||
BankSelectFine: ValueElement;
|
||||
ProgramChange: ValueElement;
|
||||
NoteEditorFoldInZoom: ValueElement;
|
||||
NoteEditorFoldInScroll: ValueElement;
|
||||
NoteEditorFoldOutZoom: ValueElement;
|
||||
NoteEditorFoldOutScroll: ValueElement;
|
||||
NoteSpellingPreference: ValueElement;
|
||||
AccidentalSpellingPreference: ValueElement;
|
||||
PreferFlatRootNote: ValueElement;
|
||||
ExpressionGrid: {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSMidiClip {
|
||||
MidiClip: ALSMidiClipContent;
|
||||
}
|
||||
340
src/lib/exporters/ableton/types/midiTrack.ts
Normal file
340
src/lib/exporters/ableton/types/midiTrack.ts
Normal file
@@ -0,0 +1,340 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import type { ALSTrackSendHolder } from './trackSendHolder';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLane: AutomationLane[];
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
export interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
Sends: ALSTrackSendHolder;
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSessionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface ClipSlot {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
ClipSlot: {
|
||||
Value?: MidiClip;
|
||||
};
|
||||
HasStop?: ValueElement;
|
||||
NeedRefreeze?: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipSlotList {
|
||||
ClipSlot: ClipSlot[];
|
||||
}
|
||||
|
||||
interface MidiClip {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface ArrangerAutomation {
|
||||
Events: {
|
||||
MidiClip: MidiClip[];
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface ClipTimeable {
|
||||
ArrangerAutomation: ArrangerAutomation;
|
||||
}
|
||||
|
||||
interface Recorder {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
}
|
||||
|
||||
interface ControllerTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
interface MidiControllers {
|
||||
[key: string]: ControllerTarget;
|
||||
}
|
||||
|
||||
interface MainSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: ClipSlotList;
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
ClipTimeable: ClipTimeable;
|
||||
Recorder: Recorder;
|
||||
MidiControllers: MidiControllers;
|
||||
}
|
||||
|
||||
interface FreezeSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: ClipSlotList;
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
Sample: {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TransientEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProFormantsModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: Recorder;
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
MainSequencer: MainSequencer;
|
||||
FreezeSequencer: FreezeSequencer;
|
||||
DeviceChain: {
|
||||
Devices: {
|
||||
'#': Array<any>;
|
||||
};
|
||||
SignalModulations: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ControllerLayoutCustomization {
|
||||
PitchClassSource: ValueElement;
|
||||
OctaveSource: ValueElement;
|
||||
KeyNoteTarget: ValueElement;
|
||||
StepSize: ValueElement;
|
||||
OctaveEvery: ValueElement;
|
||||
AllowedKeys: ValueElement;
|
||||
FillerKeysMapTo: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSMidiTrackContent {
|
||||
'@Id': number;
|
||||
'@SelectedToolPanel': number;
|
||||
'@SelectedTransformationName': string;
|
||||
'@SelectedGeneratorName': string;
|
||||
'@_internalGroupId'?: string;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
};
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ClipSlotsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ArrangementClipsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
TakeLanesListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
SavedPlayingSlot: ValueElement;
|
||||
SavedPlayingOffset: ValueElement;
|
||||
Freeze: ValueElement;
|
||||
NeedArrangerRefreeze: ValueElement;
|
||||
PostProcessFreezeClips: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
ReWireDeviceMidiTargetId: ValueElement;
|
||||
PitchbendRange: ValueElement;
|
||||
IsTuned: ValueElement;
|
||||
ControllerLayoutRemoteable: ValueElement;
|
||||
ControllerLayoutCustomization: ControllerLayoutCustomization;
|
||||
}
|
||||
|
||||
export interface ALSMidiTrack {
|
||||
MidiTrack: ALSMidiTrackContent;
|
||||
}
|
||||
372
src/lib/exporters/ableton/types/project.ts
Normal file
372
src/lib/exporters/ableton/types/project.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import type { ALSGroupTrack } from './groupTrack';
|
||||
import type { ALSMidiTrack } from './midiTrack';
|
||||
import type { ALSReturnTrack } from './returnTrack';
|
||||
import { ALSSceneContent } from './scene';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLanes: {
|
||||
AutomationLane: AutomationLane[];
|
||||
};
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
Sends: {};
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSessionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: LomIdElement;
|
||||
Tempo: ManualElement;
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
MainSequencer: {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: {
|
||||
ClipSlot: any[]; // Empty in template
|
||||
};
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
ClipTimeable: {
|
||||
ArrangerAutomation: {
|
||||
Events: {
|
||||
MidiClip: any[]; // Empty in template
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
MidiControllers: {};
|
||||
};
|
||||
FreezeSequencer: {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: {
|
||||
ClipSlot: any[]; // Empty in template
|
||||
};
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
Sample: {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TransientEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProFormantsModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
};
|
||||
DeviceChain: {
|
||||
Devices: {};
|
||||
SignalModulations: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationEnvelope {
|
||||
'@Id': number;
|
||||
EnvelopeTarget: {
|
||||
PointeeId: ValueElement;
|
||||
};
|
||||
Automation: {
|
||||
Events: {
|
||||
EnumEvent: {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
'@Value': number;
|
||||
};
|
||||
FloatEvent: {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
'@Value': number;
|
||||
};
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface AutomationEnvelopes {
|
||||
Envelopes: {
|
||||
AutomationEnvelope: AutomationEnvelope[];
|
||||
};
|
||||
}
|
||||
|
||||
interface MasterTrack {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: AutomationEnvelopes;
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ArrangementClipsListWrapper: LomIdElement;
|
||||
TakeLanesListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
interface GroovePool {
|
||||
Grooves: {
|
||||
Groove: any[];
|
||||
};
|
||||
}
|
||||
|
||||
interface AutoColorPicker {
|
||||
NextColorIndex: ValueElement;
|
||||
}
|
||||
|
||||
interface VideoWindowRect {
|
||||
'@Top': number;
|
||||
'@Left': number;
|
||||
'@Bottom': number;
|
||||
'@Right': number;
|
||||
}
|
||||
|
||||
interface LiveSet {
|
||||
NextPointeeId: ValueElement;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
Tracks: {
|
||||
'#': (ALSMidiTrack | ALSGroupTrack | ALSReturnTrack)[];
|
||||
};
|
||||
MasterTrack: MasterTrack;
|
||||
GroovePool: GroovePool;
|
||||
AutomationMode: ValueElement;
|
||||
SnapAutomationToGrid: ValueElement;
|
||||
ArrangementOverdub: ValueElement;
|
||||
ColorSequenceIndex: ValueElement;
|
||||
AutoColorPickerForPlayerAndGroupTracks: AutoColorPicker;
|
||||
AutoColorPickerForReturnAndMasterTracks: AutoColorPicker;
|
||||
ViewData: ValueElement;
|
||||
ResetNonautomatedMidiControllersOnClipStarts: ValueElement;
|
||||
MidiFoldIn: ValueElement;
|
||||
MidiFoldMode: ValueElement;
|
||||
MultiClipFocusMode: ValueElement;
|
||||
MultiClipLoopBarHeight: ValueElement;
|
||||
MidiPrelisten: ValueElement;
|
||||
LinkedTrackGroups: {};
|
||||
AccidentalSpellingPreference: ValueElement;
|
||||
PreferFlatRootNote: ValueElement;
|
||||
UseWarperLegacyHiQMode: ValueElement;
|
||||
VideoWindowRect: VideoWindowRect;
|
||||
ShowVideoWindow: ValueElement;
|
||||
TrackHeaderWidth: ValueElement;
|
||||
ViewStates: {};
|
||||
Scenes: {
|
||||
Scene: ALSSceneContent[];
|
||||
};
|
||||
SendsPre: {
|
||||
SendPreBool: {
|
||||
'@Id': number;
|
||||
'@Value': string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface Ableton {
|
||||
'@MajorVersion': string;
|
||||
'@MinorVersion': string;
|
||||
'@SchemaChangeCount': string;
|
||||
'@Creator': string;
|
||||
'@Revision': string;
|
||||
LiveSet: LiveSet;
|
||||
}
|
||||
|
||||
export interface ALSProject {
|
||||
Ableton: Ableton;
|
||||
}
|
||||
224
src/lib/exporters/ableton/types/returnTrack.ts
Normal file
224
src/lib/exporters/ableton/types/returnTrack.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import { ALSChorus } from './effectChorus';
|
||||
import { ALSCompressor } from './effectCompressor';
|
||||
import { ALSDelay } from './effectDelay';
|
||||
import { ALSDistortion } from './effectDistortion';
|
||||
import { ALSFilter } from './effectFilter';
|
||||
import { ALSReverb } from './effectReverb';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLane: AutomationLane[];
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackSendHolder {
|
||||
'@Id': number;
|
||||
Send: ManualElement;
|
||||
Active: ValueElement;
|
||||
}
|
||||
|
||||
interface Sends {
|
||||
TrackSendHolder: TrackSendHolder[];
|
||||
}
|
||||
|
||||
interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
Sends: Sends;
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSesstionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
interface Sample {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface FreezeSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
ClipSlotList: {};
|
||||
MonitoringEnum: ValueElement;
|
||||
Sample: Sample;
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationEnvelopes {
|
||||
Envelopes: {};
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
type EffectDevice = ALSChorus | ALSCompressor | ALSDelay | ALSDistortion | ALSFilter | ALSReverb;
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
DeviceChain: {
|
||||
Devices: EffectDevice;
|
||||
SignalModulations: {};
|
||||
};
|
||||
FreezeSequencer: FreezeSequencer;
|
||||
}
|
||||
|
||||
export interface ALSReturnTrackContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: AutomationEnvelopes;
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
export interface ALSReturnTrack {
|
||||
ReturnTrack: ALSReturnTrackContent;
|
||||
}
|
||||
34
src/lib/exporters/ableton/types/scene.ts
Normal file
34
src/lib/exporters/ableton/types/scene.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ValueElement } from './common';
|
||||
|
||||
interface FollowAction {
|
||||
FollowTime: ValueElement;
|
||||
IsLinked: ValueElement;
|
||||
LoopIterations: ValueElement;
|
||||
FollowActionA: ValueElement;
|
||||
FollowActionB: ValueElement;
|
||||
FollowChanceA: ValueElement;
|
||||
FollowChanceB: ValueElement;
|
||||
JumpIndexA: ValueElement;
|
||||
JumpIndexB: ValueElement;
|
||||
FollowActionEnabled: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSSceneContent {
|
||||
'@Id': number;
|
||||
FollowAction: FollowAction;
|
||||
Name: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
Color: ValueElement;
|
||||
Tempo: ValueElement;
|
||||
IsTempoEnabled: ValueElement;
|
||||
TimeSignatureId: ValueElement;
|
||||
IsTimeSignatureEnabled: ValueElement;
|
||||
LomId: ValueElement;
|
||||
ClipSlotsListWrapper: {
|
||||
'@LomId': number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSScene {
|
||||
Scene: ALSSceneContent;
|
||||
}
|
||||
545
src/lib/exporters/ableton/types/simpler.ts
Normal file
545
src/lib/exporters/ableton/types/simpler.ts
Normal file
@@ -0,0 +1,545 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
FileRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
PresetRef,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BranchSourceContext {
|
||||
_attrs: { Id: number };
|
||||
OriginalFileRef: {};
|
||||
BrowserContentPath: ValueElement;
|
||||
LocalFiltersJson: ValueElement;
|
||||
PresetRef: PresetRef;
|
||||
BranchDeviceId: ValueElement;
|
||||
}
|
||||
|
||||
interface SourceContext {
|
||||
Value: BranchSourceContext;
|
||||
}
|
||||
|
||||
interface LastPresetRef {
|
||||
Value: any;
|
||||
}
|
||||
|
||||
interface WarpMarker {
|
||||
'@Id': number;
|
||||
'@SecTime': number;
|
||||
'@BeatTime': number;
|
||||
}
|
||||
|
||||
interface WarpMarkers {
|
||||
WarpMarker: WarpMarker[];
|
||||
}
|
||||
|
||||
interface TimeSignature {
|
||||
TimeSignatures: {
|
||||
RemoteableTimeSignature: {
|
||||
_attrs: { Id: number };
|
||||
Numerator: ValueElement;
|
||||
Denominator: ValueElement;
|
||||
Time: ValueElement;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface BeatGrid {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
}
|
||||
|
||||
interface SampleWarpProperties {
|
||||
WarpMarkers: WarpMarkers;
|
||||
WarpMode: ValueElement;
|
||||
GranularityTones: ValueElement;
|
||||
GranularityTexture: ValueElement;
|
||||
FluctuationTexture: ValueElement;
|
||||
ComplexProFormants: ValueElement;
|
||||
ComplexProEnvelope: ValueElement;
|
||||
TransientResolution: ValueElement;
|
||||
TransientLoopMode: ValueElement;
|
||||
TransientEnvelope: ValueElement;
|
||||
IsWarped: ValueElement;
|
||||
Onsets: {
|
||||
UserOnsets: {};
|
||||
HasUserOnsets: ValueElement;
|
||||
};
|
||||
TimeSignature: TimeSignature;
|
||||
BeatGrid: BeatGrid;
|
||||
}
|
||||
|
||||
interface SampleRef {
|
||||
FileRef: FileRef;
|
||||
LastModDate: ValueElement;
|
||||
SourceContext: {};
|
||||
SampleUsageHint: ValueElement;
|
||||
DefaultDuration: ValueElement;
|
||||
DefaultSampleRate: ValueElement;
|
||||
SamplesToAutoWarp: ValueElement;
|
||||
}
|
||||
|
||||
interface SustainLoop {
|
||||
Start: ValueElement;
|
||||
End: ValueElement;
|
||||
Mode: ValueElement;
|
||||
Crossfade: ValueElement;
|
||||
Detune: ValueElement;
|
||||
}
|
||||
|
||||
interface ReleaseLoop {
|
||||
Start: ValueElement;
|
||||
End: ValueElement;
|
||||
Mode: ValueElement;
|
||||
Crossfade: ValueElement;
|
||||
Detune: ValueElement;
|
||||
}
|
||||
|
||||
interface MultiSamplePart {
|
||||
_attrs: {
|
||||
Id: number;
|
||||
InitUpdateAreSlicesFromOnsetsEditableAfterRead: boolean;
|
||||
HasImportedSlicePoints: boolean;
|
||||
NeedsAnalysisData: boolean;
|
||||
};
|
||||
LomId: ValueElement;
|
||||
Name: ValueElement;
|
||||
Selection: ValueElement;
|
||||
IsActive: ValueElement;
|
||||
Solo: ValueElement;
|
||||
KeyRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
VelocityRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
SelectorRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
RootKey: ValueElement;
|
||||
Detune: ValueElement;
|
||||
TuneScale: ValueElement;
|
||||
Panorama: ValueElement;
|
||||
Volume: ValueElement;
|
||||
Link: ValueElement;
|
||||
SampleStart: ValueElement;
|
||||
SampleEnd: ValueElement;
|
||||
SustainLoop: SustainLoop;
|
||||
ReleaseLoop: ReleaseLoop;
|
||||
SampleRef: SampleRef;
|
||||
SlicingThreshold: ValueElement;
|
||||
SlicingBeatGrid: ValueElement;
|
||||
SlicingRegions: ValueElement;
|
||||
SlicingStyle: ValueElement;
|
||||
SampleWarpProperties: SampleWarpProperties;
|
||||
InitialSlicePointsFromOnsets: {
|
||||
SlicePoint: {
|
||||
TimeInSeconds: number;
|
||||
Rank: number;
|
||||
NormalizedEnergy: number;
|
||||
};
|
||||
};
|
||||
SlicePoints: {};
|
||||
ManualSlicePoints: {};
|
||||
BeatSlicePoints: {};
|
||||
RegionSlicePoints: {};
|
||||
UseDynamicBeatSlices: ValueElement;
|
||||
UseDynamicRegionSlices: ValueElement;
|
||||
AreSlicesFromOnsetsEditable: ValueElement;
|
||||
}
|
||||
|
||||
interface SampleParts {
|
||||
MultiSamplePart: MultiSamplePart;
|
||||
}
|
||||
|
||||
interface MultiSampleMap {
|
||||
SampleParts: SampleParts;
|
||||
LoadInRam: ValueElement;
|
||||
LayerCrossfade: ValueElement;
|
||||
SourceContext: {};
|
||||
RoundRobin: ValueElement;
|
||||
RoundRobinMode: ValueElement;
|
||||
RoundRobinResetPeriod: ValueElement;
|
||||
RoundRobinRandomSeed: ValueElement;
|
||||
}
|
||||
|
||||
interface LoopModulator {
|
||||
IsModulated: ValueElement;
|
||||
SampleStart: ManualElement;
|
||||
SampleLength: ManualElement;
|
||||
LoopOn: ManualElement;
|
||||
LoopLength: ManualElement;
|
||||
LoopFade: ManualElement;
|
||||
}
|
||||
|
||||
interface LoopModulators {
|
||||
LoopModulator: LoopModulator;
|
||||
}
|
||||
|
||||
interface Player {
|
||||
MultiSampleMap: MultiSampleMap;
|
||||
LoopModulators: LoopModulators;
|
||||
Reverse: ManualElement;
|
||||
Snap: ManualElement;
|
||||
SampleSelector: ManualElement;
|
||||
SubOsc: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
InterpolationMode: ValueElement;
|
||||
UseConstPowCrossfade: ValueElement;
|
||||
}
|
||||
|
||||
interface SimplerPitchEnvelope {
|
||||
_attrs: { Id: number };
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
Amount: ManualElement;
|
||||
ScrollPosition: ValueElement;
|
||||
}
|
||||
|
||||
interface Pitch {
|
||||
TransposeKey: ManualElement;
|
||||
TransposeFine: ManualElement;
|
||||
PitchLfoAmount: ManualElement;
|
||||
Envelope: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: SimplerPitchEnvelope;
|
||||
};
|
||||
};
|
||||
ScrollPosition: ValueElement;
|
||||
}
|
||||
|
||||
interface SimplerFilter {
|
||||
_attrs: { Id: number };
|
||||
LegacyType: ManualElement;
|
||||
Type: ManualElement;
|
||||
CircuitLpHp: ManualElement;
|
||||
CircuitBpNoMo: ManualElement;
|
||||
Slope: ManualElement;
|
||||
Freq: ManualElement;
|
||||
LegacyQ: ManualElement;
|
||||
Res: ManualElement;
|
||||
X: ManualElement;
|
||||
Drive: ManualElement;
|
||||
Envelope: {
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
IsOn: ManualElement;
|
||||
Amount: ManualElement;
|
||||
ScrollPosition: ValueElement;
|
||||
};
|
||||
ModByPitch: ManualElement;
|
||||
ModByVelocity: ManualElement;
|
||||
ModByLfo: ManualElement;
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {
|
||||
SimplerFilter: SimplerFilter;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface VolumeAndPan {
|
||||
Volume: ManualElement;
|
||||
VolumeVelScale: ManualElement;
|
||||
VolumeKeyScale: ManualElement;
|
||||
VolumeLfoAmount: ManualElement;
|
||||
Panorama: ManualElement;
|
||||
PanoramaKeyScale: ManualElement;
|
||||
PanoramaRnd: ManualElement;
|
||||
PanoramaLfoAmount: ManualElement;
|
||||
Envelope: {
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
};
|
||||
OneShotEnvelope: {
|
||||
FadeInTime: ManualElement;
|
||||
SustainMode: ManualElement;
|
||||
FadeOutTime: ManualElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSOriginalSimplerContent {
|
||||
_attrs: { Id: number };
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
_attrs: { Id: number };
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Player: Player;
|
||||
Pitch: Pitch;
|
||||
Filter: Filter;
|
||||
Shaper: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
VolumeAndPan: VolumeAndPan;
|
||||
AuxEnv: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
Lfo: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {
|
||||
SimplerLfo: {
|
||||
_attrs: { Id: number };
|
||||
Type: ManualElement;
|
||||
Frequency: ManualElement;
|
||||
RateType: ManualElement;
|
||||
BeatRate: ManualElement;
|
||||
StereoMode: ManualElement;
|
||||
Spin: ManualElement;
|
||||
Phase: ManualElement;
|
||||
Offset: ManualElement;
|
||||
FrequencyKeyScale: ManualElement;
|
||||
Smooth: ManualElement;
|
||||
Attack: ManualElement;
|
||||
Retrigger: ManualElement;
|
||||
Width: ManualElement;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
AuxLfos: {
|
||||
'0': {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
'1': {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
KeyDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
VelDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
RelVelDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
MidiCtrl: {
|
||||
'0': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'1': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'2': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'3': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'4': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'5': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
};
|
||||
Globals: {
|
||||
NumVoices: ValueElement;
|
||||
NumVoicesEnvTimeControl: ValueElement;
|
||||
RetriggerMode: ValueElement;
|
||||
ModulationResolution: ValueElement;
|
||||
SpreadAmount: ManualElement;
|
||||
KeyZoneShift: ManualElement;
|
||||
PortamentoMode: ManualElement;
|
||||
PortamentoTime: ManualElement;
|
||||
PitchBendRange: ValueElement;
|
||||
MpePitchBendRange: ValueElement;
|
||||
ScrollPosition: ValueElement;
|
||||
EnvScale: {
|
||||
EnvTime: ManualElement;
|
||||
EnvTimeKeyScale: ManualElement;
|
||||
EnvTimeIncludeAttack: ManualElement;
|
||||
};
|
||||
IsSimpler: ValueElement;
|
||||
PlaybackMode: ValueElement;
|
||||
LegacyMode: ValueElement;
|
||||
};
|
||||
ViewSettings: {
|
||||
SelectedPage: ValueElement;
|
||||
ZoneEditorVisible: ValueElement;
|
||||
Seconds: ValueElement;
|
||||
SelectedSampleChannel: ValueElement;
|
||||
VerticalSampleZoom: ValueElement;
|
||||
IsAutoSelectEnabled: ValueElement;
|
||||
SimplerBreakoutVisible: ValueElement;
|
||||
};
|
||||
SimplerSlicing: {
|
||||
PlaybackMode: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSSimpler {
|
||||
OriginalSimpler: ALSOriginalSimplerContent;
|
||||
}
|
||||
19
src/lib/exporters/ableton/types/trackSendHolder.ts
Normal file
19
src/lib/exporters/ableton/types/trackSendHolder.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { AutomationTarget, MidiControllerRange, ModulationTarget, ValueElement } from './common';
|
||||
|
||||
interface Send {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSTrackSendHolderContent {
|
||||
'@Id': number;
|
||||
Send: Send;
|
||||
Active: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSTrackSendHolder {
|
||||
TrackSendHolder: ALSTrackSendHolderContent;
|
||||
}
|
||||
138
src/lib/exporters/ableton/utils.ts
Normal file
138
src/lib/exporters/ableton/utils.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { create } from 'xmlbuilder2';
|
||||
|
||||
export const TIME_SIGNATURES: { [key: string]: number } = {
|
||||
'1/4': 196,
|
||||
'2/4': 198,
|
||||
'3/4': 200,
|
||||
'4/4': 201,
|
||||
'5/4': 202,
|
||||
'6/4': 203,
|
||||
'7/4': 204,
|
||||
'8/4': 205,
|
||||
'5/8': 206,
|
||||
'6/8': 207,
|
||||
'7/8': 208,
|
||||
'9/8': 209,
|
||||
'12/8': 210,
|
||||
'3/2': 211,
|
||||
'4/2': 212,
|
||||
'5/2': 213,
|
||||
'6/2': 214,
|
||||
'7/2': 215,
|
||||
'1/2': 216,
|
||||
'1/1': 217,
|
||||
'2/1': 218,
|
||||
'3/1': 219,
|
||||
'4/1': 220,
|
||||
'5/1': 221,
|
||||
'3/8': 299,
|
||||
};
|
||||
const MIN_ID = 22000;
|
||||
const START_ID = 22000;
|
||||
|
||||
let _id = START_ID;
|
||||
const templateCache: Record<string, any> = {};
|
||||
|
||||
const templateMap: Record<string, () => Promise<any>> = {
|
||||
midiClip: () => import('./templates/midiClip.xml?raw'),
|
||||
midiTrack: () => import('./templates/midiTrack.xml?raw'),
|
||||
project: () => import('./templates/project.xml?raw'),
|
||||
simpler: () => import('./templates/simpler.xml?raw'),
|
||||
scene: () => import('./templates/scene.xml?raw'),
|
||||
groupTrack: () => import('./templates/groupTrack.xml?raw'),
|
||||
drumRack: () => import('./templates/drumRack.xml?raw'),
|
||||
drumBranch: () => import('./templates/drumBranch.xml?raw'),
|
||||
returnTrack: () => import('./templates/returnTrack.xml?raw'),
|
||||
trackSendHolder: () => import('./templates/trackSendHolder.xml?raw'),
|
||||
effectReverb: () => import('./templates/effectReverb.xml?raw'),
|
||||
effectDelay: () => import('./templates/effectDelay.xml?raw'),
|
||||
effectChorus: () => import('./templates/effectChorus.xml?raw'),
|
||||
effectDistortion: () => import('./templates/effectDistortion.xml?raw'),
|
||||
effectFilter: () => import('./templates/effectFilter.xml?raw'),
|
||||
effectCompressor: () => import('./templates/effectCompressor.xml?raw'),
|
||||
};
|
||||
|
||||
export async function loadTemplate<T>(templateName: string): Promise<T> {
|
||||
if (templateCache[templateName]) {
|
||||
return templateCache[templateName];
|
||||
}
|
||||
|
||||
const importFn = templateMap[templateName];
|
||||
if (!importFn) {
|
||||
throw new Error(`Unknown template: ${templateName}`);
|
||||
}
|
||||
|
||||
const templateModule = await importFn();
|
||||
const parsed = create(templateModule.default).toObject();
|
||||
templateCache[templateName] = parsed;
|
||||
return parsed as T;
|
||||
}
|
||||
|
||||
export function koEnvRangeToSeconds(value: number, maxSeconds: number) {
|
||||
if (value < 0 || value > 255) {
|
||||
throw new RangeError('Value must be between 0 and 255');
|
||||
}
|
||||
return (value / 255) * maxSeconds;
|
||||
}
|
||||
|
||||
export function fixIds(node: any): any {
|
||||
if (Array.isArray(node)) {
|
||||
return node.map((n) => fixIds(n));
|
||||
}
|
||||
|
||||
if (node?.['@Id']) {
|
||||
const idNum = parseInt(String(node['@Id']), 10);
|
||||
|
||||
if (idNum > MIN_ID) {
|
||||
node['@Id'] = _id;
|
||||
|
||||
_id++;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof node === 'object') {
|
||||
Object.keys(node).forEach((key) => {
|
||||
if (!key.startsWith('@')) {
|
||||
node[key] = fixIds(node[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
export async function gzipString(content: string) {
|
||||
const encoder = new TextEncoder();
|
||||
const input = encoder.encode(content);
|
||||
|
||||
const cs = new CompressionStream('gzip');
|
||||
const writer = cs.writable.getWriter();
|
||||
|
||||
writer.write(input);
|
||||
writer.close();
|
||||
|
||||
const compressed = await new Response(cs.readable).arrayBuffer();
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
export function filterFreqFromNormalized(x: number) {
|
||||
const fMin = 30;
|
||||
const fMax = 22000;
|
||||
|
||||
return fMin * (fMax / fMin) ** x;
|
||||
}
|
||||
|
||||
export function toNativePath(posixPath: string): string {
|
||||
const isWindows = navigator.userAgent.includes('indows');
|
||||
const pathSep = isWindows ? '\\' : '/';
|
||||
if (pathSep === '/') {
|
||||
return posixPath;
|
||||
}
|
||||
|
||||
return posixPath.split('/').join(pathSep);
|
||||
}
|
||||
482
src/lib/exporters/dawProject.ts
Normal file
482
src/lib/exporters/dawProject.ts
Normal file
@@ -0,0 +1,482 @@
|
||||
import { toXML } from 'jstoxml';
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportStatus,
|
||||
Note,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../types/types';
|
||||
import dawProjectTransformer, {
|
||||
DawClip,
|
||||
DawClipSlot,
|
||||
DawLane,
|
||||
DawScene,
|
||||
DawTrack,
|
||||
} from '../transformers/dawProject';
|
||||
import { AbortError } from '../utils';
|
||||
import { collectSamples, getNextColor, getQuarterNotesPerBar } from './utils';
|
||||
|
||||
const PROJECT_NAME = 'EP-133: Export To DAW';
|
||||
|
||||
const XML_CONFIG = {
|
||||
indent: ' ',
|
||||
header: true,
|
||||
};
|
||||
|
||||
let _id = 0;
|
||||
|
||||
function genId() {
|
||||
return `id${_id++}`;
|
||||
}
|
||||
|
||||
function buildMasterTrack() {
|
||||
return {
|
||||
_name: 'Track',
|
||||
_attrs: {
|
||||
name: 'Master',
|
||||
id: genId(),
|
||||
loaded: 'true',
|
||||
contentType: 'audio notes',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Channel',
|
||||
_attrs: {
|
||||
id: '__MASTER__',
|
||||
role: 'master',
|
||||
solo: 'false',
|
||||
audioChannels: '2',
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Mute',
|
||||
_attrs: {
|
||||
name: 'Mute',
|
||||
value: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Pan',
|
||||
_attrs: {
|
||||
name: 'Pan',
|
||||
id: genId(),
|
||||
max: '1.000000',
|
||||
min: '0.000000',
|
||||
unit: 'normalized',
|
||||
value: '0.500000',
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Volume',
|
||||
_attrs: {
|
||||
name: 'Volume',
|
||||
id: genId(),
|
||||
max: '2.000000',
|
||||
min: '0.000000',
|
||||
unit: 'linear',
|
||||
value: '1.000000',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildTrack(track: DawTrack) {
|
||||
return {
|
||||
_name: 'Track',
|
||||
_attrs: {
|
||||
name: track.soundId ? `${String(track.soundId).padStart(3, '0')} ${track.name}` : track.name,
|
||||
id: `__TRACK_${track.padCode}__`,
|
||||
loaded: 'true',
|
||||
contentType: 'notes',
|
||||
color: getNextColor(),
|
||||
},
|
||||
_content: {
|
||||
_name: 'Channel',
|
||||
_attrs: {
|
||||
audioChannels: '2',
|
||||
destination: '__MASTER__',
|
||||
role: 'regular',
|
||||
solo: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Devices',
|
||||
_content: {
|
||||
_name: 'BuiltinDevice',
|
||||
_attrs: {
|
||||
deviceName: 'Sampler',
|
||||
deviceRole: 'instrument',
|
||||
loaded: 'true',
|
||||
id: genId(),
|
||||
name: track.name,
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Parameters',
|
||||
_attrs: {},
|
||||
},
|
||||
{
|
||||
_name: 'Enabled',
|
||||
_attrs: {
|
||||
value: 'true',
|
||||
id: genId(),
|
||||
name: 'On/Off',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Mute',
|
||||
_attrs: {
|
||||
name: 'Mute',
|
||||
value: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Pan',
|
||||
_attrs: {
|
||||
name: 'Pan',
|
||||
id: genId(),
|
||||
max: '1.000000',
|
||||
min: '0.000000',
|
||||
unit: 'normalized',
|
||||
value: '0.500000',
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Volume',
|
||||
_attrs: {
|
||||
name: 'Volume',
|
||||
id: genId(),
|
||||
max: '2.000000',
|
||||
min: '0.000000',
|
||||
unit: 'linear',
|
||||
value: `${track.volume}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildStructure(tracks: DawTrack[]) {
|
||||
return {
|
||||
_name: 'Structure',
|
||||
_content: [buildMasterTrack(), ...tracks.map((t) => buildTrack(t))],
|
||||
};
|
||||
}
|
||||
|
||||
function buildNote(note: Note, index: number, notes: Note[]) {
|
||||
let dur = note.duration / 96;
|
||||
const nextNote = notes[index + 1];
|
||||
|
||||
// making sure same notes are not overlapping
|
||||
if (
|
||||
nextNote &&
|
||||
nextNote.note === note.note &&
|
||||
note.position / 96 + dur > nextNote.position / 96
|
||||
) {
|
||||
dur = nextNote.position / 96 - note.position / 96;
|
||||
}
|
||||
|
||||
return {
|
||||
_name: 'Note',
|
||||
_attrs: {
|
||||
time: note.position / 96,
|
||||
duration: dur,
|
||||
channel: 0,
|
||||
key: note.note,
|
||||
vel: note.velocity / 127,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildClip(clip: DawClip) {
|
||||
const barLength = getQuarterNotesPerBar(
|
||||
clip.sceneTimeSignature.numerator,
|
||||
clip.sceneTimeSignature.denominator,
|
||||
);
|
||||
|
||||
return {
|
||||
_name: 'Clip',
|
||||
_attrs: {
|
||||
time: clip.offset * barLength,
|
||||
duration: clip.sceneBars * barLength,
|
||||
playStart: 0,
|
||||
loopStart: 0,
|
||||
loopEnd: clip.bars * barLength,
|
||||
enable: 'true',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Notes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: clip.notes.map(buildNote),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildLane(lane: DawLane) {
|
||||
return {
|
||||
_name: 'Lanes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
track: `__TRACK_${lane.padCode}__`,
|
||||
},
|
||||
_content: {
|
||||
_name: 'Clips',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: lane.clips.map((clip) => buildClip(clip)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildArrangement(lanes: DawLane[]) {
|
||||
return {
|
||||
_name: 'Arrangement',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: {
|
||||
_name: 'Lanes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
timeUnit: 'beats',
|
||||
},
|
||||
_content: lanes.map((lane) => buildLane(lane)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildSceneClip(clip: DawClip) {
|
||||
const beatsInBar = clip.sceneTimeSignature.numerator;
|
||||
|
||||
return {
|
||||
_name: 'Clip',
|
||||
_attrs: {
|
||||
time: clip.offset * beatsInBar,
|
||||
duration: clip.bars * beatsInBar,
|
||||
playStart: 0,
|
||||
loopStart: 0,
|
||||
loopEnd: clip.bars * beatsInBar,
|
||||
enable: 'true',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Notes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: clip.notes.map(buildNote),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildClipSlot(clipSlot: DawClipSlot) {
|
||||
return {
|
||||
_name: 'ClipSlot',
|
||||
_attrs: {
|
||||
track: `__TRACK_${clipSlot.track.padCode}__`,
|
||||
id: genId(),
|
||||
hasStop: 'true',
|
||||
},
|
||||
_content: clipSlot.clip.map((clip) => buildSceneClip(clip)),
|
||||
};
|
||||
}
|
||||
|
||||
function buildScene(scene: DawScene) {
|
||||
return {
|
||||
_name: 'Scene',
|
||||
_attrs: {
|
||||
name: scene.name,
|
||||
id: `__SCENE_${scene.name}__`,
|
||||
},
|
||||
_content: {
|
||||
_name: 'Lanes',
|
||||
_content: scene.clipSlot.map((clipSlot) => buildClipSlot(clipSlot)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildScenes(scenes: DawScene[]) {
|
||||
return {
|
||||
_name: 'Scenes',
|
||||
_content: scenes.map((scene) => buildScene(scene)),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMetadataXml() {
|
||||
const xml = toXML(
|
||||
{
|
||||
MetaData: {
|
||||
Title: '',
|
||||
Artist: '',
|
||||
Album: '',
|
||||
OriginalArtist: '',
|
||||
Songwriter: '',
|
||||
Producer: '',
|
||||
Year: '',
|
||||
Genre: '',
|
||||
Copyright: '',
|
||||
Comment: `Made with ${PROJECT_NAME}`,
|
||||
},
|
||||
},
|
||||
XML_CONFIG,
|
||||
);
|
||||
|
||||
return new Blob([xml], { type: 'text/xml' });
|
||||
}
|
||||
|
||||
export async function buildProjectXml(projectData: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const transformedData = dawProjectTransformer(projectData, exporterParams);
|
||||
const { timeSignature } = projectData.scenesSettings;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('projectData', projectData);
|
||||
console.log('transformedData', transformedData);
|
||||
}
|
||||
|
||||
_id = 0;
|
||||
|
||||
const application = {
|
||||
_name: 'Application',
|
||||
_attrs: {
|
||||
name: PROJECT_NAME,
|
||||
version: '1.0',
|
||||
},
|
||||
};
|
||||
|
||||
const transport = {
|
||||
_name: 'Transport',
|
||||
_content: [
|
||||
{
|
||||
_name: 'Tempo',
|
||||
_attrs: {
|
||||
unit: 'bpm',
|
||||
value: `${projectData.settings.bpm}`,
|
||||
name: 'Tempo',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'TimeSignature',
|
||||
_attrs: {
|
||||
numerator: timeSignature.numerator,
|
||||
denominator: timeSignature.denominator,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const main = toXML(
|
||||
{
|
||||
_name: 'Project',
|
||||
_attrs: {
|
||||
version: '1.0',
|
||||
},
|
||||
_content: [
|
||||
application,
|
||||
transport,
|
||||
buildStructure(transformedData.tracks),
|
||||
buildArrangement(transformedData.lanes),
|
||||
exporterParams.clips ? buildScenes(transformedData.scenes) : false,
|
||||
].filter(Boolean),
|
||||
},
|
||||
XML_CONFIG,
|
||||
);
|
||||
|
||||
return new Blob([main], { type: 'text/xml' });
|
||||
}
|
||||
|
||||
async function exportDawProject(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
) {
|
||||
progressCallback({ progress: 1, status: 'Exporting project data...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const metadataXml = buildMetadataXml();
|
||||
const projectXml = await buildProjectXml(data, exporterParams);
|
||||
|
||||
progressCallback({ progress: 2, status: 'Creating project file...' });
|
||||
|
||||
const zipProject = new JSZip();
|
||||
|
||||
zipProject.file('metadata.xml', metadataXml);
|
||||
zipProject.file('project.xml', projectXml);
|
||||
|
||||
const projectFile = await zipProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
const files: Array<{
|
||||
name: string;
|
||||
url: string;
|
||||
type: 'project' | 'archive';
|
||||
size: number;
|
||||
}> = [
|
||||
{
|
||||
name: `${exporterParams.projectName || `project${projectId}`}.dawproject`,
|
||||
url: URL.createObjectURL(projectFile),
|
||||
type: 'project',
|
||||
size: projectFile.size,
|
||||
},
|
||||
];
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const zipSamples = new JSZip();
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
|
||||
samples.forEach((s) => {
|
||||
zipSamples.file(s.name, s.data);
|
||||
});
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle samples...' });
|
||||
|
||||
const sampleFile = await zipSamples.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
files.push({
|
||||
name: `${exporterParams.projectName || `project${projectId}`}_samples.zip`,
|
||||
url: URL.createObjectURL(sampleFile),
|
||||
type: 'archive',
|
||||
size: sampleFile.size,
|
||||
});
|
||||
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
} as ExportResult;
|
||||
}
|
||||
|
||||
export default exportDawProject;
|
||||
112
src/lib/exporters/midi.ts
Normal file
112
src/lib/exporters/midi.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Midi } from '@tonejs/midi';
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportStatus,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../types/types';
|
||||
import midiTransformer from '../transformers/midi';
|
||||
import { AbortError } from '../utils';
|
||||
import { collectSamples, ticksToMidiTicks } from './utils';
|
||||
|
||||
async function exportMidi(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
) {
|
||||
progressCallback({ progress: 1, status: 'Exporting project data...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const transformedData = midiTransformer(data, exporterParams);
|
||||
const midi = new Midi();
|
||||
const timeSignature = data.scenesSettings.timeSignature;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(data);
|
||||
console.log(transformedData);
|
||||
}
|
||||
|
||||
midi.header.setTempo(data.settings.bpm);
|
||||
midi.header.timeSignatures.push({
|
||||
ticks: 0,
|
||||
timeSignature: [timeSignature.numerator, timeSignature.denominator],
|
||||
});
|
||||
|
||||
transformedData.tracks.forEach((track) => {
|
||||
const midiTrack = midi.addTrack();
|
||||
|
||||
midiTrack.name = track.name;
|
||||
midiTrack.channel = 0;
|
||||
|
||||
track.notes.forEach((note) => {
|
||||
midiTrack.addNote({
|
||||
ticks: ticksToMidiTicks(note.position, midi.header.ppq),
|
||||
durationTicks: ticksToMidiTicks(note.duration, midi.header.ppq),
|
||||
velocity: Math.max(0, Math.min(1, note.velocity / 127)),
|
||||
midi: note.note + 12,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-expect-error wrong typing?
|
||||
const midiBlob = new Blob([midi.toArray()], { type: 'audio/midi' });
|
||||
|
||||
const files: Array<{
|
||||
name: string;
|
||||
url: string;
|
||||
type: 'project' | 'archive';
|
||||
size: number;
|
||||
}> = [
|
||||
{
|
||||
name: `${exporterParams.projectName || `project${projectId}`}.mid`,
|
||||
url: URL.createObjectURL(midiBlob),
|
||||
type: 'project',
|
||||
size: midiBlob.size,
|
||||
},
|
||||
];
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const zipSamples = new JSZip();
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
|
||||
samples.forEach((s) => {
|
||||
zipSamples.file(s.name, s.data);
|
||||
});
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle samples...' });
|
||||
|
||||
const sampleFile = await zipSamples.generateAsync({ type: 'blob', compression: 'DEFLATE' });
|
||||
|
||||
files.push({
|
||||
name: `${exporterParams.projectName || `project${projectId}`}_samples.zip`,
|
||||
url: URL.createObjectURL(sampleFile),
|
||||
type: 'archive',
|
||||
size: sampleFile.size,
|
||||
});
|
||||
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
} as ExportResult;
|
||||
}
|
||||
|
||||
export default exportMidi;
|
||||
200
src/lib/exporters/reaper/index.ts
Normal file
200
src/lib/exporters/reaper/index.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportResultFile,
|
||||
ExportStatus,
|
||||
PadCode,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../../types/types';
|
||||
import { RprTrack, reaperTransform } from '../../transformers/reaper';
|
||||
import { AbortError } from '../../utils';
|
||||
import { collectSamples, getQuarterNotesPerBar } from '../utils';
|
||||
import { generateReaperProject, ReaperTrack } from './reaperlib';
|
||||
|
||||
function buildTrack(track: RprTrack): ReaperTrack {
|
||||
return {
|
||||
name: track.name,
|
||||
tempo: track.bpm,
|
||||
volume: track.volume,
|
||||
pan: track.pan,
|
||||
sample: track.sampleName
|
||||
? {
|
||||
name: track.sampleName,
|
||||
rate: track.sampleRate,
|
||||
channels: track.sampleChannels,
|
||||
length: track.soundLength,
|
||||
timeStretch: track.timeStretch,
|
||||
timeStretchBars: track.timeStretchBars,
|
||||
timeStretchBpm: track.timeStretchBpm,
|
||||
trimLeft: track.trimLeft,
|
||||
trimRight: track.trimRight,
|
||||
rootNote: track.rootNote,
|
||||
attack: track.attack,
|
||||
release: track.release,
|
||||
playMode: track.playMode,
|
||||
pitch: track.pitch,
|
||||
}
|
||||
: null,
|
||||
timeSignature: track.timeSignature,
|
||||
guid: crypto.randomUUID().toUpperCase(),
|
||||
items: track.items.map((item) => ({
|
||||
position:
|
||||
(item.offset *
|
||||
getQuarterNotesPerBar(track.timeSignature.numerator, track.timeSignature.denominator) *
|
||||
60) /
|
||||
track.bpm,
|
||||
length:
|
||||
(item.sceneBars *
|
||||
getQuarterNotesPerBar(track.timeSignature.numerator, track.timeSignature.denominator) *
|
||||
60) /
|
||||
track.bpm,
|
||||
lengthInBars: item.bars,
|
||||
name: item.sceneName,
|
||||
events: item.notes.map((n) => ({
|
||||
note: n.note,
|
||||
position: n.position,
|
||||
length: n.duration,
|
||||
velocity: n.velocity,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function buildReaperProject(
|
||||
data: ProjectRawData,
|
||||
projectId: string,
|
||||
exporterParams: ExporterParams,
|
||||
) {
|
||||
const transformedData = reaperTransform(data, exporterParams);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(transformedData);
|
||||
}
|
||||
|
||||
let tracks: ReaperTrack[];
|
||||
if (exporterParams.groupTracks) {
|
||||
const groupedTracks: ReaperTrack[] = [];
|
||||
|
||||
['a', 'b', 'c', 'd'].forEach((group) => {
|
||||
const groupTracks = transformedData.tracks.filter((t) => t.group === group);
|
||||
|
||||
if (groupTracks.length) {
|
||||
groupedTracks.push({
|
||||
...buildTrack({
|
||||
name: `Group ${group.toUpperCase()}`,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
bpm: data.settings.bpm,
|
||||
volume: 1,
|
||||
pan: 0,
|
||||
sampleName: '',
|
||||
sampleChannels: 0,
|
||||
sampleRate: 0,
|
||||
soundLength: 0,
|
||||
attack: 0,
|
||||
release: 0,
|
||||
trimLeft: 0,
|
||||
trimRight: 0,
|
||||
rootNote: 60,
|
||||
timeStretch: 'off',
|
||||
timeStretchBpm: 0,
|
||||
timeStretchBars: 0,
|
||||
soundId: 0,
|
||||
inChokeGroup: false,
|
||||
playMode: 'oneshot',
|
||||
pitch: 0,
|
||||
items: [],
|
||||
pad: 0,
|
||||
padCode: `${group}0` as PadCode,
|
||||
group,
|
||||
}),
|
||||
|
||||
tracks: groupTracks.map(buildTrack),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tracks = groupedTracks;
|
||||
} else {
|
||||
tracks = transformedData.tracks.map(buildTrack);
|
||||
}
|
||||
|
||||
const rprContent = generateReaperProject(
|
||||
{
|
||||
projectName: exporterParams.projectName || `Project ${projectId}`,
|
||||
tempo: data.settings?.bpm ?? 120,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
tracks,
|
||||
},
|
||||
exporterParams,
|
||||
);
|
||||
|
||||
return rprContent;
|
||||
}
|
||||
|
||||
async function exportReaper(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ExportResult> {
|
||||
let sampleReport: SampleReport | undefined;
|
||||
const files: ExportResultFile[] = [];
|
||||
const zippedProject = new JSZip();
|
||||
const projectName = exporterParams.projectName || `Project${projectId}`;
|
||||
|
||||
progressCallback({ progress: 1, status: 'Preparing REAPER export...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const rprContent = buildReaperProject(data, projectId, exporterParams);
|
||||
|
||||
zippedProject.file(`${projectName}/${projectName}.RPP`, rprContent);
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
samples.forEach((s) => {
|
||||
zippedProject.file(`${projectName}/Media/samples/${s.name}`, s.data);
|
||||
});
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle everything...' });
|
||||
|
||||
const zippedProjectFile = await zippedProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
const blob = new Blob([rprContent], { type: 'application/octet-stream' });
|
||||
files.push({
|
||||
name: `${projectName}.RPP`,
|
||||
url: URL.createObjectURL(blob),
|
||||
type: 'archive',
|
||||
size: blob.size,
|
||||
});
|
||||
}
|
||||
|
||||
files.push({
|
||||
name: `${projectName}.zip`,
|
||||
url: URL.createObjectURL(zippedProjectFile),
|
||||
type: 'archive',
|
||||
size: zippedProjectFile.size,
|
||||
});
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return { files, sampleReport };
|
||||
}
|
||||
|
||||
export default exportReaper;
|
||||
501
src/lib/exporters/reaper/reaperlib.ts
Normal file
501
src/lib/exporters/reaper/reaperlib.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
import { ExporterParams, TimeSignature } from '../../../types/types';
|
||||
import { getNextColor, getQuarterNotesPerBar, TICKS_PER_BEAT, ticksToMidiTicks } from '../utils';
|
||||
import { buildVstState } from './sampler';
|
||||
|
||||
export type ReaperMidiEvent = {
|
||||
note: number;
|
||||
position: number;
|
||||
length: number;
|
||||
velocity: number;
|
||||
};
|
||||
|
||||
export type ReaperMidiItem = {
|
||||
position: number;
|
||||
length: number;
|
||||
lengthInBars: number;
|
||||
name?: string;
|
||||
events: ReaperMidiEvent[];
|
||||
};
|
||||
|
||||
export type ReaperSample = {
|
||||
name: string;
|
||||
rate: number;
|
||||
channels: number;
|
||||
length: number;
|
||||
timeStretch: string;
|
||||
timeStretchBars: number;
|
||||
timeStretchBpm: number;
|
||||
trimLeft: number;
|
||||
trimRight: number;
|
||||
rootNote: number;
|
||||
attack: number;
|
||||
release: number;
|
||||
playMode: string;
|
||||
pitch: number;
|
||||
};
|
||||
|
||||
export type ReaperTrack = {
|
||||
name: string;
|
||||
guid: string;
|
||||
tempo: number;
|
||||
volume: number;
|
||||
pan: number;
|
||||
sample: ReaperSample | null;
|
||||
timeSignature: TimeSignature;
|
||||
tracks?: ReaperTrack[];
|
||||
items?: ReaperMidiItem[];
|
||||
};
|
||||
|
||||
export type ReaperProject = {
|
||||
projectName?: string;
|
||||
tempo: number;
|
||||
timeSignature: TimeSignature;
|
||||
tracks?: ReaperTrack[];
|
||||
};
|
||||
|
||||
type ReaperFileElem = {
|
||||
name: string;
|
||||
attrs?: (string | number)[];
|
||||
content?: (ReaperFileElem | (string | number)[])[];
|
||||
};
|
||||
|
||||
const PPQ = 960; // default reaper midi ppq
|
||||
|
||||
function hexToReaperColor(hexColor: string, alpha = 0xff) {
|
||||
hexColor = hexColor.replace('#', '');
|
||||
|
||||
const red = parseInt(hexColor.substring(0, 2), 16);
|
||||
const green = parseInt(hexColor.substring(2, 4), 16);
|
||||
const blue = parseInt(hexColor.substring(4, 6), 16);
|
||||
|
||||
return (alpha << 24) | (blue << 16) | (green << 8) | red;
|
||||
}
|
||||
|
||||
function renderLine(name: string, attrs: (string | number)[] = []) {
|
||||
return `${name} ${attrs
|
||||
.map((block) => {
|
||||
if (typeof block === 'string' && block.match(/\w+-\w+-\w+-\w+-\w+/)) {
|
||||
return `{${block}}`;
|
||||
}
|
||||
|
||||
return typeof block === 'string' ? `"${block}"` : block;
|
||||
})
|
||||
.join(' ')}`;
|
||||
}
|
||||
|
||||
function addFxChain(root: ReaperFileElem['content'], rprTrack: ReaperTrack) {
|
||||
if (!rprTrack.sample?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = buildVstState({
|
||||
filePath: `Media/samples/${rprTrack.sample.name}`,
|
||||
sampleLengthMs: rprTrack.sample.length * 1000,
|
||||
rootNote: rprTrack.sample.rootNote,
|
||||
attack: rprTrack.sample.attack,
|
||||
release: rprTrack.sample.release,
|
||||
});
|
||||
|
||||
root?.push({
|
||||
name: 'FXCHAIN',
|
||||
content: [
|
||||
['WNDRECT', 32, 117, 1037, 681],
|
||||
['SHOW', 0],
|
||||
['LASTSEL', 0],
|
||||
['DOCKED', 0],
|
||||
['BYPASS', 0, 0, 0],
|
||||
{
|
||||
name: 'VST',
|
||||
attrs: [
|
||||
'VSTi: ReaSamplOmatic5000 (Cockos)',
|
||||
'reasamplomatic.vst.so',
|
||||
0,
|
||||
'',
|
||||
'1920167789<56535472736F6D72656173616D706C6F>',
|
||||
'',
|
||||
],
|
||||
content: [[result.header], [result.body], ['AAAQAAAA']],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function addTrackItem(
|
||||
root: ReaperFileElem['content'],
|
||||
rprItem: ReaperMidiItem,
|
||||
rprTrack: ReaperTrack,
|
||||
iid: number,
|
||||
) {
|
||||
if (rprItem.events.length === 0) {
|
||||
return iid;
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
let totalTicks = 0;
|
||||
const barLength = getQuarterNotesPerBar(
|
||||
rprTrack.timeSignature.numerator,
|
||||
rprTrack.timeSignature.denominator,
|
||||
);
|
||||
|
||||
const clipLengthInTicks = rprItem.lengthInBars * barLength * TICKS_PER_BEAT;
|
||||
|
||||
const events = rprItem.events
|
||||
.flat()
|
||||
.filter((evt) => evt.position < clipLengthInTicks)
|
||||
.reduce(
|
||||
(acc, evt, index) => {
|
||||
const nextEvent = rprItem.events[index + 1];
|
||||
let noteLength = evt.length;
|
||||
|
||||
if (
|
||||
nextEvent &&
|
||||
nextEvent.note === evt.note &&
|
||||
nextEvent.position < evt.position + evt.length
|
||||
) {
|
||||
// prevent notes overlapping
|
||||
noteLength = nextEvent.position - evt.position;
|
||||
}
|
||||
|
||||
// prevent notes going beyond the item length
|
||||
if (evt.position + noteLength > clipLengthInTicks) {
|
||||
noteLength = evt.position + noteLength - clipLengthInTicks;
|
||||
}
|
||||
|
||||
const noteOn = [
|
||||
'e',
|
||||
ticksToMidiTicks(evt.position - offset, PPQ),
|
||||
'90',
|
||||
evt.note.toString(16),
|
||||
evt.velocity.toString(16),
|
||||
];
|
||||
const noteOff = ['e', ticksToMidiTicks(noteLength, PPQ), '80', evt.note.toString(16), '00'];
|
||||
|
||||
offset = evt.position + noteLength;
|
||||
totalTicks += (noteOn[1] as number) + (noteOff[1] as number);
|
||||
|
||||
return acc.concat([noteOn, noteOff]);
|
||||
},
|
||||
[] as (string | number)[][],
|
||||
);
|
||||
|
||||
const barInTicks = PPQ * barLength;
|
||||
|
||||
events.push(['E', Math.max(barInTicks * rprItem.lengthInBars - totalTicks, 0), 'b0', '7b', '00']);
|
||||
|
||||
const item = {
|
||||
name: 'ITEM',
|
||||
content: [
|
||||
['POSITION', rprItem.position],
|
||||
['SNAPOFFS', 0],
|
||||
['LENGTH', rprItem.length],
|
||||
['LOOP', 1],
|
||||
['ALLTAKES', 0],
|
||||
['FADEIN', 1, 0, 0, 1, 0, 0, 0],
|
||||
['FADEOUT', 1, 0, 0, 1, 0, 0, 0],
|
||||
['MUTE', 0, 0],
|
||||
['SEL', 0],
|
||||
['IGUID', crypto.randomUUID().toUpperCase()],
|
||||
['IID', iid++],
|
||||
['NAME', rprItem.name || `MIDI Item ${iid}`],
|
||||
['VOLPAN', 1, 0, 1, -1],
|
||||
['SOFFS', 0],
|
||||
['PLAYRATE', 1, 1, 0, -1, 0, 0.0025],
|
||||
['CHANMODE', 0],
|
||||
['GUID', crypto.randomUUID().toUpperCase()],
|
||||
{
|
||||
name: 'SOURCE',
|
||||
attrs: ['MIDI'],
|
||||
content: [
|
||||
['HASDATA', 1, PPQ, 'QN'],
|
||||
['CCINTERP', 32],
|
||||
['POOLEDEVTS', crypto.randomUUID().toUpperCase()],
|
||||
...events,
|
||||
['CCINTERP', 32],
|
||||
['CHASE_CC_TAKEOFFS', 1],
|
||||
['GUID', crypto.randomUUID().toUpperCase()],
|
||||
['IGNTEMPO', 0, rprTrack.tempo, 4, 4],
|
||||
['SRCCOLOR', 6],
|
||||
['EVTFILTER', 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, -1, 0, -1, -1],
|
||||
['VELLANE', -1, 100, 0, 0, 1],
|
||||
['CFGEDITVIEW', 0, 0.226823, 65, 12, 0, 0, 0, 0, 0, 0.5],
|
||||
['KEYSNAP', 0],
|
||||
['TRACKSEL', 0],
|
||||
[
|
||||
'CFGEDIT',
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.125,
|
||||
753,
|
||||
516,
|
||||
1975,
|
||||
1078,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
64,
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
root?.push(item);
|
||||
|
||||
return iid;
|
||||
}
|
||||
|
||||
const addTrack = (
|
||||
root: ReaperFileElem['content'],
|
||||
rprTrack: ReaperTrack,
|
||||
iid: number,
|
||||
endOfGroup: boolean,
|
||||
exporterParams: ExporterParams,
|
||||
) => {
|
||||
let isBus = [0, 0];
|
||||
|
||||
if (rprTrack.tracks?.length) {
|
||||
isBus = [1, 1];
|
||||
}
|
||||
|
||||
if (endOfGroup) {
|
||||
isBus = [2, -1];
|
||||
}
|
||||
|
||||
const newTrack: ReaperFileElem = {
|
||||
name: 'TRACK',
|
||||
attrs: [rprTrack.guid],
|
||||
content: [
|
||||
['NAME', rprTrack.name || 'Track'],
|
||||
['PEAKCOL', hexToReaperColor(getNextColor())],
|
||||
['BEAT', -1],
|
||||
['AUTOMODE', 0],
|
||||
['PANLAWFLAGS', 3],
|
||||
['VOLPAN', rprTrack.volume, rprTrack.pan, -1, -1, 1],
|
||||
['MUTESOLO', 0, 0, 0],
|
||||
['IPHASE', 0],
|
||||
['PLAYOFFS', 0, 1],
|
||||
['ISBUS', ...isBus],
|
||||
['BUSCOMP', 0, 0, 0, 0, 0],
|
||||
['SHOWINMIX', 1, 0.6667, 0.5, 1, 0.5, 0, 0, 0, 0],
|
||||
['FIXEDLANES', 9, 0, 0, 0, 0],
|
||||
['SEL', 0],
|
||||
['REC', 0, 0, 1, 0, 0, 0, 0, 0],
|
||||
['VU', 64],
|
||||
['TRACKHEIGHT', 0, 0, 0, 0, 0, 0, 0],
|
||||
['INQ', 0, 0, 0, 0.5, 100, 0, 0, 100],
|
||||
['NCHAN', 2],
|
||||
['FX', 1],
|
||||
['TRACKID', rprTrack.guid],
|
||||
['PERF', 0],
|
||||
['MIDIOUT', -1],
|
||||
['MAINSEND', 1, 0],
|
||||
],
|
||||
};
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
addFxChain(newTrack.content, rprTrack);
|
||||
}
|
||||
|
||||
if (rprTrack.items) {
|
||||
rprTrack.items.forEach((rprItem) => {
|
||||
iid = addTrackItem(newTrack.content, rprItem, rprTrack, iid);
|
||||
});
|
||||
}
|
||||
|
||||
root?.push(newTrack);
|
||||
|
||||
rprTrack.tracks?.forEach((_track, idx) => {
|
||||
iid = addTrack(root, _track, iid, idx === rprTrack.tracks!.length - 1, exporterParams);
|
||||
});
|
||||
|
||||
return iid;
|
||||
};
|
||||
|
||||
function createReaperProject(
|
||||
{ tempo = 120, timeSignature, tracks = [] }: ReaperProject,
|
||||
exporterParams: ExporterParams,
|
||||
) {
|
||||
let _iid = 1;
|
||||
|
||||
const _root: ReaperFileElem[] = [
|
||||
{
|
||||
name: 'REAPER_PROJECT',
|
||||
attrs: ['0.1', '7.48/unknown-x86_64', Math.round(Date.now() / 1000)],
|
||||
content: [
|
||||
{
|
||||
name: 'NOTES',
|
||||
attrs: [0, 2],
|
||||
},
|
||||
['RIPPLE', 0, 0],
|
||||
['GROUPOVERRIDE', 0, 0, 0, 0],
|
||||
['AUTOXFADE', 129],
|
||||
['ENVATTACH', 3],
|
||||
['POOLEDENVATTACH', 0],
|
||||
['MIXERUIFLAGS', 11, 48],
|
||||
['ENVFADESZ10', 40],
|
||||
['PEAKGAIN', 1],
|
||||
['FEEDBACK', 0],
|
||||
['PANLAW', 1],
|
||||
['PROJOFFS', 0, 0, 0],
|
||||
['MAXPROJLEN', 0, 0],
|
||||
['GRID', 3199, 8, 1, 8, 1, 0, 0, 0],
|
||||
['TIMEMODE', 1, 5, -1, 30, 0, 0, -1],
|
||||
['VIDEO_CONFIG', 0, 0, 65792],
|
||||
['PANMODE', 3],
|
||||
['PANLAWFLAGS', 3],
|
||||
['CURSOR', 0],
|
||||
['ZOOM', 100, 0, 0],
|
||||
['VZOOMEX', 6, 0],
|
||||
['USE_REC_CFG', 0],
|
||||
['RECMODE', 1],
|
||||
['SMPTESYNC', 0, 30, 100, 40, 1000, 300, 0, 0, 1, 0, 0],
|
||||
['LOOP', 1],
|
||||
['LOOPGRAN', 0],
|
||||
['RECORD_PATH', 'Media', ''],
|
||||
{
|
||||
name: 'RECORD_CFG',
|
||||
content: [['ZXZhdxgAAQ==']],
|
||||
},
|
||||
{
|
||||
name: 'APPLYFX_CFG',
|
||||
},
|
||||
['RENDER_FILE', ''],
|
||||
['RENDER_PATTERN', ''],
|
||||
['RENDER_FMT', 0, 2, 0],
|
||||
['RENDER_1X', 0],
|
||||
['RENDER_RANGE', 1, 0, 0, 0, 1000],
|
||||
['RENDER_RESAMPLE', 3, 0, 1],
|
||||
['RENDER_ADDTOPROJ', 0],
|
||||
['RENDER_STEMS', 0],
|
||||
['RENDER_DITHER', 0],
|
||||
['TIMELOCKMODE', 1],
|
||||
['TEMPOENVLOCKMODE', 1],
|
||||
['ITEMMIX', 1],
|
||||
['DEFPITCHMODE', 589824, 0],
|
||||
['TAKELANE', 1],
|
||||
['SAMPLERATE', 44100, 0, 0],
|
||||
{
|
||||
name: 'RENDER_CFG',
|
||||
content: [['ZXZhdxgAAQ==']],
|
||||
},
|
||||
['LOCK', 1],
|
||||
{
|
||||
name: 'METRONOME',
|
||||
attrs: [6, 2],
|
||||
content: [
|
||||
['VOL', 0.25, 0.125],
|
||||
['BEATLEN', 4],
|
||||
['FREQ', 1760, 880, 1],
|
||||
['SAMPLES', '', '', '', ''],
|
||||
['SPLIGNORE', 0, 0],
|
||||
['SPLDEF', 2, 660, '', 0, ''],
|
||||
['SPLDEF', 3, 440, '', 0, ''],
|
||||
['PATTERN', 0, 169],
|
||||
['PATTERNSTR', 'ABBB'],
|
||||
['MULT', 1],
|
||||
],
|
||||
},
|
||||
['GLOBAL_AUTO', -1],
|
||||
['TEMPO', tempo, timeSignature.numerator, timeSignature.denominator, 0],
|
||||
['PLAYRATE', 1, 0, 0.25, 4],
|
||||
['SELECTION', 0, 0],
|
||||
['SELECTION2', 0, 0],
|
||||
['MASTERAUTOMODE', 0],
|
||||
['MASTERTRACKHEIGHT', 0, 0],
|
||||
['MASTERPEAKCOL', 16576],
|
||||
['MASTERMUTESOLO', 0],
|
||||
['MASTERTRACKVIEW', 0, 0.6667, 0.5, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
['MASTERHWOUT', 0, 0, 1, 0, 0, 0, 0, -1],
|
||||
['MASTER_NCH', 2, 2],
|
||||
['MASTER_VOLUME', 1, 0, -1, -1, 1],
|
||||
['MASTER_PANMODE', 3],
|
||||
['MASTER_PANLAWFLAGS', 3],
|
||||
['MASTER_FX', 1],
|
||||
['MASTER_SEL', 0],
|
||||
{
|
||||
name: 'MASTERPLAYSPEEDENV',
|
||||
content: [
|
||||
['EGUID', crypto.randomUUID().toUpperCase()],
|
||||
['ACT', 0, -1],
|
||||
['VIS', 0, 1, 1],
|
||||
['LANEHEIGHT', 0, 0],
|
||||
['ARM', 0],
|
||||
['DEFSHAPE', 0, -1, -1],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'TEMPOENVEX',
|
||||
content: [
|
||||
['EGUID', crypto.randomUUID().toUpperCase()],
|
||||
['ACT', 1, -1],
|
||||
['VIS', 1, 0, 1],
|
||||
['LANEHEIGHT', 0, 0],
|
||||
['ARM', 0],
|
||||
['DEFSHAPE', 1, -1, -1],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'PROJBAY',
|
||||
content: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
tracks.forEach((_track) => {
|
||||
_iid = addTrack(_root[0].content, _track, _iid, false, exporterParams);
|
||||
});
|
||||
|
||||
return {
|
||||
toString(rootElems: any[] = _root, offset = 0) {
|
||||
let result = '';
|
||||
|
||||
for (const elem of rootElems) {
|
||||
if (Array.isArray(elem)) {
|
||||
// biome-ignore lint/style/useTemplate: too messy
|
||||
result += `${' '.repeat(2 * offset)}` + renderLine(elem[0], elem.slice(1)) + '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/useTemplate: too messy
|
||||
result += `${' '.repeat(2 * offset)}<` + renderLine(elem.name, elem.attrs) + '\n';
|
||||
|
||||
if (elem.content && elem.content.length > 0) {
|
||||
result += this.toString(elem.content, offset + 1);
|
||||
}
|
||||
|
||||
result += `${' '.repeat(2 * offset)}>\n`;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function generateReaperProject(
|
||||
input: ReaperProject,
|
||||
exporterParams: ExporterParams,
|
||||
): string {
|
||||
const reaperProject = createReaperProject(input, exporterParams);
|
||||
|
||||
return reaperProject.toString();
|
||||
}
|
||||
115
src/lib/exporters/reaper/sampler.ts
Normal file
115
src/lib/exporters/reaper/sampler.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
const VST_HEADER = [
|
||||
109, 111, 115, 114, 238, 94, 237, 254, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 90, 1, 0, 0, 1, 0, 0, 0, 0, 0, 16, 0,
|
||||
];
|
||||
|
||||
const VST_BODY = [
|
||||
0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 154, 153, 153, 153, 153, 153, 177, 63, 205, 204, 204, 204, 204,
|
||||
204, 235, 63, 0, 0, 0, 0, 0, 0, 0, 0, 28, 199, 113, 28, 199, 113, 220, 63, 252, 169, 241, 210, 77,
|
||||
98, 64, 63, 252, 169, 241, 210, 77, 98, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 224, 63, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 64, 0, 0, 0, 85, 85, 85, 85, 85, 85, 197, 63, 255, 255,
|
||||
255, 255, 8, 4, 2, 129, 64, 32, 128, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206,
|
||||
164, 33, 33, 26, 101, 144, 63, 0, 0, 0, 0, 0, 0, 240, 63, 252, 169, 241, 210, 77, 98, 48, 63, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0,
|
||||
];
|
||||
|
||||
function int32ToBytes(value: number): Uint8Array {
|
||||
const bytes = new Uint8Array(4);
|
||||
|
||||
bytes[0] = value & 0xff;
|
||||
bytes[1] = (value >> 8) & 0xff;
|
||||
bytes[2] = (value >> 16) & 0xff;
|
||||
bytes[3] = (value >> 24) & 0xff;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function doubleToBytes(num: number): number[] {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
view.setFloat64(0, num, true);
|
||||
const bytes = [];
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
bytes.push(view.getUint8(i));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function dbToBytes(dbValue: number): number[] {
|
||||
// convert dB to linear gain: 10^(dB/20)
|
||||
const linearGain = 10 ** (dbValue / 20);
|
||||
|
||||
return doubleToBytes(linearGain);
|
||||
}
|
||||
|
||||
function msToBytes(msValue: number, sampleLengthMs: number): number[] {
|
||||
const attackNormalized = msValue / 255;
|
||||
const maxAttack = sampleLengthMs / 2000;
|
||||
const cappedAttackMs = Math.min(attackNormalized, maxAttack);
|
||||
|
||||
return doubleToBytes(cappedAttackMs);
|
||||
}
|
||||
|
||||
function midiNoteToBytes(midiNote: number): number[] {
|
||||
const referenceNote = 60; // C4
|
||||
const referenceValue = 0.125;
|
||||
const valuePerSemitone = 0.075 / 12; // 0.00625
|
||||
|
||||
const semitoneOffset = referenceNote - midiNote;
|
||||
const pluginValue = referenceValue + semitoneOffset * valuePerSemitone;
|
||||
|
||||
return doubleToBytes(pluginValue);
|
||||
}
|
||||
|
||||
function bytesToBase64(bytes: Array<number>): string {
|
||||
let binary = '';
|
||||
const len = bytes.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
export function buildVstState({
|
||||
sampleLengthMs,
|
||||
filePath,
|
||||
volume = 0,
|
||||
attack = 0,
|
||||
release = 0,
|
||||
rootNote = 60,
|
||||
}: {
|
||||
filePath: string;
|
||||
sampleLengthMs: number;
|
||||
volume?: number;
|
||||
attack?: number;
|
||||
release?: number;
|
||||
rootNote?: number;
|
||||
}) {
|
||||
const encoder = new TextEncoder();
|
||||
const bufferHeader = [...VST_HEADER];
|
||||
const bufferBody = [...VST_BODY];
|
||||
|
||||
bufferBody.splice(0, 8, ...dbToBytes(volume)); // volume
|
||||
bufferBody.splice(72, 8, ...msToBytes(attack, sampleLengthMs)); // atack
|
||||
bufferBody.splice(80, 8, ...msToBytes(release, sampleLengthMs)); // release
|
||||
bufferBody.splice(40, 8, ...midiNoteToBytes(rootNote)); // root note
|
||||
bufferBody.splice(88, 8, ...doubleToBytes(1)); // obey note off
|
||||
bufferBody[128] = 2; // play mode: key
|
||||
|
||||
const fullBody = [...encoder.encode(filePath), 0, ...bufferBody];
|
||||
|
||||
bufferHeader.splice(32, 4, ...int32ToBytes(fullBody.length));
|
||||
|
||||
return {
|
||||
header: bytesToBase64(bufferHeader),
|
||||
body: bytesToBase64(fullBody),
|
||||
};
|
||||
}
|
||||
358
src/lib/exporters/utils.ts
Normal file
358
src/lib/exporters/utils.ts
Normal file
@@ -0,0 +1,358 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { unzippedBackupAtom } from '~/atoms/droppedProjectFile';
|
||||
import { ExportStatus, ProjectRawData, SoundInfo } from '../../types/types';
|
||||
import { getFile, getFileNodeByPath } from '../midi/fs';
|
||||
import { pcmToWavBlob } from '../pcmToWav';
|
||||
import { store } from '../store';
|
||||
import { AbortError, audioFormatAsBitDepth, findSoundIdByPad } from '../utils';
|
||||
|
||||
let _colorIndex = 0;
|
||||
|
||||
export const TICKS_PER_BEAT = 96; // (384 / 4 beats)
|
||||
const COLORS = [
|
||||
'#B2B0E8',
|
||||
'#E27D60',
|
||||
'#26A693',
|
||||
'#E8A87C',
|
||||
'#85C1A9',
|
||||
'#C38D9E',
|
||||
'#41B3A3',
|
||||
'#F2B880',
|
||||
'#7DAF9C',
|
||||
'#F47261',
|
||||
'#9D6A89',
|
||||
'#5AA9A4',
|
||||
'#7A2A80',
|
||||
'#8FB996',
|
||||
'#47B267',
|
||||
'#B089A3',
|
||||
'#6CA6A3',
|
||||
'#A0C4B0',
|
||||
'#F5A97F',
|
||||
'#BBA0C0',
|
||||
'#79B2B2',
|
||||
];
|
||||
|
||||
function extractPcmFromWav(wavData: Uint8Array): Uint8Array {
|
||||
const view = new DataView(wavData.buffer, wavData.byteOffset, wavData.byteLength);
|
||||
|
||||
const riff = String.fromCharCode(wavData[0], wavData[1], wavData[2], wavData[3]);
|
||||
if (riff !== 'RIFF') {
|
||||
throw new Error('Invalid WAV file: missing RIFF header');
|
||||
}
|
||||
|
||||
const wave = String.fromCharCode(wavData[8], wavData[9], wavData[10], wavData[11]);
|
||||
if (wave !== 'WAVE') {
|
||||
throw new Error('Invalid WAV file: missing WAVE format');
|
||||
}
|
||||
|
||||
let offset = 12;
|
||||
while (offset < wavData.length - 8) {
|
||||
const chunkId = String.fromCharCode(
|
||||
wavData[offset],
|
||||
wavData[offset + 1],
|
||||
wavData[offset + 2],
|
||||
wavData[offset + 3],
|
||||
);
|
||||
const chunkSize = view.getUint32(offset + 4, true);
|
||||
|
||||
if (chunkId === 'data') {
|
||||
return wavData.slice(offset + 8, offset + 8 + chunkSize);
|
||||
}
|
||||
|
||||
offset += 8 + chunkSize;
|
||||
if (chunkSize % 2 !== 0) {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Invalid WAV file: data chunk not found');
|
||||
}
|
||||
|
||||
export function parseWavMetadata(wavData: Uint8Array) {
|
||||
const view = new DataView(wavData.buffer, wavData.byteOffset, wavData.byteLength);
|
||||
|
||||
const riff = String.fromCharCode(wavData[0], wavData[1], wavData[2], wavData[3]);
|
||||
if (riff !== 'RIFF') {
|
||||
throw new Error('Invalid WAV file: missing RIFF header');
|
||||
}
|
||||
|
||||
const wave = String.fromCharCode(wavData[8], wavData[9], wavData[10], wavData[11]);
|
||||
if (wave !== 'WAVE') {
|
||||
throw new Error('Invalid WAV file: missing WAVE format');
|
||||
}
|
||||
|
||||
let offset = 12;
|
||||
let channels = 1;
|
||||
let samplerate = 44100;
|
||||
let bitsPerSample = 16;
|
||||
let audioFormat = 1;
|
||||
let rootNote = 60;
|
||||
let teMeta: Record<string, unknown> | null = null;
|
||||
|
||||
while (offset < wavData.length - 8) {
|
||||
const chunkId = String.fromCharCode(
|
||||
wavData[offset],
|
||||
wavData[offset + 1],
|
||||
wavData[offset + 2],
|
||||
wavData[offset + 3],
|
||||
);
|
||||
const chunkSize = view.getUint32(offset + 4, true);
|
||||
|
||||
if (chunkId === 'fmt ') {
|
||||
audioFormat = view.getUint16(offset + 8, true);
|
||||
channels = view.getUint16(offset + 10, true);
|
||||
samplerate = view.getUint32(offset + 12, true);
|
||||
bitsPerSample = view.getUint16(offset + 22, true);
|
||||
}
|
||||
|
||||
if (chunkId === 'smpl') {
|
||||
rootNote = view.getUint32(offset + 20, true);
|
||||
}
|
||||
|
||||
if (chunkId === 'LIST') {
|
||||
const listData = wavData.subarray(offset + 8, offset + 8 + chunkSize);
|
||||
const listType = String.fromCharCode(listData[0], listData[1], listData[2], listData[3]);
|
||||
if (listType === 'INFO') {
|
||||
let subOffset = 4;
|
||||
while (subOffset < listData.length - 8) {
|
||||
const subId = String.fromCharCode(
|
||||
listData[subOffset],
|
||||
listData[subOffset + 1],
|
||||
listData[subOffset + 2],
|
||||
listData[subOffset + 3],
|
||||
);
|
||||
const subSize = new DataView(
|
||||
listData.buffer,
|
||||
listData.byteOffset + subOffset + 4,
|
||||
4,
|
||||
).getUint32(0, true);
|
||||
|
||||
if (subId === 'TNGE') {
|
||||
const raw = listData.subarray(subOffset + 8, subOffset + 8 + subSize);
|
||||
const jsonStr = new TextDecoder('ascii')
|
||||
.decode(raw)
|
||||
.replace(/\0/g, '')
|
||||
.trim();
|
||||
try {
|
||||
teMeta = JSON.parse(jsonStr);
|
||||
} catch {
|
||||
console.warn('Failed to parse TNGE metadata JSON:', jsonStr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
subOffset += 8 + subSize;
|
||||
if (subSize % 2 !== 0) {
|
||||
subOffset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 8 + chunkSize;
|
||||
if (chunkSize % 2 !== 0) {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let format: 's16' | 's24' | 'float';
|
||||
if (audioFormat === 3 && bitsPerSample === 32) {
|
||||
format = 'float';
|
||||
} else if (bitsPerSample === 24) {
|
||||
format = 's24';
|
||||
} else {
|
||||
format = 's16';
|
||||
}
|
||||
|
||||
return {
|
||||
channels,
|
||||
samplerate,
|
||||
format,
|
||||
rootNote: (teMeta?.['sound.rootnote'] as number) ?? rootNote,
|
||||
teMeta,
|
||||
};
|
||||
}
|
||||
|
||||
export async function downloadPcm(
|
||||
soundId: number,
|
||||
progressCallback?: (bytesRead: number, totalRemaining: number) => void,
|
||||
) {
|
||||
const backupZip = store.get(unzippedBackupAtom);
|
||||
|
||||
if (backupZip) {
|
||||
const wavFile = Object.values(backupZip.files).find((file) =>
|
||||
file.name.startsWith(`/sounds/${String(soundId).padStart(3, '0')} `),
|
||||
);
|
||||
|
||||
if (!wavFile) {
|
||||
throw new Error(`Sound file for sound ID ${soundId} not found in backup`);
|
||||
}
|
||||
|
||||
const wavData = await wavFile.async('uint8array');
|
||||
return extractPcmFromWav(wavData);
|
||||
}
|
||||
|
||||
const fileNode = await getFileNodeByPath(`/sounds/${String(soundId).padStart(3, '0')}.pcm`);
|
||||
|
||||
if (!fileNode) {
|
||||
throw new Error(`Sound file for sound ID ${soundId} not found`);
|
||||
}
|
||||
|
||||
const fileData = await getFile(fileNode.nodeId, progressCallback);
|
||||
|
||||
return fileData.data;
|
||||
}
|
||||
|
||||
export function getSampleName(name: string | undefined, soundId: number, extension = true) {
|
||||
if (soundId >= 1000) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (soundId === 0 && !name) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const id = soundId.toString().padStart(3, '0');
|
||||
const n = name ? `${id} ${name}` : `${id} sample`;
|
||||
|
||||
return extension ? `${n}.wav` : n;
|
||||
}
|
||||
|
||||
function getSoundsInfoFromProject(data: ProjectRawData, exportAllPadsWithSamples = false) {
|
||||
const snds: SoundInfo[] = [];
|
||||
const existingSounds = new Set<number>();
|
||||
const usedSoundIds = new Set<number>();
|
||||
|
||||
for (const scene of data.scenes) {
|
||||
for (const pattern of scene.patterns) {
|
||||
if (pattern.notes.length > 0) {
|
||||
const sid = findSoundIdByPad(pattern.pad, data.pads);
|
||||
if (sid) {
|
||||
usedSoundIds.add(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const group in data.pads) {
|
||||
for (const pad of data.pads[group]) {
|
||||
if (pad.soundId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!exportAllPadsWithSamples && !usedSoundIds.has(pad.soundId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existingSounds.has(pad.soundId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const soundMeta = data.sounds.find((s) => s.id === pad.soundId);
|
||||
if (!soundMeta) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snds.push({ soundId: pad.soundId, soundMeta: soundMeta.meta });
|
||||
existingSounds.add(pad.soundId);
|
||||
}
|
||||
}
|
||||
|
||||
return snds;
|
||||
}
|
||||
|
||||
export async function collectSamples(
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
abortSignal: AbortSignal,
|
||||
exportAllPadsWithSamples = false,
|
||||
) {
|
||||
const projectSounds = getSoundsInfoFromProject(data, exportAllPadsWithSamples);
|
||||
|
||||
const samples: { name: string; data: Blob }[] = [];
|
||||
const downloaded: string[] = [];
|
||||
const missing: { name: string; error: string }[] = [];
|
||||
const percentStart = 3;
|
||||
const totalSounds = projectSounds.length || 1;
|
||||
const percentPerSound = 80 / totalSounds;
|
||||
let cnt = 0;
|
||||
|
||||
for (const snd of projectSounds) {
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const fileName = getSampleName(snd.soundMeta.name, snd.soundId);
|
||||
|
||||
try {
|
||||
progressCallback({
|
||||
progress: percentStart + percentPerSound * cnt,
|
||||
status: `Downloading sound: ${fileName}`,
|
||||
});
|
||||
|
||||
const result = await downloadPcm(snd.soundId, (bytesRead, totalRemaining) => {
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const currentSoundProgress = bytesRead / (totalRemaining / percentPerSound);
|
||||
progressCallback({
|
||||
progress: percentStart + percentPerSound * cnt + currentSoundProgress,
|
||||
status: `Downloading sound: ${fileName}`,
|
||||
});
|
||||
});
|
||||
|
||||
const wavBlob = pcmToWavBlob(
|
||||
result,
|
||||
snd.soundMeta.samplerate,
|
||||
audioFormatAsBitDepth(snd.soundMeta.format),
|
||||
snd.soundMeta.channels,
|
||||
);
|
||||
|
||||
samples.push({
|
||||
name: fileName,
|
||||
data: wavBlob,
|
||||
});
|
||||
|
||||
downloaded.push(fileName);
|
||||
} catch (err) {
|
||||
// only report when not aborted
|
||||
if (!abortSignal.aborted) {
|
||||
console.error(err);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
|
||||
missing.push({
|
||||
name: fileName,
|
||||
error: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
} finally {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
const sampleReport = { downloaded, missing };
|
||||
|
||||
progressCallback({
|
||||
progress: projectSounds.length === 0 ? percentStart + 80 : percentStart + percentPerSound * cnt,
|
||||
status: 'Sample collection completed',
|
||||
sampleReport,
|
||||
});
|
||||
|
||||
return { samples, sampleReport };
|
||||
}
|
||||
|
||||
export function getNextColor() {
|
||||
const color = COLORS[_colorIndex];
|
||||
_colorIndex = (_colorIndex + 1) % COLORS.length;
|
||||
return color;
|
||||
}
|
||||
|
||||
export function ticksToMidiTicks(ticks: number, ppq = 480) {
|
||||
return Math.round((ticks / TICKS_PER_BEAT) * ppq);
|
||||
}
|
||||
|
||||
export function getQuarterNotesPerBar(numerator: number, denominator: number) {
|
||||
return (4 / denominator) * numerator;
|
||||
}
|
||||
5
src/lib/ga.ts
Normal file
5
src/lib/ga.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function trackEvent(action: string, eventParams?: any) {
|
||||
if (import.meta.env.VITE_GA_ID) {
|
||||
window.gtag('event', action, eventParams);
|
||||
}
|
||||
}
|
||||
31
src/lib/midi/constants.ts
Normal file
31
src/lib/midi/constants.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export const TE_SYSEX = {
|
||||
STATUS_OK: 0,
|
||||
STATUS_ERROR: 1,
|
||||
STATUS_COMMAND_NOT_FOUND: 2,
|
||||
STATUS_BAD_REQUEST: 3,
|
||||
STATUS_SPECIFIC_ERROR_START: 16,
|
||||
STATUS_SPECIFIC_SUCCESS_START: 64,
|
||||
};
|
||||
|
||||
export const BIT_IS_REQUEST = 64;
|
||||
export const BIT_REQUEST_ID_AVAILABLE = 32;
|
||||
export const MIDI_SYSEX_START = 240;
|
||||
export const MIDI_SYSEX_END = 247;
|
||||
export const TE_MIDI_ID_0 = 0;
|
||||
export const TE_MIDI_ID_1 = 32;
|
||||
export const TE_MIDI_ID_2 = 118;
|
||||
export const MIDI_SYSEX_TE = 64;
|
||||
|
||||
export const IDENTITY_SYSEX = [0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7];
|
||||
|
||||
export const TE_SYSEX_GREET = 1;
|
||||
export const TE_SYSEX_FILE = 5;
|
||||
export const TE_SYSEX_FILE_INIT = 1;
|
||||
export const TE_SYSEX_FILE_GET = 3;
|
||||
export const TE_SYSEX_FILE_GET_TYPE_INIT = 0;
|
||||
export const TE_SYSEX_FILE_GET_TYPE_DATA = 1;
|
||||
export const TE_SYSEX_FILE_LIST = 4;
|
||||
export const TE_SYSEX_FILE_METADATA = 7;
|
||||
export const TE_SYSEX_FILE_METADATA_GET = 2;
|
||||
export const TE_SYSEX_FILE_FILE_TYPE_FILE = 1;
|
||||
export const TE_SYSEX_FILE_INFO = 11;
|
||||
337
src/lib/midi/device.ts
Normal file
337
src/lib/midi/device.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
import {
|
||||
BIT_IS_REQUEST,
|
||||
BIT_REQUEST_ID_AVAILABLE,
|
||||
IDENTITY_SYSEX,
|
||||
MIDI_SYSEX_END,
|
||||
MIDI_SYSEX_START,
|
||||
MIDI_SYSEX_TE,
|
||||
TE_MIDI_ID_0,
|
||||
TE_MIDI_ID_1,
|
||||
TE_MIDI_ID_2,
|
||||
TE_SYSEX,
|
||||
TE_SYSEX_GREET,
|
||||
} from './constants';
|
||||
import { TEDevice, TEDeviceIdentification, TESysexMessage } from './types';
|
||||
import {
|
||||
binToString,
|
||||
getNextRequestId,
|
||||
metadataStringToObject,
|
||||
packToBuffer,
|
||||
parseMidiIdentityResponse,
|
||||
sysexStatusToString,
|
||||
unpackInPlace,
|
||||
} from './utils';
|
||||
|
||||
let deviceInputPort: MIDIInput | null = null;
|
||||
let deviceOutputPort: MIDIOutput | null = null;
|
||||
let pendingInitialization = false;
|
||||
let deviceInitialized = false;
|
||||
const messageHandler = new Map<
|
||||
number,
|
||||
(inputPort: MIDIInput, requestId: number, data: Uint8Array) => void
|
||||
>();
|
||||
const portListeners = new Map<MIDIInput, (event: MIDIMessageEvent) => void>();
|
||||
|
||||
async function startEventListener(midiAccess: MIDIAccess) {
|
||||
const inputs = midiAccess.inputs.values();
|
||||
|
||||
// remove old listeners if any
|
||||
// mainly for hot reload during development
|
||||
stopEventListener();
|
||||
|
||||
const handleMidiMessage = (event: MIDIMessageEvent) => {
|
||||
// skip empty messages and non-sysex messages (like clock)
|
||||
if (!event.data || event.data.length === 0 || event.data[0] !== MIDI_SYSEX_START) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputPort = event.currentTarget as MIDIInput;
|
||||
|
||||
for (const [requestId, handler] of messageHandler.entries()) {
|
||||
handler(inputPort, requestId, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
for (const inputPort of inputs) {
|
||||
inputPort.addEventListener('midimessage', handleMidiMessage);
|
||||
portListeners.set(inputPort, handleMidiMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function stopEventListener() {
|
||||
for (const [port, callback] of portListeners) {
|
||||
if (port) {
|
||||
port.removeEventListener('midimessage', callback);
|
||||
portListeners.delete(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseTeenageSysex(bytes: Uint8Array) {
|
||||
const validHeader =
|
||||
bytes.length >= 9 &&
|
||||
bytes[0] === MIDI_SYSEX_START &&
|
||||
bytes[1] === TE_MIDI_ID_0 &&
|
||||
bytes[2] === TE_MIDI_ID_1 &&
|
||||
bytes[3] === TE_MIDI_ID_2 &&
|
||||
bytes[5] === MIDI_SYSEX_TE &&
|
||||
bytes[bytes.length - 1] === MIDI_SYSEX_END;
|
||||
|
||||
if (!validHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const msg: TESysexMessage = {
|
||||
kind: 'te-sysex',
|
||||
identityCode: bytes[4],
|
||||
requestId: 0,
|
||||
hasRequestId: false,
|
||||
status: -1,
|
||||
hStatus: '',
|
||||
command: bytes[8],
|
||||
type: bytes[6] & BIT_IS_REQUEST ? 'request' : 'response',
|
||||
rawData: new Uint8Array(),
|
||||
hexData: '',
|
||||
hexCommand: '',
|
||||
string: '',
|
||||
};
|
||||
|
||||
if (bytes[6] & BIT_REQUEST_ID_AVAILABLE) {
|
||||
msg.hasRequestId = true;
|
||||
msg.requestId = ((bytes[6] & 0x1f) << 7) | (bytes[7] & 0x7f);
|
||||
}
|
||||
|
||||
let index = 9;
|
||||
|
||||
if (msg.type === 'response') {
|
||||
msg.status = bytes[index++];
|
||||
}
|
||||
|
||||
msg.hStatus = sysexStatusToString(msg.status);
|
||||
if (msg.hStatus === undefined) {
|
||||
console.error(`Cannot handle message with status ${msg.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.rawData = unpackInPlace(bytes.subarray(index, bytes.length - 1));
|
||||
msg.string = binToString(msg.rawData);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
async function sendIdentAndWaitForReponse(output: MIDIOutput, timeoutMs: number = 2_000) {
|
||||
return new Promise<{ inputPort: MIDIInput | null; data: Uint8Array } | null>((resolve) => {
|
||||
const requestId = 0;
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
messageHandler.delete(requestId);
|
||||
console.warn('Timeout waiting for identity response');
|
||||
resolve(null);
|
||||
}, timeoutMs);
|
||||
|
||||
const handleMidiMessage = (inputPort: MIDIInput, requestId: number, data: Uint8Array) => {
|
||||
if (requestId === 0) {
|
||||
clearTimeout(timeoutId);
|
||||
messageHandler.delete(requestId);
|
||||
resolve({ inputPort, data });
|
||||
}
|
||||
};
|
||||
|
||||
messageHandler.set(requestId, handleMidiMessage);
|
||||
|
||||
output.send(IDENTITY_SYSEX);
|
||||
});
|
||||
}
|
||||
|
||||
export async function discoverDevicePorts(
|
||||
midiAccess: MIDIAccess,
|
||||
): Promise<TEDeviceIdentification | null> {
|
||||
const outputs = Array.from(midiAccess.outputs.values());
|
||||
let parsedResponse: TEDeviceIdentification | null = null;
|
||||
|
||||
if (outputs.length === 0) {
|
||||
return parsedResponse;
|
||||
}
|
||||
|
||||
for (const output of outputs) {
|
||||
try {
|
||||
console.group('Trying output port:', output.name);
|
||||
|
||||
const response = await sendIdentAndWaitForReponse(output);
|
||||
if (response) {
|
||||
console.debug('Checking response for output port:', output.name);
|
||||
|
||||
parsedResponse = parseMidiIdentityResponse(response.data);
|
||||
if (parsedResponse) {
|
||||
console.log('Found TE device on port:', output.name);
|
||||
|
||||
setDeviceOutputPort(output);
|
||||
setDeviceInputPort(response.inputPort);
|
||||
// setDeviceInputPort(
|
||||
// midiAccess.inputs.values().find((inp) => inp.name?.includes('EP-13')) || null, // handle both EP-133 and EP-1320
|
||||
// );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return parsedResponse;
|
||||
}
|
||||
|
||||
function sendTESysEx(
|
||||
midiOutput: MIDIOutput,
|
||||
identityCode: number,
|
||||
command: number,
|
||||
data: Uint8Array,
|
||||
) {
|
||||
const packedLength = data.length > 0 ? data.length + Math.ceil(data.length / 7) : 0;
|
||||
const message = new Uint8Array(10 + packedLength);
|
||||
const requestId = getNextRequestId(midiOutput.id);
|
||||
|
||||
const header = new Uint8Array([
|
||||
MIDI_SYSEX_START,
|
||||
TE_MIDI_ID_0,
|
||||
TE_MIDI_ID_1,
|
||||
TE_MIDI_ID_2,
|
||||
identityCode,
|
||||
MIDI_SYSEX_TE,
|
||||
BIT_IS_REQUEST | BIT_REQUEST_ID_AVAILABLE | ((requestId >> 7) & 0x1f),
|
||||
requestId & 0x7f,
|
||||
command,
|
||||
]);
|
||||
|
||||
message.set(header, 0);
|
||||
message[message.length - 1] = MIDI_SYSEX_END;
|
||||
|
||||
packToBuffer(data, message.subarray(9, 9 + packedLength));
|
||||
|
||||
midiOutput.send(message);
|
||||
|
||||
return requestId;
|
||||
}
|
||||
|
||||
export async function sendSysexToDevice(
|
||||
command: number,
|
||||
payload: Uint8Array | Array<number> = [],
|
||||
timeoutMs: number = 5_000,
|
||||
): Promise<TESysexMessage | null> {
|
||||
return new Promise<TESysexMessage | null>((resolve, reject) => {
|
||||
if (!deviceOutputPort || !deviceInputPort) {
|
||||
reject('No device output port available');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentRequestId = sendTESysEx(deviceOutputPort, 0, command, new Uint8Array(payload));
|
||||
|
||||
const timeoutHandler = () => {
|
||||
messageHandler.delete(currentRequestId);
|
||||
reject(`Timeout waiting for sysex response for request ${currentRequestId}`);
|
||||
};
|
||||
|
||||
const timeoutId = setTimeout(timeoutHandler, timeoutMs);
|
||||
|
||||
const handleMidiMessage = (_inputPort: MIDIInput, requestId: number, data: Uint8Array) => {
|
||||
if (requestId !== currentRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = parseTeenageSysex(data);
|
||||
if (!response || response.type !== 'response' || response.requestId !== currentRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
messageHandler.delete(currentRequestId);
|
||||
|
||||
if (response.status === TE_SYSEX.STATUS_OK) {
|
||||
resolve(response);
|
||||
} else if (response.status === TE_SYSEX.STATUS_SPECIFIC_SUCCESS_START) {
|
||||
reject('Partial response handling not implemented yet');
|
||||
} else {
|
||||
reject(`Received error status in sysex response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
};
|
||||
|
||||
messageHandler.set(currentRequestId, handleMidiMessage);
|
||||
});
|
||||
}
|
||||
|
||||
export async function tryInitDevice(
|
||||
midiAccess: MIDIAccess,
|
||||
onDeviceFound?: (deviceInfo: TEDevice) => void,
|
||||
) {
|
||||
if (pendingInitialization || deviceInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
pendingInitialization = true;
|
||||
|
||||
await startEventListener(midiAccess);
|
||||
|
||||
const deviceIdentification = await discoverDevicePorts(midiAccess);
|
||||
if (!deviceIdentification) {
|
||||
return;
|
||||
}
|
||||
|
||||
const greetResponse = await sendSysexToDevice(TE_SYSEX_GREET);
|
||||
if (!greetResponse) {
|
||||
throw new Error('No greetings from device');
|
||||
}
|
||||
|
||||
const deviceMetadata = metadataStringToObject(greetResponse.string);
|
||||
const deviceInfo: TEDevice = {
|
||||
inputId: getDeviceInputPort()?.id || '',
|
||||
outputId: getDeviceOutputPort()?.id || '',
|
||||
sku: deviceMetadata.sku,
|
||||
serial: deviceMetadata.serial,
|
||||
metadata: deviceMetadata,
|
||||
};
|
||||
|
||||
onDeviceFound?.(deviceInfo);
|
||||
|
||||
deviceInitialized = true;
|
||||
|
||||
return deviceMetadata;
|
||||
} catch (error) {
|
||||
console.error('Error accessing MIDI:', error);
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
pendingInitialization = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getDeviceInputPort() {
|
||||
return deviceInputPort;
|
||||
}
|
||||
|
||||
export function getDeviceOutputPort() {
|
||||
return deviceOutputPort;
|
||||
}
|
||||
|
||||
export function setDeviceInputPort(port: MIDIInput | null) {
|
||||
deviceInputPort = port;
|
||||
}
|
||||
|
||||
export function setDeviceOutputPort(port: MIDIOutput | null) {
|
||||
deviceOutputPort = port;
|
||||
}
|
||||
|
||||
export function disconnectDevice() {
|
||||
stopEventListener();
|
||||
setDeviceInputPort(null);
|
||||
setDeviceOutputPort(null);
|
||||
deviceInitialized = false;
|
||||
}
|
||||
|
||||
export function canInitializeDevice() {
|
||||
return (
|
||||
!pendingInitialization && !deviceInitialized && getDeviceInputPort() && getDeviceOutputPort()
|
||||
);
|
||||
}
|
||||
195
src/lib/midi/fs.ts
Normal file
195
src/lib/midi/fs.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { TE_SYSEX_FILE, TE_SYSEX_FILE_FILE_TYPE_FILE } from './constants';
|
||||
import { sendSysexToDevice } from './device';
|
||||
import {
|
||||
buildSysExFileGetMetadataRequest,
|
||||
buildSysExFileInitRequest,
|
||||
buildSysExFileListRequest,
|
||||
buildSysExGetFileDataRequest,
|
||||
buildSysExGetFileInitRequest,
|
||||
parseGetMetadataResponse,
|
||||
parseSysExFileListResponse,
|
||||
parseSysExGetFileDataResponse,
|
||||
parseSysexGetFileInitResponse,
|
||||
} from './fsSysex';
|
||||
import { TEFile, TEFileNode } from './types';
|
||||
import { crc32, sanitizeBrokenJson } from './utils';
|
||||
|
||||
const fileNodesCache: Record<string, TEFileNode> = {};
|
||||
|
||||
export async function getFile(
|
||||
nodeId: number,
|
||||
progressCallback?: (bytesRead: number, totalBytes: number) => void,
|
||||
): Promise<TEFile> {
|
||||
const offset = 0;
|
||||
const options = null;
|
||||
const chunks = [];
|
||||
let bytesRead = 0;
|
||||
let pageIndex = 0;
|
||||
|
||||
const initResponseRaw = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExGetFileInitRequest(nodeId, offset, options),
|
||||
);
|
||||
|
||||
if (!initResponseRaw) {
|
||||
throw new Error('Failed to get file init response from device');
|
||||
}
|
||||
|
||||
const initResponse = parseSysexGetFileInitResponse(initResponseRaw.rawData);
|
||||
const totalRemaining = initResponse.fileSize - offset;
|
||||
|
||||
while (bytesRead < totalRemaining) {
|
||||
const chunkResponseRaw = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExGetFileDataRequest(pageIndex),
|
||||
);
|
||||
|
||||
if (!chunkResponseRaw) {
|
||||
throw new Error('Failed to get file data response from device');
|
||||
}
|
||||
|
||||
const chunkResponse = parseSysExGetFileDataResponse(chunkResponseRaw.rawData);
|
||||
if (chunkResponse.page !== pageIndex) {
|
||||
throw new Error(`Unexpected page number ${chunkResponse.page}, expected ${pageIndex}`);
|
||||
}
|
||||
|
||||
if (chunkResponse.data.byteLength === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
chunks.push(chunkResponse.data);
|
||||
|
||||
bytesRead += chunkResponse.data.byteLength;
|
||||
progressCallback?.(bytesRead, totalRemaining);
|
||||
pageIndex = chunkResponse.nextPage;
|
||||
}
|
||||
|
||||
const length = chunks.reduce((acc, buf) => acc + buf.length, 0);
|
||||
const combined = new Uint8Array(length);
|
||||
let bufOffset = 0;
|
||||
|
||||
for (const buf of chunks) {
|
||||
combined.set(buf, bufOffset);
|
||||
bufOffset += buf.length;
|
||||
}
|
||||
|
||||
return {
|
||||
name: initResponse.fileName,
|
||||
size: initResponse.fileSize,
|
||||
data: combined,
|
||||
crc32: crc32(combined),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getFileList(): Promise<TEFileNode[]> {
|
||||
await sendSysexToDevice(TE_SYSEX_FILE, buildSysExFileInitRequest(4 * 1024 * 1024, 0));
|
||||
|
||||
return getFileListInternal();
|
||||
}
|
||||
|
||||
async function getFileListInternal(
|
||||
nodeId: number = 0,
|
||||
filesList: TEFileNode[] = [],
|
||||
path = '/',
|
||||
): Promise<TEFileNode[]> {
|
||||
let page = 0;
|
||||
|
||||
while (true) {
|
||||
const fileListResponse = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExFileListRequest(page, nodeId),
|
||||
);
|
||||
|
||||
if (!fileListResponse || !fileListResponse.rawData || fileListResponse.rawData.length < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const pageNumber = ((fileListResponse.rawData[0] << 8) | fileListResponse.rawData[1]) & 0xffff;
|
||||
if (pageNumber !== page) {
|
||||
throw new Error(`Unexpected page ${pageNumber}, expected ${page}`);
|
||||
}
|
||||
|
||||
page += 1;
|
||||
|
||||
const payload = fileListResponse?.rawData.slice(2);
|
||||
const list = parseSysExFileListResponse(payload);
|
||||
if (list.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (const entry of list) {
|
||||
const filePath = path === '/' ? `${path}${entry.fileName}` : `${path}/${entry.fileName}`;
|
||||
const fileType = entry.flags & TE_SYSEX_FILE_FILE_TYPE_FILE ? 'file' : 'folder';
|
||||
|
||||
const item = {
|
||||
...entry,
|
||||
fileName: filePath,
|
||||
fileType,
|
||||
} as const;
|
||||
|
||||
fileNodesCache[filePath] = item;
|
||||
filesList.push(item);
|
||||
|
||||
if (fileType === 'folder') {
|
||||
await getFileListInternal(entry.nodeId, filesList, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filesList;
|
||||
}
|
||||
|
||||
export async function getFileMetadata<T extends Record<string, any>>(nodeId: number, pages = null) {
|
||||
const result: T = {} as T;
|
||||
|
||||
for (const pageSelector of pages || [null]) {
|
||||
let metadataStr = '';
|
||||
let page = 0;
|
||||
|
||||
while (true) {
|
||||
const response = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExFileGetMetadataRequest(nodeId, page, pageSelector),
|
||||
);
|
||||
|
||||
if (!response || response?.rawData.byteLength <= 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const parsed = parseGetMetadataResponse(response.rawData);
|
||||
if (parsed.page !== page) {
|
||||
throw new Error(`unexpected page ${parsed.page}, expected ${page}`);
|
||||
}
|
||||
|
||||
page += 1;
|
||||
metadataStr += parsed.metadata;
|
||||
|
||||
if (response.rawData.slice(-1)[0] === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(result, JSON.parse(sanitizeBrokenJson(metadataStr)));
|
||||
} catch (err) {
|
||||
throw new Error(`Could not parse ${metadataStr}: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getFileNodeByPath(path: string) {
|
||||
if (fileNodesCache[path]) {
|
||||
return fileNodesCache[path];
|
||||
}
|
||||
|
||||
const filesList = await getFileList();
|
||||
|
||||
return filesList.find((file) => file.fileName === path) || null;
|
||||
}
|
||||
|
||||
export function resetFileCache() {
|
||||
for (const key in fileNodesCache) {
|
||||
delete fileNodesCache[key];
|
||||
}
|
||||
}
|
||||
172
src/lib/midi/fsSysex.ts
Normal file
172
src/lib/midi/fsSysex.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
TE_SYSEX_FILE_GET,
|
||||
TE_SYSEX_FILE_GET_TYPE_DATA,
|
||||
TE_SYSEX_FILE_GET_TYPE_INIT,
|
||||
TE_SYSEX_FILE_INFO,
|
||||
TE_SYSEX_FILE_INIT,
|
||||
TE_SYSEX_FILE_LIST,
|
||||
TE_SYSEX_FILE_METADATA,
|
||||
TE_SYSEX_FILE_METADATA_GET,
|
||||
} from './constants';
|
||||
import { parseNullTerminatedString, writeStringToView } from './utils';
|
||||
|
||||
export function buildSysExFileInitRequest(maxResponseLength: number, flags: number) {
|
||||
const buffer = new Uint8Array(6);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_INIT);
|
||||
view.setUint8(1, flags);
|
||||
view.setUint32(2, maxResponseLength);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function buildSysExFileInfoRequest(fileId: number) {
|
||||
const buffer = new Uint8Array(3);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_INFO);
|
||||
view.setUint16(1, fileId);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseFileInfoResponse(data: Uint8Array) {
|
||||
const nodeId = (data[0] << 8) | data[1];
|
||||
const parentId = (data[2] << 8) | data[3];
|
||||
const flags = data[4];
|
||||
const fileSize = (data[5] << 24) | (data[6] << 16) | (data[7] << 8) | data[8];
|
||||
const fileName = parseNullTerminatedString(data, 9);
|
||||
|
||||
return {
|
||||
nodeId,
|
||||
parentId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExFileListRequest(page: number, nodeId: number) {
|
||||
const buffer = new Uint8Array(5);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_LIST);
|
||||
view.setUint16(1, page);
|
||||
view.setUint16(3, nodeId);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseSysExFileListResponse(data: Uint8Array) {
|
||||
const entries = [];
|
||||
let offset = 0;
|
||||
|
||||
while (offset < data.byteLength) {
|
||||
const nodeId = (data[offset] << 8) | data[offset + 1];
|
||||
const flags = data[offset + 2];
|
||||
const fileSize =
|
||||
(data[offset + 3] << 24) |
|
||||
(data[offset + 4] << 16) |
|
||||
(data[offset + 5] << 8) |
|
||||
data[offset + 6];
|
||||
const fileName = parseNullTerminatedString(data, offset + 7);
|
||||
const length = 7 + fileName.length;
|
||||
|
||||
entries.push({
|
||||
nodeId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
});
|
||||
|
||||
offset += length + 1;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export function buildSysExGetFileInitRequest(
|
||||
fileId: number,
|
||||
offset: number,
|
||||
extraArgs: Uint8Array | null = null,
|
||||
) {
|
||||
const length = extraArgs ? 16 + extraArgs.length : 8;
|
||||
const buffer = new Uint8Array(length);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_GET);
|
||||
view.setUint8(1, TE_SYSEX_FILE_GET_TYPE_INIT);
|
||||
view.setUint16(2, fileId);
|
||||
view.setUint32(4, offset);
|
||||
|
||||
if (extraArgs != null) {
|
||||
view.setBigUint64(8, 0n);
|
||||
for (let i = 0; i < extraArgs.length; i++) {
|
||||
view.setUint8(16 + i, extraArgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseSysexGetFileInitResponse(bytes: Uint8Array) {
|
||||
const fileId = (bytes[0] << 8) | bytes[1];
|
||||
const flags = bytes[2];
|
||||
const fileSize = (bytes[3] << 24) | (bytes[4] << 16) | (bytes[5] << 8) | bytes[6];
|
||||
const fileName = parseNullTerminatedString(bytes, 7);
|
||||
|
||||
return {
|
||||
fileId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExGetFileDataRequest(page: number) {
|
||||
const buffer = new Uint8Array(4);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_GET);
|
||||
view.setUint8(1, TE_SYSEX_FILE_GET_TYPE_DATA);
|
||||
view.setUint16(2, page);
|
||||
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
export function parseSysExGetFileDataResponse(bytes: Uint8Array) {
|
||||
return {
|
||||
page: (bytes[0] << 8) | bytes[1],
|
||||
nextPage: ((bytes[0] << 8) | (bytes[1] + 1)) & 0xffff,
|
||||
data: bytes.subarray(2),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExFileGetMetadataRequest(
|
||||
fileId: number,
|
||||
page: number,
|
||||
key: string | null = null,
|
||||
) {
|
||||
const extraLength = key?.length ? key.length + 1 : 0;
|
||||
const buffer = new Uint8Array(6 + extraLength);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_METADATA);
|
||||
view.setUint8(1, TE_SYSEX_FILE_METADATA_GET);
|
||||
view.setUint16(2, fileId);
|
||||
view.setUint16(4, page);
|
||||
|
||||
if (key != null) {
|
||||
writeStringToView(view, 6, key, true);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseGetMetadataResponse(data: Uint8Array) {
|
||||
const page = (data[0] << 8) | data[1];
|
||||
const metadata = parseNullTerminatedString(data, 2);
|
||||
|
||||
return { page, metadata };
|
||||
}
|
||||
64
src/lib/midi/index.ts
Normal file
64
src/lib/midi/index.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { disconnectDevice, getDeviceInputPort, getDeviceOutputPort, tryInitDevice } from './device';
|
||||
import { getFile, getFileNodeByPath, resetFileCache } from './fs';
|
||||
import { TEDevice } from './types';
|
||||
|
||||
export async function initDevice({
|
||||
onDeviceFound,
|
||||
onDeviceLost,
|
||||
onNoMidiAccess,
|
||||
}: {
|
||||
onDeviceFound?: (deviceInfo: TEDevice) => void;
|
||||
onDeviceLost?: () => void;
|
||||
onNoMidiAccess?: (error: Error) => void;
|
||||
}): Promise<void> {
|
||||
let midiAccess: MIDIAccess | null = null;
|
||||
|
||||
try {
|
||||
midiAccess = await navigator.requestMIDIAccess({ sysex: true });
|
||||
} catch (error) {
|
||||
onNoMidiAccess?.(error as Error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await tryInitDevice(midiAccess, onDeviceFound);
|
||||
|
||||
const stateChangeMidiEventHandler = (event: MIDIConnectionEvent) => {
|
||||
const currentInputPort = getDeviceInputPort();
|
||||
const currentOutputPort = getDeviceOutputPort();
|
||||
|
||||
if (
|
||||
event.port?.state === 'disconnected' &&
|
||||
(currentInputPort?.id === event.port?.id || currentOutputPort?.id === event.port?.id)
|
||||
) {
|
||||
disconnectDevice();
|
||||
resetFileCache();
|
||||
onDeviceLost?.();
|
||||
}
|
||||
|
||||
if (event.port?.state === 'connected') {
|
||||
setTimeout(() => tryInitDevice(midiAccess, onDeviceFound), 1000);
|
||||
}
|
||||
};
|
||||
|
||||
midiAccess.addEventListener('statechange', stateChangeMidiEventHandler);
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.on('vite:beforeUpdate', () => {
|
||||
midiAccess?.removeEventListener('statechange', stateChangeMidiEventHandler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectFile(projectId: number) {
|
||||
const path = `/projects/${String(projectId).padStart(2, '0')}`;
|
||||
const foundNode = await getFileNodeByPath(path);
|
||||
|
||||
if (!foundNode) {
|
||||
throw new Error(`Project ${projectId} not found`);
|
||||
}
|
||||
|
||||
const archive = await getFile(foundNode.nodeId);
|
||||
|
||||
return archive;
|
||||
}
|
||||
71
src/lib/midi/types.ts
Normal file
71
src/lib/midi/types.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
export type TEDeviceMetadata = {
|
||||
chip_id: string;
|
||||
mode: string;
|
||||
os_version: string;
|
||||
product: string;
|
||||
serial: string;
|
||||
sku: string;
|
||||
sw_version: string;
|
||||
};
|
||||
|
||||
export type TESysexMessage = {
|
||||
kind: 'te-sysex';
|
||||
identityCode: number;
|
||||
requestId: number;
|
||||
hasRequestId: boolean;
|
||||
status: number;
|
||||
hStatus: string;
|
||||
command: number;
|
||||
type: 'request' | 'response';
|
||||
rawData: Uint8Array;
|
||||
hexData: string;
|
||||
hexCommand: string;
|
||||
string: string;
|
||||
};
|
||||
|
||||
export type TEFileNode = {
|
||||
nodeId: number;
|
||||
flags: number;
|
||||
fileSize: number;
|
||||
fileName: string;
|
||||
fileType: 'file' | 'folder';
|
||||
};
|
||||
|
||||
export type TEFile = {
|
||||
name: string;
|
||||
size: number;
|
||||
data: Uint8Array;
|
||||
crc32?: number;
|
||||
};
|
||||
|
||||
export type TEDeviceIdentification = {
|
||||
id: number;
|
||||
sku: string;
|
||||
};
|
||||
|
||||
export interface TEDevice {
|
||||
inputId: string;
|
||||
outputId: string;
|
||||
sku: string;
|
||||
serial: string;
|
||||
metadata: TEDeviceMetadata;
|
||||
}
|
||||
|
||||
export type TESoundMetadata = {
|
||||
channels: number;
|
||||
samplerate: number;
|
||||
format: 's16' | 's24' | 'float';
|
||||
crc: number;
|
||||
name: string;
|
||||
'sound.loopstart': number;
|
||||
'sound.loopend': number;
|
||||
'sound.amplitude': number;
|
||||
'sound.playmode': 'oneshot' | 'loop' | 'key' | 'legato';
|
||||
'sound.pan': number;
|
||||
'sound.pitch': number;
|
||||
'sound.rootnote': number;
|
||||
'time.mode': string;
|
||||
'sound.bpm': number;
|
||||
'envelope.attack': number;
|
||||
'envelope.release': number;
|
||||
};
|
||||
170
src/lib/midi/utils.ts
Normal file
170
src/lib/midi/utils.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { TE_MIDI_ID_0, TE_MIDI_ID_1, TE_MIDI_ID_2, TE_SYSEX } from './constants';
|
||||
import { TEDeviceIdentification, TEDeviceMetadata } from './types';
|
||||
|
||||
const requestIds: Map<string, number> = new Map();
|
||||
|
||||
export const crc32 = (data: Uint8Array, initial = 0) => {
|
||||
const normalize = (value: number) => (value >= 0 ? value : 0xffffffff + value + 1);
|
||||
|
||||
let crc = normalize(initial) ^ 0xffffffff;
|
||||
|
||||
for (const byte of data) {
|
||||
crc ^= byte;
|
||||
for (let i = 0; i < 8; i++) {
|
||||
crc = crc & 1 ? (crc >>> 1) ^ 0xedb88320 : crc >>> 1;
|
||||
}
|
||||
}
|
||||
|
||||
return normalize(crc ^ 0xffffffff);
|
||||
};
|
||||
|
||||
export function unpackInPlace(packedBytes: Uint8Array): Uint8Array {
|
||||
let writeIndex = 0; // Index where unpacked bytes will be written
|
||||
let msbIndex = 0; // Index in array holding the MSB flags
|
||||
let bitIndex = 0; // Bit position within the MSB byte
|
||||
let readIndex = 1; // Read position for data bytes
|
||||
let msbByte = packedBytes[msbIndex];
|
||||
|
||||
while (readIndex < packedBytes.length) {
|
||||
const msb = (msbByte & (1 << bitIndex) ? 1 : 0) << 7; // Extract MSB bit
|
||||
const data = packedBytes[readIndex] & 0x7f; // Lower 7 bits
|
||||
const fullByte = msb | data;
|
||||
|
||||
packedBytes[writeIndex] = fullByte;
|
||||
|
||||
bitIndex++;
|
||||
readIndex++;
|
||||
writeIndex++;
|
||||
|
||||
if (bitIndex > 6) {
|
||||
// Move to next group of 8 bytes (1 MSB byte + 7 data bytes)
|
||||
readIndex++;
|
||||
bitIndex = 0;
|
||||
msbIndex += 8;
|
||||
msbByte = packedBytes[msbIndex];
|
||||
}
|
||||
}
|
||||
|
||||
return packedBytes.subarray(0, writeIndex);
|
||||
}
|
||||
|
||||
export function parseMidiIdentityResponse(data: Uint8Array): TEDeviceIdentification | null {
|
||||
if (
|
||||
data.length === 17 &&
|
||||
data[0] === 0xf0 && // SysEx start
|
||||
data[1] === 0x7e && // Universal SysEx
|
||||
data[5] === TE_MIDI_ID_0 && // TE manufacturer ID byte 0
|
||||
data[6] === TE_MIDI_ID_1 && // TE manufacturer ID byte 1
|
||||
data[7] === TE_MIDI_ID_2 // TE manufacturer ID byte 2
|
||||
) {
|
||||
const productCode = data[8] ^ (data[9] << 7);
|
||||
const assemblyCode = data[10] ^ (data[11] << 7);
|
||||
|
||||
return {
|
||||
id: data[2],
|
||||
sku: `TE${productCode.toString().padStart(3, '0')}AS${assemblyCode.toString().padStart(3, '0')}`,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function metadataStringToObject(metaString: string) {
|
||||
const result: TEDeviceMetadata = {
|
||||
chip_id: '',
|
||||
mode: '',
|
||||
os_version: '',
|
||||
product: '',
|
||||
serial: '',
|
||||
sku: '',
|
||||
sw_version: '',
|
||||
};
|
||||
|
||||
metaString.split(';').forEach((entry) => {
|
||||
const [key, value] = entry.split(':');
|
||||
if (key && value) {
|
||||
(result as Record<string, string>)[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function binToString(byteArray: Uint8Array) {
|
||||
return String.fromCharCode.apply(null, Array.from(byteArray));
|
||||
}
|
||||
|
||||
export function sysexStatusToString(status: number) {
|
||||
if (status === TE_SYSEX.STATUS_OK) return 'ok';
|
||||
if (status >= TE_SYSEX.STATUS_SPECIFIC_SUCCESS_START) return 'command-specific-success';
|
||||
if (status === TE_SYSEX.STATUS_ERROR) return 'error';
|
||||
if (status === TE_SYSEX.STATUS_COMMAND_NOT_FOUND) return 'not-found';
|
||||
if (status === TE_SYSEX.STATUS_BAD_REQUEST) return 'bad-request';
|
||||
if (
|
||||
status >= TE_SYSEX.STATUS_SPECIFIC_ERROR_START &&
|
||||
status < TE_SYSEX.STATUS_SPECIFIC_SUCCESS_START
|
||||
) {
|
||||
return 'command-specific-error';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
export function packToBuffer(data: Uint8Array, outBuffer: Uint8Array) {
|
||||
let outIndex = 1;
|
||||
let msbIndex = 0;
|
||||
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
const positionInGroup = i % 7;
|
||||
const msb = data[i] >> 7;
|
||||
|
||||
outBuffer[msbIndex] |= msb << positionInGroup;
|
||||
outBuffer[outIndex++] = data[i] & 0x7f;
|
||||
|
||||
if (positionInGroup === 6 && i < data.length - 1) {
|
||||
msbIndex += 8;
|
||||
outIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getNextRequestId(outputId: string): number {
|
||||
if (!requestIds.has(outputId)) {
|
||||
requestIds.set(outputId, Math.floor(Math.random() * 4095));
|
||||
}
|
||||
const nextId = ((requestIds.get(outputId) ?? 0) + 1) % 4096;
|
||||
requestIds.set(outputId, nextId);
|
||||
return nextId;
|
||||
}
|
||||
|
||||
export function parseNullTerminatedString(buffer: Uint8Array, startIndex: number) {
|
||||
let endIndex = startIndex;
|
||||
|
||||
while (endIndex < buffer.length && buffer[endIndex] !== 0) {
|
||||
endIndex++;
|
||||
}
|
||||
|
||||
return new TextDecoder().decode(buffer.subarray(startIndex, endIndex));
|
||||
}
|
||||
|
||||
export function writeStringToView(
|
||||
dataView: DataView,
|
||||
offset: number,
|
||||
str: string,
|
||||
nullTerminate = false,
|
||||
) {
|
||||
let pos = offset;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
dataView.setUint8(pos++, str.charCodeAt(i));
|
||||
}
|
||||
if (nullTerminate && pos > 0) {
|
||||
dataView.setUint8(pos++, 0);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
export function sanitizeBrokenJson(input: string) {
|
||||
return input.replace(/:"([^"]*?)"([^,}]*)/g, (_, part1, part2) => {
|
||||
const fixed = (part1 + part2).replace(/"/g, '\\"');
|
||||
return `:"${fixed}"`;
|
||||
});
|
||||
}
|
||||
518
src/lib/parsers.ts
Normal file
518
src/lib/parsers.ts
Normal file
@@ -0,0 +1,518 @@
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
GroupFaderParam,
|
||||
Note,
|
||||
Pad,
|
||||
PadCode,
|
||||
Pattern,
|
||||
ProjectSettings,
|
||||
Scene,
|
||||
ScenesSettings,
|
||||
Sound,
|
||||
} from '../types/types';
|
||||
import { GROUPS, NOTE_NAMES, PADS, SKU_EP40 } from './constants';
|
||||
import { parseWavMetadata } from './exporters/utils';
|
||||
import { getFileMetadata, getFileNodeByPath } from './midi/fs';
|
||||
import { TESoundMetadata } from './midi/types';
|
||||
import { TarFile } from './untar';
|
||||
import { calculateSoundLength } from './utils';
|
||||
|
||||
type SceneMeta = {
|
||||
a: number;
|
||||
b: number;
|
||||
c: number;
|
||||
d: number;
|
||||
};
|
||||
|
||||
type IntermediateScenes = Record<
|
||||
number,
|
||||
{
|
||||
name: string;
|
||||
groups: Record<string, { bars: number; notes: Record<number, Note[]> }>;
|
||||
}
|
||||
>;
|
||||
|
||||
const defaultProjectSettings = {
|
||||
bpm: 120,
|
||||
scale: 0,
|
||||
rootNote: 0,
|
||||
groupFaderParams: {
|
||||
a: { 0: -1, 1: -1, 2: -1, 3: -1, 4: -1, 5: -1, 6: -1, 7: -1, 8: -1, 9: -1, 10: -1, 11: -1 },
|
||||
b: { 0: -1, 1: -1, 2: -1, 3: -1, 4: -1, 5: -1, 6: -1, 7: -1, 8: -1, 9: -1, 10: -1, 11: -1 },
|
||||
c: { 0: -1, 1: -1, 2: -1, 3: -1, 4: -1, 5: -1, 6: -1, 7: -1, 8: -1, 9: -1, 10: -1, 11: -1 },
|
||||
d: { 0: -1, 1: -1, 2: -1, 3: -1, 4: -1, 5: -1, 6: -1, 7: -1, 8: -1, 9: -1, 10: -1, 11: -1 },
|
||||
},
|
||||
faderAssignment: { a: 0, b: 0, c: 0, d: 0 },
|
||||
};
|
||||
|
||||
const defaultScenesSettings = {
|
||||
timeSignature: { numerator: 4, denominator: 4 },
|
||||
};
|
||||
|
||||
export function noteNumberToName(noteNumber: number): string {
|
||||
const noteIndex = noteNumber % 12;
|
||||
const octave = Math.floor(noteNumber / 12) - 1;
|
||||
|
||||
return `${NOTE_NAMES[noteIndex]}${octave}`;
|
||||
}
|
||||
|
||||
function timeStretch(data: number): Pad['timeStretch'] {
|
||||
switch (data) {
|
||||
case 1:
|
||||
return 'bpm';
|
||||
case 2:
|
||||
return 'bars';
|
||||
case 3:
|
||||
return 'rev';
|
||||
default:
|
||||
return 'off';
|
||||
}
|
||||
}
|
||||
|
||||
function timeStretchBars(data: number) {
|
||||
switch (data) {
|
||||
case 0:
|
||||
return 1;
|
||||
case 1:
|
||||
return 2;
|
||||
case 2:
|
||||
return 4;
|
||||
case 255:
|
||||
return 0.5;
|
||||
case 254:
|
||||
return 0.25;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
function bytesToFloat32(bytes: Uint8Array): number {
|
||||
const buffer = new ArrayBuffer(4);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
bytes.forEach((b, i) => {
|
||||
view.setUint8(i, b);
|
||||
});
|
||||
|
||||
return view.getFloat32(0, true);
|
||||
}
|
||||
|
||||
function genPadFileName(group: string, pad: number) {
|
||||
return `pads/${group}/p${String(pad).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function chunkArray(arr: Uint8Array, size: number, offset = 0) {
|
||||
const result = [];
|
||||
|
||||
for (let i = offset; i < arr.length; i += size) {
|
||||
result.push(arr.slice(i, i + size));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function collectSounds(files: TarFile[]) {
|
||||
const soundIds = new Set<number>();
|
||||
|
||||
for (let g = 0; g < 4; g++) {
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
const file = files.find((f) => f.name === genPadFileName(GROUPS[g].id, i));
|
||||
|
||||
if (file?.data) {
|
||||
const soundId = (file.data[2] << 8) + file.data[1];
|
||||
if (soundId > 0 && soundId < 1000) {
|
||||
soundIds.add(soundId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedSounds: Sound[] = [];
|
||||
|
||||
for (const soundId of soundIds) {
|
||||
const fileNode = await getFileNodeByPath(`/sounds/${String(soundId).padStart(3, '0')}.pcm`);
|
||||
|
||||
if (!fileNode) {
|
||||
console.warn(`Sound file for sound ID ${soundId} not found`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const meta = await getFileMetadata<TESoundMetadata>(fileNode.nodeId);
|
||||
|
||||
selectedSounds.push({
|
||||
id: soundId,
|
||||
fileNode,
|
||||
meta,
|
||||
});
|
||||
}
|
||||
|
||||
return selectedSounds;
|
||||
}
|
||||
|
||||
export function collectPads(files: TarFile[], sounds: Sound[]) {
|
||||
const result: Record<string, Pad[]> = {};
|
||||
|
||||
for (const group of GROUPS) {
|
||||
result[group.id] = [];
|
||||
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
const file = files.find((f) => f.name === genPadFileName(group.id, i));
|
||||
if (file?.data) {
|
||||
const soundId = (file.data[2] << 8) + file.data[1];
|
||||
const sound = sounds.find((s) => s.id === soundId);
|
||||
const pitch = file.data[17] <= 12 ? file.data[17] : -(256 - file.data[17]); // pitch from -12 to +12
|
||||
const pitchDecimal = file.data[26];
|
||||
const pan = (file.data[18] >= 240 ? -(256 - file.data[18]) : file.data[18]) / 16; // normalized pan
|
||||
const trimLeft = (file.data[6] << 16) + (file.data[5] << 8) + file.data[4];
|
||||
const trimRight = trimLeft + (file.data[10] << 16) + (file.data[9] << 8) + file.data[8];
|
||||
|
||||
result[group.id].push({
|
||||
pad: i,
|
||||
group: group.id,
|
||||
name: `${group.name} ${PADS[i - 1]}`,
|
||||
file,
|
||||
rawData: file.data,
|
||||
soundId,
|
||||
pan,
|
||||
volume: file.data[16],
|
||||
attack: file.data[19],
|
||||
release: file.data[20],
|
||||
trimLeft,
|
||||
trimRight,
|
||||
playMode: file.data[23] === 0 ? 'oneshot' : file.data[23] === 1 ? 'key' : 'legato',
|
||||
soundLength: sound ? calculateSoundLength(sound) : 0,
|
||||
pitch: Math.max(-12, Math.min(12, parseFloat(`${pitch}.${pitchDecimal}`))),
|
||||
rootNote: file.data[24],
|
||||
timeStretch: timeStretch(file.data[21]),
|
||||
timeStretchBpm: Number(bytesToFloat32(file.data.slice(12, 16)).toFixed(2)),
|
||||
timeStretchBars: timeStretchBars(file.data[25]),
|
||||
inChokeGroup: file.data[22] === 1,
|
||||
midiChannel: file.data[3],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function parsePatterns(data: Uint8Array, startOffset: number = 4) {
|
||||
const chunks = chunkArray(data, 8, startOffset);
|
||||
const notes: Record<number, Note[]> = {};
|
||||
|
||||
chunks.forEach((chunk) => {
|
||||
// I discovered there could be some weird chunks with pad numbers not multiple of 8
|
||||
// since I don't know what they are, just skip them
|
||||
if (chunk[2] % 8 !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pad = chunk[2] / 8;
|
||||
if (!notes[pad]) {
|
||||
notes[pad] = [];
|
||||
}
|
||||
|
||||
notes[pad].push({
|
||||
note: chunk[3],
|
||||
position: (chunk[1] << 8) + chunk[0],
|
||||
duration: (chunk[6] << 8) + chunk[5],
|
||||
velocity: chunk[4],
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
bars: data[1],
|
||||
notes,
|
||||
};
|
||||
}
|
||||
|
||||
function getPatternsForScene(scenesIntermediate: IntermediateScenes, scenePatternUsage: SceneMeta) {
|
||||
const patterns: Pattern[] = [];
|
||||
|
||||
for (const group of ['a', 'b', 'c', 'd'] as const) {
|
||||
const scene = scenePatternUsage[group];
|
||||
const lookupSceneData = scenesIntermediate[scene];
|
||||
|
||||
if (lookupSceneData?.groups[group]) {
|
||||
const groupData = lookupSceneData.groups[group];
|
||||
Object.entries(groupData.notes).forEach(([padNum, notes]) => {
|
||||
patterns.push({
|
||||
pad: `${group}${padNum}` as PadCode,
|
||||
group,
|
||||
notes,
|
||||
bars: groupData.bars,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
export function collectScenesAndPatterns(files: TarFile[], sku: string) {
|
||||
const scenesIntermediateData: IntermediateScenes = {};
|
||||
const scenes: Record<string, Scene> = {};
|
||||
const scenePatternUsage: Record<string, SceneMeta> = {};
|
||||
|
||||
// build default scene metadata (in case "scenes" file is missing)
|
||||
for (let i = 1; i <= 99; i++) {
|
||||
scenePatternUsage[i] = {
|
||||
a: i,
|
||||
b: i,
|
||||
c: i,
|
||||
d: i,
|
||||
};
|
||||
}
|
||||
|
||||
// build group/pattern lookup table
|
||||
const scenesFile = files.find((f) => f.name === 'scenes' && f.type === 'file');
|
||||
if (scenesFile?.data) {
|
||||
const chunks = chunkArray(scenesFile.data, 6, 7);
|
||||
chunks.forEach((chunk, i) => {
|
||||
if (i > 98) {
|
||||
return;
|
||||
}
|
||||
|
||||
scenePatternUsage[i + 1] = {
|
||||
a: chunk[0],
|
||||
b: chunk[1],
|
||||
c: chunk[2],
|
||||
d: chunk[3],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.name.startsWith('patterns') || file.type !== 'file' || !file.data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const matches = file.name.match(/patterns\/([abcd])(\d+)/);
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const [, group, scene] = matches;
|
||||
const sceneIndex = Number(scene);
|
||||
if (!scenesIntermediateData[sceneIndex]) {
|
||||
scenesIntermediateData[sceneIndex] = {
|
||||
name: String(scene).padStart(2, '0'),
|
||||
groups: {},
|
||||
};
|
||||
}
|
||||
|
||||
// build intermediate scenes data
|
||||
// will be used to resolve patterns later using sceneMetadata
|
||||
scenesIntermediateData[sceneIndex] = {
|
||||
...scenesIntermediateData[sceneIndex],
|
||||
groups: {
|
||||
...scenesIntermediateData[sceneIndex].groups,
|
||||
[group]: parsePatterns(file.data, sku === SKU_EP40 ? 6 : 4), // EP-40 has 6-byte header
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Object.entries(scenesIntermediateData).forEach(([sceneIndex, sceneData]) => {
|
||||
if (!scenePatternUsage[sceneIndex]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const patterns = getPatternsForScene(scenesIntermediateData, scenePatternUsage[sceneIndex]);
|
||||
if (patterns.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
scenes[sceneIndex] = {
|
||||
name: sceneData.name,
|
||||
patterns,
|
||||
};
|
||||
});
|
||||
|
||||
return Object.values(scenes).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
export function collectSettings(files: TarFile[]): ProjectSettings {
|
||||
const settings = files.find((f) => f.name === 'settings' && f.type === 'file');
|
||||
|
||||
if (!settings || !settings.data) {
|
||||
console.warn('Could not find "settings" file');
|
||||
|
||||
return {
|
||||
...defaultProjectSettings,
|
||||
rawData: new Uint8Array(),
|
||||
};
|
||||
}
|
||||
|
||||
const faderParamsData: GroupFaderParam = {};
|
||||
|
||||
for (const groupNum of [0, 1, 2, 3]) {
|
||||
const groupId = GROUPS[groupNum].id;
|
||||
for (let paramNum = 0; paramNum <= 11; paramNum++) {
|
||||
faderParamsData[groupId] = {
|
||||
...faderParamsData[groupId],
|
||||
[paramNum]: Number(
|
||||
bytesToFloat32(
|
||||
settings.data.slice(
|
||||
24 + groupNum * 48 + paramNum * 4,
|
||||
28 + groupNum * 48 + paramNum * 4,
|
||||
),
|
||||
).toFixed(2),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
bpm: Number(bytesToFloat32(settings.data.slice(4, 8)).toFixed(2)),
|
||||
scale: settings.data?.[222] ?? 0,
|
||||
rootNote: settings.data?.[223] ?? 0,
|
||||
groupFaderParams: faderParamsData,
|
||||
faderAssignment: {
|
||||
a: settings.data[216],
|
||||
b: settings.data[217],
|
||||
c: settings.data[218],
|
||||
d: settings.data[219],
|
||||
},
|
||||
rawData: settings.data,
|
||||
};
|
||||
}
|
||||
|
||||
export function collectEffects(files: TarFile[]) {
|
||||
const fxFile = files.find((f) => f.name === 'fx_settings' && f.type === 'file');
|
||||
|
||||
if (!fxFile || !fxFile.data) {
|
||||
console.warn('Could not find "fx_settings" file');
|
||||
|
||||
return {
|
||||
rawData: new Uint8Array(),
|
||||
effectType: 0,
|
||||
param1: 0,
|
||||
param2: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const effectType = fxFile.data[4];
|
||||
const param1 = bytesToFloat32(
|
||||
fxFile.data.slice(12 + (effectType - 1) * 4, 16 + (effectType - 1) * 4),
|
||||
);
|
||||
const param2 = bytesToFloat32(
|
||||
fxFile.data.slice(76 + (effectType - 1) * 4, 80 + (effectType - 1) * 4),
|
||||
);
|
||||
|
||||
return {
|
||||
rawData: fxFile.data,
|
||||
effectType,
|
||||
param1,
|
||||
param2,
|
||||
};
|
||||
}
|
||||
|
||||
export function collectScenesSettings(files: TarFile[]): ScenesSettings {
|
||||
const scenesSettingsFile = files.find((f) => f.name === 'scenes' && f.type === 'file');
|
||||
|
||||
if (!scenesSettingsFile || !scenesSettingsFile.data) {
|
||||
console.warn('Could not find "scenes" file');
|
||||
return defaultScenesSettings;
|
||||
}
|
||||
|
||||
const numerator = scenesSettingsFile.data[11];
|
||||
const denominator = scenesSettingsFile.data[12];
|
||||
|
||||
return {
|
||||
...defaultScenesSettings,
|
||||
timeSignature: { numerator, denominator },
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadSoundsFromBackup(
|
||||
unzippedBackup: JSZip,
|
||||
projectFiles: TarFile[],
|
||||
): Promise<Sound[]> {
|
||||
const soundIds = new Set<number>();
|
||||
|
||||
for (let group = 0; group < 4; group++) {
|
||||
for (let pad = 1; pad <= 12; pad++) {
|
||||
const file = projectFiles.find((f) => f.name === genPadFileName(GROUPS[group].id, pad));
|
||||
|
||||
if (file?.data) {
|
||||
const soundId = (file.data[2] << 8) + file.data[1];
|
||||
if (soundId > 0 && soundId < 1000) {
|
||||
soundIds.add(soundId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedSounds: Sound[] = [];
|
||||
|
||||
for (const soundId of soundIds) {
|
||||
const wavFile = Object.values(unzippedBackup.files).find((file) =>
|
||||
file.name.startsWith(`/sounds/${String(soundId).padStart(3, '0')} `),
|
||||
);
|
||||
|
||||
if (!wavFile) {
|
||||
console.warn(`Sound file for sound ID ${soundId} not found in backup`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const soundFile = unzippedBackup.file(wavFile.name);
|
||||
if (!soundFile) {
|
||||
console.warn(`Sound file for sound ID ${soundId} not found in backup`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const soundData = await soundFile.async('uint8array');
|
||||
const fileName = (soundFile.name.split('/').pop()?.split(' ').slice(1).join(' ') || '').replace(
|
||||
'.wav',
|
||||
'',
|
||||
);
|
||||
const fileNode = {
|
||||
nodeId: soundId,
|
||||
flags: 0,
|
||||
fileSize: soundData.length,
|
||||
fileName,
|
||||
fileType: 'file' as const,
|
||||
};
|
||||
|
||||
let wavMeta: ReturnType<typeof parseWavMetadata>;
|
||||
try {
|
||||
wavMeta = parseWavMetadata(soundData);
|
||||
} catch (err) {
|
||||
console.warn(`Failed to parse WAV metadata for sound ${soundId}:`, err);
|
||||
wavMeta = {
|
||||
channels: 1,
|
||||
samplerate: 44100,
|
||||
format: 's16' as const,
|
||||
rootNote: 60,
|
||||
teMeta: null,
|
||||
};
|
||||
}
|
||||
|
||||
const te = wavMeta.teMeta;
|
||||
|
||||
selectedSounds.push({
|
||||
id: soundId,
|
||||
fileNode,
|
||||
meta: {
|
||||
channels: wavMeta.channels,
|
||||
samplerate: wavMeta.samplerate,
|
||||
format: wavMeta.format,
|
||||
crc: 0,
|
||||
name: fileName,
|
||||
'sound.loopstart': (te?.['sound.loopstart'] as number) ?? 0,
|
||||
'sound.loopend': (te?.['sound.loopend'] as number) ?? 0,
|
||||
'sound.amplitude': (te?.['sound.amplitude'] as number) ?? 1,
|
||||
'sound.playmode':
|
||||
(te?.['sound.playmode'] as TESoundMetadata['sound.playmode']) ?? 'oneshot',
|
||||
'sound.pan': (te?.['sound.pan'] as number) ?? 0,
|
||||
'sound.pitch': (te?.['sound.pitch'] as number) ?? 0,
|
||||
'sound.rootnote': wavMeta.rootNote,
|
||||
'time.mode': (te?.['time.mode'] as string) ?? '',
|
||||
'sound.bpm': 0,
|
||||
'envelope.attack': (te?.['envelope.attack'] as number) ?? 0,
|
||||
'envelope.release': (te?.['envelope.release'] as number) ?? 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return selectedSounds;
|
||||
}
|
||||
58
src/lib/pcmToWav.ts
Normal file
58
src/lib/pcmToWav.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
function createWavHeader(
|
||||
dataLength: number,
|
||||
sampleRate: number,
|
||||
channels: number,
|
||||
bitDepth: number,
|
||||
): Uint8Array {
|
||||
const header = new Uint8Array(44);
|
||||
const view = new DataView(header.buffer);
|
||||
|
||||
// RIFF header
|
||||
header.set([0x52, 0x49, 0x46, 0x46], 0); // "RIFF"
|
||||
view.setUint32(4, 36 + dataLength, true); // File size - 8 bytes
|
||||
header.set([0x57, 0x41, 0x56, 0x45], 8); // "WAVE"
|
||||
|
||||
// Format chunk
|
||||
header.set([0x66, 0x6d, 0x74, 0x20], 12); // "fmt "
|
||||
view.setUint32(16, 16, true); // Format chunk size
|
||||
view.setUint16(20, 1, true); // Audio format (PCM)
|
||||
view.setUint16(22, channels, true); // Number of channels
|
||||
view.setUint32(24, sampleRate, true); // Sample rate
|
||||
view.setUint32(28, sampleRate * channels * (bitDepth / 8), true); // Byte rate
|
||||
view.setUint16(32, channels * (bitDepth / 8), true); // Block align
|
||||
view.setUint16(34, bitDepth, true); // Bits per sample
|
||||
|
||||
// Data chunk
|
||||
header.set([0x64, 0x61, 0x74, 0x61], 36); // "data"
|
||||
view.setUint32(40, dataLength, true); // Data size
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
export function pcmToWavBlob(
|
||||
pcmData: Uint8Array,
|
||||
sampleRate: number = 44100,
|
||||
bitDepth: number = 16,
|
||||
channels: number = 1,
|
||||
): Blob {
|
||||
const header = createWavHeader(pcmData.length, sampleRate, channels, bitDepth);
|
||||
|
||||
const wavData = new Uint8Array(header.length + pcmData.length);
|
||||
wavData.set(header, 0);
|
||||
wavData.set(pcmData, header.length);
|
||||
|
||||
return new Blob([wavData], { type: 'audio/wav' });
|
||||
}
|
||||
|
||||
export function pcmToWavArray(
|
||||
pcmData: Uint8Array,
|
||||
sampleRate: number = 44100,
|
||||
bitDepth: number = 16,
|
||||
channels: number = 1,
|
||||
): Uint8Array {
|
||||
const header = createWavHeader(pcmData.length, sampleRate, channels, bitDepth);
|
||||
const wavData = new Uint8Array(header.length + pcmData.length);
|
||||
wavData.set(header, 0);
|
||||
wavData.set(pcmData, header.length);
|
||||
return wavData;
|
||||
}
|
||||
12
src/lib/queryClient.ts
Normal file
12
src/lib/queryClient.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default queryClient;
|
||||
3
src/lib/store.ts
Normal file
3
src/lib/store.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createStore } from 'jotai';
|
||||
|
||||
export const store = createStore();
|
||||
15
src/lib/toast.ts
Normal file
15
src/lib/toast.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { type ToastSeverity, toastsAtom } from '../atoms/toast';
|
||||
import { store } from './store';
|
||||
|
||||
export function showToast(message: string, severity: ToastSeverity = 'info') {
|
||||
const existingToasts = store.get(toastsAtom);
|
||||
|
||||
const newToast = {
|
||||
message,
|
||||
severity,
|
||||
id: Math.random().toString(36).substring(2, 9),
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
store.set(toastsAtom, [...existingToasts, newToast]);
|
||||
}
|
||||
316
src/lib/transformers/ableton.ts
Normal file
316
src/lib/transformers/ableton.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import omit from 'lodash/omit';
|
||||
import {
|
||||
ExporterParams,
|
||||
FaderParam,
|
||||
Note,
|
||||
Pad,
|
||||
PadCode,
|
||||
ProjectRawData,
|
||||
TimeSignature,
|
||||
} from '../../types/types';
|
||||
import { getSampleName } from '../exporters/utils';
|
||||
import { findPad, findSoundByPad, findSoundIdByPad } from '../utils';
|
||||
|
||||
export type AblData = {
|
||||
tracks: AblTrack[];
|
||||
scenes: AblScene[];
|
||||
};
|
||||
|
||||
export type AblTrack = Omit<Pad, 'file' | 'rawData' | 'midiChannel'> & {
|
||||
padCode: PadCode;
|
||||
group: string;
|
||||
sampleName: string;
|
||||
sampleChannels: number;
|
||||
sampleRate: number;
|
||||
sampleRootNote: number;
|
||||
samplePitch: number;
|
||||
bpm: number;
|
||||
drumRack: boolean;
|
||||
lane?: AblLane;
|
||||
tracks: AblTrack[];
|
||||
faderParams: { [K in FaderParam]: number };
|
||||
timeSignature: TimeSignature;
|
||||
};
|
||||
|
||||
export type AblLane = {
|
||||
padCode: PadCode;
|
||||
clips: AblClip[];
|
||||
};
|
||||
|
||||
export type AblNote = Note;
|
||||
|
||||
export type AblClip = {
|
||||
notes: AblNote[];
|
||||
bars: number;
|
||||
offset: number;
|
||||
sceneBars: number;
|
||||
sceneIndex: number;
|
||||
sceneName: string;
|
||||
timeSignature: TimeSignature;
|
||||
faderParams: { [K in FaderParam]: number };
|
||||
};
|
||||
|
||||
export type AblScene = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
function abletonTransformer(data: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const { pads, scenes } = data;
|
||||
const customSceneNames = exporterParams.customSceneNames || {};
|
||||
const lanes: AblLane[] = [];
|
||||
const ablScenes: AblScene[] = [];
|
||||
let tracks: AblTrack[] = [];
|
||||
let offset = 0;
|
||||
|
||||
scenes.forEach((scene, sceneIndex) => {
|
||||
const sceneBars = Math.max(...scene.patterns.map((p) => p.bars));
|
||||
const ablScene: AblScene = {
|
||||
name: customSceneNames[scene.name] || scene.name,
|
||||
};
|
||||
|
||||
scene.patterns.forEach((pattern) => {
|
||||
let track = tracks.find((c) => c.padCode === pattern.pad);
|
||||
let pad = findPad(pattern.pad, pads);
|
||||
|
||||
if (!pad) {
|
||||
console.warn('Could not find pad data for', pattern.pad);
|
||||
|
||||
pad = {
|
||||
group: pattern.group,
|
||||
volume: 100,
|
||||
attack: 0,
|
||||
release: 0,
|
||||
trimLeft: 0,
|
||||
trimRight: 0,
|
||||
pad: 0,
|
||||
playMode: 'oneshot',
|
||||
pan: 0,
|
||||
pitch: 0,
|
||||
rootNote: 60,
|
||||
timeStretch: 'off',
|
||||
timeStretchBpm: 0,
|
||||
timeStretchBars: 0,
|
||||
soundId: 0,
|
||||
name: '',
|
||||
file: null,
|
||||
rawData: null,
|
||||
soundLength: 0,
|
||||
inChokeGroup: false,
|
||||
midiChannel: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const faderParams = data.settings.groupFaderParams[pad.group];
|
||||
|
||||
if (!track) {
|
||||
const soundId = findSoundIdByPad(pattern.pad, pads) || 0;
|
||||
const sound = findSoundByPad(pattern.pad, pads, data.sounds);
|
||||
track = {
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId,
|
||||
padCode: pattern.pad,
|
||||
name: sound?.meta?.name || pattern.pad,
|
||||
volume: pad.volume / 200, // converting from 0-200 to 0.0-1.0
|
||||
sampleName: getSampleName(sound?.meta?.name, soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
sampleRootNote: pad.rootNote,
|
||||
samplePitch: sound?.meta?.['sound.pitch'] ?? 0,
|
||||
bpm: data.settings.bpm,
|
||||
drumRack: false,
|
||||
tracks: [],
|
||||
faderParams,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
};
|
||||
|
||||
tracks.push(track);
|
||||
}
|
||||
|
||||
let lane = lanes.find((c) => c.padCode === pattern.pad);
|
||||
if (!lane) {
|
||||
lane = {
|
||||
padCode: pattern.pad,
|
||||
clips: [],
|
||||
};
|
||||
|
||||
lanes.push(lane);
|
||||
}
|
||||
|
||||
lane.clips.push({
|
||||
offset,
|
||||
notes: pattern.notes,
|
||||
bars: pattern.bars,
|
||||
sceneBars,
|
||||
sceneIndex,
|
||||
sceneName: customSceneNames[scene.name] || scene.name,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
faderParams,
|
||||
});
|
||||
|
||||
track.lane = lane;
|
||||
});
|
||||
|
||||
offset += sceneBars;
|
||||
|
||||
ablScenes.push(ablScene);
|
||||
});
|
||||
|
||||
tracks.sort((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
|
||||
if (exporterParams.exportAllPadsWithSamples) {
|
||||
for (const group in pads) {
|
||||
pads[group].forEach((pad, index) => {
|
||||
if (pad.soundId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const padCode = `${group}${index}` as PadCode;
|
||||
if (tracks.some((t) => t.padCode === padCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sound = data.sounds.find((s) => s.id === pad.soundId);
|
||||
const faderParams = data.settings.groupFaderParams[pad.group];
|
||||
|
||||
tracks.push({
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId: pad.soundId,
|
||||
padCode,
|
||||
name: sound?.meta?.name || padCode,
|
||||
volume: pad.volume / 200,
|
||||
sampleName: getSampleName(sound?.meta?.name, pad.soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
sampleRootNote: pad.rootNote,
|
||||
samplePitch: sound?.meta?.['sound.pitch'] ?? 0,
|
||||
bpm: data.settings.bpm,
|
||||
drumRack: false,
|
||||
tracks: [],
|
||||
faderParams,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tracks.sort((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to create a drum rack track for a specific group
|
||||
const createDrumRackTrack = (group: 'a' | 'b' | 'c' | 'd'): AblTrack | null => {
|
||||
const groupTracksForDrumRack = tracks.filter((t) => t.group === group);
|
||||
if (groupTracksForDrumRack.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const drumTrack: AblTrack = {
|
||||
padCode: `${group}0` as PadCode,
|
||||
group,
|
||||
sampleName: '',
|
||||
sampleChannels: 0,
|
||||
sampleRate: 0,
|
||||
sampleRootNote: 60,
|
||||
samplePitch: 0,
|
||||
bpm: data.settings.bpm,
|
||||
drumRack: true,
|
||||
soundId: 0,
|
||||
name: `Drums ${group.toUpperCase()}`,
|
||||
volume: 1, // 1 means 0dB
|
||||
attack: 0,
|
||||
release: 0,
|
||||
trimLeft: 0,
|
||||
trimRight: 0,
|
||||
pad: 0,
|
||||
lane: undefined,
|
||||
playMode: 'oneshot',
|
||||
pan: 0,
|
||||
pitch: 0,
|
||||
rootNote: 60,
|
||||
timeStretch: 'off',
|
||||
timeStretchBpm: 0,
|
||||
timeStretchBars: 0,
|
||||
soundLength: 0,
|
||||
tracks: groupTracksForDrumRack,
|
||||
inChokeGroup: false,
|
||||
faderParams: data.settings.groupFaderParams[group],
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
};
|
||||
|
||||
// We need to merge notes from all tracks in the group into one track
|
||||
const newClips: Record<string, AblClip> = {};
|
||||
|
||||
drumTrack.tracks
|
||||
.toSorted((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
)
|
||||
.forEach((track) => {
|
||||
const padNumber = parseInt(track.padCode.slice(1), 10);
|
||||
// EP133 is 3x4 (top→bottom), Ableton Drum Rack is 4x4 (bottom→top)
|
||||
// flip vertically: row 3 → row 0, row 0 → row 3
|
||||
const row = 3 - Math.floor(padNumber / 3);
|
||||
const col = padNumber % 3;
|
||||
const drumPad = 36 + row * 4 + col; // remaping notes starting from C1 (36)
|
||||
|
||||
track.lane?.clips.forEach((clip) => {
|
||||
if (!newClips[clip.sceneName]) {
|
||||
newClips[clip.sceneName] = structuredClone(clip);
|
||||
newClips[clip.sceneName].notes = [];
|
||||
}
|
||||
|
||||
newClips[clip.sceneName].notes = [
|
||||
...newClips[clip.sceneName].notes,
|
||||
...clip.notes.map((n) => ({
|
||||
...n,
|
||||
note: drumPad,
|
||||
})),
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
drumTrack.lane = {
|
||||
padCode: `${group}0` as PadCode,
|
||||
clips: Object.values(newClips),
|
||||
};
|
||||
|
||||
return drumTrack;
|
||||
};
|
||||
|
||||
// Process drum racks for each group that has the option enabled
|
||||
const drumRackTracks: AblTrack[] = [];
|
||||
const groupsToProcess: Array<{ group: 'a' | 'b' | 'c' | 'd'; enabled: boolean }> = [
|
||||
{ group: 'a', enabled: exporterParams.drumRackGroupA || false },
|
||||
{ group: 'b', enabled: exporterParams.drumRackGroupB || false },
|
||||
{ group: 'c', enabled: exporterParams.drumRackGroupC || false },
|
||||
{ group: 'd', enabled: exporterParams.drumRackGroupD || false },
|
||||
];
|
||||
|
||||
for (const { group, enabled } of groupsToProcess) {
|
||||
if (enabled) {
|
||||
const drumTrack = createDrumRackTrack(group);
|
||||
if (drumTrack) {
|
||||
drumRackTracks.push(drumTrack);
|
||||
// Remove tracks from this group from the main tracks array
|
||||
tracks = tracks.filter((t) => t.group !== group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert drum rack tracks at the beginning, maintaining group order (A, B, C, D)
|
||||
tracks.unshift(...drumRackTracks);
|
||||
|
||||
Sentry.setContext(`abletonData`, {
|
||||
tracks,
|
||||
scenes: ablScenes,
|
||||
});
|
||||
|
||||
return {
|
||||
tracks,
|
||||
scenes: ablScenes,
|
||||
} as AblData;
|
||||
}
|
||||
|
||||
export default abletonTransformer;
|
||||
290
src/lib/transformers/dawProject.ts
Normal file
290
src/lib/transformers/dawProject.ts
Normal file
@@ -0,0 +1,290 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { omit } from 'lodash';
|
||||
import {
|
||||
ExporterParams,
|
||||
Note,
|
||||
Pad,
|
||||
PadCode,
|
||||
ProjectRawData,
|
||||
TimeSignature,
|
||||
} from '../../types/types';
|
||||
import { getSampleName } from '../exporters/utils';
|
||||
import { findPad, findSoundByPad, findSoundIdByPad } from '../utils';
|
||||
|
||||
export type DawData = {
|
||||
tracks: DawTrack[];
|
||||
lanes: DawLane[];
|
||||
scenes: DawScene[];
|
||||
};
|
||||
|
||||
export type DawTrack = Omit<Pad, 'file' | 'rawData' | 'midiChannel'> & {
|
||||
padCode: PadCode;
|
||||
group: string;
|
||||
sampleName: string;
|
||||
sampleChannels: number;
|
||||
sampleRate: number;
|
||||
bpm: number;
|
||||
};
|
||||
|
||||
export type DawLane = {
|
||||
padCode: PadCode;
|
||||
group?: string;
|
||||
clips: DawClip[];
|
||||
};
|
||||
|
||||
export type DawClip = {
|
||||
notes: Note[];
|
||||
bars: number;
|
||||
offset: number;
|
||||
sceneBars: number;
|
||||
sceneIndex: number;
|
||||
sceneName: string;
|
||||
sceneTimeSignature: TimeSignature;
|
||||
};
|
||||
|
||||
export type DawClipSlot = {
|
||||
clip: DawClip[];
|
||||
track: DawTrack;
|
||||
};
|
||||
|
||||
export type DawScene = {
|
||||
name: string;
|
||||
clipSlot: DawClipSlot[];
|
||||
};
|
||||
|
||||
function dawProjectTransformer(data: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const { pads, scenes } = data;
|
||||
const customSceneNames = exporterParams.customSceneNames || {};
|
||||
const dawScenes: DawScene[] = [];
|
||||
let lanes: DawLane[] = [];
|
||||
let tracks: DawTrack[] = [];
|
||||
let offset = 0;
|
||||
|
||||
scenes.forEach((scene, sceneIndex) => {
|
||||
const sceneBars = Math.max(...scene.patterns.map((p) => p.bars));
|
||||
const dawScene: DawScene = {
|
||||
name: customSceneNames[scene.name] || scene.name,
|
||||
clipSlot: [],
|
||||
};
|
||||
|
||||
for (const pattern of scene.patterns) {
|
||||
let track = tracks.find((c) => c.padCode === pattern.pad);
|
||||
|
||||
if (!track) {
|
||||
const soundId = findSoundIdByPad(pattern.pad, pads) || 0;
|
||||
const sound = findSoundByPad(pattern.pad, pads, data.sounds);
|
||||
const pad = findPad(pattern.pad, pads);
|
||||
|
||||
if (!pad) {
|
||||
throw new Error(`Could not find pad for ${pattern.pad}`);
|
||||
}
|
||||
|
||||
track = {
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId,
|
||||
padCode: pattern.pad,
|
||||
name: sound?.meta?.name || pattern.pad,
|
||||
volume: pad.volume * (2 / 200),
|
||||
sampleName: getSampleName(sound?.meta?.name, soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
bpm: data.settings.bpm,
|
||||
};
|
||||
|
||||
tracks.push(track);
|
||||
}
|
||||
|
||||
let lane = lanes.find((c) => c.padCode === pattern.pad);
|
||||
|
||||
if (!lane) {
|
||||
lane = {
|
||||
padCode: pattern.pad,
|
||||
group: track.group,
|
||||
clips: [],
|
||||
};
|
||||
|
||||
lanes.push(lane);
|
||||
}
|
||||
|
||||
lane.clips.push({
|
||||
offset,
|
||||
notes: pattern.notes,
|
||||
bars: pattern.bars,
|
||||
sceneBars,
|
||||
sceneIndex,
|
||||
sceneName: customSceneNames[scene.name] || scene.name,
|
||||
sceneTimeSignature: data.scenesSettings.timeSignature,
|
||||
});
|
||||
|
||||
dawScene.clipSlot.push({
|
||||
clip: lane.clips,
|
||||
track: track,
|
||||
});
|
||||
}
|
||||
|
||||
offset += sceneBars;
|
||||
|
||||
dawScenes.push(dawScene);
|
||||
});
|
||||
|
||||
if (exporterParams.exportAllPadsWithSamples) {
|
||||
for (const group in pads) {
|
||||
pads[group].forEach((pad, index) => {
|
||||
if (pad.soundId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const padCode = `${group}${index}` as PadCode;
|
||||
if (tracks.some((t) => t.padCode === padCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sound = data.sounds.find((s) => s.id === pad.soundId);
|
||||
|
||||
tracks.push({
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId: pad.soundId,
|
||||
padCode,
|
||||
name: sound?.meta?.name || padCode,
|
||||
volume: pad.volume * (2 / 200),
|
||||
sampleName: getSampleName(sound?.meta?.name, pad.soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
bpm: data.settings.bpm,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tracks.sort((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to create a drum rack track and lane for a specific group
|
||||
const createDrumRackForGroup = (
|
||||
group: 'a' | 'b' | 'c' | 'd',
|
||||
): {
|
||||
drumTrack: DawTrack;
|
||||
drumLane: DawLane;
|
||||
} | null => {
|
||||
const groupTracks = tracks.filter((t) => t.group === group);
|
||||
const groupLanes = lanes.filter((l) => l.group === group);
|
||||
|
||||
if (groupTracks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const drumTrack: DawTrack = {
|
||||
padCode: `${group}0` as PadCode,
|
||||
group,
|
||||
sampleName: '',
|
||||
sampleChannels: 0,
|
||||
sampleRate: 0,
|
||||
bpm: data.settings.bpm,
|
||||
soundId: 0,
|
||||
name: `Drums ${group.toUpperCase()}`,
|
||||
volume: 1,
|
||||
attack: 0,
|
||||
release: 0,
|
||||
trimLeft: 0,
|
||||
trimRight: 0,
|
||||
pad: 0,
|
||||
playMode: 'oneshot',
|
||||
pan: 0,
|
||||
pitch: 0,
|
||||
rootNote: 60,
|
||||
timeStretch: 'off',
|
||||
timeStretchBpm: 0,
|
||||
timeStretchBars: 0,
|
||||
soundLength: 0,
|
||||
inChokeGroup: false,
|
||||
};
|
||||
|
||||
const drumLane: DawLane = {
|
||||
padCode: `${group}0` as PadCode,
|
||||
group,
|
||||
clips: [],
|
||||
};
|
||||
|
||||
const newClips: Record<string, DawClip> = {};
|
||||
|
||||
groupLanes
|
||||
.toSorted((a, b) => a.padCode.localeCompare(b.padCode))
|
||||
.forEach((lane, idx) => {
|
||||
lane.clips.forEach((clip) => {
|
||||
if (!newClips[clip.sceneName]) {
|
||||
newClips[clip.sceneName] = structuredClone(clip);
|
||||
newClips[clip.sceneName].notes = []; // resetting notes, they will be added below with new mapping
|
||||
}
|
||||
|
||||
newClips[clip.sceneName].notes = [
|
||||
...newClips[clip.sceneName].notes,
|
||||
...clip.notes.map((n) => ({
|
||||
...n,
|
||||
note: 36 + idx,
|
||||
})), // remaping notes starting from C1 (36)
|
||||
];
|
||||
});
|
||||
});
|
||||
|
||||
drumLane.clips = Object.values(newClips);
|
||||
|
||||
return { drumTrack, drumLane };
|
||||
};
|
||||
|
||||
// Process drum racks for each group that has the option enabled
|
||||
const drumRackData: Array<{ drumTrack: DawTrack; drumLane: DawLane }> = [];
|
||||
const groupsToProcess: Array<{ group: 'a' | 'b' | 'c' | 'd'; enabled: boolean }> = [
|
||||
{ group: 'a', enabled: exporterParams.drumRackGroupA || false },
|
||||
{ group: 'b', enabled: exporterParams.drumRackGroupB || false },
|
||||
{ group: 'c', enabled: exporterParams.drumRackGroupC || false },
|
||||
{ group: 'd', enabled: exporterParams.drumRackGroupD || false },
|
||||
];
|
||||
|
||||
for (const { group, enabled } of groupsToProcess) {
|
||||
if (enabled) {
|
||||
const drumRack = createDrumRackForGroup(group);
|
||||
if (drumRack) {
|
||||
drumRackData.push(drumRack);
|
||||
// Remove tracks and lanes from this group from the main arrays
|
||||
tracks = tracks.filter((t) => t.group !== group);
|
||||
lanes = lanes.filter((l) => l.group !== group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert drum rack tracks and lanes at the beginning, maintaining group order (A, B, C, D)
|
||||
tracks.unshift(...drumRackData.map((d) => d.drumTrack));
|
||||
lanes.unshift(...drumRackData.map((d) => d.drumLane));
|
||||
|
||||
// Update scenes to include drum rack clip slots
|
||||
const groupsConvertedToDrumRack = drumRackData.map((d) => d.drumTrack.group);
|
||||
dawScenes.forEach((scene) => {
|
||||
// Remove clip slots for groups that were converted to drum racks
|
||||
scene.clipSlot = scene.clipSlot.filter(
|
||||
(cs) => !groupsConvertedToDrumRack.includes(cs.track.group),
|
||||
);
|
||||
|
||||
// Add drum rack clip slots at the beginning, maintaining group order
|
||||
for (const { drumTrack, drumLane } of drumRackData) {
|
||||
scene.clipSlot.unshift({
|
||||
clip: drumLane.clips.filter((c) => c.sceneName === scene.name),
|
||||
track: drumTrack,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Sentry.setContext(`dawprojectData`, {
|
||||
tracks,
|
||||
lanes,
|
||||
scenes: dawScenes,
|
||||
});
|
||||
|
||||
return {
|
||||
tracks,
|
||||
lanes,
|
||||
scenes: dawScenes,
|
||||
} as DawData;
|
||||
}
|
||||
|
||||
export default dawProjectTransformer;
|
||||
160
src/lib/transformers/midi.ts
Normal file
160
src/lib/transformers/midi.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { ExporterParams, Note, ProjectRawData } from '../../types/types';
|
||||
import { getQuarterNotesPerBar, TICKS_PER_BEAT } from '../exporters/utils';
|
||||
import { findSoundByPad } from '../utils';
|
||||
|
||||
export type MidiData = {
|
||||
tracks: MidiTrack[];
|
||||
};
|
||||
|
||||
export type MidiTrack = {
|
||||
name: string;
|
||||
group: string;
|
||||
padCode: string;
|
||||
notes: Note[];
|
||||
};
|
||||
|
||||
function midiTransformer(data: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const { pads, scenes } = data;
|
||||
let midiTracks: MidiTrack[] = [];
|
||||
let offset = 0;
|
||||
|
||||
const barLength =
|
||||
getQuarterNotesPerBar(
|
||||
data.scenesSettings.timeSignature.numerator,
|
||||
data.scenesSettings.timeSignature.denominator,
|
||||
) * TICKS_PER_BEAT;
|
||||
|
||||
scenes.forEach((scene) => {
|
||||
const sceneMaxBars = Math.max(...scene.patterns.map((p) => p.bars));
|
||||
scene.patterns.forEach((pattern) => {
|
||||
let track = midiTracks.find((t) => t.padCode === pattern.pad);
|
||||
if (!track) {
|
||||
const sound = findSoundByPad(pattern.pad, pads, data.sounds);
|
||||
|
||||
track = {
|
||||
name: sound?.meta.name || pattern.pad,
|
||||
padCode: pattern.pad,
|
||||
group: pattern.group,
|
||||
notes: [],
|
||||
};
|
||||
|
||||
midiTracks.push(track);
|
||||
}
|
||||
|
||||
track.notes.push(
|
||||
...pattern.notes.map((note) => ({
|
||||
...note,
|
||||
position: note.position + offset * barLength,
|
||||
})),
|
||||
);
|
||||
|
||||
// copy pattern for the rest of the scene
|
||||
if (pattern.bars < sceneMaxBars) {
|
||||
for (let ofs = offset + pattern.bars; ofs < offset + sceneMaxBars; ofs++) {
|
||||
track.notes.push(
|
||||
...pattern.notes.map((note) => ({
|
||||
...note,
|
||||
position: note.position + ofs * barLength,
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
offset += sceneMaxBars;
|
||||
});
|
||||
|
||||
if (exporterParams.exportAllPadsWithSamples) {
|
||||
for (const group in pads) {
|
||||
pads[group].forEach((pad, index) => {
|
||||
if (pad.soundId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const padCode = `${group}${index}`;
|
||||
if (midiTracks.some((t) => t.padCode === padCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sound = data.sounds.find((s) => s.id === pad.soundId);
|
||||
|
||||
midiTracks.push({
|
||||
name: sound?.meta?.name || padCode,
|
||||
padCode,
|
||||
group,
|
||||
notes: [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
midiTracks.sort((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
}
|
||||
|
||||
// Helper function to create a drum rack track for a specific group
|
||||
const createDrumRackTrack = (group: 'a' | 'b' | 'c' | 'd'): MidiTrack | null => {
|
||||
const groupTracks = midiTracks.filter((t) => t.group === group);
|
||||
if (groupTracks.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const mergedNotes: Note[] = [];
|
||||
|
||||
groupTracks
|
||||
.toSorted((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
)
|
||||
.forEach((track, idx) => {
|
||||
mergedNotes.push(
|
||||
...track.notes.map((note) => ({
|
||||
...note,
|
||||
note: 36 + idx,
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
const drumTrack: MidiTrack = {
|
||||
name: `Drums ${group.toUpperCase()}`,
|
||||
group,
|
||||
padCode: `${group}0`,
|
||||
notes: mergedNotes,
|
||||
};
|
||||
|
||||
return drumTrack;
|
||||
};
|
||||
|
||||
// Process drum racks for each group that has the option enabled
|
||||
const drumRackTracks: MidiTrack[] = [];
|
||||
const groupsToProcess: Array<{ group: 'a' | 'b' | 'c' | 'd'; enabled: boolean }> = [
|
||||
{ group: 'a', enabled: exporterParams.drumRackGroupA || false },
|
||||
{ group: 'b', enabled: exporterParams.drumRackGroupB || false },
|
||||
{ group: 'c', enabled: exporterParams.drumRackGroupC || false },
|
||||
{ group: 'd', enabled: exporterParams.drumRackGroupD || false },
|
||||
];
|
||||
|
||||
for (const { group, enabled } of groupsToProcess) {
|
||||
if (enabled) {
|
||||
const drumTrack = createDrumRackTrack(group);
|
||||
if (drumTrack) {
|
||||
drumRackTracks.push(drumTrack);
|
||||
// Remove tracks from this group from the main tracks array
|
||||
midiTracks = midiTracks.filter((t) => t.group !== group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert drum rack tracks at the beginning, maintaining group order (A, B, C, D)
|
||||
midiTracks.unshift(...drumRackTracks);
|
||||
|
||||
Sentry.setContext('midiData', {
|
||||
tracks: midiTracks,
|
||||
});
|
||||
|
||||
return {
|
||||
tracks: midiTracks,
|
||||
} as MidiData;
|
||||
}
|
||||
|
||||
export default midiTransformer;
|
||||
132
src/lib/transformers/reaper.ts
Normal file
132
src/lib/transformers/reaper.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { omit } from 'lodash';
|
||||
import {
|
||||
ExporterParams,
|
||||
Note,
|
||||
Pad,
|
||||
PadCode,
|
||||
ProjectRawData,
|
||||
TimeSignature,
|
||||
} from '../../types/types';
|
||||
import { getSampleName } from '../exporters/utils';
|
||||
import { findPad, findSoundByPad, findSoundIdByPad } from '../utils';
|
||||
|
||||
export type RprData = {
|
||||
tracks: RprTrack[];
|
||||
};
|
||||
|
||||
export type RprTrack = Omit<Pad, 'file' | 'rawData' | 'group' | 'midiChannel'> & {
|
||||
padCode: PadCode;
|
||||
group: string;
|
||||
sampleName: string;
|
||||
sampleChannels: number;
|
||||
sampleRate: number;
|
||||
bpm: number;
|
||||
items: RprTrackItem[];
|
||||
timeSignature: TimeSignature;
|
||||
};
|
||||
|
||||
export type RprTrackItem = {
|
||||
notes: Note[];
|
||||
bars: number;
|
||||
offset: number;
|
||||
sceneBars: number;
|
||||
sceneIndex: number;
|
||||
sceneName: string;
|
||||
};
|
||||
|
||||
export function reaperTransform(data: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const { pads, scenes } = data;
|
||||
const customSceneNames = exporterParams.customSceneNames || {};
|
||||
const tracks: RprTrack[] = [];
|
||||
let offset = 0;
|
||||
|
||||
scenes.forEach((scene, sceneIndex) => {
|
||||
const sceneBars = Math.max(...scene.patterns.map((p) => p.bars));
|
||||
scene.patterns.forEach((pattern) => {
|
||||
let track = tracks.find((c) => c.padCode === pattern.pad);
|
||||
|
||||
if (!track) {
|
||||
const soundId = findSoundIdByPad(pattern.pad, pads) || 0;
|
||||
const sound = findSoundByPad(pattern.pad, pads, data.sounds);
|
||||
const pad = findPad(pattern.pad, pads);
|
||||
|
||||
if (!pad) {
|
||||
throw new Error(`Could not find pad for ${pattern.pad}`);
|
||||
}
|
||||
|
||||
track = {
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId,
|
||||
padCode: pattern.pad,
|
||||
name: sound?.meta?.name || pattern.pad,
|
||||
volume: pad.volume * (2 / 200),
|
||||
sampleName: getSampleName(sound?.meta?.name, soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
bpm: data.settings.bpm,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
items: [],
|
||||
};
|
||||
|
||||
tracks.push(track);
|
||||
}
|
||||
|
||||
track.items.push({
|
||||
offset,
|
||||
notes: pattern.notes,
|
||||
bars: pattern.bars,
|
||||
sceneBars,
|
||||
sceneIndex,
|
||||
sceneName: customSceneNames[scene.name] || scene.name,
|
||||
});
|
||||
});
|
||||
|
||||
offset += sceneBars;
|
||||
});
|
||||
|
||||
if (exporterParams.exportAllPadsWithSamples) {
|
||||
for (const group in pads) {
|
||||
pads[group].forEach((pad, index) => {
|
||||
if (pad.soundId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const padCode = `${group}${index}` as PadCode;
|
||||
if (tracks.some((t) => t.padCode === padCode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sound = data.sounds.find((s) => s.id === pad.soundId);
|
||||
|
||||
tracks.push({
|
||||
...omit(pad, ['file', 'rawData']),
|
||||
soundId: pad.soundId,
|
||||
padCode,
|
||||
name: sound?.meta?.name || padCode,
|
||||
volume: pad.volume * (2 / 200),
|
||||
sampleName: getSampleName(sound?.meta?.name, pad.soundId),
|
||||
sampleChannels: sound?.meta?.channels || 0,
|
||||
sampleRate: sound?.meta?.samplerate || 0,
|
||||
bpm: data.settings.bpm,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
items: [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tracks.sort((a, b) =>
|
||||
a.padCode.localeCompare(b.padCode, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
}
|
||||
|
||||
Sentry.setContext('reaperData', {
|
||||
tracks,
|
||||
});
|
||||
|
||||
return {
|
||||
tracks,
|
||||
} as RprData;
|
||||
}
|
||||
|
||||
export default reaperTransform;
|
||||
136
src/lib/transformers/webView.ts
Normal file
136
src/lib/transformers/webView.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Note, Pad, ProjectRawData } from '../../types/types';
|
||||
import { getSampleName } from '../exporters/utils';
|
||||
import { noteNumberToName } from '../parsers';
|
||||
import { findPad, findSoundByPad, findSoundIdByPad } from '../utils';
|
||||
|
||||
export type ViewNote = Note & { name: string };
|
||||
|
||||
export type ViewPattern = {
|
||||
pad: string;
|
||||
bars: number;
|
||||
soundName: string;
|
||||
notes: ViewNote[];
|
||||
group: string;
|
||||
padNumber: number;
|
||||
midiChannel: number;
|
||||
soundId: number;
|
||||
};
|
||||
|
||||
export type ViewScene = {
|
||||
name: string;
|
||||
patterns: ViewPattern[];
|
||||
maxBars: number;
|
||||
};
|
||||
|
||||
export type ViewPad = Pad;
|
||||
|
||||
export type ViewData = {
|
||||
pads: Record<string, ViewPad[]>;
|
||||
scenes: ViewScene[];
|
||||
scenesSettings: ProjectRawData['scenesSettings'];
|
||||
};
|
||||
|
||||
function webViewTransformer(data: ProjectRawData): ViewData {
|
||||
const { pads, scenes } = data;
|
||||
const newScenes: ViewScene[] = [];
|
||||
const usedPads = new Set<string>();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
// construct new scenes array
|
||||
// and collect the used pads
|
||||
scenes.forEach((scene, idx) => {
|
||||
newScenes[idx] = {
|
||||
name: scene.name,
|
||||
maxBars: Math.max(...scene.patterns.map((p) => p.bars), 0),
|
||||
patterns: [],
|
||||
};
|
||||
|
||||
for (const pattern of scene.patterns) {
|
||||
if (pattern.notes.length > 0) {
|
||||
usedPads.add(pattern.pad);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// also add pads that have samples assigned (even without patterns)
|
||||
for (const group in pads) {
|
||||
pads[group].forEach((pad, index) => {
|
||||
if (pad.soundId > 0) {
|
||||
usedPads.add(`${group}${index}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newScenes.forEach((scene, idx) => {
|
||||
// make sure each scene have the same tracks/pads
|
||||
usedPads.forEach((pad) => {
|
||||
const patternByPad = scene.patterns.find((p) => p.pad === pad);
|
||||
if (!patternByPad) {
|
||||
const group = pad[0];
|
||||
const padNumber = parseInt(pad.slice(1), 10);
|
||||
const soundId = findSoundIdByPad(pad, pads) || 0;
|
||||
const sound = findSoundByPad(pad, pads, data.sounds);
|
||||
const padObject = findPad(pad, pads);
|
||||
|
||||
scene.patterns.push({
|
||||
pad,
|
||||
notes: [],
|
||||
bars: 0,
|
||||
group,
|
||||
padNumber,
|
||||
soundName: getSampleName(sound?.meta?.name, soundId, false),
|
||||
midiChannel: padObject?.midiChannel || 0,
|
||||
soundId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// copy notes to exiting patterns
|
||||
scenes[idx].patterns.forEach((pattern) => {
|
||||
const patternInScene = scene.patterns.find((p) => p.pad === pattern.pad);
|
||||
if (!patternInScene) {
|
||||
return;
|
||||
}
|
||||
|
||||
patternInScene.notes = pattern.notes
|
||||
.map((note) => ({
|
||||
...note,
|
||||
name: noteNumberToName(note.note),
|
||||
}))
|
||||
.reduce((acc, note) => {
|
||||
const existingNoteInThisPosition = acc.find((n) => n.position === note.position);
|
||||
|
||||
if (existingNoteInThisPosition) {
|
||||
existingNoteInThisPosition.name = `${existingNoteInThisPosition.name},${note.name}`;
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.push({ ...note });
|
||||
return acc;
|
||||
}, [] as ViewNote[]);
|
||||
patternInScene.bars = pattern.bars;
|
||||
});
|
||||
|
||||
scene.patterns.sort((a, b) =>
|
||||
a.pad.localeCompare(b.pad, undefined, { numeric: true, sensitivity: 'base' }),
|
||||
);
|
||||
});
|
||||
|
||||
Sentry.setContext('webViewData', {
|
||||
pads,
|
||||
scenes: newScenes,
|
||||
scenesSettings: data.scenesSettings,
|
||||
});
|
||||
|
||||
return {
|
||||
pads,
|
||||
scenes: newScenes,
|
||||
scenesSettings: data.scenesSettings,
|
||||
};
|
||||
}
|
||||
|
||||
export default webViewTransformer;
|
||||
224
src/lib/untar.ts
Normal file
224
src/lib/untar.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
class UntarError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'UntarError';
|
||||
}
|
||||
}
|
||||
|
||||
interface TarHeader {
|
||||
name: string;
|
||||
mode: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
size: number;
|
||||
mtime: number;
|
||||
checksum: number;
|
||||
type: string;
|
||||
linkname: string;
|
||||
magic: string;
|
||||
version: string;
|
||||
uname: string;
|
||||
gname: string;
|
||||
devmajor: number;
|
||||
devminor: number;
|
||||
prefix: string;
|
||||
fullName: string;
|
||||
}
|
||||
|
||||
export class TarFile {
|
||||
name: string;
|
||||
size: number;
|
||||
data: Uint8Array | null;
|
||||
type: string;
|
||||
mode: number;
|
||||
uid: number;
|
||||
gid: number;
|
||||
mtime: Date;
|
||||
linkname: string;
|
||||
uname: string;
|
||||
gname: string;
|
||||
|
||||
constructor(name: string, size: number, data: Uint8Array | null, type: string = 'file') {
|
||||
this.name = name;
|
||||
this.size = size;
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
this.mode = 0;
|
||||
this.uid = 0;
|
||||
this.gid = 0;
|
||||
this.mtime = new Date();
|
||||
this.linkname = '';
|
||||
this.uname = '';
|
||||
this.gname = '';
|
||||
}
|
||||
}
|
||||
|
||||
function parseTarHeader(buffer: ArrayBuffer, offset: number): TarHeader | null {
|
||||
const view = new Uint8Array(buffer, offset, 512);
|
||||
|
||||
if (view.every((byte) => byte === 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function readString(start: number, length: number): string {
|
||||
let str = '';
|
||||
for (let i = start; i < start + length && view[i] !== 0; i++) {
|
||||
str += String.fromCharCode(view[i]);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function readOctal(start: number, length: number): number {
|
||||
const str = readString(start, length).trim();
|
||||
return str ? parseInt(str, 8) : 0;
|
||||
}
|
||||
|
||||
const header: TarHeader = {
|
||||
name: readString(0, 100),
|
||||
mode: readOctal(100, 8),
|
||||
uid: readOctal(108, 8),
|
||||
gid: readOctal(116, 8),
|
||||
size: readOctal(124, 12),
|
||||
mtime: readOctal(136, 12),
|
||||
checksum: readOctal(148, 8),
|
||||
type: String.fromCharCode(view[156]) || '0',
|
||||
linkname: readString(157, 100),
|
||||
magic: readString(257, 6),
|
||||
version: readString(263, 2),
|
||||
uname: readString(265, 32),
|
||||
gname: readString(297, 32),
|
||||
devmajor: readOctal(329, 8),
|
||||
devminor: readOctal(337, 8),
|
||||
prefix: readString(345, 155),
|
||||
fullName: '',
|
||||
};
|
||||
|
||||
let fullName = header.name;
|
||||
if (header.prefix) {
|
||||
fullName = `${header.prefix}/${header.name}`;
|
||||
}
|
||||
header.fullName = fullName;
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
function validateChecksum(buffer: ArrayBuffer, offset: number, expectedChecksum: number): boolean {
|
||||
const view = new Uint8Array(buffer, offset, 512);
|
||||
let sum = 0;
|
||||
|
||||
for (let i = 0; i < 512; i++) {
|
||||
if (i >= 148 && i < 156) {
|
||||
sum += 32;
|
||||
} else {
|
||||
sum += view[i];
|
||||
}
|
||||
}
|
||||
|
||||
return sum === expectedChecksum;
|
||||
}
|
||||
|
||||
export async function untar(file: File | ArrayBuffer | Uint8Array): Promise<TarFile[]> {
|
||||
if (!(file instanceof File) && !(file instanceof ArrayBuffer) && !(file instanceof Uint8Array)) {
|
||||
throw new UntarError('Input must be a File object or an ArrayBuffer or Uint8Array');
|
||||
}
|
||||
|
||||
let buffer: ArrayBuffer;
|
||||
|
||||
if (file instanceof File) {
|
||||
buffer = await file.arrayBuffer();
|
||||
} else if (file instanceof Uint8Array) {
|
||||
buffer = new ArrayBuffer(file.length);
|
||||
const view = new Uint8Array(buffer);
|
||||
view.set(file);
|
||||
} else {
|
||||
buffer = file;
|
||||
}
|
||||
|
||||
const files: TarFile[] = [];
|
||||
let offset = 0;
|
||||
|
||||
while (offset < buffer.byteLength) {
|
||||
const header = parseTarHeader(buffer, offset);
|
||||
|
||||
if (!header) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (header.checksum > 0) {
|
||||
if (!validateChecksum(buffer, offset, header.checksum)) {
|
||||
throw new UntarError(`Invalid checksum for file: ${header.fullName}`);
|
||||
}
|
||||
}
|
||||
|
||||
offset += 512;
|
||||
|
||||
let fileData: Uint8Array | null = null;
|
||||
if (header.size > 0) {
|
||||
fileData = new Uint8Array(buffer, offset, header.size);
|
||||
}
|
||||
|
||||
let fileType = 'file';
|
||||
switch (header.type) {
|
||||
case '0':
|
||||
case '\0':
|
||||
fileType = 'file';
|
||||
break;
|
||||
case '1':
|
||||
fileType = 'hard-link';
|
||||
break;
|
||||
case '2':
|
||||
fileType = 'symbolic-link';
|
||||
break;
|
||||
case '3':
|
||||
fileType = 'character-device';
|
||||
break;
|
||||
case '4':
|
||||
fileType = 'block-device';
|
||||
break;
|
||||
case '5':
|
||||
fileType = 'directory';
|
||||
break;
|
||||
case '6':
|
||||
fileType = 'fifo';
|
||||
break;
|
||||
case '7':
|
||||
fileType = 'contiguous-file';
|
||||
break;
|
||||
default:
|
||||
fileType = 'unknown';
|
||||
}
|
||||
|
||||
const tarFile = new TarFile(header.fullName, header.size, fileData, fileType);
|
||||
|
||||
tarFile.mode = header.mode;
|
||||
tarFile.uid = header.uid;
|
||||
tarFile.gid = header.gid;
|
||||
tarFile.mtime = new Date(header.mtime * 1000);
|
||||
tarFile.linkname = header.linkname;
|
||||
tarFile.uname = header.uname;
|
||||
tarFile.gname = header.gname;
|
||||
|
||||
files.push(tarFile);
|
||||
|
||||
const paddedSize = Math.ceil(header.size / 512) * 512;
|
||||
offset += paddedSize;
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export function arrayBufferToText(data: Uint8Array, encoding: string = 'utf-8'): string {
|
||||
const decoder = new TextDecoder(encoding);
|
||||
return decoder.decode(data);
|
||||
}
|
||||
|
||||
export function createBlobUrl(
|
||||
tarFile: TarFile,
|
||||
mimeType: string = 'application/octet-stream',
|
||||
): string {
|
||||
if (!tarFile.data) {
|
||||
throw new Error('Tar file data is null');
|
||||
}
|
||||
const blob = new Blob([new Uint8Array(tarFile.data)], { type: mimeType });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
98
src/lib/utils.ts
Normal file
98
src/lib/utils.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Pad, Sound } from '../types/types';
|
||||
import { PAD_DISPLAY_FROM_INDEX } from './constants';
|
||||
|
||||
export function findPad(pad: string, pads: Record<string, Pad[]>) {
|
||||
const group = pad[0];
|
||||
const padNumber = parseInt(pad.slice(1), 10);
|
||||
const padData = pads[group][padNumber];
|
||||
|
||||
return padData;
|
||||
}
|
||||
|
||||
export function findSoundIdByPad(pad: string, pads: Record<string, Pad[]>) {
|
||||
const padData = findPad(pad, pads);
|
||||
|
||||
if (!padData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (padData.soundId === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return padData.soundId;
|
||||
}
|
||||
|
||||
export function findSoundByPad(pad: string, pads: Record<string, Pad[]>, sounds: Sound[]) {
|
||||
const soundId = findSoundIdByPad(pad, pads);
|
||||
|
||||
return sounds.find((s) => s.id === soundId) || null;
|
||||
}
|
||||
|
||||
export function audioFormatAsBitDepth(s: string) {
|
||||
switch (s) {
|
||||
case 's16':
|
||||
return 16;
|
||||
case 's24':
|
||||
return 24;
|
||||
case 'float':
|
||||
return 32;
|
||||
default:
|
||||
throw new Error('unknown bit depth');
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateSoundLength(sound: Sound) {
|
||||
const sampleRate = sound.meta?.samplerate;
|
||||
const channels = sound.meta?.channels;
|
||||
const fileSize = sound.fileNode?.fileSize;
|
||||
|
||||
if (!sampleRate || !channels || !fileSize) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fileSize / 2 / sampleRate / channels;
|
||||
}
|
||||
|
||||
export class AbortError extends Error {
|
||||
constructor() {
|
||||
super('The operation was aborted.');
|
||||
this.name = 'AbortError';
|
||||
}
|
||||
}
|
||||
|
||||
export function getPadDisplayName(group: string, index: number): string {
|
||||
const padChar = PAD_DISPLAY_FROM_INDEX[index] ?? '?';
|
||||
return `${group.toUpperCase()} ${padChar}`;
|
||||
}
|
||||
|
||||
export function hasMultipleNoteVariations(
|
||||
group: string,
|
||||
scenes: { patterns: { pad: string; notes: { note: number }[] }[] }[],
|
||||
) {
|
||||
const trackNotes: Record<string, Set<number>> = {};
|
||||
|
||||
for (const scene of scenes) {
|
||||
for (const pattern of scene.patterns) {
|
||||
if (!pattern.pad.startsWith(group)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!trackNotes[pattern.pad]) {
|
||||
trackNotes[pattern.pad] = new Set();
|
||||
}
|
||||
|
||||
for (const note of pattern.notes) {
|
||||
trackNotes[pattern.pad].add(note.note);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const padCode in trackNotes) {
|
||||
if (trackNotes[padCode].size > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user