using UdonSharp; using UnityEngine; using VRC.SDK3.UdonNetworkCalling; public enum MusicEventType { None, OpeningLoop, CarmenSandiegoTheme, CarmenSandiegoWindDown, TheChase, ACMECrimenetComputerIdle, // So that the computer can play other sounds ThinkAboutIt, CapitalLoop, RecoverTheLoot, IFeelGood, CarmenChaseMusic, WhereInTheWorld, RockapellaIdent } public enum SFXEventType { None, Ding, Buzzer, Round1Correct, Round1Incorrect, VideoMusicClue, FileCabinetOpen, FileCabinetClose, ModemOperation, TheLoot, TheWarrant, FogHorn, JailDoorSlam, InJail, MapCorrect, MapIncorrect, CarmenInJail, CarmenEscape, LeverCartoon, Boing, Boing2, Printer, Thunder, DistantFoghorn, LightSwitch, PhoneRing, TimerEnd } [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class AudioManager : UdonSharpBehaviour { [SerializeField] private AudioSource _MusicPlayer = null; [SerializeField] private AudioSource _SFXPlayer = null; [Space][Header("Music")] [SerializeField] private AudioClip _OpeningLoop = null; [SerializeField] private AudioClip _CarmenSandiegoTheme = null; [SerializeField] private AudioClip _CarmenSandiegoWindDown = null; [SerializeField] private AudioClip _TheChase = null; [SerializeField] private AudioClip _ThinkAboutIt = null; [SerializeField] private AudioClip _CapitalLoop = null; [SerializeField] private AudioClip _RecoverTheLoot = null; [SerializeField] private AudioClip _IFeelGood = null; [SerializeField] private AudioClip _CarmenChaseMusic = null; [SerializeField] private AudioClip _WhereInTheWorld = null; [SerializeField] private AudioClip _RockapellaIdent = null; [Space][Header("SFX")] [SerializeField] private AudioClip _Ding = null; [SerializeField] private AudioClip _Buzzer = null; [SerializeField] private AudioClip _Round1Correct = null; [SerializeField] private AudioClip _Round1Incorrect = null; [SerializeField] private AudioClip _ACMECrimenetComputerIdle = null; [SerializeField] private AudioClip _VideoMusicClue = null; [SerializeField] private AudioClip _FileCabinetOpen = null; [SerializeField] private AudioClip _FileCabinetClose = null; [SerializeField] private AudioClip _ModemOperation = null; [SerializeField] private AudioClip _TheLoot = null; [SerializeField] private AudioClip _TheWarrant = null; [SerializeField] private AudioClip _FogHorn = null; [SerializeField] private AudioClip _JailDoorSlam = null; [SerializeField] private AudioClip _MapCorrect = null; [SerializeField] private AudioClip _MapIncorrect = null; [SerializeField] private AudioClip _CarmenInJail = null; [SerializeField] private AudioClip _CarmenEscape = null; [SerializeField] private AudioClip _LeverCartoon = null; [SerializeField] private AudioClip _Boing = null; [SerializeField] private AudioClip _Boing2 = null; [SerializeField] private AudioClip _Printer = null; [SerializeField] private AudioClip _Thunder = null; [SerializeField] private AudioClip _DistantFoghorn = null; [SerializeField] private AudioClip _LightSwitch = null; [SerializeField] private AudioClip _PhoneRing = null; [SerializeField] private AudioClip _TimerEnd = null; [Space][Header("Crook Themes")] [SerializeField] private AudioClip _CrookTheme_Contessa = null; [SerializeField] private AudioClip _CrookTheme_DoubleTrouble = null; [SerializeField] private AudioClip _CrookTheme_EarthaBrute = null; [SerializeField] private AudioClip _CrookTheme_Kneemoi = null; [SerializeField] private AudioClip _CrookTheme_PattyLarceny = null; [SerializeField] private AudioClip _CrookTheme_Robocrook = null; [SerializeField] private AudioClip _CrookTheme_SarahNade = null; [SerializeField] private AudioClip _CrookTheme_TopGrunge = null; [SerializeField] private AudioClip _CrookTheme_VicTheSlick = null; [SerializeField] private AudioClip _CrookTheme_WonderRat = null; [Space] [Header("Crook In Jail")] [SerializeField] private AudioClip _CrookInJail_Contessa = null; [SerializeField] private AudioClip _CrookInJail_DoubleTrouble = null; [SerializeField] private AudioClip _CrookInJail_EarthaBrute = null; [SerializeField] private AudioClip _CrookInJail_Kneemoi = null; [SerializeField] private AudioClip _CrookInJail_PattyLarceny = null; [SerializeField] private AudioClip _CrookInJail_Robocrook = null; [SerializeField] private AudioClip _CrookInJail_SarahNade = null; [SerializeField] private AudioClip _CrookInJail_TopGrunge = null; [SerializeField] private AudioClip _CrookInJail_VicTheSlick = null; [SerializeField] private AudioClip _CrookInJail_WonderRat = null; [Space][Header("Crook Jail Calls")] [SerializeField] private AudioClip _CrookJailCall_Contessa_Africa = null; [SerializeField] private AudioClip _CrookJailCall_Contessa_Asia = null; [SerializeField] private AudioClip _CrookJailCall_Contessa_Europe = null; [SerializeField] private AudioClip _CrookJailCall_Contessa_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_Contessa_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_Contessa_SouthAmerica = null; [Space] [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_Africa = null; [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_Asia = null; [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_Europe = null; [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_DoubleTrouble_SouthAmerica = null; [Space] [SerializeField] private AudioClip _CrookJailCall_Eartha_Africa = null; [SerializeField] private AudioClip _CrookJailCall_Eartha_Asia = null; [SerializeField] private AudioClip _CrookJailCall_Eartha_Europe = null; [SerializeField] private AudioClip _CrookJailCall_Eartha_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_Eartha_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_Eartha_SouthAmerica = null; [Space] [SerializeField] private AudioClip _CrookJailCall_Patty_Africa = null; [SerializeField] private AudioClip _CrookJailCall_Patty_Asia = null; [SerializeField] private AudioClip _CrookJailCall_Patty_Europe = null; [SerializeField] private AudioClip _CrookJailCall_Patty_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_Patty_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_Patty_SouthAmerica = null; [Space] [SerializeField] private AudioClip _CrookJailCall_Robocrook_Africa = null; [SerializeField] private AudioClip _CrookJailCall_Robocrook_Asia = null; [SerializeField] private AudioClip _CrookJailCall_Robocrook_Europe = null; [SerializeField] private AudioClip _CrookJailCall_Robocrook_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_Robocrook_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_Robocrook_SouthAmerica = null; [Space] [SerializeField] private AudioClip _CrookJailCall_WonderRat_Africa = null; [SerializeField] private AudioClip _CrookJailCall_WonderRat_Asia = null; [SerializeField] private AudioClip _CrookJailCall_WonderRat_Europe = null; [SerializeField] private AudioClip _CrookJailCall_WonderRat_NorthAmerica = null; [SerializeField] private AudioClip _CrookJailCall_WonderRat_Oceania = null; [SerializeField] private AudioClip _CrookJailCall_WonderRat_SouthAmerica = null; private bool _FadeOutMusic = false; public void Update() { if (_FadeOutMusic) { float NewVolume = _MusicPlayer.volume - (Time.deltaTime / 2.0f); if (NewVolume <= 0.0f) { StopMusic(); _FadeOutMusic = false; } else { _MusicPlayer.volume = NewVolume; } } } [NetworkCallable] public void PlayMusic(MusicEventType MusicEvent) { _PlayMusic_Internal(MusicEvent, false); } [NetworkCallable] public void PlayMusicLoop(MusicEventType MusicEvent) { _PlayMusic_Internal(MusicEvent, true); } [NetworkCallable] public void StopMusic() { _PlayMusic_Internal(MusicEventType.None, false); } [NetworkCallable] public void FadeOutMusic() { _FadeOutMusic = true; } public bool IsMusicPlaying() { return _MusicPlayer.isPlaying; } private void _PlayMusic_Internal(MusicEventType MusicEvent, bool Loop) { _MusicPlayer.Stop(); _FadeOutMusic = false; switch (MusicEvent) { case MusicEventType.OpeningLoop: _MusicPlayer.clip = _OpeningLoop; break; case MusicEventType.CarmenSandiegoTheme: _MusicPlayer.clip = _CarmenSandiegoTheme; break; case MusicEventType.CarmenSandiegoWindDown: _MusicPlayer.clip = _CarmenSandiegoWindDown; break; case MusicEventType.ACMECrimenetComputerIdle: _MusicPlayer.clip = _ACMECrimenetComputerIdle; break; case MusicEventType.TheChase: _MusicPlayer.clip = _TheChase; break; case MusicEventType.ThinkAboutIt: _MusicPlayer.clip = _ThinkAboutIt; break; case MusicEventType.CapitalLoop: _MusicPlayer.clip = _CapitalLoop; break; case MusicEventType.RecoverTheLoot: _MusicPlayer.clip = _RecoverTheLoot; break; case MusicEventType.IFeelGood: _MusicPlayer.clip = _IFeelGood; break; case MusicEventType.CarmenChaseMusic: _MusicPlayer.clip = _CarmenChaseMusic; break; case MusicEventType.WhereInTheWorld: _MusicPlayer.clip = _WhereInTheWorld; break; case MusicEventType.RockapellaIdent: _MusicPlayer.clip = _RockapellaIdent; break; default: _MusicPlayer.clip = null; break; } _MusicPlayer.loop = Loop; if (_MusicPlayer.clip != null) { _MusicPlayer.volume = 1.0f; _MusicPlayer.Play(); } } [NetworkCallable] public void PlaySFX(SFXEventType SFXEvent) { _PlaySFX_Internal(SFXEvent); } [NetworkCallable] public void PlaySFXLoop(SFXEventType SFXEvent) { _PlaySFX_Internal(SFXEvent, true); } [NetworkCallable] public void PlaySFXAtPitch(SFXEventType SFXEvent, float Pitch) { _PlaySFX_Internal(SFXEvent, false, Pitch); } [NetworkCallable] public void PlaySFXLoopAtPitch(SFXEventType SFXEvent, float Pitch) { _PlaySFX_Internal(SFXEvent, true, Pitch); } [NetworkCallable] public void StopSFX() { _PlaySFX_Internal(SFXEventType.None); } public bool IsSFXPlaying() { return _SFXPlayer.isPlaying; } private void _PlaySFX_Internal(SFXEventType SFXEvent, bool Loop = false, float Pitch = 1.0f) { _SFXPlayer.Stop(); switch (SFXEvent) { case SFXEventType.Ding: _SFXPlayer.clip = _Ding; break; case SFXEventType.Buzzer: _SFXPlayer.clip = _Buzzer; break; case SFXEventType.Round1Correct: _SFXPlayer.clip = _Round1Correct; break; case SFXEventType.Round1Incorrect: _SFXPlayer.clip = _Round1Incorrect; break; case SFXEventType.VideoMusicClue: _SFXPlayer.clip = _VideoMusicClue; break; case SFXEventType.FileCabinetOpen: _SFXPlayer.clip = _FileCabinetOpen; break; case SFXEventType.FileCabinetClose: _SFXPlayer.clip = _FileCabinetClose; break; case SFXEventType.ModemOperation: _SFXPlayer.clip = _ModemOperation; break; case SFXEventType.TheLoot: _SFXPlayer.clip = _TheLoot; break; case SFXEventType.TheWarrant: _SFXPlayer.clip = _TheWarrant; break; case SFXEventType.FogHorn: _SFXPlayer.clip = _FogHorn; break; case SFXEventType.JailDoorSlam: _SFXPlayer.clip = _JailDoorSlam; break; case SFXEventType.MapCorrect: _SFXPlayer.clip = _MapCorrect; break; case SFXEventType.MapIncorrect: _SFXPlayer.clip = _MapIncorrect; break; case SFXEventType.CarmenInJail: _SFXPlayer.clip = _CarmenInJail; break; case SFXEventType.CarmenEscape: _SFXPlayer.clip = _CarmenEscape; break; case SFXEventType.LeverCartoon: _SFXPlayer.clip = _LeverCartoon; break; case SFXEventType.Boing: _SFXPlayer.clip = _Boing; break; case SFXEventType.Boing2: _SFXPlayer.clip = _Boing2; break; case SFXEventType.Printer: _SFXPlayer.clip = _Printer; break; case SFXEventType.Thunder: _SFXPlayer.clip = _Thunder; break; case SFXEventType.DistantFoghorn: _SFXPlayer.clip = _DistantFoghorn; break; case SFXEventType.LightSwitch: _SFXPlayer.clip = _LightSwitch; break; case SFXEventType.PhoneRing: _SFXPlayer.clip = _PhoneRing; break; case SFXEventType.TimerEnd: _SFXPlayer.clip = _TimerEnd; break; default: _SFXPlayer.clip = null; break; } _SFXPlayer.loop = Loop; if (_SFXPlayer.clip != null) { _SFXPlayer.pitch = Pitch; _SFXPlayer.Play(); } } [NetworkCallable] public void PlayCrookTheme(AccusedCrook Crook) { _PlayCrookTheme_Internal(Crook); } private void _PlayCrookTheme_Internal(AccusedCrook Crook) { _SFXPlayer.Stop(); switch (Crook) { case AccusedCrook.Contessa: _SFXPlayer.clip = _CrookTheme_Contessa; break; case AccusedCrook.DoubleTrouble: _SFXPlayer.clip = _CrookTheme_DoubleTrouble; break; case AccusedCrook.EarthaBrute: _SFXPlayer.clip = _CrookTheme_EarthaBrute; break; case AccusedCrook.Kneemoi: _SFXPlayer.clip = _CrookTheme_Kneemoi; break; case AccusedCrook.PattyLarceny: _SFXPlayer.clip = _CrookTheme_PattyLarceny; break; case AccusedCrook.Robocrook: _SFXPlayer.clip = _CrookTheme_Robocrook; break; case AccusedCrook.SarahNade: _SFXPlayer.clip = _CrookTheme_SarahNade; break; case AccusedCrook.TopGrunge: _SFXPlayer.clip = _CrookTheme_TopGrunge; break; case AccusedCrook.VicTheSlick: _SFXPlayer.clip = _CrookTheme_VicTheSlick; break; case AccusedCrook.WonderRat: _SFXPlayer.clip = _CrookTheme_WonderRat; break; default: _SFXPlayer.clip = null; break; } _SFXPlayer.loop = false; if (_SFXPlayer.clip != null) { _SFXPlayer.pitch = 1.0f; _SFXPlayer.Play(); } } [NetworkCallable] public void PlayCrookInJail(AccusedCrook Crook) { _PlayCrookInJail_Internal(Crook); } private void _PlayCrookInJail_Internal(AccusedCrook Crook) { _MusicPlayer.Stop(); _FadeOutMusic = false; switch (Crook) { case AccusedCrook.Contessa: _MusicPlayer.clip = _CrookInJail_Contessa; break; case AccusedCrook.DoubleTrouble: _MusicPlayer.clip = _CrookInJail_DoubleTrouble; break; case AccusedCrook.EarthaBrute: _MusicPlayer.clip = _CrookInJail_EarthaBrute; break; case AccusedCrook.Kneemoi: _MusicPlayer.clip = _CrookInJail_Kneemoi; break; case AccusedCrook.PattyLarceny: _MusicPlayer.clip = _CrookInJail_PattyLarceny; break; case AccusedCrook.Robocrook: _MusicPlayer.clip = _CrookInJail_Robocrook; break; case AccusedCrook.SarahNade: _MusicPlayer.clip = _CrookInJail_SarahNade; break; case AccusedCrook.TopGrunge: _MusicPlayer.clip = _CrookInJail_TopGrunge; break; case AccusedCrook.VicTheSlick: _MusicPlayer.clip = _CrookInJail_VicTheSlick; break; case AccusedCrook.WonderRat: _MusicPlayer.clip = _CrookInJail_WonderRat; break; default: _MusicPlayer.clip = null; break; } _MusicPlayer.loop = false; if (_MusicPlayer.clip != null) { _MusicPlayer.volume = 1.0f; _MusicPlayer.Play(); } } [NetworkCallable] public void PlayJailCall(AccusedCrook Crook, ContinentMap Map) { _PlayJailCall_Internal(Crook, Map); } private void _PlayJailCall_Internal(AccusedCrook Crook, ContinentMap Map) { _SFXPlayer.Stop(); switch (Crook) { case AccusedCrook.Contessa: switch (Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_Contessa_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_Contessa_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_Contessa_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_Contessa_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_Contessa_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_Contessa_SouthAmerica; break; } break; case AccusedCrook.DoubleTrouble: switch (Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_DoubleTrouble_SouthAmerica; break; } break; case AccusedCrook.EarthaBrute: switch(Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_Eartha_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_Eartha_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_Eartha_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_Eartha_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_Eartha_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_Eartha_SouthAmerica; break; } break; case AccusedCrook.PattyLarceny: switch (Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_Patty_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_Patty_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_Patty_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_Patty_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_Patty_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_Patty_SouthAmerica; break; } break; case AccusedCrook.Robocrook: switch (Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_Robocrook_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_Robocrook_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_Robocrook_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_Robocrook_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_Robocrook_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_Robocrook_SouthAmerica; break; } break; case AccusedCrook.WonderRat: switch (Map) { case ContinentMap.Africa: _SFXPlayer.clip = _CrookJailCall_WonderRat_Africa; break; case ContinentMap.Asia: _SFXPlayer.clip = _CrookJailCall_WonderRat_Asia; break; case ContinentMap.Europe: _SFXPlayer.clip = _CrookJailCall_WonderRat_Europe; break; case ContinentMap.NorthAmerica: _SFXPlayer.clip = _CrookJailCall_WonderRat_NorthAmerica; break; case ContinentMap.Oceania: _SFXPlayer.clip = _CrookJailCall_WonderRat_Oceania; break; case ContinentMap.SouthAmerica: _SFXPlayer.clip = _CrookJailCall_WonderRat_SouthAmerica; break; } break; default: _SFXPlayer.clip = null; break; } _SFXPlayer.loop = false; if (_SFXPlayer.clip != null) { _SFXPlayer.pitch = 1.0f; _SFXPlayer.Play(); } } // A messy group of variables that are used for pitch correction of sound effects // Since Udon doesn't support accessing public static values in structs, we're doing it here // As5 = 932.33 Hz, PitchShift = TargetNoteFreq / As5Freq public const float As4 = 0.49999463709201677517617152724894f; public const float B4 = 0.52972659895101519848122445915073f; public const float C5 = 0.56122832044447781364967339890382f; public const float Cs5 = 0.5946070597320691171580878015295f; public const float D5 = 0.62995934915748715583538017654693f; public const float Ds5 = 0.66741389851232932545343386998166f; public const float E5 = 0.70709941758819302178413222785923f; public const float F5 = 0.74915534199264209024701554170734f; public const float Fs5 = 0.79369965570130747696631021204938f; public const float G5 = 0.84089324595368592665687042141731f; public const float Gs5 = 0.89089699998927418403355035234305f; public const float A5 = 0.94387180504756899381120418735855f; public const float As5 = 1.0f; public const float B5 = 1.0594639237179968466101058638036f; public const float C6 = 1.1224566408889556272993467978076f; public const float Cs6 = 1.1892033936481717846685186575569f; public const float D6 = 1.2599186983149743116707603530939f; public const float Ds6 = 1.3348385228406251005545246854654f; public const float E6 = 1.4142095609923524932159214012206f; public const float F6 = 1.4982999581693177308463741379125f; public const float Fs6 = 1.5873993114026149539326204240988f; public const float G6 = 1.6817864919073718533137408428346f; public const float Gs6 = 1.7817939999785483680671007046861f; public const float A6 = 1.8877436100951379876224083747171f; public const float As6 = 2.0f; public const float B6 = 2.1183486533738054122467366704922f; public const float C7 = 2.2449132817779112545986935956153f; public const float Cs7 = 2.3784067872963435693370373151137f; }