feat: integrate authored nightdrive soundtrack

This commit is contained in:
2026-09-04 14:39:28 +03:00
parent eb7f276b91
commit 745fa8169e
39 changed files with 1013 additions and 714 deletions

View File

@@ -1,4 +1,5 @@
import {
computed,
onMounted,
onUnmounted,
readonly,
@@ -7,14 +8,17 @@ import {
type Ref,
} from 'vue';
import { createAudioController } from '@/audio/create-audio-controller';
import { createBrowserSynthEngine } from '@/audio/synth-engine';
import type { AudioController, AudioUiState } from '@/audio/types';
import { createBrowserSoundtrackEngine } from '@/audio/soundtrack-engine';
import { resolveJourneyMusicState } from '@/audio/journey-music-state';
import type { AudioController, AudioUiState, SoundtrackSources } from '@/audio/types';
declare const __PORTFOLIO_TEST_BUILD__: boolean;
const TEST_BUILD = typeof __PORTFOLIO_TEST_BUILD__ !== 'undefined' && __PORTFOLIO_TEST_BUILD__;
export interface AudioEngineSources {
activeSectionId: Readonly<Ref<string>>;
intensity: Readonly<Ref<number>>;
soundtrack: Readonly<Ref<SoundtrackSources | null>>;
volume: Readonly<Ref<number>>;
setVolume(value: number): void;
}
@@ -34,20 +38,28 @@ export function useAudioEngine(sources: AudioEngineSources): AudioEngineBinding
let controller: AudioController | null = null;
let removeSubscription = (): void => {};
let removePageListeners = (): void => {};
const journeyState = computed(() => resolveJourneyMusicState(
sources.activeSectionId.value,
sources.intensity.value,
));
const ensureController = (): AudioController => {
if (controller) return controller;
controller = createAudioController({
createGraph: createBrowserSynthEngine,
createGraph: (initialState) => {
const soundtrack = sources.soundtrack.value;
if (soundtrack === null) throw new Error('Soundtrack assets are unavailable.');
return createBrowserSoundtrackEngine(soundtrack, initialState);
},
initialVolume: sources.volume.value,
initiallyVisible: document.visibilityState !== 'hidden',
initialJourneyState: journeyState.value,
});
removeSubscription = controller.subscribe((next) => { state.value = next; });
controller.setIntensity(sources.intensity.value);
return controller;
};
watch(sources.intensity, (value) => controller?.setIntensity(value));
watch(journeyState, (state) => controller?.setJourneyState(state), { flush: 'sync' });
watch(sources.volume, (value) => controller?.setVolume(value));
onMounted(() => {