import { musicTrackIntegratedLoudnessLufs, type AmbienceTrackKey, type MusicTrackKey } from '../audio/audioAssets'; import type { CampSkinId } from './campSkins'; export type CampMusicGroupId = 'rally' | 'wayfarer' | 'grand-strategy' | 'frontier'; export type CampAmbienceId = 'campfire' | 'river-night' | 'mountain-wind' | 'nanzhong-night'; export type CampMusicTrackKey = Extract< MusicTrackKey, 'camp-rally' | 'camp-wayfarer' | 'camp-grand-strategy' | 'camp-frontier' >; export type CampAmbienceTrackKey = Extract< AmbienceTrackKey, 'campfire-ambience' | 'river-night-ambience' | 'mountain-wind-ambience' | 'nanzhong-night-ambience' >; export type CampMusicGroupDefinition = { id: CampMusicGroupId; trackKey: CampMusicTrackKey; label: string; volume: number; }; export type CampAmbienceDefinition = { id: CampAmbienceId; trackKey: CampAmbienceTrackKey; label: string; volume: number; }; export type CampSoundscapeMapping = { musicGroupId: CampMusicGroupId; ambienceId: CampAmbienceId; }; export type CampSoundscape = { skinId: CampSkinId; music: CampMusicGroupDefinition; ambience: CampAmbienceDefinition; }; export const campMusicGroups: Record = { rally: { id: 'rally', trackKey: 'camp-rally', label: '결의와 출정', volume: 0.22 }, wayfarer: { id: 'wayfarer', trackKey: 'camp-wayfarer', label: '유랑의 밤', volume: 0.18 }, 'grand-strategy': { id: 'grand-strategy', trackKey: 'camp-grand-strategy', label: '대업의 계책', volume: 0.2 }, frontier: { id: 'frontier', trackKey: 'camp-frontier', label: '변경과 북벌', volume: 0.2 } }; export const campAmbienceDefinitions: Record = { campfire: { id: 'campfire', trackKey: 'campfire-ambience', label: '장작불', volume: 0.12 }, 'river-night': { id: 'river-night', trackKey: 'river-night-ambience', label: '밤의 강물', volume: 0.1 }, 'mountain-wind': { id: 'mountain-wind', trackKey: 'mountain-wind-ambience', label: '산바람', volume: 0.09 }, 'nanzhong-night': { id: 'nanzhong-night', trackKey: 'nanzhong-night-ambience', label: '남중의 밤', volume: 0.11 } }; export const campSoundscapeMapping: Record = { 'yellow-turban': { musicGroupId: 'rally', ambienceId: 'campfire' }, 'anti-dong': { musicGroupId: 'rally', ambienceId: 'campfire' }, xuzhou: { musicGroupId: 'wayfarer', ambienceId: 'campfire' }, wandering: { musicGroupId: 'wayfarer', ambienceId: 'campfire' }, wolong: { musicGroupId: 'wayfarer', ambienceId: 'mountain-wind' }, 'red-cliffs': { musicGroupId: 'grand-strategy', ambienceId: 'river-night' }, 'jing-yi': { musicGroupId: 'grand-strategy', ambienceId: 'river-night' }, 'hanzhong-shuhan': { musicGroupId: 'grand-strategy', ambienceId: 'mountain-wind' }, 'jingzhou-crisis': { musicGroupId: 'grand-strategy', ambienceId: 'river-night' }, 'yiling-baidi': { musicGroupId: 'grand-strategy', ambienceId: 'river-night' }, nanzhong: { musicGroupId: 'frontier', ambienceId: 'nanzhong-night' }, northern: { musicGroupId: 'frontier', ambienceId: 'mountain-wind' } }; export function getCampSoundscape(skinId: CampSkinId): CampSoundscape { const mapping = campSoundscapeMapping[skinId]; return { skinId, music: campMusicGroups[mapping.musicGroupId], ambience: campAmbienceDefinitions[mapping.ambienceId] }; } export function campMusicVolumeForTrackKey( trackKey: string, fallbackVolume = 0.2 ) { return Object.values(campMusicGroups).find( (definition) => definition.trackKey === trackKey )?.volume ?? fallbackVolume; } export function musicVolumeMatchedToCampTrack( trackKey: MusicTrackKey, referenceTrackKey: CampMusicTrackKey = 'camp-rally', fallbackVolume = campMusicVolumeForTrackKey(referenceTrackKey) ) { const trackLoudness = musicTrackIntegratedLoudnessLufs[trackKey]; const referenceLoudness = musicTrackIntegratedLoudnessLufs[referenceTrackKey]; const referenceVolume = campMusicVolumeForTrackKey( referenceTrackKey, fallbackVolume ); if ( !Number.isFinite(trackLoudness) || !Number.isFinite(referenceLoudness) || !Number.isFinite(referenceVolume) || referenceVolume <= 0 ) { return fallbackVolume; } const matchedVolume = referenceVolume * 10 ** ((referenceLoudness - trackLoudness) / 20); return Math.round( Math.min(1, Math.max(0.01, matchedVolume)) * 1000 ) / 1000; }