260 lines
11 KiB
C#
260 lines
11 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 Dictionary<AudioType, AudioMixerGroup> _audioMixerGroups = new();
|
|
private readonly AudioSourcePool _audioSourcePool;
|
|
|
|
// Юнити не может - говнокод поможет.
|
|
private Dictionary<AudioSnapshotState, Dictionary<AudioType, float>> _snapshotsVolumes = new();
|
|
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
|
|
|
|
public AudioService(IAudioFactory audioFactory,
|
|
Dictionary<AudioSourceType, AudioSourceSettingsConfig> 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<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.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();
|
|
}
|
|
}
|
|
} |