162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
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<CampMusicGroupId, CampMusicGroupDefinition> = {
|
|
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<CampAmbienceId, CampAmbienceDefinition> = {
|
|
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<CampSkinId, CampSoundscapeMapping> = {
|
|
'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;
|
|
}
|