XAudio2 - Play generated sine, when changing frequency clicking sound

三世轮回 提交于 2021-02-08 04:57:28

问题


I want to develop an app to match your tinnitus frequency : A frequency is played and the user decrease or increase the freqency by pressing a plus or minus button. (see part of the codes, based on some coding from stackoverflow 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);
    }

Playing the frequency is fine, but when the user presses a button you here a clicking sound when the frequency is updated. Do you have any idea what makes the sound and how i can get rid of it? Thanks.


回答1:


When you change the frequency, you're causing a discontinuity in the waveform that manifests as a click. Instead of making your signal calculations against absolute time, you should keep track of the phase of your sine calculation (e.g. a value from 0 to 2*pi), and figure out how much you need to add to your phase (subtracting 2*pi every time you exceed 2*pi) for the next sample when playing a specific frequency. This way, when you change frequency, the phase that you supply as a parameter to Math.Sin doesn't change abruptly causing a click.




回答2:


Expanding on the answer @spender gave (I need 50 rep to add comment to his answer), I had a similar problem with naudio. I was able to solve the issue by adding two bool values that monitored the current sign of the sine value and the previous sign of the sine value. If the previous sine was negative and the current sine is positive, we know we can safely adjust the frequency of the sine wave.

            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;
            }



回答3:


Here's an example how you can get rid of the clicking. Instead of using a time, you should keep track of the current phase and calculate how much the phase is changed on the required frequency. Also this _currentPhase must be persistent so it will have the previous value. (declaring it within the method would result in a click aswell (on most frequencies)

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;
    }
}


来源:https://stackoverflow.com/questions/28013797/xaudio2-play-generated-sine-when-changing-frequency-clicking-sound

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!