You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#如何将Double数组转为NAudio WaveStream循环播放音频采样

替换NAudio正弦波逻辑为自定义数组循环播放

需求背景

  • 目标效果可参考波表振荡器演示视频的176秒位置
  • 待播放音源为长度256、存储居中sinc函数值的double数组
  • 已掌握NAudio下正弦波生成播放的实现方式
  • 核心诉求:修改现有正弦波生成代码,将实时计算正弦值的逻辑替换为读取指定double数组内容,实现数组内容作为单周期波形循环播放。

原有正弦波实现代码如下:

public class SineWaveProvider32 : WaveProvider32
{
    int sample;

    public SineWaveProvider32()
    {
        Frequency = 1000;
        Amplitude = 0.25f; // 控制音量避免伤耳
    }

    public float Frequency { get; set; }
    public float Amplitude { get; set; }

    public override int Read(float[] buffer, int offset, int sampleCount)
    {
        int sampleRate = WaveFormat.SampleRate;
        for (int n = 0; n < sampleCount; n++)
        {
            buffer[n+offset] = (float)(Amplitude * Math.Sin((2 * Math.PI * sample * Frequency) / sampleRate)); 
            // 上述行是正弦波生成逻辑,需要替换为自定义数组采样读取逻辑
            sample++;
            if (sample >= sampleRate) sample = 0;
        }
        return sampleCount;
    }
}

实现方法

直接替换正弦值计算逻辑为数组循环读取逻辑即可,保留原有的音量控制、播放频率控制能力。为了支持音高调节,读取位置用double类型存储,通过调整读取步进值控制数组循环速度,不需要修改原始采样数组。

修改后的可直接运行代码:

public class ArrayOscillatorProvider32 : WaveProvider32
{
    private readonly double[] _waveTable;
    private double _readPos;
    private double _readStep;

    public ArrayOscillatorProvider32(double[] waveSampleArray)
    {
        if (waveSampleArray == null || waveSampleArray.Length == 0)
            throw new ArgumentException("采样数组不能为空", nameof(waveSampleArray));
        
        _waveTable = waveSampleArray;
        Amplitude = 0.25f;
        SetFrequency(1000);
    }

    public float Frequency { get; private set; }
    public float Amplitude { get; set; }

    public void SetFrequency(float targetFreq)
    {
        Frequency = targetFreq;
        // 计算读取步进:数组长度 * 目标频率 / 采样率 = 每次读取需要移动的索引距离
        _readStep = _waveTable.Length * (double)targetFreq / WaveFormat.SampleRate;
    }

    public override int Read(float[] buffer, int offset, int sampleCount)
    {
        for (int n = 0; n < sampleCount; n++)
        {
            // 取当前位置的整数索引,超出数组长度自动取模循环
            int sampleIdx = (int)_readPos % _waveTable.Length;
            // 读取采样值、乘振幅后写入输出缓冲区
            buffer[n + offset] = (float)(Amplitude * _waveTable[sampleIdx]);
            
            // 移动读取位置
            _readPos += _readStep;
            // 位置超过数组长度时回退,避免数值无限增大溢出
            if (_readPos >= _waveTable.Length)
                _readPos -= _waveTable.Length;
        }
        return sampleCount;
    }
}

使用注意事项

  • 初始化时直接传入你存储sinc函数值的256长度double数组即可
  • 调用SetFrequency()方法可调整播放音高:步进值越大,数组循环速度越快,输出音高越高
  • 如果不需要调整音高,直接把_readStep固定为1即可,此时数组循环速度固定,44100Hz采样率下256长度数组每秒循环约172次
  • 若对音质要求较高,可在两个相邻采样点之间增加线性插值逻辑,消除步进值为非整数时的波形失真,常规测试场景下直接取整读取即可满足需求
  • Amplitude参数建议保持0.25及以下的小值,避免输出音量过大损坏听力或播放设备

内容的提问来源于stack exchange,提问作者AceOfSpadesProduc100

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 14:45:32