XAudio2 - 播放生成的正弦波,当改变频率时点击声音

XAudio2 - Play generated sine, when changing frequency clicking sound

我想开发一个应用程序来匹配您的耳鸣频率:播放一个频率,用户通过按加号或减号按钮来降低或增加频率。 (查看部分代码,基于来自 Whosebug 的一些编码 thx :-))

   public static short[] BufferSamples = new short[44100 * 1 * 2];  

    private SourceVoice sourceVoice;
    private AudioBuffer buffer;
    private int Tfreq;

    public MatchTinn()
    {
        InitializeComponent();
        Loaded += MatchTinn_Loaded;
        TFreq = 5000;
    }

    private void MatchTinn_Loaded(object sender, RoutedEventArgs e)
    {
        var dataStream = DataStream.Create(BufferSamples, true, true);

        buffer = new AudioBuffer
        {
            LoopCount = AudioBuffer.LoopInfinite,
            Stream = dataStream,
            AudioBytes = (int)dataStream.Length,
            Flags = BufferFlags.EndOfStream
        };

        FillBuffer(BufferSamples, 44100, Tfreq);

        var waveFormat = new WaveFormat();

        XAudio2 xaudio = new XAudio2();
        MasteringVoice masteringVoice = new MasteringVoice(xaudio);

        sourceVoice = new SourceVoice(xaudio, waveFormat, true);

        // Submit the buffer
        sourceVoice.SubmitSourceBuffer(buffer, null);
    }

    private void FillBuffer(short[] buffer, int sampleRate, int frequency)
    {
        if (sourceVoice != null)
        {
            sourceVoice.FlushSourceBuffers();
        }

        double totalTime = 0;

        for (int i = 0; i < buffer.Length - 1; i += 2)
        {
            double time = (double)totalTime / (double)sampleRate;

            short currentSample = (short)(Math.Sin(2 * Math.PI * frequency * time) * (double)short.MaxValue);

            buffer[i] = currentSample;
            buffer[i + 1] = currentSample;

            totalTime++;
        }


    private void m1_OnTap(object sender, GestureEventArgs e)
    {
        Tfreq = Tfreq - 1;

        if (Tfreq < 0)
        {
            Tfreq = 0;
        }

        FillBuffer(BufferSamples, 44100, Tfreq);

    }

    private void p1_OnTap(object sender, GestureEventArgs e)
    {
        Tfreq = Tfreq + 1;

        if (Tfreq > 16000)
        {
            Tfreq = 16000;
        }

        FillBuffer(BufferSamples, 44100, Tfreq);
    }

播放频率没问题,但是当用户按下按钮时,您会在频率更新时发出咔嗒声。你知道是什么发出声音以及我如何摆脱它吗? 谢谢。

当您更改频率时,会导致波形不连续,表现为咔嗒声。您不应根据绝对时间进行信号计算,而应跟踪正弦计算的相位(例如从 0 到 2*pi 的值),并计算出需要添加多少到您的相位(减去 2*pi每次播放特定频率时下一个样本超过 2*pi)。这样,当您更改频率时,您作为参数提供给 Math.Sin 的相位不会突然改变而导致点击。

扩展@spender 给出的答案(我需要 50 个代表来为他的答案添加评论),我遇到了与 类似的问题。我能够通过添加两个监控正弦值的当前符号和正弦值的前一个符号的 bool 值来解决这个问题。如果之前的正弦为负而当前的正弦为正,我们就知道我们可以安全地调整正弦波的频率。

            double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
            isPreviousSineWaveValPositive = isSineWaveValPositive;
            if (sine < 0)
            {
                isSineWaveValPositive = false;
            }
            else
            {
                isSineWaveValPositive = true;
            }

            // When the time is right, change the frequency
            if ( false == isPreviousSineWaveValPositive && true == isSineWaveValPositive )
            {
                time = 0.0;
                frequency = newFrequency;
            }

这是一个如何消除点击的示例。您不应使用时间,而应跟踪当前相位并计算相位在所需频率上的变化量。此外,此 _currentPhase 必须是持久的,因此它将具有以前的值。 (在方法中声明它也会导致点击 (在大多数频率上)

private double _currentPhase = 0;

private void FillBuffer(short[] buffer, int sampleRate, int frequency)
{
    if (sourceVoice != null)
    {
        sourceVoice.FlushSourceBuffers();
    }

    var phaseStep = ((Math.PI * 2) / (double)sampleRate) * frequency;

    for (int i = 0; i < buffer.Length - 1; i += 2)
    {
        _currentPhase += phaseStep;

        short currentSample = (short)(Math.Sin(_currentPhase) * (double)short.MaxValue);

        buffer[i] = currentSample;
        buffer[i + 1] = currentSample;
    }
}