【问题标题】:Java .wav file frequency analysis - incorrect frequencyJava .wav 文件频率分析 - 频率不正确
【发布时间】:2016-02-04 10:16:42
【问题描述】:

我正在做一个项目,我必须分析.wav 文件,这对我来说基本上归结为能够显示所述文件中存在的频率。 我正在使用 WavFile 类来读取文件,然后我使用 JTransforms 类对它们进行 FFT(我实际上做了一个 realForward,因为我只提供实数)。

在我将数据输入 Excel 之前,一切似乎都运行良好:我在第一列中输入了一个行编号(我正在测试的文件为 1-8000),然后我将 FFT 的输出放入下一栏。我输入的文件是一种简单的单频声音,频率为 440Hz,持续时间为 1 秒。

但是,在看到图表后,有一个问题:我有一个单一的频率峰值,这正是我所期望的,但峰值位于 880 位置,是实际频率的两倍。谁能给我解释一下这是为什么?

额外问题:为什么我会得到 e-16 附近的值?除了峰值以外的所有值都应该是 0,对吧? (每次我得到的数据是<= 1 时,我都会在文件中写入一个 0 来解决这个问题 - 请参见下面的代码)。也许是“噪音”?


代码:

有两个班级。第一个 readWav 用于读取 .wav 文件。第二个,wavFFT,是实际对数据进行 FFT 处理的人。

readWav.java 的代码:

import WavFile.*;
import java.io.*;

public class readWav {

    // getter methods
    public static long getWavFrames(File file)
    {
        // try loop to catch any exception
        try {
            // open the wav file
            WavFile wavFile = WavFile.openWavFile(file);

            // return the number of frames
            return wavFile.getNumFrames();

        } catch (Exception e) {
            System.err.println(e);

            // error value
            return -1;
        }

    }

    public static int getWavChannels(File file)
    {
        // try loop to catch any exception
        try {
            WavFile wavFile = WavFile.openWavFile(file);

             return wavFile.getNumChannels();

        } catch (Exception e) {
            System.err.println(e);

            // error value
            return -1;
        }
    }

    public static double[] getWavData(File file)
    {
        // try loop to catch any exception
        try {
            // open the file
            WavFile wavFile = WavFile.openWavFile(file);

            // use the getter method to get the channel number (should be mono)
            int numChannels = getWavChannels(file);

            // same, but with the frame getter method
            int numFrames = (int) getWavFrames(file); // possible data loss

            // create a buffer the size of the number of frames
            double[] buffer = new double[numFrames * numChannels];

            // Read frames into buffer
            wavFile.readFrames(buffer, numFrames);

            // Close the wavFile
            wavFile.close();

            return buffer;

        } catch (Exception e) {
            System.err.println(e);

            // throw an error, if this runs something went wrong in reading the .wav file
            throw new RuntimeException("[could not read wav file " + file + "]");
        }
    }


    // main method, solely for testing purposes
    public static void main(String[] args)
    {
        // test, everything seems to be working
        File fichier_son = new File("son/freq1.wav");
        double[] test = getWavData(fichier_son);
        for(int i = 0; i<test.length; i++){
            System.out.println(test[i]);
        }
    }

}

wavFFT.java 的代码:

import org.jtransforms.fft.DoubleFFT_1D;
import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;

public class wavFFT {

    public static double[] realFFT(File file)
    {
        // Get the .wav data using the readWav class
        double[] data_to_fft = readWav.getWavData(file);

        /* Get the length of the array.
        Since we are feeding real numbers into the fft,
        the length of the array should be equal to the
        number of frames, which we get using the readWav class. */
        int n = (int) readWav.getWavFrames(file);

        // Make a new fft object
        DoubleFFT_1D fft = new DoubleFFT_1D(n);

        // Perform the realForward fft
        fft.realForward(data_to_fft);

        // Return the final data
        return data_to_fft;
    }


    public static void writeToFile(File in, File out) throws IOException
    {
        PrintWriter print_out = new PrintWriter(out);
        int i;
        double[] data_to_file = realFFT(in);

        for(i=0; i<data_to_file.length; i++){
            if(data_to_file[i] > 1){
                print_out.println(data_to_file[i]);
            } else {
                print_out.println(0);
            }

        }
        print_out.close();
    }

    // main method, solely for testing purposes
    public static void main(String[] args) {
        File fichier_son = new File("son/freq1.wav");
        double[] test = realFFT(fichier_son); 
        int i;

        for(i=0; i<test.length; i++){
            System.out.println(test[i]);
        }


        try{
            writeToFile(fichier_son, new File("datafiles/output.txt"));
        } catch (IOException e){
            System.out.println("error");
        }
    }

}

【问题讨论】:

  • 嗨。当我运行您的代码时,我收到“包 WavFile 不存在”的错误。这是为什么?如何克服?
  • @VibhavChaddha 您尚未在项目中导入 WavFile 类。我在答案中链接到它。
  • @VibhavChaddha 取决于您使用的 IDE。我建议谷歌搜索。
  • 好的。感谢您的帮助。

标签: java audio wav frequency


【解决方案1】:

您没有说明如何解释使用上述代码生成的 Excel 中的结果。然而,一个可能的错误是误解了 FFTfft.realForward() 的输出 - 这是一个复数数组,其实部和虚部占据连续的元素,如 here 所记录的那样。如果您只是使用出现峰值的数组的索引,您的结果将相差两倍。请注意,此 FFT 实现仅计算最高 Nyqvist 速率(除此之外仅产生“别名”)。

其他注意事项:

  • 您正在将rectangular window function 应用于样本。坦率地说,我很惊讶其他垃圾箱中的出血量与10e-16 一样小。您的应用程序有更好的选择。
  • 窗口似乎是文件的全长,可能会导致巨大的 FFT。作为一般规则,2 次方 FFTS 的效率要高得多。您通常会看到在固定长度窗口上执行的频率分析。
  • FFT 实际上是一系列带通滤波器,它们是输入样本的加权和。您看到的非零值的贡献只是浮点舍入误差。您将整个文件放在窗口中的事实意味着有很多操作对此做出了贡献。

【讨论】:

  • 谢谢。不过,你能提出解决这个问题的方法吗?您所说的“更好的选择”是什么? (附带问题:垂直轴上的测量单位是什么?是分贝吗?既然阵列输出是固定的,我在水平轴上的位置 440 和垂直轴上的位置 1355 处得到峰值)。
  • FFT 的输入是幅度,所以这也是 FFT 的输出。 dB 标度通常是信号功率,因此您需要自己计算。至于更好的窗口功能,这是一个应用问题。我想这将是汉明窗。
  • 第二堂课我把代码改成了“Hamming.hamming(data_to_fft);” (marf.sourceforge.net/api/marf/math/Algorithms.Hamming.html) 在进行 FFT 之前,但我仍然得到不需要的值。这里有什么问题?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-20
  • 1970-01-01
  • 2011-09-11
相关资源
最近更新 更多