Audio fixes

This commit is contained in:
Savya Bikram Shah
2026-05-29 19:07:21 +05:45
parent 0b22ed6d09
commit 41c9969996
300 changed files with 3549 additions and 33 deletions

View File

@@ -10,12 +10,14 @@ namespace Darkmatter.Services.Audio
public class AudioServiceModule : MonoBehaviour, IModule
{
[SerializeField] private SfxCatalogSO sfxCatalog;
[SerializeField] private AudioService audioService;
public void Register(IContainerBuilder builder)
{
if (sfxCatalog != null)
builder.RegisterComponent(sfxCatalog);
builder.Register<IAudioService, AudioService>(Lifetime.Singleton);
if (audioService != null)
builder.RegisterComponent<IAudioService>(audioService);
builder.Register<ISfxPlayer, SfxPlayer>(Lifetime.Singleton);
}
}

View File

@@ -2,6 +2,7 @@ using Darkmatter.Core.Contracts.Services.Music;
using Darkmatter.Libs.Installers;
using Darkmatter.Services.Music.Systems;
using UnityEngine;
using UnityEngine.Audio;
using VContainer;
using VContainer.Unity;
@@ -12,10 +13,11 @@ namespace Darkmatter.Services.Music.Installers
[SerializeField] private AudioClip defaultTrack;
[SerializeField, Range(0f, 1f)] private float defaultVolume = 0.7f;
[SerializeField, Min(0f)] private float crossFadeSeconds = 0.4f;
[SerializeField] private AudioMixerGroup mixerGroup;
public void Register(IContainerBuilder builder)
{
builder.RegisterInstance(new MusicConfig(defaultTrack, defaultVolume, crossFadeSeconds));
builder.RegisterInstance(new MusicConfig(defaultTrack, defaultVolume, crossFadeSeconds, mixerGroup));
builder.RegisterEntryPoint<MusicService>(Lifetime.Singleton).As<IMusicService>();
}
}

View File

@@ -1,5 +1,6 @@
using System;
using UnityEngine;
using UnityEngine.Audio;
namespace Darkmatter.Services.Music.Systems
{
@@ -9,12 +10,14 @@ namespace Darkmatter.Services.Music.Systems
public AudioClip DefaultTrack { get; }
public float DefaultVolume { get; }
public float CrossFadeSeconds { get; }
public AudioMixerGroup MixerGroup { get; }
public MusicConfig(AudioClip defaultTrack, float defaultVolume, float crossFadeSeconds)
public MusicConfig(AudioClip defaultTrack, float defaultVolume, float crossFadeSeconds, AudioMixerGroup mixerGroup)
{
DefaultTrack = defaultTrack;
DefaultVolume = defaultVolume;
CrossFadeSeconds = crossFadeSeconds;
MixerGroup = mixerGroup;
}
}
}

View File

