simplify more

This commit is contained in:
Andras Schmelczer 2026-05-20 21:37:30 +01:00
commit 2fe3c69963
40 changed files with 690 additions and 873 deletions

View file

@ -46,20 +46,6 @@ const degreeToSemitone = (profile: GardenAudioVibeProfile, degree: number): numb
type GardenAudioStyleIndex = 0 | 1 | 2;
interface RenderLookaheadRequest {
vibe: VibePreset;
now: number;
activity: number;
lookaheadSeconds?: number;
}
interface StrokeAccentRequest {
vibe: VibePreset;
now: number;
activity: number;
maniaAmount?: number;
}
interface TouchDownRequest {
vibe: VibePreset;
now: number;
@ -118,10 +104,6 @@ export class GenerativePianoEngine {
private readonly playNote: (note: PianoNote) => void
) {}
private get generation(): typeof generativePianoTuning {
return generativePianoTuning;
}
public prime(now: number, profile: GardenAudioVibeProfile): void {
this.activeProfile = profile;
this.timelineStartedAt ??= now;
@ -162,7 +144,7 @@ export class GenerativePianoEngine {
this.brushPhraseLayers = [];
this.brushStreamNoteCountsByBar.clear();
return releaseStart + this.generation.releaseResolution.fadeAfterSeconds;
return releaseStart + generativePianoTuning.releaseResolution.fadeAfterSeconds;
}
private recordTouchDown({
@ -198,7 +180,12 @@ export class GenerativePianoEngine {
now,
activity,
maniaAmount = 0,
}: StrokeAccentRequest): void {
}: {
vibe: VibePreset;
now: number;
activity: number;
maniaAmount?: number;
}): void {
const profile = getVibeProfile(vibe);
this.prime(now, profile);
const strength = clamp01(activity);
@ -206,12 +193,12 @@ export class GenerativePianoEngine {
const styleIndex = this.getStyleIndex(now);
const accentStep = this.getNextStepIndexAt(
now,
this.generation.gestureAccent.quantizeStepLookahead
generativePianoTuning.gestureAccent.quantizeStepLookahead
);
if (
this.isWaitingForGestureAccent &&
now - this.lastGestureAccentAt >= this.generation.gestureAccentMinIntervalSeconds
now - this.lastGestureAccentAt >= generativePianoTuning.gestureAccentMinIntervalSeconds
) {
this.recordTouchDown({
vibe,
@ -230,8 +217,8 @@ export class GenerativePianoEngine {
maniaAmount: normalizedManiaAmount,
});
if (
strength >= this.generation.strokeAccentThreshold &&
accentStep - this.lastStrokeAccentStep >= this.generation.strokeAccentMinSteps
strength >= generativePianoTuning.strokeAccentThreshold &&
accentStep - this.lastStrokeAccentStep >= generativePianoTuning.strokeAccentMinSteps
) {
this.lastStrokeAccentStep = accentStep;
this.playGestureAccent(vibe, accentStep, styleIndex, strength);
@ -243,7 +230,12 @@ export class GenerativePianoEngine {
now,
activity,
lookaheadSeconds = GENERATIVE_LOOKAHEAD_SECONDS,
}: RenderLookaheadRequest): void {
}: {
vibe: VibePreset;
now: number;
activity: number;
lookaheadSeconds?: number;
}): void {
const profile = getVibeProfile(vibe);
this.prime(now, profile);
this.skipLateBeats(now);
@ -253,13 +245,14 @@ export class GenerativePianoEngine {
}
const lookaheadEnd = now + lookaheadSeconds;
const expression = this.getExpression(activity);
while (this.getTimeForStep(this.nextBeatStep) <= lookaheadEnd) {
const beatIndex = this.getBeatIndexForStep(this.nextBeatStep);
this.renderBeat({
profile,
beatIndex,
startTime: this.getTimeForStep(this.nextBeatStep),
expression: this.getExpression(activity),
expression,
});
this.nextBeatStep += this.config.rhythm.stepsPerBeat;
}
@ -267,7 +260,7 @@ export class GenerativePianoEngine {
vibe,
now,
lookaheadEnd,
activity,
activity: expression,
});
}
@ -276,7 +269,7 @@ export class GenerativePianoEngine {
const chord = this.getChord(profile, 0);
const intervals = getChordIntervals(chord, true);
const rootMidi = profile.rootMidi + chord.rootOffset;
const stinger = this.generation.vibeChangeStinger;
const stinger = generativePianoTuning.vibeChangeStinger;
const offsetsByVoice: ReadonlyArray<ReadonlyArray<number>> = [
[0],
[intervals[1], intervals[2]],
@ -286,7 +279,7 @@ export class GenerativePianoEngine {
offsetsByVoice.forEach((offsets, index) => {
const midi = this.chooseMidi(
{ baseMidi: rootMidi, offsets },
this.generation.padRegisters[index]
generativePianoTuning.padRegisters[index]
);
this.playProfileNote(profile, {
midi,
@ -340,7 +333,7 @@ export class GenerativePianoEngine {
const barIndex = Math.floor(beatIndex / beatsPerBar);
const styleIndex = this.getStyleIndex(startTime);
if (beatInBar === 0 && barIndex % this.generation.chordBars === 0) {
if (beatInBar === 0 && barIndex % generativePianoTuning.chordBars === 0) {
this.playPadChord(profile, barIndex, startTime, expression);
}
@ -349,21 +342,21 @@ export class GenerativePianoEngine {
}
if (
beatInBar === this.generation.textureBeat &&
beatInBar === generativePianoTuning.textureBeat &&
this.shouldPlayTexture(expression, barIndex)
) {
this.playTextureNote(profile, barIndex, startTime, expression, styleIndex);
}
if (
beatInBar === this.generation.highActivityExtraBeat &&
expression >= this.generation.highActivityExtraThreshold
beatInBar === generativePianoTuning.highActivityExtraBeat &&
expression >= generativePianoTuning.highActivityExtraThreshold
) {
this.playTextureNote(
profile,
barIndex + this.generation.highActivityExtra.barOffset,
barIndex + generativePianoTuning.highActivityExtra.barOffset,
startTime,
expression * this.generation.highActivityExtra.expressionMultiplier,
expression * generativePianoTuning.highActivityExtra.expressionMultiplier,
styleIndex
);
}
@ -380,23 +373,23 @@ export class GenerativePianoEngine {
const rootMidi = profile.rootMidi + chord.rootOffset;
const durationSeconds =
this.getBarDurationSeconds() *
this.generation.chordBars *
this.generation.padDurationBarScale;
generativePianoTuning.chordBars *
generativePianoTuning.padDurationBarScale;
const notes = [
{
source: { baseMidi: rootMidi, offsets: [0] },
register: this.generation.padRegisters[0],
velocity: this.generation.padChord.velocities[0],
register: generativePianoTuning.padRegisters[0],
velocity: generativePianoTuning.padChord.velocities[0],
},
{
source: { baseMidi: rootMidi, offsets: [intervals[1]] },
register: this.generation.padRegisters[1],
velocity: this.generation.padChord.velocities[1],
register: generativePianoTuning.padRegisters[1],
velocity: generativePianoTuning.padChord.velocities[1],
},
{
source: { baseMidi: rootMidi, offsets: [intervals[3], intervals[2]] },
register: this.generation.padRegisters[2],
velocity: this.generation.padChord.velocities[2],
register: generativePianoTuning.padRegisters[2],
velocity: generativePianoTuning.padChord.velocities[2],
},
];
@ -411,16 +404,16 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
velocity + expression * this.generation.padChord.expressionVelocityWeight,
velocity + expression * generativePianoTuning.padChord.expressionVelocityWeight,
startTime,
durationSeconds,
pan: register.pan,
role: 'pad',
delaySend: this.generation.padChord.delaySend,
delaySend: generativePianoTuning.padChord.delaySend,
lowpassHz: this.getLowpassHz(
profile,
midi,
expression * this.generation.padChord.lowpassExpressionWeight
expression * generativePianoTuning.padChord.lowpassExpressionWeight
),
});
});
@ -434,7 +427,7 @@ export class GenerativePianoEngine {
const chord = this.getChord(profile, this.getBarIndexForStep(stepIndex));
const intervals = getChordIntervals(chord, true);
const rootMidi = profile.rootMidi + chord.rootOffset;
const release = this.generation.releaseResolution;
const release = generativePianoTuning.releaseResolution;
const offsetsByVoice: ReadonlyArray<ReadonlyArray<number>> = [
[0],
[intervals[1], intervals[2]],
@ -442,7 +435,7 @@ export class GenerativePianoEngine {
];
offsetsByVoice.forEach((offsets, index) => {
const register = this.generation.padRegisters[index];
const register = generativePianoTuning.padRegisters[index];
const midi = this.chooseMidi(
{ baseMidi: rootMidi, offsets },
register,
@ -469,7 +462,7 @@ export class GenerativePianoEngine {
expression: number,
styleIndex: GardenAudioStyleIndex
): void {
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const chord = this.getChord(profile, barIndex);
const chordIntervals = getChordIntervals(chord, false);
const rootMidi = profile.rootMidi + chord.rootOffset;
@ -488,22 +481,22 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.supportNote.velocityBase +
expression * this.generation.supportNote.velocityExpressionWeight) *
(generativePianoTuning.supportNote.velocityBase +
expression * generativePianoTuning.supportNote.velocityExpressionWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime,
durationSeconds:
this.generation.supportNote.durationBaseSeconds +
expression * this.generation.supportNote.durationExpressionSeconds,
generativePianoTuning.supportNote.durationBaseSeconds +
expression * generativePianoTuning.supportNote.durationExpressionSeconds,
pan: this.getStylePan(styleIndex),
role: 'support',
delaySend:
this.generation.supportNote.delaySendBase +
expression * this.generation.supportNote.delaySendExpressionWeight,
generativePianoTuning.supportNote.delaySendBase +
expression * generativePianoTuning.supportNote.delaySendExpressionWeight,
lowpassHz: this.getLowpassHz(
profile,
midi,
expression * this.generation.supportNote.lowpassExpressionWeight
expression * generativePianoTuning.supportNote.lowpassExpressionWeight
),
});
}
@ -515,7 +508,7 @@ export class GenerativePianoEngine {
expression: number,
styleIndex: GardenAudioStyleIndex
): void {
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const chord = this.getChord(profile, barIndex);
const chordIntervals = getChordIntervals(chord, false);
const degrees = this.rotate(pool.scaleDegrees, barIndex + styleIndex);
@ -534,18 +527,18 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.textureNote.velocityBase +
expression * this.generation.textureNote.velocityExpressionWeight) *
(generativePianoTuning.textureNote.velocityBase +
expression * generativePianoTuning.textureNote.velocityExpressionWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime,
durationSeconds:
this.generation.textureNote.durationBaseSeconds +
expression * this.generation.textureNote.durationExpressionSeconds,
generativePianoTuning.textureNote.durationBaseSeconds +
expression * generativePianoTuning.textureNote.durationExpressionSeconds,
pan: this.getStylePan(styleIndex),
role: 'texture',
delaySend:
this.generation.textureNote.delaySendBase +
expression * this.generation.textureNote.delaySendExpressionWeight,
generativePianoTuning.textureNote.delaySendBase +
expression * generativePianoTuning.textureNote.delaySendExpressionWeight,
lowpassHz: this.getLowpassHz(profile, midi, expression),
});
}
@ -557,13 +550,13 @@ export class GenerativePianoEngine {
strength: number
): void {
const profile = getVibeProfile(vibe);
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const startTime = this.getTimeForStep(stepIndex);
const chord = this.getChord(profile, this.getBarIndexForStep(stepIndex));
const chordIntervals = getChordIntervals(chord, false);
const degrees = this.rotate(
pool.scaleDegrees,
Math.round(strength * this.generation.gestureAccent.rotationStrengthMultiplier)
Math.round(strength * generativePianoTuning.gestureAccent.rotationStrengthMultiplier)
);
const midi = this.chooseMidi(
@ -581,16 +574,16 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.gestureAccent.velocityBase +
strength * this.generation.gestureAccent.velocityStrengthWeight) *
(generativePianoTuning.gestureAccent.velocityBase +
strength * generativePianoTuning.gestureAccent.velocityStrengthWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime,
durationSeconds:
this.generation.gestureAccent.durationBaseSeconds +
strength * this.generation.gestureAccent.durationStrengthSeconds,
generativePianoTuning.gestureAccent.durationBaseSeconds +
strength * generativePianoTuning.gestureAccent.durationStrengthSeconds,
pan: this.getStylePan(styleIndex),
role: 'gesture',
delaySend: this.generation.gestureAccent.delaySend,
delaySend: generativePianoTuning.gestureAccent.delaySend,
lowpassHz: this.getLowpassHz(profile, midi, strength),
});
}
@ -607,7 +600,7 @@ export class GenerativePianoEngine {
strength: number;
}): void {
const profile = getVibeProfile(vibe);
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const chord = this.getChord(profile, this.getGlobalBarIndex(now));
const chordIntervals = getChordIntervals(chord, false);
const rootMidi = profile.rootMidi + chord.rootOffset;
@ -626,22 +619,22 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.touchNote.velocityBase +
strength * this.generation.touchNote.velocityStrengthWeight) *
(generativePianoTuning.touchNote.velocityBase +
strength * generativePianoTuning.touchNote.velocityStrengthWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime: now,
durationSeconds:
this.generation.touchNote.durationBaseSeconds +
strength * this.generation.touchNote.durationStrengthSeconds,
generativePianoTuning.touchNote.durationBaseSeconds +
strength * generativePianoTuning.touchNote.durationStrengthSeconds,
pan: this.getStylePan(styleIndex),
role: 'gesture',
delaySend: this.generation.touchNote.delaySend,
delaySend: generativePianoTuning.touchNote.delaySend,
lowpassHz: this.getLowpassHz(
profile,
midi,
clamp01(
this.generation.touchNote.lowpassBaseExpression +
strength * this.generation.touchNote.lowpassStrengthWeight
generativePianoTuning.touchNote.lowpassBaseExpression +
strength * generativePianoTuning.touchNote.lowpassStrengthWeight
)
),
});
@ -661,8 +654,8 @@ export class GenerativePianoEngine {
maniaAmount: number;
}): void {
const lifetimeSeconds =
this.generation.brushLayerBaseSeconds +
strength * this.generation.brushLayerEnergySeconds;
generativePianoTuning.brushLayerBaseSeconds +
strength * generativePianoTuning.brushLayerEnergySeconds;
const expiresAt = this.getNextBarTimeAt(now + lifetimeSeconds);
this.brushPhraseLayers.push({
@ -672,13 +665,13 @@ export class GenerativePianoEngine {
expiresAt,
styleIndex,
energy: strength,
motifOffsets: [styleIndex + this.generation.brushPhrase.initialMotifOffset],
motifOffsets: [styleIndex + generativePianoTuning.brushPhrase.initialMotifOffset],
maniaAmount,
});
if (this.brushPhraseLayers.length > this.generation.maxBrushPhraseLayers) {
if (this.brushPhraseLayers.length > generativePianoTuning.maxBrushPhraseLayers) {
this.brushPhraseLayers = this.brushPhraseLayers.slice(
-this.generation.maxBrushPhraseLayers
-generativePianoTuning.maxBrushPhraseLayers
);
}
}
@ -704,17 +697,17 @@ export class GenerativePianoEngine {
layer.styleIndex = styleIndex;
layer.energy = Math.max(
layer.energy *
Math.exp(-elapsedSeconds / this.generation.brushPhrase.energyDecaySeconds),
Math.exp(-elapsedSeconds / generativePianoTuning.brushPhrase.energyDecaySeconds),
strength
);
layer.maniaAmount = Math.max(
layer.maniaAmount *
Math.exp(-elapsedSeconds / this.generation.brushPhrase.maniaDecaySeconds),
Math.exp(-elapsedSeconds / generativePianoTuning.brushPhrase.maniaDecaySeconds),
maniaAmount
);
layer.motifOffsets.push(this.getMotifOffset(strength));
if (layer.motifOffsets.length > this.generation.brushMotifMaxSteps) {
layer.motifOffsets = layer.motifOffsets.slice(-this.generation.brushMotifMaxSteps);
if (layer.motifOffsets.length > generativePianoTuning.brushMotifMaxSteps) {
layer.motifOffsets = layer.motifOffsets.slice(-generativePianoTuning.brushMotifMaxSteps);
}
}
@ -750,7 +743,7 @@ export class GenerativePianoEngine {
const startTime = this.getTimeForStep(this.nextBrushStreamStep);
const frame = this.getBrushStreamFrame(startTime, activity);
if (
frame.intensity >= this.generation.brushLayerMinIntensity &&
frame.intensity >= generativePianoTuning.brushLayerMinIntensity &&
this.reserveBrushStreamNote(this.nextBrushStreamStep)
) {
this.playBrushStreamNote({
@ -783,22 +776,22 @@ export class GenerativePianoEngine {
layer: BrushPhraseLayer | null;
}): void {
const profile = getVibeProfile(vibe);
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const maniaAmount =
layer?.maniaAmount ??
clamp01(
(intensity - this.generation.brushStream.inferredManiaThreshold) /
this.generation.brushStream.inferredManiaRange
(intensity - generativePianoTuning.brushStream.inferredManiaThreshold) /
generativePianoTuning.brushStream.inferredManiaRange
);
const register = this.getBiasedRegister(
pool,
maniaAmount * this.generation.brushStream.registerManiaShift
maniaAmount * generativePianoTuning.brushStream.registerManiaShift
);
const chord = this.getChord(profile, this.getBarIndexForStep(stepIndex));
const chordIntervals = getChordIntervals(chord, false);
const rootMidi = profile.rootMidi + chord.rootOffset;
const useChordTone =
this.brushStreamNoteIndex % this.generation.brushStream.chordToneEverySteps === 0;
this.brushStreamNoteIndex % generativePianoTuning.brushStream.chordToneEverySteps === 0;
const source = useChordTone
? {
baseMidi: rootMidi,
@ -817,18 +810,18 @@ export class GenerativePianoEngine {
const midi = this.chooseMidi(source, register, this.lastBrushStreamMidi, true);
const pan = this.getStylePan(styleIndex);
const durationSeconds = clamp(
this.generation.brushStream.durationBaseSeconds +
intensity * this.generation.brushStream.durationIntensitySeconds -
maniaAmount * this.generation.brushStream.durationManiaSeconds,
this.generation.brushStream.durationMinSeconds,
this.generation.brushStream.durationMaxSeconds
generativePianoTuning.brushStream.durationBaseSeconds +
intensity * generativePianoTuning.brushStream.durationIntensitySeconds -
maniaAmount * generativePianoTuning.brushStream.durationManiaSeconds,
generativePianoTuning.brushStream.durationMinSeconds,
generativePianoTuning.brushStream.durationMaxSeconds
);
const delaySend = clamp(
this.generation.brushStream.delaySendBase +
intensity * this.generation.brushStream.delaySendIntensityWeight -
maniaAmount * this.generation.brushStream.delaySendManiaWeight,
this.generation.brushStream.delaySendMin,
this.generation.brushStream.delaySendMax
generativePianoTuning.brushStream.delaySendBase +
intensity * generativePianoTuning.brushStream.delaySendIntensityWeight -
maniaAmount * generativePianoTuning.brushStream.delaySendManiaWeight,
generativePianoTuning.brushStream.delaySendMin,
generativePianoTuning.brushStream.delaySendMax
);
this.lastBrushStreamMidi = midi;
@ -836,8 +829,8 @@ export class GenerativePianoEngine {
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.brushStream.velocityBase +
intensity * this.generation.brushStream.velocityIntensityWeight) *
(generativePianoTuning.brushStream.velocityBase +
intensity * generativePianoTuning.brushStream.velocityIntensityWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime,
durationSeconds,
@ -848,46 +841,46 @@ export class GenerativePianoEngine {
profile,
midi,
clamp01(
this.generation.brushStream.lowpassBaseExpression +
intensity * this.generation.brushStream.lowpassIntensityWeight +
maniaAmount * this.generation.brushStream.lowpassManiaWeight
generativePianoTuning.brushStream.lowpassBaseExpression +
intensity * generativePianoTuning.brushStream.lowpassIntensityWeight +
maniaAmount * generativePianoTuning.brushStream.lowpassManiaWeight
)
),
});
if (
maniaAmount >= this.generation.brushStreamEcho.maniaThreshold &&
(this.brushStreamNoteIndex % this.generation.brushStreamEcho.stepModulo ===
this.generation.brushStreamEcho.stepRemainder ||
intensity >= this.generation.brushStreamEcho.intensityThreshold)
maniaAmount >= generativePianoTuning.brushStreamEcho.maniaThreshold &&
(this.brushStreamNoteIndex % generativePianoTuning.brushStreamEcho.stepModulo ===
generativePianoTuning.brushStreamEcho.stepRemainder ||
intensity >= generativePianoTuning.brushStreamEcho.intensityThreshold)
) {
const echoMidi =
midi + this.generation.brushStreamEcho.octaveSemitones <=
this.generation.brushStreamEcho.maxMidi
? midi + this.generation.brushStreamEcho.octaveSemitones
: midi - this.generation.brushStreamEcho.octaveSemitones;
midi + generativePianoTuning.brushStreamEcho.octaveSemitones <=
generativePianoTuning.brushStreamEcho.maxMidi
? midi + generativePianoTuning.brushStreamEcho.octaveSemitones
: midi - generativePianoTuning.brushStreamEcho.octaveSemitones;
this.playProfileNote(profile, {
midi: echoMidi,
velocity:
(this.generation.brushStreamEcho.velocityBase +
intensity * this.generation.brushStreamEcho.velocityIntensityWeight) *
(generativePianoTuning.brushStreamEcho.velocityBase +
intensity * generativePianoTuning.brushStreamEcho.velocityIntensityWeight) *
styleVoices[styleIndex].velocityMultiplier,
startTime: startTime + this.generation.brushMotifCanonDelaySeconds,
startTime: startTime + generativePianoTuning.brushMotifCanonDelaySeconds,
durationSeconds: Math.max(
this.generation.brushStreamEcho.durationMinSeconds,
durationSeconds * this.generation.brushStreamEcho.durationScale
generativePianoTuning.brushStreamEcho.durationMinSeconds,
durationSeconds * generativePianoTuning.brushStreamEcho.durationScale
),
pan: clamp(pan * this.generation.brushStreamEcho.panScale, -1, 1),
pan: clamp(pan * generativePianoTuning.brushStreamEcho.panScale, -1, 1),
role: 'brush',
delaySend: Math.max(
this.generation.brushStreamEcho.delaySendMin,
delaySend * this.generation.brushStreamEcho.delaySendScale
generativePianoTuning.brushStreamEcho.delaySendMin,
delaySend * generativePianoTuning.brushStreamEcho.delaySendScale
),
lowpassHz: this.getLowpassHz(
profile,
echoMidi,
this.generation.brushStreamEcho.lowpassBaseExpression +
maniaAmount * this.generation.brushStreamEcho.lowpassManiaWeight
generativePianoTuning.brushStreamEcho.lowpassBaseExpression +
maniaAmount * generativePianoTuning.brushStreamEcho.lowpassManiaWeight
),
});
}
@ -905,8 +898,8 @@ export class GenerativePianoEngine {
intensity:
layer.energy *
this.getBrushPhraseFade(layer, startTime) *
(this.generation.brushPhrase.layerIntensityBase +
layer.maniaAmount * this.generation.brushPhrase.layerIntensityManiaWeight),
(generativePianoTuning.brushPhrase.layerIntensityBase +
layer.maniaAmount * generativePianoTuning.brushPhrase.layerIntensityManiaWeight),
}));
const dominant = layerStates.reduce<{
layer: BrushPhraseLayer;
@ -924,10 +917,10 @@ export class GenerativePianoEngine {
return {
intensity: clamp01(
activity * this.generation.brushPhrase.frameActivityWeight +
activity * generativePianoTuning.brushPhrase.frameActivityWeight +
layeredIntensity +
(dominant?.layer.maniaAmount ?? 0) *
this.generation.brushPhrase.frameManiaWeight
generativePianoTuning.brushPhrase.frameManiaWeight
),
layer: dominant?.layer ?? null,
};
@ -935,11 +928,11 @@ export class GenerativePianoEngine {
private getBrushStreamIntervalSteps(intensity: number): number {
const intervalBeats =
intensity >= this.generation.brushStream.intenseThreshold
? this.generation.brushStreamIntenseIntervalBeats
: intensity >= this.generation.brushStream.activeThreshold
? this.generation.brushStreamActiveIntervalBeats
: this.generation.brushStreamIdleIntervalBeats;
intensity >= generativePianoTuning.brushStream.intenseThreshold
? generativePianoTuning.brushStreamIntenseIntervalBeats
: intensity >= generativePianoTuning.brushStream.activeThreshold
? generativePianoTuning.brushStreamActiveIntervalBeats
: generativePianoTuning.brushStreamIdleIntervalBeats;
return Math.max(1, Math.round(intervalBeats * this.config.rhythm.stepsPerBeat));
}
@ -950,11 +943,11 @@ export class GenerativePianoEngine {
}
private getMotifOffset(strength: number): number {
return strength >= this.generation.brushMotif.highThreshold
? this.generation.brushMotif.highOffset
: strength >= this.generation.brushMotif.mediumThreshold
? this.generation.brushMotif.mediumOffset
: this.generation.brushMotif.lowOffset;
return strength >= generativePianoTuning.brushMotif.highThreshold
? generativePianoTuning.brushMotif.highOffset
: strength >= generativePianoTuning.brushMotif.mediumThreshold
? generativePianoTuning.brushMotif.mediumOffset
: generativePianoTuning.brushMotif.lowOffset;
}
private getBrushMotifDegrees({
@ -986,17 +979,17 @@ export class GenerativePianoEngine {
maniaAmount: number
): GardenAudioRegister {
const shift = Math.round(
maniaAmount * this.generation.registerBias.maniaShiftSemitones
maniaAmount * generativePianoTuning.registerBias.maniaShiftSemitones
);
const midiMin = clamp(
register.midiMin + shift,
this.generation.registerBias.midiMin,
this.generation.registerBias.midiMaxForMin
generativePianoTuning.registerBias.midiMin,
generativePianoTuning.registerBias.midiMaxForMin
);
const midiMax = clamp(
register.midiMax + shift,
midiMin + this.generation.registerBias.minimumSpan,
this.generation.registerBias.midiMax
midiMin + generativePianoTuning.registerBias.minimumSpan,
generativePianoTuning.registerBias.midiMax
);
return {
@ -1039,8 +1032,8 @@ export class GenerativePianoEngine {
pitchSource.offsets.forEach((offset, preference) => {
for (
let octave = this.generation.candidateOctaveSearch.min;
octave <= this.generation.candidateOctaveSearch.max;
let octave = generativePianoTuning.candidateOctaveSearch.min;
octave <= generativePianoTuning.candidateOctaveSearch.max;
octave += 1
) {
const midi = pitchSource.baseMidi + offset + octave * PITCH_SEMITONES_PER_OCTAVE;
@ -1067,38 +1060,38 @@ export class GenerativePianoEngine {
return (
Math.abs(candidate.midi - previousMidi) +
Math.abs(candidate.midi - register.preferredMidi) *
this.generation.noteScoreRegisterWeight +
candidate.preference * this.generation.noteScorePreferenceWeight +
candidate.chordToneDistance * this.generation.noteScoreChordToneWeight +
generativePianoTuning.noteScoreRegisterWeight +
candidate.preference * generativePianoTuning.noteScorePreferenceWeight +
candidate.chordToneDistance * generativePianoTuning.noteScoreChordToneWeight +
(avoidRepeat && candidate.midi === previousMidi
? this.generation.noteScoreRepeatPenalty
? generativePianoTuning.noteScoreRepeatPenalty
: 0)
);
}
private shouldPlaySupport(expression: number, barIndex: number): boolean {
if (expression >= this.generation.supportNote.expressionThreshold) {
if (expression >= generativePianoTuning.supportNote.expressionThreshold) {
return true;
}
return (
barIndex % this.generation.supportBarSpacing === this.generation.supportBarOffset
barIndex % generativePianoTuning.supportBarSpacing === generativePianoTuning.supportBarOffset
);
}
private shouldPlayTexture(expression: number, barIndex: number): boolean {
const spacing =
expression < this.generation.textureNote.idleExpressionThreshold
? this.generation.idleTextureBarSpacing
: expression < this.generation.textureNote.mediumExpressionThreshold
? this.generation.mediumTextureBarSpacing
: this.generation.textureNote.intenseSpacing;
expression < generativePianoTuning.textureNote.idleExpressionThreshold
? generativePianoTuning.idleTextureBarSpacing
: expression < generativePianoTuning.textureNote.mediumExpressionThreshold
? generativePianoTuning.mediumTextureBarSpacing
: generativePianoTuning.textureNote.intenseSpacing;
return (
barIndex % spacing ===
(spacing === this.generation.textureNote.intenseSpacing
(spacing === generativePianoTuning.textureNote.intenseSpacing
? 0
: this.generation.textureNote.idlePhase)
: generativePianoTuning.textureNote.idlePhase)
);
}
@ -1106,14 +1099,14 @@ export class GenerativePianoEngine {
chordIntervals: ReadonlyArray<number>,
styleIndex: GardenAudioStyleIndex
): Array<number> {
return this.generation.supportNote.offsetsByStyle[styleIndex].map((offset) =>
return generativePianoTuning.supportNote.offsetsByStyle[styleIndex].map((offset) =>
getConfiguredChordOffset(chordIntervals, offset)
);
}
private getChord(profile: GardenAudioVibeProfile, barIndex: number): GardenAudioChord {
const progressionIndex =
Math.floor(barIndex / this.generation.chordBars) % profile.progression.length;
Math.floor(barIndex / generativePianoTuning.chordBars) % profile.progression.length;
return profile.progression[progressionIndex];
}
@ -1122,17 +1115,17 @@ export class GenerativePianoEngine {
}
private getStyleIndex(startTime: number): GardenAudioStyleIndex {
const styleCount = this.generation.stylePools.length;
const rotationBars = Math.max(1, Math.round(this.generation.styleRotationBars));
const styleCount = generativePianoTuning.stylePools.length;
const rotationBars = Math.max(1, Math.round(generativePianoTuning.styleRotationBars));
return (Math.floor(this.getGlobalBarIndex(startTime) / rotationBars) %
styleCount) as GardenAudioStyleIndex;
}
private getStylePan(styleIndex: GardenAudioStyleIndex): number {
const pool = this.generation.stylePools[styleIndex];
const pool = generativePianoTuning.stylePools[styleIndex];
const styleVoice = styleVoices[styleIndex];
return clamp(
pool.pan + styleVoice.panOffset * this.generation.stylePanOffsetScale,
pool.pan + styleVoice.panOffset * generativePianoTuning.stylePanOffsetScale,
-1,
1
);
@ -1145,13 +1138,13 @@ export class GenerativePianoEngine {
): number {
const midiLift =
clamp01(
(midi - this.generation.lowpass.midiBase) / this.generation.lowpass.midiRange
) * this.generation.lowpass.midiLiftHz;
(midi - generativePianoTuning.lowpass.midiBase) / generativePianoTuning.lowpass.midiRange
) * generativePianoTuning.lowpass.midiLiftHz;
return clamp(
this.config.piano.lowpassHz *
profile.brightness *
(this.generation.lowpass.expressionBase +
expression * this.generation.lowpass.expressionWeight) +
(generativePianoTuning.lowpass.expressionBase +
expression * generativePianoTuning.lowpass.expressionWeight) +
midiLift,
this.config.piano.lowpassMinHz,
this.config.piano.lowpassMaxHz
@ -1174,14 +1167,14 @@ export class GenerativePianoEngine {
}
private getExpression(activity: number): number {
const liftedActivity = Math.max(
activity,
this.activeProfile?.idleIntensity ?? this.config.rhythm.idleIntensity
);
return clamp01(
(liftedActivity - this.config.rhythm.sparseActivity) /
const activityExpression = clamp01(
(activity - this.config.rhythm.sparseActivity) /
(1 - this.config.rhythm.sparseActivity)
);
const idleExpression = clamp01(
this.activeProfile?.idleIntensity ?? this.config.rhythm.idleIntensity
);
return Math.max(activityExpression, idleExpression);
}
private getBeatDurationSeconds(): number {
@ -1263,7 +1256,7 @@ export class GenerativePianoEngine {
private reserveBrushStreamNote(stepIndex: number): boolean {
const barIndex = this.getBarIndexForStep(stepIndex);
const noteCount = this.brushStreamNoteCountsByBar.get(barIndex) ?? 0;
if (noteCount >= this.generation.maxBrushStreamNotesPerBar) {
if (noteCount >= generativePianoTuning.maxBrushStreamNotesPerBar) {
return false;
}