using System; using System.Collections.Generic; using System.Threading; using Code.Configs; using Code.Constants; using Cysharp.Threading.Tasks; using DG.Tweening; using Reflex.Attributes; using Unity.VisualScripting; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Audio; using Random = UnityEngine.Random; namespace Code.Audio { public class AudioService : IAudioService { private IAudioFactory _audioFactory; private AudioMixer _mixer; private AudioSource _actionMusicSource; private AudioSource _peacefulMusicSource; private Dictionary _audioMixerGroups = new(); private readonly AudioSourcePool _audioSourcePool; // Юнити не может - говнокод поможет. private Dictionary> _snapshotsVolumes = new(); private Dictionary _settings; public AudioService(IAudioFactory audioFactory, Dictionary settings, DefaultAudioSettingsConfig defaultAudioSettingsConfig) { _audioFactory = audioFactory; _audioSourcePool = new(settings); _settings = settings; _snapshotsVolumes.Add(AudioSnapshotState.None,defaultAudioSettingsConfig.noneVolume); _snapshotsVolumes.Add(AudioSnapshotState.Menu,defaultAudioSettingsConfig.menuVolume); _snapshotsVolumes.Add(AudioSnapshotState.Action,defaultAudioSettingsConfig.actionVolume); _snapshotsVolumes.Add(AudioSnapshotState.Peaceful,defaultAudioSettingsConfig.peacefulVolume); } public float GetSetting(AudioType type) { _mixer.GetFloat(_audioMixerGroups[type].name, out float value); value = Mathf.Pow(10f, value / 20); return value; } public void ChangeSetting(AudioType type, float value) { float volume = Mathf.Log10(value) * 20; switch (type) { case AudioType.DeathMusic: case AudioType.Master: _snapshotsVolumes[AudioSnapshotState.Menu][type] = value; _snapshotsVolumes[AudioSnapshotState.Action][type] = value; _snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value; _mixer.SetFloat(_audioMixerGroups[type].name, volume); break; case AudioType.ActionMusic: _snapshotsVolumes[AudioSnapshotState.Action][type] = value; _mixer.SetFloat(_audioMixerGroups[type].name, volume); break; case AudioType.PeacefulMusic: _snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value; _mixer.SetFloat(_audioMixerGroups[type].name, volume); break; case AudioType.MenuMusic: _snapshotsVolumes[AudioSnapshotState.Menu][type] = value; _mixer.SetFloat(_audioMixerGroups[type].name, volume); break; case AudioType.SFX: case AudioType.Dispatcher: case AudioType.Courier: case AudioType.Enemies: _snapshotsVolumes[AudioSnapshotState.Action][type] = value; _snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value; _mixer.SetFloat(_audioMixerGroups[type].name, volume); break; } } public async UniTask Initialize() { _mixer = _audioFactory.AudioMixer; _musicAudioGroups.Add(MusicType.Menu, _mixer.FindMatchingGroups(AudioGroups.MenuMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Death, _mixer.FindMatchingGroups(AudioGroups.DeathMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Action, _mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Peaceful, _mixer.FindMatchingGroups(AudioGroups.PeacefulMusicGroup)[0]); _cancellationTokenSources.Add(MusicType.Menu, new()); _cancellationTokenSources.Add(MusicType.Death, new()); _cancellationTokenSources.Add(MusicType.Action, new()); _cancellationTokenSources.Add(MusicType.Peaceful, new()); _musicAudioSources.Add(MusicType.Menu, null); _musicAudioSources.Add(MusicType.Death, null); _musicAudioSources.Add(MusicType.Action, null); _musicAudioSources.Add(MusicType.Peaceful, null); _audioMixerGroups.Add(AudioType.Master, _mixer.FindMatchingGroups(AudioGroups.MasterGroup)[0]); _audioMixerGroups.Add(AudioType.SFX, _mixer.FindMatchingGroups(AudioGroups.SFXGroup)[0]); _audioMixerGroups.Add(AudioType.Dispatcher, _mixer.FindMatchingGroups(AudioGroups.DispatcherMusicGroup)[0]); _audioMixerGroups.Add(AudioType.Courier, _mixer.FindMatchingGroups(AudioGroups.CourierGroup)[0]); _audioMixerGroups.Add(AudioType.Enemies, _mixer.FindMatchingGroups(AudioGroups.EnemiesGroup)[0]); _audioMixerGroups.Add(AudioType.MenuMusic, _musicAudioGroups[MusicType.Menu]); _audioMixerGroups.Add(AudioType.DeathMusic, _musicAudioGroups[MusicType.Death]); _audioMixerGroups.Add(AudioType.ActionMusic, _musicAudioGroups[MusicType.Action]); _audioMixerGroups.Add(AudioType.PeacefulMusic, _musicAudioGroups[MusicType.Peaceful]); await _audioFactory.LoadAudioSourcePrefab(_audioSourcePool.Initialize); } private Dictionary _cancellationTokenSources = new(); private Dictionary _musicAudioSources = new(); private Dictionary _musicAudioGroups = new(); public async UniTaskVoid PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType musicType) { if (!_musicAudioSources[musicType]) { _musicAudioSources[musicType] = _audioSourcePool.GetSource(); _musicAudioSources[musicType].spatialBlend = _settings[AudioSourceType.NoBlend].spatialBlend; _musicAudioSources[musicType].outputAudioMixerGroup = _musicAudioGroups[musicType]; } _musicAudioSources[musicType].resource = _audioFactory.MusicDictionary[musicType][ Random.Range(0, _audioFactory.MusicDictionary[musicType].Count)]; _musicAudioSources[musicType].Play(); await UniTask.NextFrame(); var (isCanceled, _) = await UniTask.WaitUntilValueChanged(_musicAudioSources[musicType], _ => !_musicAudioSources[musicType].isPlaying, cancellationToken: _cancellationTokenSources[musicType].Token).SuppressCancellationThrow(); Debug.LogError("Music skipping"); if (isCanceled) { _cancellationTokenSources[musicType].Dispose(); _cancellationTokenSources[musicType] = new(); _audioSourcePool.ReleaseSource(_musicAudioSources[musicType]); _musicAudioSources[musicType] = null; return; } PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(musicType).Forget(); } public async UniTaskVoid GladlyAskToStopMusic(MusicType musicType, AudioSnapshotState audioSnapshotState = AudioSnapshotState.None, float time = 1f, Action callback = null) { //TransitToSnapshot(audioSnapshotState, time); await UniTask.WaitForSeconds(time); _cancellationTokenSources[musicType].Cancel(); callback?.Invoke(); } public void StopMusic() { _peacefulMusicSource.Stop(); _actionMusicSource.Stop(); _cancellationTokenSources[MusicType.Peaceful].Cancel(); _cancellationTokenSources[MusicType.Action].Cancel(); } public void GladlyAskToStopMusic(Action callback = null) { // TransitToSnapshot(AudioSnapshotState.None); _peacefulMusicSource.Stop(); _actionMusicSource.Stop(); callback?.Invoke(); } public void TransitToSnapshot(AudioSnapshotState state, float time = 1) { foreach (var key in _audioMixerGroups.Keys) _mixer.DOSetFloat(_audioMixerGroups[key].name, Mathf.Log10(_snapshotsVolumes[state][key]) * 20, time); } public void PlaySFX(SFXSounds sfxId, Vector3 position) { _audioSourcePool .PlayOneShot(_audioFactory.SFXAudios[(int)sfxId], position, _audioMixerGroups[AudioType.SFX]).Forget(); } public void ApplyStun(float value, float time) { _mixer.DOSetFloat("Stun", value, time); } public void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position) { _audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position, _audioMixerGroups[AudioType.Enemies]).Forget(); } public AudioSource GetSourceOfGroup(AudioType group) { AudioSource source = _audioSourcePool.GetSource(); source.outputAudioMixerGroup = _audioMixerGroups[group]; return source; } public void ReleaseSourceOfGroup(AudioSource source) => _audioSourcePool.ReleaseSource(source); // Чинить долго и дорого пусть Артёмов будет много, ведь он наш друг public event IAudioService.OnAudioPlayingDelegate OnDispatcherStarted; public event IAudioService.OnAudioPlayingDelegate OnDispatcherEnded; public void PlayDispatcher(DispatcherSounds dispatcherSoundId) { OnDispatcherStarted?.Invoke(); _audioSourcePool.PlayOneShot(_audioFactory.DispatcherAudios[(int)dispatcherSoundId], _audioMixerGroups[AudioType.Dispatcher], AudioSourceType.NoBlend, () => OnDispatcherEnded?.Invoke()).Forget(); } public void PlayCourier(CourierSounds courierSounds) { _audioSourcePool.PlayOneShot(_audioFactory.CourierAudios[(int)courierSounds], _audioMixerGroups[AudioType.Courier], AudioSourceType.NoBlend).Forget(); } private CancellationTokenSource _testClipCancellationTokenSource = new(); private void ResetCurrentTestAudioSource() => _currentTestClipAudioType = AudioType.Master; private AudioType _currentTestClipAudioType; // TODO Засунуть в словарь public void PlayTestClip(AudioType audioType) { if (_currentTestClipAudioType == audioType) return; _testClipCancellationTokenSource.Cancel(); _testClipCancellationTokenSource = new(); _currentTestClipAudioType = audioType; _audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType], _audioMixerGroups[audioType], _testClipCancellationTokenSource, AudioSourceType.NoBlend, ResetCurrentTestAudioSource).Forget(); } } }