Обнаружение фортепианной ноты с помощью C# с NAudio

Я пытаюсь написать программу, которая идентифицирует ноту, которую я играю на пианино, я обнаружил, что фильтр Гертцеля - это простой в реализации алгоритм, но я не знаю, как его использовать.

Вот код:

using NAudio.Wave;
using System.Windows;
using System;
using System.Collections.Generic;

namespace WpfTest {
    public partial class MainWindow : Window {
        private BufferedWaveProvider buffer;
        private WaveIn waveIn;
        private WaveOut waveOut;
        private const double TargetFreaquency = 261.626;//C4 note
        private const int SampleRate = 44100;

        public MainWindow() {
            InitializeComponent();
            InitializeSound();
            waveIn.StartRecording();
            waveOut.Play();
        }

        private void InitializeSound() {
            waveIn = new WaveIn();
            waveOut = new WaveOut();
            buffer = new BufferedWaveProvider(waveIn.WaveFormat);
            waveIn.DataAvailable += WaveInDataAvailable;
            waveOut.Init(buffer);
        }

        private void WaveInDataAvailable(object sender, WaveInEventArgs e) {
            buffer.AddSamples(e.Buffer, 0, e.BytesRecorded);

            var floatBuffer = new List<float>();
            for (int index = 0; index < e.BytesRecorded; index += 2) {
                short sample = (short)((e.Buffer[index + 1] << 8) |
                                        e.Buffer[index + 0]);
                float sample32 = sample / 32768f;
                floatBuffer.Add(sample32);
            }

            if (NotePlayed(floatBuffer.ToArray(), e.BytesRecorded)) {
                Console.WriteLine("You have played C4");
            }
        }

        private bool NotePlayed(float[] buffer, int end) {
            double power = GoertzelFilter(buffer, TargetFreaquency, buffer.Length);
            if (power > 500) return true;
            return false;
        }

        private double GoertzelFilter(float[] samples, double targetFreaquency, int end) {
            double sPrev = 0.0;
            double sPrev2 = 0.0;
            int i;
            double normalizedfreq = targetFreaquency / SampleRate;
            double coeff = 2 * Math.Cos(2 * Math.PI * normalizedfreq);
            for (i = 0; i < end; i++) {
                double s = samples[i] + coeff * sPrev - sPrev2;
                sPrev2 = sPrev;
                sPrev = s;
            }
            double power = sPrev2 * sPrev2 + sPrev * sPrev - coeff * sPrev * sPrev2;
            return power;
        }
    }
}

Код не работает правильно, но как мне сделать, чтобы написать в консоли:"Вы играли в С4" каждый раз, когда я играю ноту С4 в микрофон?

1 ответ

Решение

Похоже, вы предполагаете, что микрофонный вход будет 16-битным сэмплом PCM на частоте 44100 Гц. Это не обязательно так. Вы можете проверить формат микрофона "по умолчанию", а также настроить его так, как вы ожидаете, следующим образом:

private void InitializeSound()
{
    waveIn = new WaveIn();

    // Add this here to see what the waveIn default format is.
    // Step through this line in the debugger.  If this isn't
    // 44100Hz sampling rate, 16-bit PCM, 1-channel, then that's
    // probably what's going wrong.
    WaveFormat checkformat = waveIn.WaveFormat;

    // Note that these are the default values if we used the 
    // parameterless WaveFormat constructor, but just expanding
    // here to show that we're forcing the input to what you're 
    // expecting:
    WaveFormat myformat = new WaveFormat(44100, 16, 2);
    waveIn.WaveFormat = myformat;
    SampleRate = myformat.SampleRate;

    waveIn.DataAvailable += WaveInDataAvailable;

    waveOut = new WaveOut();
    buffer = new BufferedWaveProvider(waveIn.WaveFormat);
    waveOut.Init(buffer);
}

Я не уверен в порядке байтов, когда вы конвертируете short в float в вашем обработчике событий (я старый, и я не могу вспомнить, какой конец какой больше:)), так что это также может быть проблемой. Вам, вероятно, будет лучше использовать BitConverter.ToInt16 чтобы сделать это, а не сдвиг / добавление, которое вы делаете сейчас:

private void WaveInDataAvailable(object sender, WaveInEventArgs e)
{
    buffer.AddSamples(e.Buffer, 0, e.BytesRecorded);

    var floatBuffer = new List<float>();
    for (int index = 0; index < e.BytesRecorded; index += 2)
    {
        short sample = BitConvert.ToInt16(e.Buffer, index);
        float sample32 = (float)sample;
        sample32 /= (float)Int16.MaxValue;
        floatBuffer.Add(sample32);
    }

    if (NotePlayed(floatBuffer.ToArray(), e.BytesRecorded))
    {
        Console.WriteLine("You have played C4");
    }
}

Это также выглядит как end параметр для NotePlayed не используется, что на самом деле хорошо! Подчеркивая значение end параметр, e.BytesRecorded в любом случае не будет правильным значением, так как это не количество отсчетов.

Другие вопросы по тегам