Files
2026-05-26 16:15:54 +08:00

324 lines
11 KiB
C#

using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.AddressableAssets;
using UnityEngine.Assertions;
using asap.core.common;
using UnityEngine.ResourceManagement.AsyncOperations;
using System.Threading;
using System;
using UniRx;
namespace asap.core
{
public interface ISoundService
{
[Inject]
public IObjectPoolService poolService { set; }
public int MasterVol { get; set; }
public int BGMVol { get; set; }
public int SFXVol { get; set; }
public int VoiceVol { get; set; }
public Task InitAsync(string mixerAddr = "AudioMixer", string soundPrefabAddr = "sound",
string bgmGroupId = "BGM", string fishingGroupId = "Fishing", string uiGroupId = "UI", string voiceGroupId = "Voice");
public void Release();
public Sound GetNewBgmSound(AudioClip clip, bool loop = true, int priority = 0, float time = 0);
public Sound GetNewFishingSound(AudioClip clip, bool loop = false, int priority = 128, float time = 0);
public Sound GetNewUISound(AudioClip clip, bool loop = false, int priority = 128, float time = 0);
public Sound GetNewVoiceSound(AudioClip clip);
public void ReturnSound(Sound sound);
public void FadeIn(Sound sound, float duration);
public void FadeOut(Sound sound, float duration);
}
public struct LowpassEffectEvt
{
public bool active;
public float transTime;
}
public class SoundService : ISoundService
{
[Inject]
public IObjectPoolService poolService { set; private get; }
private AudioMixer mixer;
private AudioMixerGroup bgmGroup;
private AudioMixerGroup fishingGroup;
private AudioMixerGroup uiGroup;
private AudioMixerGroup voiceGroup;
private AsyncOperationHandle<AudioMixer> mixerHandle;
private AsyncOperationHandle<GameObject> soundPrefabHandle;
private Dictionary<Sound, CancellationTokenSource> SoundFadeTaskDic;
private float bgmVol = 0;
private float fishingVol = 0;
private const float Min_Vol = -80.0f;
private const float Max_Vol = 20.0f;
private const string Master_Vol_Param_Name = "masterVol";
private const string Bgm_Vol_Param_Name = "bgmVol";
private const string Fishing_Vol_Param_Name = "fishingVol";
private const string UI_Vol_Param_Name = "uiVol";
private const string Voice_Vol_Param_Name = "voiceVol";
private const string Lowpass_Cutoff_Frq = "low_cut";
private AudioMixerSnapshot normal;
//private AudioMixerSnapshot lowpass;
private IDisposable lowpassEvtDisposable;
private int _masterVol;
public int MasterVol
{
get { return _masterVol; }
set
{
_masterVol = value;
mixer.SetFloat(Master_Vol_Param_Name, VolInt2Float(_masterVol));
}
}
private int _bgmVol;
public int BGMVol
{
get { return _bgmVol; }
set
{
_bgmVol = value;
mixer.SetFloat(Bgm_Vol_Param_Name, VolInt2Float(_bgmVol) + bgmVol);
}
}
private int _sfxVol;
public int SFXVol
{
get { return _sfxVol; }
set
{
_sfxVol = value;
mixer.SetFloat(Fishing_Vol_Param_Name, VolInt2Float(_sfxVol) + fishingVol);
mixer.SetFloat(UI_Vol_Param_Name, VolInt2Float(_sfxVol));
}
}
private int _voiceVol;
public int VoiceVol
{
get { return _voiceVol; }
set
{
_voiceVol = value;
mixer.SetFloat(Voice_Vol_Param_Name, VolInt2Float(_voiceVol));
}
}
private float VolInt2Float(int value)
{
var vol = Mathf.Clamp(value, 0f, 100f) / 100.0f;
vol = Min_Vol + (Max_Vol - Min_Vol) * vol;
return vol;
}
private AudioMixerGroup FindMixerGroup(string groupId)
{
var groups = mixer.FindMatchingGroups(groupId);
if (groups != null && groups.Length > 0)
return groups[0];
else
{
Debug.LogWarning("Audio mixer group {groupId} is not found");
return null;
}
}
public async Task InitAsync(string mixerAddr = "AudioMixer", string soundPrefabAddr = "sound",
string bgmGroupId = "BGM", string fishingGroupId = "Fishing", string uiGroupId = "UI", string voiceGroupId = "Voice")
{
SoundFadeTaskDic = new Dictionary<Sound, CancellationTokenSource>();
mixerHandle = Addressables.LoadAssetAsync<AudioMixer>(mixerAddr);
mixer = await mixerHandle.Task;
Assert.IsNotNull(mixer);
mixer.GetFloat(Bgm_Vol_Param_Name, out bgmVol);
mixer.GetFloat(Fishing_Vol_Param_Name, out fishingVol);
bgmGroup = FindMixerGroup(bgmGroupId);
fishingGroup = FindMixerGroup(fishingGroupId);
uiGroup = FindMixerGroup(uiGroupId);
voiceGroup = FindMixerGroup(voiceGroupId);
normal = mixer.FindSnapshot("Snapshot");// ("normal");
//lowpass = mixer.FindSnapshot("lowpass");
Assert.IsNotNull(bgmGroup);
Assert.IsNotNull(fishingGroup);
Assert.IsNotNull(uiGroup);
soundPrefabHandle = Addressables.LoadAssetAsync<GameObject>(soundPrefabAddr);
var soundPrefab = await soundPrefabHandle.Task;
Assert.IsNotNull(soundPrefab);
var sound = soundPrefab.GetComponent<Sound>();
Assert.IsNotNull(sound);
lowpassEvtDisposable = GContext.OnEvent<LowpassEffectEvt>().Subscribe(OnLowpassEffectEvt);
poolService.CreatePool<Sound>(sound, 10, 50);
}
private void OnLowpassEffectEvt(LowpassEffectEvt evt)
{
//if (evt.active)
//{
// lowpass.TransitionTo(evt.transTime);
// return;
//}
normal.TransitionTo(evt.transTime);
}
public void Release()
{
foreach (var source in SoundFadeTaskDic.Values)
{
source.Cancel();
}
SoundFadeTaskDic.Clear();
SoundFadeTaskDic = null;
poolService.DestroyPool<Sound>();
Addressables.Release(mixerHandle);
Addressables.Release(soundPrefabHandle);
lowpassEvtDisposable.Dispose();
lowpassEvtDisposable = null;
}
public void FadeIn(Sound sound, float duration)
{
if (SoundFadeTaskDic.TryGetValue(sound, out var s))
{
s.Cancel();
SoundFadeTaskDic[sound].Dispose();
SoundFadeTaskDic.Remove(sound);
}
var source = new CancellationTokenSource();
var token = source.Token;
SoundFadeTaskDic.Add(sound, source);
DoFade(0f, 1f, sound, duration, token);
}
public void FadeOut(Sound sound, float duration)
{
if (SoundFadeTaskDic.TryGetValue(sound, out var s))
{
s.Cancel();
SoundFadeTaskDic[sound].Dispose();
SoundFadeTaskDic.Remove(sound);
}
var source = new CancellationTokenSource();
var token = source.Token;
SoundFadeTaskDic.Add(sound, source);
DoFade(1f, 0f, sound, duration, token);
}
public Sound GetNewBgmSound(AudioClip clip, bool loop = true, int priority = 0, float time = 0)
{
var sound = poolService.SpawnObject<Sound>();
Assert.IsNotNull(sound, "Sound Pool reached max num");
var audioSource = sound.audioSource;
audioSource.clip = clip;
audioSource.loop = loop;
audioSource.priority = priority;
audioSource.time = time;
audioSource.outputAudioMixerGroup = bgmGroup;
audioSource.Pause();
return sound;
}
public Sound GetNewFishingSound(AudioClip clip, bool loop = false, int priority = 128, float time = 0)
{
var sound = poolService.SpawnObject<Sound>();
Assert.IsNotNull(sound, "Sound Pool reached max num");
var audioSource = sound.audioSource;
audioSource.clip = clip;
audioSource.loop = loop;
audioSource.priority = priority;
audioSource.time = time;
audioSource.outputAudioMixerGroup = fishingGroup;
return sound;
}
public Sound GetNewUISound(AudioClip clip, bool loop = false, int priority = 128, float time = 0)
{
var sound = poolService.SpawnObject<Sound>();
Assert.IsNotNull(sound, "Sound Pool reached max num");
var audioSource = sound.audioSource;
audioSource.clip = clip;
audioSource.loop = loop;
audioSource.priority = priority;
audioSource.time = time;
audioSource.outputAudioMixerGroup = uiGroup;
return sound;
}
public Sound GetNewVoiceSound(AudioClip clip)
{
var sound = poolService.SpawnObject<Sound>();
Assert.IsNotNull(sound, "Sound Pool reached max num");
var audioSource = sound.audioSource;
audioSource.clip = clip;
audioSource.loop = false;
audioSource.priority = 128;
audioSource.time = 0;
audioSource.outputAudioMixerGroup = voiceGroup;
return sound;
}
public void ReturnSound(Sound sound)
{
if (SoundFadeTaskDic == null || sound == null)
return;
if (SoundFadeTaskDic.TryGetValue(sound, out var s))
{
s.Cancel();
SoundFadeTaskDic[sound].Dispose();
SoundFadeTaskDic.Remove(sound);
}
if (sound.enabled)
{
poolService.DespawnObject(sound);
}
}
private async void DoFade(float startVol, float endVol, Sound sound, float duration, CancellationToken ct)
{
var elapsedTime = 0f;
sound.audioSource.volume = startVol;
try
{
while (elapsedTime < duration)
{
if (ct.IsCancellationRequested)
{
break;
}
else
{
sound.audioSource.volume = Mathf.Lerp(startVol, endVol, elapsedTime / duration);
elapsedTime += Time.deltaTime;
await Task.Yield();
}
}
Assert.IsNotNull(sound);
sound.audioSource.volume = endVol;
}
finally
{
if (!ct.IsCancellationRequested && SoundFadeTaskDic != null)
{
SoundFadeTaskDic[sound].Dispose();
SoundFadeTaskDic.Remove(sound);
}
}
}
}
}