Частотная характеристика звука.
От: adontz Грузия http://adontz.wordpress.com/
Дата: 25.12.05 02:44
Оценка:
Надо получить частотну характеристику звука. Как я делаю ниже.
Проблема в том, что в качестве пиков выдаются какие-то ну очень приблизительные значения (разброс 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 реализовал правильно, надеюсь вы мне верите
A journey of a thousand miles must begin with a single step © Lau Tsu
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.