Implement fader policies and pad sound state extraction for Ableton export
This commit is contained in:
126
src/lib/exporters/ableton/faderPolicies.ts
Normal file
126
src/lib/exporters/ableton/faderPolicies.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { FaderParam } from '../../../types/types';
|
||||
import type { AblTrack } from '../../transformers/ableton';
|
||||
|
||||
export type FaderRenderStrategy = 'target' | 'note' | 'unsupported';
|
||||
export type FaderTargetScope = 'track' | 'pad';
|
||||
export type FaderSupportStatus = 'mapped' | 'research' | 'unsupported';
|
||||
|
||||
export type FaderConversionContext = {
|
||||
track: AblTrack;
|
||||
groupMixer?: boolean;
|
||||
valueOffset?: number;
|
||||
};
|
||||
|
||||
export type FaderPolicy = {
|
||||
defaultValue: number;
|
||||
renderStrategy: FaderRenderStrategy;
|
||||
targetScope: FaderTargetScope;
|
||||
status: FaderSupportStatus;
|
||||
toLive: (value: number, context: FaderConversionContext) => number;
|
||||
};
|
||||
|
||||
const identity = (value: number) => value;
|
||||
|
||||
export const FADER_POLICIES = {
|
||||
[FaderParam.LVL]: {
|
||||
defaultValue: 1,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'track',
|
||||
status: 'mapped',
|
||||
toLive: (value, context) =>
|
||||
context.groupMixer ? value : context.track.volume * value,
|
||||
},
|
||||
[FaderParam.PTC]: {
|
||||
defaultValue: 0.5,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'pad',
|
||||
status: 'research',
|
||||
toLive: (value, context) => (context.valueOffset ?? 0) + (value - 0.5) * 10,
|
||||
},
|
||||
[FaderParam.TIM]: {
|
||||
defaultValue: 0.5,
|
||||
renderStrategy: 'unsupported',
|
||||
targetScope: 'pad',
|
||||
status: 'unsupported',
|
||||
toLive: identity,
|
||||
},
|
||||
[FaderParam.LPF]: {
|
||||
defaultValue: 1,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'track',
|
||||
status: 'research',
|
||||
toLive: (value) => 20 + value * 0.9 * 115,
|
||||
},
|
||||
[FaderParam.HPF]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'track',
|
||||
status: 'research',
|
||||
toLive: (value) => 20 + value * 0.7 * 115,
|
||||
},
|
||||
[FaderParam.FX]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'track',
|
||||
status: 'mapped',
|
||||
toLive: identity,
|
||||
},
|
||||
[FaderParam.ATK]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'unsupported',
|
||||
targetScope: 'pad',
|
||||
status: 'research',
|
||||
toLive: identity,
|
||||
},
|
||||
[FaderParam.REL]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'unsupported',
|
||||
targetScope: 'pad',
|
||||
status: 'research',
|
||||
toLive: identity,
|
||||
},
|
||||
[FaderParam.PAN]: {
|
||||
defaultValue: 0.5,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'track',
|
||||
status: 'mapped',
|
||||
toLive: (value) => (value - 0.5) * 2,
|
||||
},
|
||||
[FaderParam.TUNE]: {
|
||||
defaultValue: 0.5,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'pad',
|
||||
status: 'research',
|
||||
toLive: (value) => Math.max(-0.5, Math.min(0.5, value - 0.5)) * 100,
|
||||
},
|
||||
[FaderParam.VEL]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'note',
|
||||
targetScope: 'track',
|
||||
status: 'research',
|
||||
toLive: identity,
|
||||
},
|
||||
[FaderParam.MOD]: {
|
||||
defaultValue: 0,
|
||||
renderStrategy: 'target',
|
||||
targetScope: 'pad',
|
||||
status: 'research',
|
||||
toLive: (value) => value / 20,
|
||||
},
|
||||
} satisfies Record<FaderParam, FaderPolicy>;
|
||||
|
||||
export function getFaderPolicy(parameter: FaderParam) {
|
||||
return FADER_POLICIES[parameter];
|
||||
}
|
||||
|
||||
export function getFaderDefaultValue(parameter: FaderParam) {
|
||||
return getFaderPolicy(parameter).defaultValue;
|
||||
}
|
||||
|
||||
export function convertGroupFaderValue(
|
||||
parameter: FaderParam,
|
||||
value: number,
|
||||
context: FaderConversionContext,
|
||||
) {
|
||||
return getFaderPolicy(parameter).toLive(value, context);
|
||||
}
|
||||
62
src/lib/exporters/ableton/padSound.ts
Normal file
62
src/lib/exporters/ableton/padSound.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { AblTrack } from '../../transformers/ableton';
|
||||
import { koEnvRangeToSeconds } from './utils';
|
||||
|
||||
export type PadSoundState = {
|
||||
amp: number;
|
||||
pitch: number;
|
||||
playMode: AblTrack['playMode'];
|
||||
pan: number;
|
||||
trimStart: number;
|
||||
trimEnd: number;
|
||||
envelope: {
|
||||
attack: number;
|
||||
release: number;
|
||||
};
|
||||
time: {
|
||||
mode: AblTrack['timeStretch'];
|
||||
bpm: number;
|
||||
bars: number;
|
||||
};
|
||||
rootNote: number;
|
||||
soundLength: number;
|
||||
};
|
||||
|
||||
export type PadEnvelopeState = {
|
||||
classicAttackMs: number;
|
||||
classicReleaseMs: number;
|
||||
oneShotFadeInMs: number;
|
||||
oneShotFadeOutMs: number;
|
||||
};
|
||||
|
||||
export function getPadSoundState(track: AblTrack): PadSoundState {
|
||||
return {
|
||||
amp: track.volume,
|
||||
pitch: track.pitch,
|
||||
playMode: track.playMode,
|
||||
pan: track.pan,
|
||||
trimStart: track.trimLeft,
|
||||
trimEnd: track.trimRight,
|
||||
envelope: {
|
||||
attack: track.attack,
|
||||
release: track.release,
|
||||
},
|
||||
time: {
|
||||
mode: track.timeStretch,
|
||||
bpm: track.timeStretchBpm,
|
||||
bars: track.timeStretchBars,
|
||||
},
|
||||
rootNote: track.rootNote,
|
||||
soundLength: track.soundLength,
|
||||
};
|
||||
}
|
||||
|
||||
export function getPadEnvelopeState(sound: PadSoundState): PadEnvelopeState {
|
||||
return {
|
||||
classicAttackMs:
|
||||
koEnvRangeToSeconds(sound.envelope.attack, sound.soundLength) * 1000,
|
||||
classicReleaseMs:
|
||||
koEnvRangeToSeconds(sound.envelope.release, sound.soundLength) * 1000,
|
||||
oneShotFadeInMs: 0,
|
||||
oneShotFadeOutMs: 5,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user