mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
66 lines
2.0 KiB
TypeScript
66 lines
2.0 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core';
|
|
import type { Track } from '../../store/playerStoreTypes';
|
|
import {
|
|
INTERRUPT_BLEND_PREP_FADE_SEC,
|
|
isCrossfadeNextReady,
|
|
kickEagerCrossfadePreload,
|
|
waitForCrossfadeNextReady,
|
|
} from '../../store/crossfadePreload';
|
|
|
|
export { INTERRUPT_BLEND_PREP_FADE_SEC };
|
|
|
|
/** Play generation with a pending interrupt handoff (suppress spurious `audio:ended`). */
|
|
let pendingHandoffGen: number | null = null;
|
|
|
|
export function armInterruptHandoff(gen: number): void {
|
|
pendingHandoffGen = gen;
|
|
}
|
|
|
|
export function clearInterruptHandoff(): void {
|
|
pendingHandoffGen = null;
|
|
}
|
|
|
|
export function isInterruptHandoffPending(): boolean {
|
|
return pendingHandoffGen !== null;
|
|
}
|
|
|
|
/** Keep player-bar metadata on A until B is buffered and the engine handoff runs. */
|
|
export function shouldDeferInterruptHandoffUi(
|
|
wantInterruptBlend: boolean,
|
|
bReadyNow: boolean,
|
|
): boolean {
|
|
return wantInterruptBlend && !bReadyNow;
|
|
}
|
|
|
|
function sleepMs(ms: number): Promise<void> {
|
|
return new Promise(resolve => { window.setTimeout(resolve, ms); });
|
|
}
|
|
|
|
export interface InterruptBlendPrepResult {
|
|
/** B is playable for a stable crossfade handoff. */
|
|
ready: boolean;
|
|
}
|
|
|
|
/**
|
|
* Win preload time before the incoming track starts: fade the outgoing engine
|
|
* source for ~1 s while eagerly buffering B. Caller should pass
|
|
* `outgoingFadeSec: 0` on the blend plan when prep ran — A was volume-ducked only.
|
|
*/
|
|
export async function runInterruptBlendPrep(
|
|
track: Track,
|
|
profileId: string | null,
|
|
cacheKey: string | null,
|
|
isStale: () => boolean,
|
|
): Promise<InterruptBlendPrepResult> {
|
|
kickEagerCrossfadePreload(track, profileId, cacheKey);
|
|
void invoke('audio_begin_outgoing_fade', { fadeSecs: INTERRUPT_BLEND_PREP_FADE_SEC }).catch(() => {});
|
|
|
|
const prepMs = Math.round(INTERRUPT_BLEND_PREP_FADE_SEC * 1000);
|
|
await Promise.all([
|
|
sleepMs(prepMs),
|
|
waitForCrossfadeNextReady(track.id, profileId, cacheKey, prepMs, isStale),
|
|
]);
|
|
if (isStale()) return { ready: false };
|
|
return { ready: isCrossfadeNextReady(track.id, profileId, cacheKey) };
|
|
}
|