diff --git a/package-lock.json b/package-lock.json index ad28f65..7edb7db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@steeze-ui/heroicons": "^2.3.0", "@steeze-ui/svelte-icon": "^1.5.0", "idb": "^8.0.0", - "mini-svg-data-uri": "^1.4.4" + "mini-svg-data-uri": "^1.4.4", + "smplr": "^0.12.2" }, "devDependencies": { "@sveltejs/adapter-auto": "^3.1.0", @@ -7777,6 +7778,14 @@ "node": ">=8" } }, + "node_modules/smplr": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/smplr/-/smplr-0.12.2.tgz", + "integrity": "sha512-fmJYIpFJVcoVCH5OtFg+UUf9aErFHk8RaRrJBkS78hNCKzdK1pFlF4KPR/mGerVAVKg78D3cSawQVk0mgiH0PA==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/sorcery": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.0.tgz", diff --git a/package.json b/package.json index 5311e63..18947b4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ "@steeze-ui/heroicons": "^2.3.0", "@steeze-ui/svelte-icon": "^1.5.0", "idb": "^8.0.0", - "mini-svg-data-uri": "^1.4.4" + "mini-svg-data-uri": "^1.4.4", + "smplr": "^0.12.2" }, "devDependencies": { "@sveltejs/adapter-auto": "^3.1.0", diff --git a/src/lib/timer/tick.ts b/src/lib/timer/tick.ts index 30515f0..7f2cc5c 100644 --- a/src/lib/timer/tick.ts +++ b/src/lib/timer/tick.ts @@ -273,7 +273,7 @@ export class TempoTimer extends AudioClockTimer { } }; const onAudioTick: AudioTickCallback = (state) => { - if (currentTime + start >= state.time) { + if (currentTime + start <= state.time) { audioCb(state); this.removeAudioTick(onAudioTick); } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 40ad381..dfdb960 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,7 +1,6 @@ diff --git a/src/routes/tools/render-sample/+page.svelte b/src/routes/tools/render-sample/+page.svelte index 8a08c8a..1b74669 100644 --- a/src/routes/tools/render-sample/+page.svelte +++ b/src/routes/tools/render-sample/+page.svelte @@ -11,9 +11,10 @@ import RandomBoxOptions from '$/lib/practice/RandomBox/RandomBoxOptions.svelte'; import { getRandomBoxContext } from '$/lib/practice/RandomBox/context'; import type { PracticeBoard, PracticeScore } from '$/lib/practice/types'; - import { getPitchFromFingerPosition } from '$/utils/music/pitch'; + import { getPitchFromFingerPosition, numberingPitch } from '$/utils/music/pitch'; import { onDestroy, onMount } from 'svelte'; import { practice } from './data'; + import { Soundfont, CacheStorage } from 'smplr'; const metronome = getMetronomeContext(); const randomBox = getRandomBoxContext<(typeof practice.scores)[number]>(); @@ -25,7 +26,16 @@ function replaceScore() { let score = randomBox.open(); const schedule = () => { - scheduleScore(score); + if (!guitarSoundfont) { + guitarSoundfont = new Soundfont(timer.audioCtx!!, { + instrument: 'acoustic_guitar_steel', + storage: new CacheStorage() + }); + } + // preload soundfont + guitarSoundfont.load.then(() => { + scheduleScore(score); + }); timer.removeTick(schedule); }; timer.onTick(schedule); @@ -46,6 +56,9 @@ }; }) as FingerInfo[]; + let guitarSoundfont: Soundfont | null = null; + + let previousTime = 0; function scheduleScore(score: PracticeScore) { /** Now scheduling */ @@ -57,7 +70,11 @@ currentActiveFingers.clear(); currentBoard = board; }, - ({ audioCtx }) => {} + ({ audioCtx }) => { + // guitarSoundfont!!.start({ + // note: 50 + 12 + // }); + } ); }); @@ -69,6 +86,7 @@ ); return { ...note, pitch }; }); + for (let i = 0; i < notes.length; i++) { const note = notes[i]; const nextThreeFingers = notes.slice(i + 1, i + 4).map((n) => n.position); @@ -83,8 +101,17 @@ currentActiveFingers = currentActiveFingers; }; }, - ({ audioCtx }) => { + ({ audioCtx, time }) => { // play audio with pitch + if (note.pitch) { + guitarSoundfont!!.start({ + note: numberingPitch(note.pitch) + 12, + time: time, + duration: note.time.duration + ? timer.convert(note.time.duration, 'note', 'second') + : note.time.duration + }); + } } ); }