Надо получить частотну характеристику звука. Как я делаю ниже.
Проблема в том, что в качестве пиков выдаются какие-то ну очень приблизительные значения (разброс 100-200 герц), хотя, скажем, soundforge по тому же сигналу определяет пики очень чётко.
Формат записи 8бит, 22500Гц, моно.
static void dsAudioCapture_OnCapture(CaptureParameters captureParameters, byte[] data)
{
/* получили новые данные и добавили в буффер */
foreach (byte sample in data)
{
buffer.Add(sample);
}
/* если данных в буффере достаточно для обработки */
if (buffer.Count > BlockSize)
{
/* найдём минимальное и максимальное значения */
double bufferMin = buffer[0];
double bufferMax = buffer[0];
for (int index = 0; index < BlockSize; ++index)
{
if (buffer[index] < bufferMin)
{
bufferMin = buffer[index];
}
if (buffer[index] > bufferMax)
{
bufferMax = buffer[index];
}
}
/* тут хранится сам сигнал */
Complex[] signal = new Complex[BlockSize];
/* отмасштабируем данные, чтобы работать с числами определённого порядка и избежать ошибок округления */
for (int index = 0; index < BlockSize; ++index)
{
signal[index] = new Complex((buffer[index] - bufferMin) / (bufferMax - bufferMin), 0.0);
}
/* выкинем обработанный блок из буффера */
buffer.RemoveRange(0, BlockSize);
/* тут будут записаны результаты */
Complex[] result = new Complex[BlockSize];
/* натравили БПФ */
fft.Calculate(signal, result);
/* тут будут храниться пары интенсивность-частота */
SortedList<double, double> frequencyList = new SortedList<double, double>();
/* интересуют частоты ниже 2КГц */
int indexTo = (int)Math.Round(2000.0 * BlockSize / captureParameters.SamplePerSecondNumber);
/* заполняем сортированный список частот. Самые интенсивные в конце */
for (int index = 1; index < indexTo; ++index)
{
frequencyList.Add(
result[index].Re,
((double)index) * ((double)captureParameters.SamplePerSecondNumber) / ((double)BlockSize));
}
/* только интересующие частоты */
int frequencyFrom = 500;
int frequencyTo = 2000;
int frequencyCount = 0;
/* есть ли среди 5 самых интенсивных частот интересующие? */
for (int index = Math.Max(0, frequencyList.Count - 5); index < frequencyList.Count; ++index)
{
if ((frequencyList.Values[index] > frequencyFrom) && (frequencyList.Values[index] < frequencyTo))
{
++frequencyCount;
}
}
/* если да, то выводим их */
if (frequencyCount > 0)
{
Console.Write("FREQ:");
for (int index = Math.Max(0, frequencyList.Count - 5); index < frequencyList.Count; ++index)
{
if ((frequencyList.Values[index] > frequencyFrom) && (frequencyList.Values[index] < frequencyTo))
{
Console.Write(" [{0}] ", Math.Round(frequencyList.Values[index]).ToString().PadLeft(5));
}
}
Console.WriteLine();
}
}
}
Во избежание лишних вопросов, привожду так же код класса БПФ (Совершенно не оптимизирован, но мне пока важно, чтобы всё работало. E — even, O — odd.).
class FFT
{
public void Calculate(Complex[] signal, Complex[] result)
{
if (signal.Length == 1)
{
result[0] = signal[0];
}
else
{
Complex[] signalE = new Complex[signal.Length / 2];
Complex[] signalO = new Complex[signal.Length / 2];
for (int index = 0; index < signal.Length / 2; ++index)
{
signalE[index] = signal[2 * index];
signalO[index] = signal[2 * index + 1];
}
Complex[] resultE = new Complex[signal.Length / 2];
Complex[] resultO = new Complex[signal.Length / 2];
Calculate(signalE, resultE);
Calculate(signalO, resultO);
Complex omegaN = new Complex(Math.Cos(2 * Math.PI / signal.Length), Math.Sin(2 * Math.PI / signal.Length));
Complex omega = new Complex(1.0);
for (int index = 0; index < signal.Length / 2; ++index)
{
result[index] = resultE[index] + resultO[index] * omega;
result[index + signal.Length / 2] = resultE[index] - resultO[index] * omega;
omega = omega * omegaN;
}
}
}
}
Ну то что я Complex реализовал правильно, надеюсь вы мне верите