Получение частоты потокового аудио с микрофона. Naudio
Нужно постоянно получать частоту с микрофона с неким интервалом. Я не понимаю, как брать данные микрофона и обрабатывать их постоянно. Я видел примеры, где берут готовый Wav файл и находят частоту звука этого файла по первым 1024 семплам с помощью Быстрого Преобразования Фурье. Но мне нужно, чтобы данные с микрофона обрабатывались постоянно и частота выводилась в TextBox.
Данные с Wav файла преобразовываются в PCM формат. Насколько я понимаю, в таком формате хранятся и данные с микрофона, которые можно вытащить из e.Buffer в переменную типа Byte[].
Сначала я подумал постоянно перезаписывать Wav файл, примерно каждые 0.1 секунду, потом таким способом находить частоту и выводить на экран. Но я понимаю, что есть более эффективные способ. Помогите, пожалуйста разобраться
Здесь берётся Wav файл, преобразовывается в PCM поток. Дальше в метод PrintFrequency передаётся float[] с количеством ячеек 1024, насколько я понимаю. Так же предаётся prov, который получили из PCM потока с помощью ToSampleProvider. Я хотел сделать то же самое с WaveIn, но не удаётся преобразовать его в SampleProvider.
Я чувствую, что нужно использовать bute[] из е.Buffer, но не понимаю как
WaveStream readerStream = new WaveFileReader("C:\\Users\\alenu\\Downloads\\Sound_21119_Violin_440.000.wav");
WaveStream pcmStream;
WaveStream stream;
//создаем поток в PCM-формате
if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
{
pcmStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
stream = new BlockAlignReductionStream(pcmStream);
}
else
{
pcmStream = readerStream;
stream = readerStream;
}
float[] samples;
const int N_SAMPLES = 1024; //количество сэмплов для спектрального анализа
ISampleProvider prov;
using (stream)
using (readerStream)
using (pcmStream)
{
prov = stream.ToSampleProvider();
samples = new float[N_SAMPLES * prov.WaveFormat.Channels];
int res = prov.Read(samples, 0, N_SAMPLES * prov.WaveFormat.Channels);
if (res < N_SAMPLES * prov.WaveFormat.Channels) throw new Exception("Not enough data");
}
MessageBox.Show(Convert.ToString(samples));
PrintFrequency(samples, N_SAMPLES, prov.WaveFormat);
Вот метод PrintFrequency
void PrintFrequency(float[] samples, int n_samples, WaveFormat fmt)
{
textBox1.Text = "";
for (int i = 0; i < fmt.Channels; i++)
{
SampleAggregator aggregator = new SampleAggregator(n_samples);
aggregator.PerformFFT = true;
int j;
float f;
//выделяем данные одного канала
for (j = 0; j < n_samples; j++)
{
int index = (j * fmt.Channels) + i;
f = samples[index];
aggregator.Add(f);
}
float[] fft_x = new float[aggregator.FftBuffer.Length / 2]; //только первая половина БПФ имеет смысл
for (j = 0; j < fft_x.Length; j++)
{
float real = aggregator.FftBuffer[j].X;
float imag = aggregator.FftBuffer[j].Y;
fft_x[j] = (float)Math.Sqrt(real * real + imag * imag); //получаем амплитуду
}
fft_x[0] = 0.0f;//избавляемся от постоянной составляющей
int i_peak = fft_x.ToList().IndexOf(fft_x.Max());
for (j = 0; j < fft_x.Length; j++)
{
fft_x[j] = (float)Math.Log(Math.Abs(aggregator.FftBuffer[j].X));
}
var i_interp = parabolic(fft_x, i_peak);
float freq = fmt.SampleRate * i_interp / (float)n_samples;
textBox1.Text += ("Channel " + i.ToString() + ": " + freq.ToString() + " Hz" + Environment.NewLine);
}
Класс SampleAgreggator
public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
private float maxValue;
private float minValue;
public int NotificationCount { get; set; }
public Complex[] FftBuffer { get { return this.fftBuffer; } }
int count;
// FFT
public event EventHandler<FftEventArgs> FftCalculated;
public bool PerformFFT { get; set; }
private Complex[] fftBuffer;
private FftEventArgs fftArgs;
private int fftPos;
private int fftLength;
private int m;
public SampleAggregator(int fftLength = 1024)
{
if (!IsPowerOfTwo(fftLength))
{
throw new ArgumentException("FFT Length must be a power of two");
}
this.m = (int)Math.Log(fftLength, 2.0);
this.fftLength = fftLength;
this.fftBuffer = new Complex[fftLength];
this.fftArgs = new FftEventArgs(fftBuffer);
}
bool IsPowerOfTwo(int x)
{
return (x & (x - 1)) == 0;
}
public void Reset()
{
count = 0;
maxValue = minValue = 0;
}
public void Add(float value)
{
if (PerformFFT)
{
fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftBuffer.Length));
fftBuffer[fftPos].Y = 0;
fftPos++;
if (fftPos >= fftBuffer.Length)
{
fftPos = 0;
// 1024 = 2^10
FastFourierTransform.FFT(true, m, fftBuffer);
if (FftCalculated != null) FftCalculated(this, fftArgs);
}
}
maxValue = Math.Max(maxValue, value);
minValue = Math.Min(minValue, value);
count++;
if (count >= NotificationCount && NotificationCount > 0)
{
if (MaximumCalculated != null)
{
MaximumCalculated(this, new MaxSampleEventArgs(minValue, maxValue));
}
Reset();
}
}
}
public class MaxSampleEventArgs : EventArgs
{
[DebuggerStepThrough]
public MaxSampleEventArgs(float minValue, float maxValue)
{
this.MaxSample = maxValue;
this.MinSample = minValue;
}
public float MaxSample { get; private set; }
public float MinSample { get; private set; }
}
public class FftEventArgs : EventArgs
{
[DebuggerStepThrough]
public FftEventArgs(Complex[] result)
{
this.Result = result;
}
public Complex[] Result { get; private set; }
}