实时播放C#中的合成声音

本文关键字:声音 播放 实时 | 更新日期: 2023-09-27 18:26:46

我正在尝试编写一个迷你合成器程序。基本上,每200毫秒就会触发一个定时器。当触发时,环路会生成价值200mS的PCM样本(例如锯齿波、正弦波等),样本数量与采样率和扫描周期有关。例如每200mS在波阵列中准备4000个样本用于播放。我的问题是,我该如何玩这个数组,即PlaySound方法应该做什么?每200mS调用一次PlaySound应以连续的方式播放后续样本。我玩过DirectSound和NAudio,但都没用。

    const int SAMPLE_RATE = 20000;
    const double SCAN_PERIOD = .2;  // S
    Timer _ScanTimer = new Timer();

    double[] wave = new double[(int)((double)SAMPLE_RATE * SCAN_PERIOD)];

 _ScanTimer.Tick += new EventHandler(_ScanTimer_Tick);


    private void _ScanTimer_Tick(object sender, EventArgs e) {
        int numSamplesPerTick = (int)((double)SAMPLE_RATE * SCAN_PERIOD);
        double secondsPerTick = (double)2 / SAMPLE_RATE;
        for (int i = 0; i < numSamplesPerTick; i++) {
            wave[i] = GetSynthOutput();
            _CurrentTime += secondsPerTick;
            if (_CurrentTime > double.MaxValue - 1)
                _CurrentTime = 0;
        }

        PlaySound(wave);
    }

我意识到计时器可能不会每200mS就启动一次,4000个样本中的样本可能比实际时间略少或过多。我并不担心这一点,因为我可以根据后续定时器启动之间的实际时间调整样本数量。

实时播放C#中的合成声音

大多数音频合成程序不是在计时器上创建声音,而是使用两个缓冲区——一个正在填充,另一个正在播放。NAudio允许您通过在ISampleProvider或IWaveProvider派生类中实现Read函数来实现这一点。每当声卡想要播放更多音频时,就会调用此方法。如果需要,可以将缓冲区大小设置为200ms。