XAudio2 - Воспроизведение сгенерированного синуса, при изменении частоты звука щелчка

Я хочу разработать приложение, соответствующее вашей частоте шума в ушах: воспроизводится частота, и пользователь уменьшает или увеличивает частоту, нажимая кнопку «плюс» или «минус». (см. часть кодов, основанную на некоторой кодировке из stackoverflow, спасибо :-))

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

Воспроизведение частоты в порядке, но когда пользователь нажимает кнопку, вы слышите щелчок при обновлении частоты. У вас есть идеи, что делает звук и как я могу избавиться от него? Спасибо.


person c0ldsun    schedule 18.01.2015    source источник


Ответы (3)


Когда вы меняете частоту, вы вызываете разрыв в форме сигнала, который проявляется как щелчок. Вместо того, чтобы выполнять вычисления сигнала относительно абсолютного времени, вы должны отслеживать фазу вашего вычисления синуса (например, значение от 0 до 2 * пи) и выяснить, сколько вам нужно добавить к вашей фазе (вычитая 2 * пи). каждый раз, когда вы превышаете 2*pi) для следующего семпла при воспроизведении определенной частоты. Таким образом, когда вы меняете частоту, фаза, которую вы указываете в качестве параметра для Math.Sin, не изменяется резко, вызывая щелчок.

person spender    schedule 18.01.2015
comment
Похоже, мне нужно копнуть глубже в генерацию аудиосинтеза, но у меня есть место, чтобы начать сейчас... если у вас есть какие-либо примеры, дополнительная информация и т. д. по проблеме, пожалуйста, дайте мне знать.. большое спасибо заранее - person c0ldsun; 19.01.2015

Расширяя ответ, который дал @spender (мне нужно 50 представителей, чтобы добавить комментарий к его ответу), у меня была аналогичная проблема с naudio. Мне удалось решить проблему, добавив два логических значения, которые отслеживали текущий знак значения синуса и предыдущий знак значения синуса. Если предыдущий синус был отрицательным, а текущий синус положительным, мы знаем, что можем безопасно отрегулировать частоту синусоиды.

            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;
            }
person Rhubarb    schedule 16.07.2015

Вот пример того, как можно избавиться от щелчков. Вместо использования времени следует отслеживать текущую фазу и вычислять, насколько фаза изменяется на требуемой частоте. Также этот _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;
    }
}
person Jeroen van Langen    schedule 06.04.2017