Files
CourierOld/Game/CourierOld/Assets/Code/Audio/AudioService.cs
T
2025-11-27 21:30:00 +03:00

352 lines
16 KiB
C#

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 AudioMixerGroup _masterGroup,
// _musicGroup,
_actionMusicGroup,
_menuMusicGroup,
_peacefulMusicGroup,
_SFXGroup,
_creaturesGroup,
_dispatcherGroup,
_courierGroup,
_enemiesGroup;
private readonly AudioSourcePool _audioSourcePool;
private Dictionary<AudioSnapshotState, AudioMixerSnapshot> _snapshots = new();
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
public AudioService(IAudioFactory audioFactory, Dictionary<AudioSourceType, AudioSourceSettingsConfig> settings)
{
_audioFactory = audioFactory;
_audioSourcePool = new(settings);
_settings = settings;
}
// TODO Грязно, убрать снапшоты НАХУЙ
public float GetSetting(AudioType type)
{
float value;
switch (type)
{
case AudioType.SFX:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.GetFloat(_SFXGroup.name, out value);
break;
case AudioType.Master:
_snapshots[AudioSnapshotState.Menu].audioMixer.GetFloat(_masterGroup.name, out value);
break;
case AudioType.Creatures:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.GetFloat(_creaturesGroup.name, out value);
break;
case AudioType.GameplayMusic:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.GetFloat(_peacefulMusicGroup.name, out value);
break;
case AudioType.MenuMusic:
_snapshots[AudioSnapshotState.Menu].audioMixer.GetFloat(_menuMusicGroup.name, out value);
break;
case AudioType.DispatcherSounds:
_snapshots[AudioSnapshotState.Action].audioMixer.GetFloat(_dispatcherGroup.name, out value);
break;
default:
value = 1;
break;
}
value = Mathf.Pow(10f, value / 20);
return value;
}
// TODO Грязно, убрать снапшоты НАХУЙ
public void ChangeSetting(AudioType type, float value)
{
float volume = Mathf.Log10(value) * 20;
switch (type)
{
case AudioType.SFX:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.SetFloat(_SFXGroup.name, volume);
_snapshots[AudioSnapshotState.Action].audioMixer.SetFloat(_SFXGroup.name, volume);
break;
case AudioType.Master:
_snapshots[AudioSnapshotState.Menu].audioMixer.SetFloat(_masterGroup.name, volume);
_snapshots[AudioSnapshotState.Peaceful].audioMixer.SetFloat(_masterGroup.name, volume);
_snapshots[AudioSnapshotState.Action].audioMixer.SetFloat(_masterGroup.name, volume);
break;
case AudioType.Creatures:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.SetFloat(_creaturesGroup.name, volume);
_snapshots[AudioSnapshotState.Action].audioMixer.SetFloat(_creaturesGroup.name, volume);
break;
case AudioType.GameplayMusic:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.SetFloat(_peacefulMusicGroup.name, volume);
_snapshots[AudioSnapshotState.Action].audioMixer.SetFloat(_actionMusicGroup.name, volume);
break;
case AudioType.MenuMusic:
_snapshots[AudioSnapshotState.Menu].audioMixer.SetFloat(_menuMusicGroup.name, volume);
break;
case AudioType.DispatcherSounds:
_snapshots[AudioSnapshotState.Peaceful].audioMixer.SetFloat(_dispatcherGroup.name, volume);
_snapshots[AudioSnapshotState.Action].audioMixer.SetFloat(_dispatcherGroup.name, volume);
break;
}
// _mixer.FindSnapshot("Action").audioMixer.SetFloat("music", volume);
}
public async UniTask Initialize()
{
// _mixer = await _audioFactory.CreateMixer();
_mixer = _audioFactory.AudioMixer;
_masterGroup = _mixer.FindMatchingGroups(AudioGroups.MasterGroup)[0];
_menuMusicGroup = _mixer.FindMatchingGroups(AudioGroups.MenuMusicGroup)[0];
_actionMusicGroup = _mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0];
_peacefulMusicGroup = _mixer.FindMatchingGroups(AudioGroups.PeacefulMusicGroup)[0];
// _musicGroup = _mixer.FindMatchingGroups(AudioGroups.MusicGroup)[0];
_musicAudioGroups.Add(MusicType.Menu, _mixer.FindMatchingGroups(AudioGroups.MenuMusicGroup)[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.Action, new());
_cancellationTokenSources.Add(MusicType.Peaceful, new());
_musicAudioSources.Add(MusicType.Menu, null);
_musicAudioSources.Add(MusicType.Action, null);
_musicAudioSources.Add(MusicType.Peaceful, null);
_SFXGroup = _mixer.FindMatchingGroups(AudioGroups.SFXGroup)[0];
_creaturesGroup = _mixer.FindMatchingGroups(AudioGroups.CreaturesGroup)[0];
_dispatcherGroup = _mixer.FindMatchingGroups(AudioGroups.DispatcherMusicGroup)[0];
_courierGroup = _mixer.FindMatchingGroups(AudioGroups.CourierMusicGroup)[0];
_enemiesGroup = _mixer.FindMatchingGroups(AudioGroups.EnemiesMusicGroup)[0];
_snapshots[AudioSnapshotState.None] = _mixer.FindSnapshot(AudioGroups.NoneSnapshot);
_snapshots[AudioSnapshotState.Peaceful] = _mixer.FindSnapshot(AudioGroups.PeacefulSnapshot);
_snapshots[AudioSnapshotState.Action] = _mixer.FindSnapshot(AudioGroups.ActionSnapshot);
_snapshots[AudioSnapshotState.Menu] = _mixer.FindSnapshot(AudioGroups.MenuSnapshot);
await _audioFactory.LoadAudioSourcePrefab(_audioSourcePool.Initialize);
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
private Dictionary<MusicType, CancellationTokenSource> _cancellationTokenSources = new();
private Dictionary<MusicType, AudioSource> _musicAudioSources = new();
private Dictionary<MusicType, AudioMixerGroup> _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.WaitUntilValueChanged(_musicAudioSources[musicType],
// _ => !_musicAudioSources[musicType].isPlaying,
// cancellationToken: _cancellationTokenSources[musicType].Token);
await UniTask.NextFrame();
// Debug.Log($"Playing {musicType}");
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(_musicAudioSources[musicType],
_ => !_musicAudioSources[musicType].isPlaying,
cancellationToken: _cancellationTokenSources[musicType].Token).SuppressCancellationThrow();
// Debug.Log($"Finished {musicType}");
if (isCanceled)
{
// Debug.Log($"Cancelled {musicType}");
_cancellationTokenSources[musicType].Dispose();
_cancellationTokenSources[musicType] = new();
_audioSourcePool.ReleaseSource(_musicAudioSources[musicType]);
_musicAudioSources[musicType] = null;
return;
}
PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(musicType);
}
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();
}
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void StopMusic()
{
_peacefulMusicSource.Stop();
_actionMusicSource.Stop();
}
public void GladlyAskToStopMusic(Action callback = null)
{
TransitToSnapshot(AudioSnapshotState.None);
_peacefulMusicSource.Stop();
_actionMusicSource.Stop();
callback?.Invoke();
}
public void TransitToSnapshot(AudioSnapshotState state, float time = 1f)
{
_snapshots[state].TransitionTo(time);
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void PlaySFX(SFXSounds sfxId, Vector3 position)
{
_audioSourcePool.PlayOneShot(_audioFactory.SFXAudios[(int)sfxId], position, _SFXGroup);
}
public void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position)
{
_audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position, _enemiesGroup);
}
// Чинить долго и дорого пусть Артёмов будет много, ведь он наш друг
public event IAudioService.OnAudioPlayingDelegate OnDispatcherStarted;
public event IAudioService.OnAudioPlayingDelegate OnDispatcherEnded;
public void PlayDispatcher(DispatcherSounds dispatcherSoundId)
{
OnDispatcherStarted?.Invoke();
_audioSourcePool.PlayOneShot(_audioFactory.DispatcherAudios[(int)dispatcherSoundId], _dispatcherGroup,
AudioSourceType.NoBlend, () => OnDispatcherEnded?.Invoke());
}
private CancellationTokenSource _testClipCancellationTokenSource = new();
private void ResetCurrentTestAudioSource() => _currentTestClipAudioType = AudioType.Master;
// TODO Засунуть в словарь
public void PlayTestClip(AudioType audioType)
{
if(_currentTestClipAudioType == audioType) return;
_testClipCancellationTokenSource.Cancel();
_testClipCancellationTokenSource = new();
_currentTestClipAudioType = audioType;
switch (audioType)
{
case AudioType.SFX:
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_SFXGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
case AudioType.Master:
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_masterGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
case AudioType.Creatures:
_audioSourcePool.PlayOneShot( _audioFactory.AudioTestClips[audioType],
_creaturesGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
case AudioType.GameplayMusic:
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_actionMusicGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
case AudioType.MenuMusic:
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_menuMusicGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
case AudioType.DispatcherSounds:
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_dispatcherGroup, _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource);
break;
}
}
private AudioSource _testClipAudioSource;
private AudioType _currentTestClipAudioType;
// Эффективно и просто, может не так хитровыебано
private void ReleaseTestAudioClipSource()
{
_testClipAudioSource = null;
}
// TODO Засунуть в словарь
public void PlayTestClipDeprecated(AudioType audioType)
{
if (!_testClipAudioSource) _testClipAudioSource = _audioSourcePool.GetSource();
else if (_currentTestClipAudioType != audioType) _testClipAudioSource.Stop();
else return;
_currentTestClipAudioType = audioType;
switch (audioType)
{
case AudioType.SFX:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_SFXGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
case AudioType.Master:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_masterGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
case AudioType.Creatures:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_creaturesGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
case AudioType.GameplayMusic:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_actionMusicGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
case AudioType.MenuMusic:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_menuMusicGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
case AudioType.DispatcherSounds:
_audioSourcePool.PlayOneShot(_testClipAudioSource, _audioFactory.AudioTestClips[audioType],
_dispatcherGroup,
AudioSourceType.NoBlend, ReleaseTestAudioClipSource);
break;
}
}
}
}