Improve fader value conversion and support target max value in Ableton exports
This commit is contained in:
@@ -5,6 +5,7 @@ import { koEnvRangeToSeconds } from './utils';
|
|||||||
export type AbletonAutomationTarget = {
|
export type AbletonAutomationTarget = {
|
||||||
arrangement?: number;
|
arrangement?: number;
|
||||||
session?: number;
|
session?: number;
|
||||||
|
maxValue?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AbletonAutomationTargets = Partial<Record<FaderParam, AbletonAutomationTarget[]>>;
|
export type AbletonAutomationTargets = Partial<Record<FaderParam, AbletonAutomationTarget[]>>;
|
||||||
@@ -37,34 +38,52 @@ export function convertFaderValue(
|
|||||||
value: number,
|
value: number,
|
||||||
track: AblTrack,
|
track: AblTrack,
|
||||||
groupMixer: boolean,
|
groupMixer: boolean,
|
||||||
|
target?: AbletonAutomationTarget,
|
||||||
) {
|
) {
|
||||||
|
let convertedValue: number;
|
||||||
switch (parameter) {
|
switch (parameter) {
|
||||||
case FaderParam.LVL:
|
case FaderParam.LVL:
|
||||||
if (groupMixer) {
|
if (groupMixer) {
|
||||||
return value;
|
convertedValue = value;
|
||||||
|
} else {
|
||||||
|
convertedValue = track.volume * value;
|
||||||
}
|
}
|
||||||
return track.volume * value;
|
break;
|
||||||
case FaderParam.PTC:
|
case FaderParam.PTC:
|
||||||
return (value - 0.5) * 10;
|
convertedValue = (value - 0.5) * 10;
|
||||||
|
break;
|
||||||
case FaderParam.LPF:
|
case FaderParam.LPF:
|
||||||
return 20 + value * 0.9 * 115;
|
convertedValue = 20 + value * 0.9 * 115;
|
||||||
|
break;
|
||||||
case FaderParam.HPF:
|
case FaderParam.HPF:
|
||||||
return 20 + value * 0.7 * 115;
|
convertedValue = 20 + value * 0.7 * 115;
|
||||||
|
break;
|
||||||
case FaderParam.FX:
|
case FaderParam.FX:
|
||||||
return value;
|
convertedValue = value;
|
||||||
|
break;
|
||||||
case FaderParam.ATK:
|
case FaderParam.ATK:
|
||||||
return koEnvRangeToSeconds(value * 255, track.soundLength) * 1000;
|
convertedValue = koEnvRangeToSeconds(value * 255, track.soundLength) * 1000;
|
||||||
|
break;
|
||||||
case FaderParam.REL:
|
case FaderParam.REL:
|
||||||
return koEnvRangeToSeconds(value * 255, track.soundLength) * 1000;
|
convertedValue = koEnvRangeToSeconds(value * 255, track.soundLength) * 1000;
|
||||||
|
break;
|
||||||
case FaderParam.PAN:
|
case FaderParam.PAN:
|
||||||
return (value - 0.5) * 2;
|
convertedValue = (value - 0.5) * 2;
|
||||||
|
break;
|
||||||
case FaderParam.TUNE:
|
case FaderParam.TUNE:
|
||||||
return Math.max(-0.5, Math.min(0.5, value - 0.5)) * 100;
|
convertedValue = Math.max(-0.5, Math.min(0.5, value - 0.5)) * 100;
|
||||||
|
break;
|
||||||
case FaderParam.MOD:
|
case FaderParam.MOD:
|
||||||
return value / 20;
|
convertedValue = value / 20;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return value;
|
convertedValue = value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return target?.maxValue === undefined
|
||||||
|
? convertedValue
|
||||||
|
: Math.min(target.maxValue, convertedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFaderValueAt(
|
export function getFaderValueAt(
|
||||||
@@ -95,6 +114,7 @@ function getArrangementPoints(
|
|||||||
parameter: FaderParam,
|
parameter: FaderParam,
|
||||||
track: AblTrack,
|
track: AblTrack,
|
||||||
groupMixer: boolean,
|
groupMixer: boolean,
|
||||||
|
target: AbletonAutomationTarget,
|
||||||
) {
|
) {
|
||||||
const points: AutomationPoint[] = [];
|
const points: AutomationPoint[] = [];
|
||||||
let hasAutomation = false;
|
let hasAutomation = false;
|
||||||
@@ -103,6 +123,7 @@ function getArrangementPoints(
|
|||||||
patterns[0] ? getStaticValue(patterns[0], parameter) : defaultValue(parameter),
|
patterns[0] ? getStaticValue(patterns[0], parameter) : defaultValue(parameter),
|
||||||
track,
|
track,
|
||||||
groupMixer,
|
groupMixer,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
|
|
||||||
patterns.forEach((pattern) => {
|
patterns.forEach((pattern) => {
|
||||||
@@ -118,6 +139,7 @@ function getArrangementPoints(
|
|||||||
getStaticValue(pattern, parameter),
|
getStaticValue(pattern, parameter),
|
||||||
track,
|
track,
|
||||||
groupMixer,
|
groupMixer,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (sourcePoints.length === 0) {
|
if (sourcePoints.length === 0) {
|
||||||
@@ -139,7 +161,7 @@ function getArrangementPoints(
|
|||||||
if (loopOffset + point.position >= sceneTicks) {
|
if (loopOffset + point.position >= sceneTicks) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = convertFaderValue(parameter, point.value, track, groupMixer);
|
const value = convertFaderValue(parameter, point.value, track, groupMixer, target);
|
||||||
points.push({ time: startTime + (loopOffset + point.position) / 96, value });
|
points.push({ time: startTime + (loopOffset + point.position) / 96, value });
|
||||||
previousValue = value;
|
previousValue = value;
|
||||||
});
|
});
|
||||||
@@ -158,6 +180,7 @@ function getClipPoints(
|
|||||||
parameter: FaderParam,
|
parameter: FaderParam,
|
||||||
track: AblTrack,
|
track: AblTrack,
|
||||||
groupMixer: boolean,
|
groupMixer: boolean,
|
||||||
|
target: AbletonAutomationTarget,
|
||||||
) {
|
) {
|
||||||
const sourcePoints = pattern.faderAutomation
|
const sourcePoints = pattern.faderAutomation
|
||||||
.filter((point) => point.parameter === parameter)
|
.filter((point) => point.parameter === parameter)
|
||||||
@@ -171,13 +194,14 @@ function getClipPoints(
|
|||||||
getStaticValue(pattern, parameter),
|
getStaticValue(pattern, parameter),
|
||||||
track,
|
track,
|
||||||
groupMixer,
|
groupMixer,
|
||||||
|
target,
|
||||||
);
|
);
|
||||||
return [
|
return [
|
||||||
{ time: SENTINEL_TIME, value: staticValue },
|
{ time: SENTINEL_TIME, value: staticValue },
|
||||||
{ time: 0, value: staticValue },
|
{ time: 0, value: staticValue },
|
||||||
...sourcePoints.map((point) => ({
|
...sourcePoints.map((point) => ({
|
||||||
time: point.position / 96,
|
time: point.position / 96,
|
||||||
value: convertFaderValue(parameter, point.value, track, groupMixer),
|
value: convertFaderValue(parameter, point.value, track, groupMixer, target),
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -212,14 +236,14 @@ export function buildArrangementEnvelopes(
|
|||||||
if (parameter === FaderParam.TIM || parameter === FaderParam.VEL) {
|
if (parameter === FaderParam.TIM || parameter === FaderParam.VEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const points = getArrangementPoints(patterns, parameter, track, groupMixer);
|
|
||||||
if (points.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
targetList?.forEach((target) => {
|
targetList?.forEach((target) => {
|
||||||
if (target.arrangement === undefined) {
|
if (target.arrangement === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const points = getArrangementPoints(patterns, parameter, track, groupMixer, target);
|
||||||
|
if (points.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
envelopes.push({
|
envelopes.push({
|
||||||
'@Id': envelopes.length,
|
'@Id': envelopes.length,
|
||||||
EnvelopeTarget: { PointeeId: { '@Value': target.arrangement } },
|
EnvelopeTarget: { PointeeId: { '@Value': target.arrangement } },
|
||||||
@@ -246,14 +270,14 @@ export function buildClipEnvelopes(
|
|||||||
if (parameter === FaderParam.TIM || parameter === FaderParam.VEL) {
|
if (parameter === FaderParam.TIM || parameter === FaderParam.VEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const points = getClipPoints(pattern, parameter, track, groupMixer);
|
|
||||||
if (points.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
targetList?.forEach((target) => {
|
targetList?.forEach((target) => {
|
||||||
if (target.session === undefined) {
|
if (target.session === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const points = getClipPoints(pattern, parameter, track, groupMixer, target);
|
||||||
|
if (points.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
envelopes.push({
|
envelopes.push({
|
||||||
'@Id': envelopes.length,
|
'@Id': envelopes.length,
|
||||||
EnvelopeTarget: { PointeeId: { '@Value': target.session } },
|
EnvelopeTarget: { PointeeId: { '@Value': target.session } },
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function setLive10SampleReference(fileRef: any, sampleName: string) {
|
|||||||
fileRef.LivePackId = { '@Value': '' };
|
fileRef.LivePackId = { '@Value': '' };
|
||||||
}
|
}
|
||||||
|
|
||||||
function targetFor(parameter: any): { arrangement?: number; session?: number } {
|
function targetFor(parameter: any, maxValue?: number) {
|
||||||
const automationTarget =
|
const automationTarget =
|
||||||
parameter?.AutomationTarget?.['@Id'] === undefined
|
parameter?.AutomationTarget?.['@Id'] === undefined
|
||||||
? undefined
|
? undefined
|
||||||
@@ -112,6 +112,7 @@ function targetFor(parameter: any): { arrangement?: number; session?: number } {
|
|||||||
return {
|
return {
|
||||||
arrangement: automationTarget,
|
arrangement: automationTarget,
|
||||||
session: automationTarget,
|
session: automationTarget,
|
||||||
|
maxValue,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,10 +187,29 @@ 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;
|
||||||
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(targets, FaderParam.ATK, targetFor(simpler.VolumeAndPan.Envelope.AttackTime));
|
addTarget(
|
||||||
addTarget(targets, FaderParam.REL, targetFor(simpler.VolumeAndPan.Envelope.ReleaseTime));
|
targets,
|
||||||
|
FaderParam.ATK,
|
||||||
|
targetFor(
|
||||||
|
oneshot
|
||||||
|
? simpler.VolumeAndPan.OneShotEnvelope.FadeInTime
|
||||||
|
: simpler.VolumeAndPan.Envelope.AttackTime,
|
||||||
|
oneshot ? 2000 : undefined,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
addTarget(
|
||||||
|
targets,
|
||||||
|
FaderParam.REL,
|
||||||
|
targetFor(
|
||||||
|
oneshot
|
||||||
|
? simpler.VolumeAndPan.OneShotEnvelope.FadeOutTime
|
||||||
|
: simpler.VolumeAndPan.Envelope.ReleaseTime,
|
||||||
|
oneshot ? 2000 : undefined,
|
||||||
|
),
|
||||||
|
);
|
||||||
addTarget(targets, FaderParam.MOD, targetFor(simpler.Pitch.PitchLfoAmount));
|
addTarget(targets, FaderParam.MOD, targetFor(simpler.Pitch.PitchLfoAmount));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user