feat: integrate authored nightdrive soundtrack
This commit is contained in:
53
src/audio/soundtrack-transport.ts
Normal file
53
src/audio/soundtrack-transport.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import type { AudioJourneyState } from '@/audio/types';
|
||||
|
||||
export const SOUNDTRACK_SAMPLE_RATE = 48_000;
|
||||
export const MUSIC_BPM = 86;
|
||||
export const MUSIC_BAR_SECONDS = 4 * 60 / MUSIC_BPM;
|
||||
export const MAIN_PHRASE_BARS = 8;
|
||||
|
||||
export const INTRO_IN_END_SAMPLE = 267_907;
|
||||
export const INTRO_FIRST_PASS_END_SAMPLE = 1_339_535;
|
||||
export const INTRO_LOOP_START_SAMPLE = INTRO_FIRST_PASS_END_SAMPLE;
|
||||
export const INTRO_LOOP_END_SAMPLE = 2_411_163;
|
||||
export const INTRO_OUT_START_SAMPLE = INTRO_LOOP_END_SAMPLE;
|
||||
export const INTRO_OUT_END_SAMPLE = 2_679_070;
|
||||
export const INTRO_RUNTIME_END_SAMPLE = 2_691_070;
|
||||
|
||||
export const INTRO_IN_END_SECONDS = INTRO_IN_END_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_FIRST_PASS_END_SECONDS = INTRO_FIRST_PASS_END_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_LOOP_START_SECONDS = INTRO_LOOP_START_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_LOOP_END_SECONDS = INTRO_LOOP_END_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_OUT_START_SECONDS = INTRO_OUT_START_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_OUT_END_SECONDS = INTRO_OUT_END_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
export const INTRO_RUNTIME_END_SECONDS = INTRO_RUNTIME_END_SAMPLE / SOUNDTRACK_SAMPLE_RATE;
|
||||
|
||||
export type InitialPlayback =
|
||||
| { readonly kind: 'intro' }
|
||||
| { readonly kind: 'main'; readonly offsetSeconds: number };
|
||||
|
||||
export function resolveInitialPlayback(state: AudioJourneyState): InitialPlayback {
|
||||
return state.atStart
|
||||
? { kind: 'intro' }
|
||||
: { kind: 'main', offsetSeconds: mainPhraseOffsetSeconds(state.entryPhrase) };
|
||||
}
|
||||
|
||||
export function mainPhraseOffsetSeconds(entryPhrase: number): number {
|
||||
const finitePhrase = Number.isFinite(entryPhrase) ? Math.trunc(entryPhrase) : 0;
|
||||
const phrase = Math.min(7, Math.max(0, finitePhrase));
|
||||
const targetSamples = Math.round(
|
||||
phrase * MAIN_PHRASE_BARS * MUSIC_BAR_SECONDS * SOUNDTRACK_SAMPLE_RATE,
|
||||
);
|
||||
return targetSamples / SOUNDTRACK_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
export function nextSafeBarTime(
|
||||
now: number,
|
||||
anchorTime: number,
|
||||
minimumLeadSeconds: number,
|
||||
): number {
|
||||
const earliest = Math.max(anchorTime, now + Math.max(0, minimumLeadSeconds));
|
||||
const elapsedBars = (earliest - anchorTime) / MUSIC_BAR_SECONDS;
|
||||
const oneSampleInBars = 1 / SOUNDTRACK_SAMPLE_RATE / MUSIC_BAR_SECONDS;
|
||||
const nextBar = Math.ceil(elapsedBars - oneSampleInBars);
|
||||
return anchorTime + nextBar * MUSIC_BAR_SECONDS;
|
||||
}
|
||||
Reference in New Issue
Block a user