import { findAssetOf } from '../../core/model/Project'; import { soundLibrary } from '../../viewport/SoundLibrary'; import { centreOf, type HookContext } from './hookHelpers'; /** The Pattern tab and its piano roll (#333). */ export function patternHooks({ state, editor }: HookContext) { const { patternEditor } = editor; const roll = patternEditor.canvas; return { pattern: { current: () => patternEditor.currentAssetId, info: () => patternEditor.shownInfo, rect: () => patternEditor.canvasRect(), /** The notes as the model holds them, and the loop if one is set. */ notes: (assetId: string) => { const asset = findAssetOf(state.project, assetId, 'pattern'); return asset ? (JSON.parse(JSON.stringify(asset.notes)) as unknown) : null; }, loop: (assetId: string) => findAssetOf(state.project, assetId, 'pattern')?.loop ?? null, picked: () => roll.pickedIds, /** Where a tick of a note's row, a tick of the ruler, a key, a velocity of the lane are on screen. */ gridAt: (tick: number, note: number) => roll.gridPoint(tick, note), rulerAt: (tick: number) => roll.rulerPoint(tick), loopAt: (tick: number) => roll.loopPoint(tick), keyAt: (note: number) => roll.keyPoint(note), laneAt: (tick: number, velocity: number) => roll.lanePoint(tick, velocity), view: () => roll.viewInfo(), keys: () => roll.keysDrawn(), playAt: () => ({ ...centreOf(patternEditor.playButton), text: patternEditor.playButton.text }), gridButton: () => ({ ...centreOf(patternEditor.gridButton), text: patternEditor.gridButton.text }), fitAt: () => centreOf(patternEditor.fitButton), auditionAt: () => ({ ...centreOf(patternEditor.auditionButton), toggled: patternEditor.auditionButton.toggled, }), /** The ghost notes drawn (#339), where their round starts in the song, and the switch. */ ghosts: () => ({ notes: roll.ghostsShown?.notes ?? [], origin: roll.ghostsShown?.origin ?? null, source: patternEditor.ghostSource.text, ...centreOf(patternEditor.ghostsButton), toggled: patternEditor.ghostsButton.toggled, enabled: patternEditor.ghostsButton.enabled, }), playing: () => patternEditor.isPlaying, /** The notes the scheduler handed to the player since Play, and where it stands. */ planned: () => [...patternEditor.music.planned], position: () => patternEditor.music.current?.position() ?? null, clock: () => soundLibrary.clock(), }, }; }