- 积分
- 662
- 在线时间
- 226 小时
- 最后登录
- 2025-7-4
- 阅读权限
- 50
- 精华
- 0
 
- UID
- 1102940
- 帖子
- 627
- 精华
- 0
- 经验
- 662 点
- 金钱
- 594 ¥
- 注册时间
- 2024-6-15
|
using NAudio.Wave;
using NAudio.Dsp;
namespace NAudioPlayer
{
public class LowFreqCrossFeedProvider : ISampleProvider
{
private readonly ISampleProvider source;
private readonly int channels;
private readonly BiQuadFilter lowpassLeft;
private readonly BiQuadFilter lowpassRight;
private readonly BiQuadFilter highpassLeft;
private readonly BiQuadFilter highpassRight;
private readonly BiQuadFilter lowShelfL, lowShelfR; // 低频补色
private readonly BiQuadFilter highLowpassL, highLowpassR; // 高频柔和
private readonly float mixRatioLow;
private readonly float mixRatioHigh;
private readonly int delaySamples; // 延迟采样数
public float drive = 0.2f; // 驱动强度:谐波/饱和感,0=原味,增大更明显
private readonly Random rng = new Random();
// 环形缓冲,存每个高频分量
private readonly Queue<float> leftHighDelayBuffer;
private readonly Queue<float> rightHighDelayBuffer;
public bool enable = false;
public LowFreqCrossFeedProvider(ISampleProvider source, float lowCut = 500f, float highCut = 5000f, float delayMs = 8.8f, float ratioLow = 0.5f, float ratioHigh = 0.5f, float q = 0.707f)
{
if (source.WaveFormat.Channels != 2)
throw new InvalidOperationException("Only supports stereo.");
this.source = source;
this.channels = source.WaveFormat.Channels;
this.mixRatioLow = ratioLow;
this.mixRatioHigh = ratioHigh;
// 每个声道一个低通
lowpassLeft = BiQuadFilter.LowPassFilter(source.WaveFormat.SampleRate, lowCut, q); // Q值可微调
lowpassRight = BiQuadFilter.LowPassFilter(source.WaveFormat.SampleRate, lowCut, q);
highpassLeft = BiQuadFilter.HighPassFilter(source.WaveFormat.SampleRate, highCut, q);
highpassRight = BiQuadFilter.HighPassFilter(source.WaveFormat.SampleRate, highCut, q);
lowShelfL = BiQuadFilter.LowShelf(source.WaveFormat.SampleRate, 80, 0.7f, MathF.Pow(10, 1.5f / 20f));
lowShelfR = BiQuadFilter.LowShelf(source.WaveFormat.SampleRate, 80, 0.7f, MathF.Pow(10, 1.5f / 20f));
// 高频cut,模拟柔和
highLowpassL = BiQuadFilter.LowPassFilter(source.WaveFormat.SampleRate, 11000, 0.8f);
highLowpassR = BiQuadFilter.LowPassFilter(source.WaveFormat.SampleRate, 11000, 0.8f);
// 延迟高频的sample数量,0.2ms在44100Hz下是8.8采样,取整9个样本
delaySamples = (int)Math.Round(source.WaveFormat.SampleRate * (delayMs / 1000f)); // delayMs单位是ms
if (delaySamples < 1) delaySamples = 1;
leftHighDelayBuffer = new Queue<float>(new float[delaySamples]);
rightHighDelayBuffer = new Queue<float>(new float[delaySamples]);
}
public WaveFormat WaveFormat => source.WaveFormat;
public int Read(float[] buffer, int offset, int count)
{
int read = source.Read(buffer, offset, count);
if (!enable)
{
return read;
}
for (int i = 0; i < read; i += channels)
{
// 左声道、右声道原始采样
float lSample = buffer[offset + i];
float rSample = buffer[offset + i + 1];
// 低频润色
lSample = lowShelfL.Transform(lSample);
rSample = lowShelfR.Transform(rSample);
// 高频柔化
lSample = highLowpassL.Transform(lSample);
rSample = highLowpassR.Transform(rSample);
// 低通高通分量
float lLow = lowpassLeft.Transform(lSample);
float lHigh = highpassLeft.Transform(lSample);
float rLow = lowpassRight.Transform(rSample);
float rHigh = highpassRight.Transform(rSample);
// 高频延时环形缓冲
// 将当前高频分量写入队列, 得到延迟后的输出
float lHighDelayed = 0;
float rHighDelayed = 0;
leftHighDelayBuffer.Enqueue(lHigh);
rightHighDelayBuffer.Enqueue(rHigh);
if (leftHighDelayBuffer.Count > delaySamples)
lHighDelayed = leftHighDelayBuffer.Dequeue();
if (rightHighDelayBuffer.Count > delaySamples)
rHighDelayed = rightHighDelayBuffer.Dequeue();
// 右声道混入左侧低频(即刻)和高频(延迟)
float rCliped = SoftClip(rSample);
rSample = rSample * (1 - drive) + rCliped * drive;
buffer[offset + i + 1] = rSample
+ lLow * mixRatioLow
+ lHighDelayed * mixRatioHigh;
// 左声道混入右侧低频(即刻)和高频(延迟)
float lCliped = SoftClip(lSample);
lSample = lSample * (1 - drive) + lCliped * drive;
buffer[offset + i] = lSample
+ rLow * mixRatioLow
+ rHighDelayed * mixRatioHigh;
// 环境噪声模拟
// buffer[offset + i] += (float)(rng.NextDouble() - 0.5) * 0.01f;
// buffer[offset + i + 1] += (float)(rng.NextDouble() - 0.5) * 0.01f;
}
return read;
}
// 常用软削波(soft clip)函数,tanh风格
private float SoftClip(float x)
{
// 经典tanh
// return (float)Math.Tanh(x);
// Cubic (三次), 强调偶次谐波
// return x - (x * x * x) / 3f;
// 更模拟“电子管柔化”
const float k = 2.5f; // 驱动感,可调
float a = MathF.Min(MathF.Max(x * k, -3f), 3f);
return MathF.Tanh(a) / MathF.Tanh(3f) * 1.0f / k;
// 或试试 f(x)=x+αx²+βx³
//float α = 0.2f, β = 0.15f;
//return x + α * x * x + β * x * x * x;
}
}
}
|
评分
-
查看全部评分
|