import { instrumentBuildFor, instrumentSourcesOf } from '../instrument/compile'; import { findAssetOf, type Project } from '../model/Project'; import { assetFxOf } from '../properties/assetFx'; import { playedSoundById } from '../properties/playedSound'; import { soundFxBuildFor } from '../soundfx/compile'; import { DRY_VOICE, type FxParams, type FxPlan, type FxVoice } from '../soundfx/nodes'; import type { SequenceNeeds } from './playbacks'; import type { Sequence } from './sequence'; /** * A sound FX by its asset id with values for its parameters: null where there is none, it is gone or * it does not compile – what plays through it then plays dry, as with `Play sound`. */ export function fxVoiceOf( project: Project, fxId: string, tempo: number, params: FxParams = {}, ): FxVoice | null { const fx = fxId ? findAssetOf(project, fxId, 'soundfx') : undefined; const plan = fx && soundFxBuildFor(fx, project).compiled; return plan ? { plan, params, tempo } : null; } /** The sound FX a clip goes through. */ export function clipFx(project: Project, fxId: string, tempo: number): FxVoice { return fxVoiceOf(project, fxId, tempo) ?? DRY_VOICE; } /** Every layer and FX of every note and every file and FX of every clip a sequence plays. */ export function sequenceNeeds(project: Project, sequence: Sequence): SequenceNeeds { const sources = instrumentSourcesOf(project); const seen = new Set(); const layers: FxPlan[] = []; for (const { instrument, note } of sequence.notes) { const key = `${instrument}\u0000${note}`; const asset = findAssetOf(project, instrument, 'instrument'); if (seen.has(key) || !asset) continue; if (!seen.has(instrument)) { seen.add(instrument); const fx = fxVoiceOf(project, assetFxOf(asset.props), 120); if (fx) layers.push(fx.plan); } seen.add(key); layers.push(...instrumentBuildFor(asset, note, sources).layers); } const files = new Set(); for (const clip of sequence.clips ?? []) { const file = playedSoundById(project, clip.sound); if (file) files.add(file); const fx = clipFx(project, clip.fx, 120); if (fx !== DRY_VOICE) layers.push(fx.plan); } return { layers, files: [...files] }; }