@@ -1,9 +1,8 @@
using System;
using Darkmatter.Core.Contracts.Services.Audio;
using System.Threading;
using Cysharp.Threading.Tasks;
using Darkmatter.Core.Contracts.Services.Music;
using Darkmatter.Core.Data.Dynamic.Services.Audio;
using Darkmatter.Core.Data.Signals.Features.AppBoot;
using Darkmatter.Core.Enums.Services.Audio;
using Darkmatter.Libs.Observer;
using UnityEngine;
using VContainer.Unity;
@@ -12,59 +11,136 @@ namespace Darkmatter.Services.Music.Systems
{
public class MusicService : IMusicService, IStartable, IDisposable
{
private readonly IAudioService _audio;
private readonly IEventBus _bus;
private readonly MusicConfig _config;
private AudioHandle _current;
private GameObject _host;
private AudioSource _source;
private IDisposable _introSub;
private CancellationTokenSource _fadeCts;
public MusicService(IAudioService audio, IEventBus bus, MusicConfig config)
public MusicService(IEventBus bus, MusicConfig config)
{
_audio = audio;
_bus = bus;
_config = config;
}
public void Start()
{
_host = new GameObject("[MusicService]");
UnityEngine.Object.DontDestroyOnLoad(_host);
_source = _host.AddComponent<AudioSource>();
_source.playOnAwake = false;
_source.loop = true;
_source.spatialBlend = 0f;
_source.volume = _config.DefaultVolume;
if (_config.MixerGroup != null) _source.outputAudioMixerGroup = _config.MixerGroup;
_introSub = _bus.Subscribe<IntroCompletedSignal>(OnIntroCompleted);
}
private void OnIntroCompleted(IntroCompletedSignal _)
{
if (_config.DefaultTrack != null && !_current.IsValid)
if (_config.DefaultTrack != null && (_source == null || !_source.isPlaying))
Play(_config.DefaultTrack, _config.DefaultVolume, loop: true);
}
public void Play(AudioClip clip, float volume01 = 1f, bool loop = true)
{
if (clip == null) return;
if (_current.IsValid) _audio.Stop(_current, _config.CrossFadeSeconds);
if (clip == null || _source == null) return;
CancelFade();
var req = new AudioRequest(
clip: clip,
channel: AudioChannel.Music,
mode: loop ? AudioPlayMode.Loop : AudioPlayMode.OneShot,
stopChannelBeforePlay: true,
volume01: Mathf.Clamp01(volume01));
_current = _audio.Play(req);
if (_source.isPlaying && _config.CrossFadeSeconds > 0f)
{
_fadeCts = new CancellationTokenSource();
CrossFadeAsync(clip, Mathf.Clamp01(volume01), loop, _fadeCts.Token).Forget();
return;
}
_source.clip = clip;
_source.loop = loop;
_source.volume = Mathf.Clamp01(volume01);
_source.Play();
}
public void Stop(float fadeOutSeconds = 0f)
{
if (_current.IsValid) _audio.Stop(_current, fadeOutSeconds);
_current = AudioHandle.Invalid;
if (_source == null) return;
CancelFade();
if (fadeOutSeconds <= 0f) { _source.Stop(); return; }
_fadeCts = new CancellationTokenSource();
FadeOutAsync(fadeOutSeconds, _fadeCts.Token).Forget();
}
public void SetVolume(float volume01)
{
if (_current.IsValid) _audio.SetVolume(_current, Mathf.Clamp01(volume01));
if (_source == null) return;
_source.volume = Mathf.Clamp01(volume01);
}
private async UniTaskVoid CrossFadeAsync(AudioClip nextClip, float targetVolume, bool loop, CancellationToken ct)
{
try
{
float start = _source.volume;
float t = 0f;
float dur = _config.CrossFadeSeconds;
while (t < dur)
{
ct.ThrowIfCancellationRequested();
t += Time.unscaledDeltaTime;
_source.volume = Mathf.Lerp(start, 0f, t / dur);
await UniTask.Yield(PlayerLoopTiming.Update, ct);
}
_source.Stop();
_source.clip = nextClip;
_source.loop = loop;
_source.volume = 0f;
_source.Play();
t = 0f;
while (t < dur)
{
ct.ThrowIfCancellationRequested();
t += Time.unscaledDeltaTime;
_source.volume = Mathf.Lerp(0f, targetVolume, t / dur);
await UniTask.Yield(PlayerLoopTiming.Update, ct);
}
_source.volume = targetVolume;
}
catch (OperationCanceledException) { }
}
private async UniTaskVoid FadeOutAsync(float seconds, CancellationToken ct)
{
try
{
float start = _source.volume;
float t = 0f;
while (t < seconds)
{
ct.ThrowIfCancellationRequested();
t += Time.unscaledDeltaTime;
_source.volume = Mathf.Lerp(start, 0f, t / seconds);
await UniTask.Yield(PlayerLoopTiming.Update, ct);
}
_source.Stop();
_source.volume = start;
}
catch (OperationCanceledException) { }
}
private void CancelFade()
{
_fadeCts?.Cancel();
_fadeCts?.Dispose();
_fadeCts = null;
}
public void Dispose()
{
CancelFade();
_introSub?.Dispose();
if (_host != null) UnityEngine.Object.Destroy(_host);
}
}
}