208 lines
8.2 KiB
C#
208 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Code.Infrastructure.Configs;
|
|
using Code.Infrastructure.EventBus;
|
|
using Cysharp.Threading.Tasks;
|
|
using PrimeTween;
|
|
using UnityEngine;
|
|
using UnityEngine.AddressableAssets;
|
|
using UnityEngine.Audio;
|
|
|
|
namespace Code.Infrastructure.Audio
|
|
{
|
|
public interface IAudioService
|
|
{
|
|
AudioClip GetClip(AudioNames groupName, int id);
|
|
void PlayTest();
|
|
|
|
UniTaskVoid PlayOneShot(AudioNames groupName, int id, AudioGroupType audioGroupType, Vector3 position = new Vector3(),
|
|
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
|
AudioSource audioSource = null);
|
|
|
|
UniTaskVoid PlayRepeatedly(AudioNames groupName, int id, AudioGroupType audioGroupType,
|
|
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null);
|
|
|
|
bool CheckClip(AudioNames groupName, int id);
|
|
void CancelMusic(AudioNames groupName, int id);
|
|
void ReleaseCancellationSource(AudioNames groupName, int id);
|
|
void ApplyParameter(string effectName, float value, float time, Action callback = null);
|
|
void ChangeGroupVolume(AudioGroupType groupType, float value, float time, Action callback = null);
|
|
}
|
|
|
|
public partial class AudioService : IAudioClipsEventHandle, IAudioService
|
|
{
|
|
private EventBus.EventBus _eventBus;
|
|
private IAudioFactory _audioFactory;
|
|
private IAudioSourcePool _pool;
|
|
private Dictionary<AudioNames, Dictionary<int, AudioClip>> _audioClips = new();
|
|
private AudioMixer _mixer;
|
|
|
|
private Dictionary<AudioGroupType, AudioMixerGroup> _audioMixerGroups = new();
|
|
public AudioService(EventBus.EventBus eventBus, IAudioFactory audioFactory, IAudioSourcePool pool, IConfigLocator configLocator)
|
|
{
|
|
_eventBus = eventBus;
|
|
_audioFactory = audioFactory;
|
|
_pool = pool;
|
|
|
|
_eventBus.Subscribe(this);
|
|
|
|
for (int i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++)
|
|
_audioClips.Add((AudioNames)i, new Dictionary<int, AudioClip>());
|
|
|
|
LoadMixerAsset(configLocator.TryGetConfigAssetReference<AudioMixerConfigConfig>()).Forget();
|
|
}
|
|
|
|
private partial void InitializeMixer();
|
|
|
|
private async UniTaskVoid LoadMixerAsset(AssetReference audioMixerConfig)
|
|
{
|
|
var audioMixer = await Addressables.LoadAssetAsync<AudioMixerConfigConfig>(audioMixerConfig);
|
|
|
|
_mixer = audioMixer.mixerAsset;
|
|
|
|
InitializeMixer();
|
|
|
|
_eventBus.RaiseEvent<IAudioServiceHandle>(x=> x.AudioServiceLoaded());
|
|
}
|
|
public void PlayTest()
|
|
{
|
|
_pool.GetSource(AudioSourceType.NoBlend).PlayOneShot(GetClip(AudioNames.UISounds, 0));
|
|
}
|
|
|
|
public async UniTaskVoid PlayOneShot(AudioNames groupName, int id, AudioGroupType audioGroupType, Vector3 position = new Vector3(),
|
|
AudioSourceType sourceType = AudioSourceType.NoBlend,
|
|
AudioSource audioSource = null)
|
|
{
|
|
if (!CheckClip(groupName, id)) return;
|
|
|
|
if (!audioSource) audioSource = _pool.GetSource(sourceType);
|
|
|
|
audioSource.transform.position = position;
|
|
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
|
audioSource.PlayOneShot(GetClip(groupName, id));
|
|
|
|
await UniTask.NextFrame();
|
|
await UniTask.WaitUntilValueChanged(audioSource, _ => !audioSource.isPlaying);
|
|
|
|
_pool.ReleaseSource(audioSource);
|
|
}
|
|
|
|
private Dictionary<AudioNameIdPair, CancellationTokenSource> _cancellationTokenSources = new();
|
|
|
|
public void CancelMusic(AudioNames groupName, int id)
|
|
{
|
|
var key = new AudioNameIdPair(groupName, id);
|
|
|
|
if (!_cancellationTokenSources.ContainsKey(key)) return;
|
|
|
|
_cancellationTokenSources[key].Cancel();
|
|
_cancellationTokenSources.Remove(key);
|
|
}
|
|
|
|
public void ReleaseCancellationSource(AudioNames groupName, int id)
|
|
{
|
|
// _cancellationTokenSources[new KeyValuePair<AudioNames, int>(groupName, id)] = null; никаких полумер
|
|
_cancellationTokenSources.Remove(new AudioNameIdPair(groupName, id));
|
|
}
|
|
|
|
public async UniTaskVoid PlayRepeatedly(AudioNames groupName, int id, AudioGroupType audioGroupType,
|
|
AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null)
|
|
{
|
|
if (!CheckClip(groupName, id)) return;
|
|
|
|
if(cancellationTokenSource == null)
|
|
{
|
|
cancellationTokenSource = new CancellationTokenSource();
|
|
_cancellationTokenSources[new AudioNameIdPair(groupName, id)] = cancellationTokenSource;
|
|
}
|
|
|
|
if (!audioSource) audioSource = _pool.GetSource(sourceType);
|
|
if(!audioSource.resource) audioSource.resource = GetClip(groupName, id);
|
|
audioSource.outputAudioMixerGroup = _audioMixerGroups[audioGroupType];
|
|
audioSource.Play();
|
|
|
|
// await UniTask.WaitUntil(audioSource, _ => audioSource.isPlaying);
|
|
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(audioSource,
|
|
_ => !audioSource.isPlaying,
|
|
cancellationToken: cancellationTokenSource.Token).SuppressCancellationThrow();
|
|
|
|
if (isCanceled)
|
|
{
|
|
cancellationTokenSource.Dispose();
|
|
|
|
_pool.ReleaseSource(audioSource);
|
|
|
|
ReleaseCancellationSource(groupName,id);
|
|
|
|
return;
|
|
}
|
|
|
|
PlayRepeatedly(groupName, id, AudioGroupType.Music, sourceType, audioSource, cancellationTokenSource).Forget();
|
|
}
|
|
|
|
public AudioClip GetClip(AudioNames groupName, int id) => _audioClips[groupName][id];
|
|
|
|
public bool CheckClip(AudioNames groupName, int id) => _audioClips[groupName].ContainsKey(id);
|
|
|
|
public void AudioClipsLoaded(AudioNames groupname, Dictionary<int, AudioClip> clips)
|
|
{
|
|
_audioClips[groupname] = clips;
|
|
_eventBus.RaiseEvent<IReadyToPlayHandle>(x => x.OnReadyToPlay(groupname));
|
|
}
|
|
|
|
public void AudioClipsUnLoaded(AudioNames groupName)
|
|
{
|
|
_audioClips[groupName].Clear();
|
|
_audioClips[groupName] = null;
|
|
}
|
|
|
|
public void ApplyParameter(string effectName, float value, float time, Action callback = null)
|
|
{
|
|
_mixer.GetFloat(effectName, out float startValue);
|
|
Tween.Custom(startValue, value, time, newValue => {
|
|
_mixer.SetFloat(effectName, newValue);
|
|
}).OnComplete(callback);
|
|
}
|
|
|
|
public void ChangeGroupVolume(AudioGroupType groupType, float value, float time, Action callback = null)
|
|
{
|
|
ApplyParameter(_audioMixerGroups[groupType].name, Mathf.Log10(value == 0f ? 0.001f : value) * 20, time, callback);
|
|
}
|
|
|
|
private struct AudioNameIdPair : IEquatable<AudioNameIdPair>
|
|
{
|
|
public AudioNames AudioNames;
|
|
public int ID;
|
|
|
|
public AudioNameIdPair(AudioNames audioNames, int id)
|
|
{
|
|
AudioNames = audioNames;
|
|
ID = id;
|
|
}
|
|
public bool Equals(AudioNameIdPair other)
|
|
{
|
|
return AudioNames == other.AudioNames && ID == other.ID;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return obj is AudioNameIdPair other && Equals(other);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return HashCode.Combine((int)AudioNames, ID);
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface IReadyToPlayHandle : IGlobalSubscriber
|
|
{
|
|
void OnReadyToPlay(AudioNames groupName);
|
|
}
|
|
public interface IAudioServiceHandle : IGlobalSubscriber
|
|
{
|
|
void AudioServiceLoaded();
|
|
}
|
|
} |