Update Simpler device building to use PadSound state and parameters

This commit is contained in:
2026-07-14 11:26:01 +02:00
parent 8b377204c0
commit e8f75c6b39

View File

@@ -343,10 +343,16 @@ async function buildMidiClip(
return midiClip;
}
async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVersion) {
async function buildSimplerDevice(
koTrack: AblTrack,
abletonVersion: AbletonVersion,
automatedParameters: ReadonlySet<FaderParam> = new Set(),
) {
const simplerTemplate = await loadTemplate<ALSSimpler>('simpler', abletonVersion);
const device = structuredClone(simplerTemplate.OriginalSimpler);
const playbackState = getSimplerPlaybackState(koTrack.playMode);
const padSound = getPadSoundState(koTrack);
const padEnvelope = getPadEnvelopeState(padSound);
const playbackState = getSimplerPlaybackState(padSound.playMode);
const samplePart = device.Player.MultiSampleMap.SampleParts.MultiSamplePart;
const loopModulators = device.Player.LoopModulators;
const simplerFilter = device.Filter.Slot.Value.SimplerFilter;
@@ -367,11 +373,13 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
simplerFilter.ModByLfo.Manual['@Value'] = 0;
device.Lfo.IsOn.Manual['@Value'] = 'false';
device.Pitch.PitchLfoAmount.Manual['@Value'] = 0;
device.VolumeAndPan.OneShotEnvelope.FadeInTime.Manual['@Value'] = SIMPLER_FADE_IN_MS;
device.VolumeAndPan.OneShotEnvelope.FadeOutTime.Manual['@Value'] = SIMPLER_FADE_OUT_MS;
device.VolumeAndPan.OneShotEnvelope.FadeInTime.Manual['@Value'] =
padEnvelope.oneShotFadeInMs;
device.VolumeAndPan.OneShotEnvelope.FadeOutTime.Manual['@Value'] =
padEnvelope.oneShotFadeOutMs;
device.VolumeAndPan.OneShotEnvelope.SustainMode.Manual['@Value'] = 0;
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] = SIMPLER_ATTACK_MS;
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] = SIMPLER_RELEASE_MS;
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] = padEnvelope.classicAttackMs;
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] = padEnvelope.classicReleaseMs;
device.Globals.PlaybackMode['@Value'] = playbackState.playbackMode;
device.Globals.NumVoices['@Value'] = playbackState.voices;
device.Globals.RetriggerMode['@Value'] = playbackState.retrigger;
@@ -389,17 +397,17 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
sampleRef.LastModDate['@Value'] = 0;
}
if (sampleRef.DefaultDuration) {
sampleRef.DefaultDuration['@Value'] = Math.round(koTrack.soundLength * koTrack.sampleRate);
sampleRef.DefaultDuration['@Value'] = Math.round(padSound.soundLength * koTrack.sampleRate);
}
if (sampleRef.DefaultSampleRate) {
sampleRef.DefaultSampleRate['@Value'] = koTrack.sampleRate;
}
samplePart.Name['@Value'] = koTrack.name;
samplePart.RootKey['@Value'] = koTrack.rootNote;
samplePart.SampleStart['@Value'] = koTrack.trimLeft;
samplePart.SampleEnd['@Value'] = koTrack.trimRight;
samplePart.SustainLoop.Start['@Value'] = koTrack.trimLeft;
samplePart.SustainLoop.End['@Value'] = koTrack.trimRight;
samplePart.RootKey['@Value'] = padSound.rootNote;
samplePart.SampleStart['@Value'] = padSound.trimStart;
samplePart.SampleEnd['@Value'] = padSound.trimEnd;
samplePart.SustainLoop.Start['@Value'] = padSound.trimStart;
samplePart.SustainLoop.End['@Value'] = padSound.trimEnd;
samplePart.SustainLoop.Mode['@Value'] = 0;
samplePart.SustainLoop.Crossfade['@Value'] = 0;
samplePart.ReleaseLoop.Start['@Value'] = 0;
@@ -414,41 +422,54 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
// setting velocity scaling to 20% to match KO behavior
device.VolumeAndPan.VolumeVelScale.Manual['@Value'] = 0.2;
device.VolumeAndPan.Panorama.Manual['@Value'] = koTrack.pan;
device.VolumeAndPan.Panorama.Manual['@Value'] = padSound.pan;
let pitch = koTrack.pitch || 0;
let pitch = padSound.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);
pitch += Math.round(
convertGroupFaderValue(FaderParam.PTC, koTrack.faderParams[FaderParam.PTC], {
track: koTrack,
}),
);
}
if (koTrack.faderParams[FaderParam.TUNE] !== -1) {
device.Pitch.TransposeFine.Manual['@Value'] =
Math.max(-0.5, Math.min(0.5, koTrack.faderParams[FaderParam.TUNE] - 0.5)) * 100;
device.Pitch.TransposeFine.Manual['@Value'] = convertGroupFaderValue(
FaderParam.TUNE,
koTrack.faderParams[FaderParam.TUNE],
{ track: koTrack },
);
}
device.Pitch.TransposeKey.Manual['@Value'] = pitch;
// adding vibrato from MOD fader param if defined
if (koTrack.faderParams[FaderParam.MOD] !== -1) {
if (
koTrack.faderParams[FaderParam.MOD] !== -1 ||
automatedParameters.has(FaderParam.MOD)
) {
device.Lfo.IsOn.Manual['@Value'] = 'true';
if (!device.Lfo.Slot.Value.SimplerLfo && abletonVersion === '10') {
const simplerLfoTemplate = await loadTemplate<any>('simplerLfo', abletonVersion);
device.Lfo.Slot.Value.SimplerLfo = structuredClone(simplerLfoTemplate.SimplerLfo);
}
// 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.Pitch.PitchLfoAmount.Manual['@Value'] = convertGroupFaderValue(
FaderParam.MOD,
Math.max(0, koTrack.faderParams[FaderParam.MOD]),
{ track: koTrack },
);
device.Lfo.Slot.Value.SimplerLfo.RateType.Manual['@Value'] = 1; // tempo synced 1/16
}
// TODO: add bandpass filter
if (koTrack.timeStretch === 'bars') {
if (padSound.time.mode === 'bars') {
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
'@Value'
] = 'true';
@@ -464,18 +485,18 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
},
{
'@Id': 1,
'@SecTime': koTrack.soundLength,
'@SecTime': padSound.soundLength,
'@BeatTime':
koTrack.timeStretchBars *
padSound.time.bars *
koTrack.timeSignature.numerator *
(4 / koTrack.timeSignature.denominator), // convert bars to beats
},
];
}
if (koTrack.timeStretch === 'bpm') {
if (padSound.time.mode === 'bpm') {
// calculating beat length using koTrack.timeStretchBpm
const beatTime = koTrack.soundLength / (60 / koTrack.timeStretchBpm);
const beatTime = padSound.soundLength / (60 / padSound.time.bpm);
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
'@Value'
@@ -492,13 +513,13 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
},
{
'@Id': 1,
'@SecTime': koTrack.soundLength,
'@SecTime': padSound.soundLength,
'@BeatTime': beatTime,
},
];
}
if (koTrack.timeStretch === 'rev') {
if (padSound.time.mode === 'rev') {
device.Player.Reverse.Manual['@Value'] = 'true';
}
@@ -510,6 +531,7 @@ async function buildSimplerDevice(koTrack: AblTrack, abletonVersion: AbletonVers
async function buildFaderFilterDevice(
parameter: FaderParam.LPF | FaderParam.HPF,
value: number,
track: AblTrack,
abletonVersion: AbletonVersion,
) {
const filterTemplate = await loadTemplate<ALSFilter>('effectFilter', abletonVersion);
@@ -519,7 +541,7 @@ async function buildFaderFilterDevice(
filter.FilterType.Manual['@Value'] = highPass ? 1 : 0;
filter.LegacyFilterType.Manual['@Value'] = highPass ? 1 : 0;
filter.Slope.Manual['@Value'] = highPass ? 'false' : 'true';
filter.Cutoff.Manual['@Value'] = highPass ? 20 + value * 0.7 * 115 : 20 + value * 0.9 * 115;
filter.Cutoff.Manual['@Value'] = convertGroupFaderValue(parameter, value, { track });
return { AutoFilter: filter };
}