352 lines
16 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
} |