More clean up

This commit is contained in:
Andras Schmelczer 2026-05-20 21:03:41 +01:00
commit f03da42b5e
43 changed files with 818 additions and 1076 deletions

View file

@ -37,10 +37,7 @@ const getChordIntervals = (
: chordVoicings.minorClosed;
};
const degreeToSemitone = (
profile: GardenAudioVibeProfile,
degree: number
): number => {
const degreeToSemitone = (profile: GardenAudioVibeProfile, degree: number): number => {
const scaleIndex =
((degree % profile.scale.length) + profile.scale.length) % profile.scale.length;
const octave = Math.floor(degree / profile.scale.length);
@ -96,6 +93,7 @@ interface BrushPhraseLayer {
export class GenerativePianoEngine {
private nextBeatStep: number | null = null;
private timelineStartedAt: number | null = null;
private activeProfile: GardenAudioVibeProfile | null = null;
private isWaitingForGestureAccent = false;
private lastGestureAccentAt = Number.NEGATIVE_INFINITY;
private lastStrokeAccentStep = Number.NEGATIVE_INFINITY;
@ -124,13 +122,17 @@ export class GenerativePianoEngine {
return generativePianoTuning;
}
public prime(now: number): void {
public prime(now: number, profile: GardenAudioVibeProfile): void {
this.activeProfile = profile;
this.timelineStartedAt ??= now;
this.nextBeatStep ??= 0;
this.nextBrushStreamStep ??= 0;
}
public cue(now: number): void {
public cue(now: number, profile?: GardenAudioVibeProfile): void {
if (profile) {
this.activeProfile = profile;
}
this.nextBeatStep = 0;
this.timelineStartedAt = now;
this.nextBrushStreamStep = 0;
@ -148,10 +150,9 @@ export class GenerativePianoEngine {
}
public release(vibe: VibePreset, now: number): number {
this.prime(now);
this.isWaitingForGestureAccent = false;
const profile = getVibeProfile(vibe);
this.prime(now, profile);
this.isWaitingForGestureAccent = false;
const releaseStep = this.getReleaseResolutionStep(now);
const releaseStart = Math.max(now, this.getTimeForStep(releaseStep));
@ -198,7 +199,8 @@ export class GenerativePianoEngine {
activity,
maniaAmount = 0,
}: StrokeAccentRequest): void {
this.prime(now);
const profile = getVibeProfile(vibe);
this.prime(now, profile);
const strength = clamp01(activity);
const normalizedManiaAmount = clamp01(maniaAmount);
const styleIndex = this.getStyleIndex(now);
@ -242,14 +244,14 @@ export class GenerativePianoEngine {
activity,
lookaheadSeconds = GENERATIVE_LOOKAHEAD_SECONDS,
}: RenderLookaheadRequest): void {
this.prime(now);
const profile = getVibeProfile(vibe);
this.prime(now, profile);
this.skipLateBeats(now);
if (this.nextBeatStep === null) {
return;
}
const profile = getVibeProfile(vibe);
const lookaheadEnd = now + lookaheadSeconds;
while (this.getTimeForStep(this.nextBeatStep) <= lookaheadEnd) {
const beatIndex = this.getBeatIndexForStep(this.nextBeatStep);
@ -286,7 +288,7 @@ export class GenerativePianoEngine {
{ baseMidi: rootMidi, offsets },
this.generation.padRegisters[index]
);
this.playNote({
this.playProfileNote(profile, {
midi,
velocity: stinger.velocities[index],
pan: stinger.pans[index],
@ -302,6 +304,7 @@ export class GenerativePianoEngine {
public reset(): void {
this.nextBeatStep = null;
this.timelineStartedAt = null;
this.activeProfile = null;
this.isWaitingForGestureAccent = false;
this.lastGestureAccentAt = Number.NEGATIVE_INFINITY;
this.lastStrokeAccentStep = Number.NEGATIVE_INFINITY;
@ -314,6 +317,13 @@ export class GenerativePianoEngine {
this.brushStreamNoteCountsByBar.clear();
}
private playProfileNote(profile: GardenAudioVibeProfile, note: PianoNote): void {
this.playNote({
...note,
sustainSeconds: profile.noteLength,
});
}
private renderBeat({
profile,
beatIndex,
@ -398,7 +408,7 @@ export class GenerativePianoEngine {
false
);
this.lastPadMidiByVoice[index] = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
velocity + expression * this.generation.padChord.expressionVelocityWeight,
@ -433,8 +443,13 @@ export class GenerativePianoEngine {
offsetsByVoice.forEach((offsets, index) => {
const register = this.generation.padRegisters[index];
const midi = this.chooseMidi({ baseMidi: rootMidi, offsets }, register, null, false);
this.playNote({
const midi = this.chooseMidi(
{ baseMidi: rootMidi, offsets },
register,
null,
false
);
this.playProfileNote(profile, {
midi,
velocity: release.velocities[index],
startTime: startTime + index * release.strumSeconds,
@ -470,7 +485,7 @@ export class GenerativePianoEngine {
);
this.lastMidiByStyle[styleIndex] = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.supportNote.velocityBase +
@ -516,7 +531,7 @@ export class GenerativePianoEngine {
);
this.lastMidiByStyle[styleIndex] = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.textureNote.velocityBase +
@ -563,7 +578,7 @@ export class GenerativePianoEngine {
);
this.lastMidiByStyle[styleIndex] = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.gestureAccent.velocityBase +
@ -608,7 +623,7 @@ export class GenerativePianoEngine {
this.lastMidiByStyle[styleIndex] = midi;
this.lastBrushStreamMidi = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.touchNote.velocityBase +
@ -818,7 +833,7 @@ export class GenerativePianoEngine {
this.lastBrushStreamMidi = midi;
this.lastMidiByStyle[styleIndex] = midi;
this.playNote({
this.playProfileNote(profile, {
midi,
velocity:
(this.generation.brushStream.velocityBase +
@ -851,7 +866,7 @@ export class GenerativePianoEngine {
this.generation.brushStreamEcho.maxMidi
? midi + this.generation.brushStreamEcho.octaveSemitones
: midi - this.generation.brushStreamEcho.octaveSemitones;
this.playNote({
this.playProfileNote(profile, {
midi: echoMidi,
velocity:
(this.generation.brushStreamEcho.velocityBase +
@ -1159,14 +1174,18 @@ export class GenerativePianoEngine {
}
private getExpression(activity: number): number {
const liftedActivity = Math.max(
activity,
this.activeProfile?.idleIntensity ?? this.config.rhythm.idleIntensity
);
return clamp01(
(activity - this.config.rhythm.sparseActivity) /
(liftedActivity - this.config.rhythm.sparseActivity) /
(1 - this.config.rhythm.sparseActivity)
);
}
private getBeatDurationSeconds(): number {
return 60 / this.config.rhythm.bpm;
return 60 / (this.activeProfile?.bpm ?? this.config.rhythm.bpm);
}
private getStepDurationSeconds(): number {