SHA256
Add RMS Compressor
This commit is contained in:
7 files changed
+262
-36
No files matched your search
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ADOFAIRenderer.Util.Audio.Filter;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ADOFAIRenderer.Util.Audio
|
||||
@@ -51,7 +54,7 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error($"[AudioClipCache] Failed to cache and resample clip {clipName}: {e}");
|
||||
MelonLogger.Error($"[AudioClipCache] Failed to cache and resample clip {clipName}: {e}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -78,13 +81,16 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
}
|
||||
|
||||
private readonly List<SoundEvent> _activeEvents = new();
|
||||
private System.Runtime.CompilerServices.ConditionalWeakTable<AudioSource, SoundEvent> _sourceMappings = new();
|
||||
private ConditionalWeakTable<AudioSource, SoundEvent> _sourceMappings = new();
|
||||
private readonly object _lock = new();
|
||||
|
||||
// Target Audio Settings
|
||||
private readonly int _targetChannels;
|
||||
private readonly int _targetSampleRate;
|
||||
private const float MasterVolume = 0.05f;
|
||||
private const float MasterVolume = 1f;
|
||||
|
||||
// Audio Filter Settings
|
||||
private readonly LinkedList<IAudioFilter> _filters = new();
|
||||
|
||||
// BGM Cached Samples (Pre-resampled)
|
||||
private float[]? _bgmSamples;
|
||||
@@ -97,6 +103,19 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
{
|
||||
_targetChannels = targetChannels;
|
||||
_targetSampleRate = targetSampleRate;
|
||||
|
||||
var rmsCompressor = new RmsCompressor
|
||||
{
|
||||
ThresholdDb = -10, Ratio = 2,
|
||||
AttackTime = 0.1f, ReleaseTime = 0.1f,
|
||||
KneeDb = 10, RmsWindowTime = 0.02f
|
||||
};
|
||||
rmsCompressor.Initialize(_targetSampleRate, _targetChannels);
|
||||
_filters.AddLast(rmsCompressor);
|
||||
|
||||
var hardClip = new HardClip();
|
||||
hardClip.Initialize(_targetSampleRate, _targetChannels);
|
||||
_filters.AddLast(hardClip);
|
||||
}
|
||||
|
||||
public void InitializeBGM(AudioClip? clip)
|
||||
@@ -121,11 +140,11 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
_bgmSamples = AudioResampler.Resample(rawSamples, originalChannels, clip.frequency, _targetChannels, _targetSampleRate);
|
||||
_bgmDuration = (double)_bgmSamples.Length / (_targetChannels * _targetSampleRate);
|
||||
|
||||
MelonLoader.MelonLogger.Msg($"[OfflineAudioMixer] Initialized resampled BGM: {clip.name}, Samples: {_bgmSamples.Length / _targetChannels}, TargetFreq: {_targetSampleRate}Hz, TargetChannels: {_targetChannels}");
|
||||
Melon<RenderMod>.Logger.Msg($"[OfflineAudioMixer] Initialized resampled BGM: {clip.name}, Samples: {_bgmSamples.Length / _targetChannels}, TargetFreq: {_targetSampleRate}Hz, TargetChannels: {_targetChannels}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error($"[OfflineAudioMixer] Failed to initialize and resample BGM: {e}");
|
||||
Melon<RenderMod>.Logger.Error($"[OfflineAudioMixer] Failed to initialize and resample BGM: {e}");
|
||||
_bgmSamples = null;
|
||||
_bgmDuration = 0;
|
||||
}
|
||||
@@ -189,7 +208,7 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
lock (_lock)
|
||||
{
|
||||
_activeEvents.Clear();
|
||||
_sourceMappings = new System.Runtime.CompilerServices.ConditionalWeakTable<AudioSource, SoundEvent>();
|
||||
_sourceMappings = new ConditionalWeakTable<AudioSource, SoundEvent>();
|
||||
_bgmSamples = null;
|
||||
_bgmDuration = 0;
|
||||
_clipCache.Clear();
|
||||
@@ -209,11 +228,12 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
// 2. Mix Hitsounds/Effect Sounds
|
||||
MixEffectSounds(mixedBuffer, samplesNeeded, frameStartTime);
|
||||
|
||||
// 3. Saturation Guard (Clamping)
|
||||
for (int i = 0; i < samplesNeeded * _targetChannels; i++)
|
||||
foreach (var filter in _filters)
|
||||
filter.ProcessBlock(mixedBuffer.AsSpan(0, samplesNeeded * _targetChannels));
|
||||
|
||||
for (var i = 0; i < samplesNeeded * _targetChannels; i++)
|
||||
{
|
||||
if (mixedBuffer[i] > 1.0f) mixedBuffer[i] = 1.0f;
|
||||
else if (mixedBuffer[i] < -1.0f) mixedBuffer[i] = -1.0f;
|
||||
if (Math.Abs(mixedBuffer[i]) > 1) Melon<RenderMod>.Logger.Msg("Warning Why Audio Sample Is Larger Than 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,16 +246,16 @@ namespace ADOFAIRenderer.Util.Audio
|
||||
if (!conductor) return;
|
||||
|
||||
double pitch = conductor.song.pitch;
|
||||
double dspTimeSong = conductor.dspTimeSong;
|
||||
double separateCountdownOffset = conductor.separateCountdownTime
|
||||
? (conductor.crotchetAtStart / pitch * conductor.adjustedCountdownTicks)
|
||||
var dspTimeSong = conductor.dspTimeSong;
|
||||
var separateCountdownOffset = conductor.separateCountdownTime
|
||||
? conductor.crotchetAtStart / pitch * conductor.adjustedCountdownTicks
|
||||
: 0.0;
|
||||
|
||||
// BGM Playback start time relative to dspTimeBase
|
||||
double time1 = dspTimeSong + separateCountdownOffset;
|
||||
double relativeTime1 = time1 - dspTimeBase;
|
||||
var time1 = dspTimeSong + separateCountdownOffset;
|
||||
var relativeTime1 = time1 - dspTimeBase;
|
||||
|
||||
int totalSamples = _bgmSamples.Length / _targetChannels;
|
||||
var totalSamples = _bgmSamples.Length / _targetChannels;
|
||||
|
||||
for (int s = 0; s < samplesNeeded; s++)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user