49 lines
1.5 KiB
C#
49 lines
1.5 KiB
C#
namespace VoiceCat.Dsp;
|
|||
|
|
|
||
|
|
public sealed class EnergyVadProcessor
|
||
|
|
{
|
||
|
|
private readonly TimeProvider timeProvider;
|
||
|
|
private long lastVoiceTimestamp;
|
||
|
|
private bool hasVoice;
|
||
|
|
private float threshold;
|
||
|
|
|
||
|
|
public float Threshold
|
||
|
|
{
|
||
|
|
get => Volatile.Read(ref threshold);
|
||
|
|
set
|
||
|
|
{
|
||
|
|
if (!float.IsFinite(value) || value is < 0 or > 1) throw new ArgumentOutOfRangeException(nameof(value));
|
||
|
|
Volatile.Write(ref threshold, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
public TimeSpan HangTime { get; }
|
||
|
|
|
||
|
|
public EnergyVadProcessor(float threshold = 0.02f, TimeSpan? hangTime = null, TimeProvider? timeProvider = null)
|
||
|
|
{
|
||
|
|
Threshold = threshold;
|
||
|
|
HangTime = hangTime ?? TimeSpan.FromMilliseconds(300);
|
||
|
|
if (HangTime < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(hangTime));
|
||
|
|
this.timeProvider = timeProvider ?? TimeProvider.System;
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Process(ReadOnlySpan<short> pcm)
|
||
|
|
{
|
||
|
|
long now = timeProvider.GetTimestamp();
|
||
|
|
if (!pcm.IsEmpty)
|
||
|
|
{
|
||
|
|
double sum = 0;
|
||
|
|
foreach (short sample in pcm)
|
||
|
|
{
|
||
|
|
double normalized = sample / 32768.0;
|
||
|
|
sum += normalized * normalized;
|
||
|
|
}
|
||
|
|
if (Math.Sqrt(sum / pcm.Length) >= Threshold)
|
||
|
|
{
|
||
|
|
lastVoiceTimestamp = now;
|
||
|
|
hasVoice = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return hasVoice && timeProvider.GetElapsedTime(lastVoiceTimestamp, now) < HangTime;
|
||
|
|
}
|
||
|
|
}
|