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), 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), 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(), }, }; }