Enhance Ableton exporter to support static automation values and update simpler playback duration calculation
This commit is contained in:
@@ -8,6 +8,7 @@ export type AbletonAutomationTarget = {
|
|||||||
maxValue?: number;
|
maxValue?: number;
|
||||||
soundLength?: number;
|
soundLength?: number;
|
||||||
invertValue?: boolean;
|
invertValue?: boolean;
|
||||||
|
staticValue?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AbletonAutomationTargets = Partial<Record<FaderParam, AbletonAutomationTarget[]>>;
|
export type AbletonAutomationTargets = Partial<Record<FaderParam, AbletonAutomationTarget[]>>;
|
||||||
@@ -119,6 +120,19 @@ function getStaticValue(pattern: AblGroupPatternOccurrence, parameter: FaderPara
|
|||||||
return value === -1 ? defaultValue(parameter) : value;
|
return value === -1 ? defaultValue(parameter) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getConvertedStaticValue(
|
||||||
|
pattern: AblGroupPatternOccurrence,
|
||||||
|
parameter: FaderParam,
|
||||||
|
track: AblTrack,
|
||||||
|
groupMixer: boolean,
|
||||||
|
target: AbletonAutomationTarget,
|
||||||
|
) {
|
||||||
|
if (pattern.faderParams[parameter] === -1 && target.staticValue !== undefined) {
|
||||||
|
return target.staticValue;
|
||||||
|
}
|
||||||
|
return convertFaderValue(parameter, getStaticValue(pattern, parameter), track, groupMixer, target);
|
||||||
|
}
|
||||||
|
|
||||||
function getArrangementPoints(
|
function getArrangementPoints(
|
||||||
patterns: AblGroupPatternOccurrence[],
|
patterns: AblGroupPatternOccurrence[],
|
||||||
parameter: FaderParam,
|
parameter: FaderParam,
|
||||||
@@ -128,13 +142,9 @@ function getArrangementPoints(
|
|||||||
) {
|
) {
|
||||||
const points: AutomationPoint[] = [];
|
const points: AutomationPoint[] = [];
|
||||||
let hasAutomation = false;
|
let hasAutomation = false;
|
||||||
let previousValue = convertFaderValue(
|
let previousValue = patterns[0]
|
||||||
parameter,
|
? getConvertedStaticValue(patterns[0], parameter, track, groupMixer, target)
|
||||||
patterns[0] ? getStaticValue(patterns[0], parameter) : defaultValue(parameter),
|
: convertFaderValue(parameter, defaultValue(parameter), track, groupMixer, target);
|
||||||
track,
|
|
||||||
groupMixer,
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
|
|
||||||
patterns.forEach((pattern) => {
|
patterns.forEach((pattern) => {
|
||||||
const beats = pattern.timeSignature.numerator * (4 / pattern.timeSignature.denominator);
|
const beats = pattern.timeSignature.numerator * (4 / pattern.timeSignature.denominator);
|
||||||
@@ -144,13 +154,7 @@ function getArrangementPoints(
|
|||||||
const sourcePoints = pattern.faderAutomation
|
const sourcePoints = pattern.faderAutomation
|
||||||
.filter((point) => point.parameter === parameter)
|
.filter((point) => point.parameter === parameter)
|
||||||
.sort((a, b) => a.position - b.position);
|
.sort((a, b) => a.position - b.position);
|
||||||
const staticValue = convertFaderValue(
|
const staticValue = getConvertedStaticValue(pattern, parameter, track, groupMixer, target);
|
||||||
parameter,
|
|
||||||
getStaticValue(pattern, parameter),
|
|
||||||
track,
|
|
||||||
groupMixer,
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (sourcePoints.length === 0) {
|
if (sourcePoints.length === 0) {
|
||||||
if (hasAutomation) {
|
if (hasAutomation) {
|
||||||
@@ -199,13 +203,7 @@ function getClipPoints(
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const staticValue = convertFaderValue(
|
const staticValue = getConvertedStaticValue(pattern, parameter, track, groupMixer, target);
|
||||||
parameter,
|
|
||||||
getStaticValue(pattern, parameter),
|
|
||||||
track,
|
|
||||||
groupMixer,
|
|
||||||
target,
|
|
||||||
);
|
|
||||||
return [
|
return [
|
||||||
{ time: SENTINEL_TIME, value: staticValue },
|
{ time: SENTINEL_TIME, value: staticValue },
|
||||||
{ time: 0, value: staticValue },
|
{ time: 0, value: staticValue },
|
||||||
|
|||||||
@@ -78,6 +78,13 @@ function simplerEnvelopeMilliseconds(
|
|||||||
return oneshot ? Math.min(2000, milliseconds) : milliseconds;
|
return oneshot ? Math.min(2000, milliseconds) : milliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function simplerPlaybackDuration(koTrack: AblTrack) {
|
||||||
|
const trimmedFrames = koTrack.trimRight - koTrack.trimLeft;
|
||||||
|
return trimmedFrames > 0 && koTrack.sampleRate > 0
|
||||||
|
? trimmedFrames / koTrack.sampleRate
|
||||||
|
: koTrack.soundLength;
|
||||||
|
}
|
||||||
|
|
||||||
function setColorValue(node: any, value: number) {
|
function setColorValue(node: any, value: number) {
|
||||||
const color = node.Color || node.ColorIndex;
|
const color = node.Color || node.ColorIndex;
|
||||||
color['@Value'] = value;
|
color['@Value'] = value;
|
||||||
@@ -121,6 +128,10 @@ function targetFor(parameter: any, maxValue?: number, soundLength?: number, inve
|
|||||||
maxValue,
|
maxValue,
|
||||||
soundLength,
|
soundLength,
|
||||||
invertValue,
|
invertValue,
|
||||||
|
staticValue:
|
||||||
|
parameter?.Manual?.['@Value'] === undefined
|
||||||
|
? undefined
|
||||||
|
: Number(parameter.Manual['@Value']),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,10 +207,15 @@ function trackAutomationTargets(midiTrack: any, includeArrangementMixer: boolean
|
|||||||
|
|
||||||
findSimplers(midiTrack.DeviceChain.DeviceChain.Devices).forEach((simpler) => {
|
findSimplers(midiTrack.DeviceChain.DeviceChain.Devices).forEach((simpler) => {
|
||||||
const oneshot = Number(simpler.Globals.PlaybackMode['@Value']) === 1;
|
const oneshot = Number(simpler.Globals.PlaybackMode['@Value']) === 1;
|
||||||
const sampleRef = simpler.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleRef;
|
const samplePart = simpler.Player.MultiSampleMap.SampleParts.MultiSamplePart;
|
||||||
|
const sampleRef = samplePart.SampleRef;
|
||||||
const sampleRate = Number(sampleRef.DefaultSampleRate?.['@Value']);
|
const sampleRate = Number(sampleRef.DefaultSampleRate?.['@Value']);
|
||||||
const sampleDuration = Number(sampleRef.DefaultDuration?.['@Value']);
|
const sampleDuration = Number(sampleRef.DefaultDuration?.['@Value']);
|
||||||
const soundLength = sampleRate > 0 ? sampleDuration / sampleRate : undefined;
|
const trimmedFrames = Number(samplePart.SampleEnd['@Value']) - Number(samplePart.SampleStart['@Value']);
|
||||||
|
const soundLength =
|
||||||
|
sampleRate > 0
|
||||||
|
? (trimmedFrames > 0 ? trimmedFrames : sampleDuration) / sampleRate
|
||||||
|
: undefined;
|
||||||
addTarget(targets, FaderParam.PTC, targetFor(simpler.Pitch.TransposeKey));
|
addTarget(targets, FaderParam.PTC, targetFor(simpler.Pitch.TransposeKey));
|
||||||
addTarget(targets, FaderParam.TUNE, targetFor(simpler.Pitch.TransposeFine));
|
addTarget(targets, FaderParam.TUNE, targetFor(simpler.Pitch.TransposeFine));
|
||||||
addTarget(
|
addTarget(
|
||||||
@@ -376,6 +392,7 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
|
|||||||
const samplePart = device.Player.MultiSampleMap.SampleParts.MultiSamplePart;
|
const samplePart = device.Player.MultiSampleMap.SampleParts.MultiSamplePart;
|
||||||
const loopModulators = device.Player.LoopModulators;
|
const loopModulators = device.Player.LoopModulators;
|
||||||
const simplerFilter = device.Filter.Slot.Value.SimplerFilter;
|
const simplerFilter = device.Filter.Slot.Value.SimplerFilter;
|
||||||
|
const playbackDuration = simplerPlaybackDuration(koTrack);
|
||||||
|
|
||||||
device.LastPresetRef.Value = {};
|
device.LastPresetRef.Value = {};
|
||||||
device.ShouldShowPresetName['@Value'] = 'false';
|
device.ShouldShowPresetName['@Value'] = 'false';
|
||||||
@@ -454,12 +471,12 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
|
|||||||
: device.VolumeAndPan.Envelope.ReleaseTime;
|
: device.VolumeAndPan.Envelope.ReleaseTime;
|
||||||
attackTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
attackTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
||||||
koTrack.attack,
|
koTrack.attack,
|
||||||
koTrack.soundLength,
|
playbackDuration,
|
||||||
oneshot,
|
oneshot,
|
||||||
);
|
);
|
||||||
releaseTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
releaseTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
||||||
koTrack.release,
|
koTrack.release,
|
||||||
koTrack.soundLength,
|
playbackDuration,
|
||||||
oneshot,
|
oneshot,
|
||||||
oneshot,
|
oneshot,
|
||||||
);
|
);
|
||||||
@@ -469,7 +486,7 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
|
|||||||
if (koTrack.faderParams[FaderParam.ATK] !== -1) {
|
if (koTrack.faderParams[FaderParam.ATK] !== -1) {
|
||||||
attackTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
attackTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
||||||
koTrack.faderParams[FaderParam.ATK] * 255,
|
koTrack.faderParams[FaderParam.ATK] * 255,
|
||||||
koTrack.soundLength,
|
playbackDuration,
|
||||||
oneshot,
|
oneshot,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -478,7 +495,7 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
|
|||||||
if (koTrack.faderParams[FaderParam.REL] !== -1) {
|
if (koTrack.faderParams[FaderParam.REL] !== -1) {
|
||||||
releaseTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
releaseTarget.Manual['@Value'] = simplerEnvelopeMilliseconds(
|
||||||
koTrack.faderParams[FaderParam.REL] * 255,
|
koTrack.faderParams[FaderParam.REL] * 255,
|
||||||
koTrack.soundLength,
|
playbackDuration,
|
||||||
oneshot,
|
oneshot,
|
||||||
oneshot,
|
oneshot,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user