从音频文件计算FFT

10

之前,我问了一个问题关于如何使用FFT和复数类获取wav音频的频率

在那里,我需要从麦克风中计算AudioRecord输入的FFT值,我设法获得了FFT值...

现在我需要从*.wav音频文件中计算FFT值,这些文件我之前保存在项目的'res'文件夹下的'raw'文件夹中

我仍然使用同样的FFT类:http://www.cs.princeton.edu/introcs/97data/FFT.java

配套的复数类为:http://introcs.cs.princeton.edu/java/97data/Complex.java.html

我使用这个方法从我的raw文件夹中读取音频文件,然后调用calculateFFT方法进行计算

private static final int RECORDER_BPP = 16;
  private static final int RECORDER_SAMPLERATE = 44100;
  private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO;
  private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;


private void asli(){

            int counter = 0;
            int data;
            InputStream inputStream  = getResources().openRawResource(R.raw.b1);
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            List<Integer> content = new ArrayList<Integer>(); 

            try {
                while ((data = dataInputStream.read()) != -1) {
                    content.add(data);
                    counter++; }
            } catch (IOException e) {
                e.printStackTrace();}

                int[] b = new int[content.size()];
                int cont = 0;
                byte[] audio = convertArray(b);
        }

转换为字节的方法

public byte[] convertArray(int[] array) { 

            int minBufferSize = AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING);
                AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING,minBufferSize, AudioTrack.MODE_STREAM);

        byte[] newarray = new byte[array.length];
        for (int i = 0; i < array.length; i++) {
        newarray[i] = (byte) ((array[i]) & 0xFF);       }

            absNormalizedSignal = calculateFFT(newarray);
            return newarray;
        }

这是 CalculateFFT 方法

public double[] calculateFFT(byte[] signal)
        {           
            final int mNumberOfFFTPoints =1024;
            double mMaxFFTSample;
            double temp;
            Complex[] y;
            Complex[] complexSignal = new Complex[mNumberOfFFTPoints];
            double[] absSignal = new double[mNumberOfFFTPoints/2];

            for(int i = 0; i < mNumberOfFFTPoints; i++){
                temp = (double)((signal[2*i] & 0xFF) | (signal[2*i+1] << 8)) / 32768.0F;
                complexSignal[i] = new Complex(temp,0.0);
            }

            y = FFT.fft(complexSignal);

            mMaxFFTSample = 0.0;
            mPeakPos = 0;
            for(int i = 0; i < (mNumberOfFFTPoints/2); i++)
            {
                 absSignal[i] = Math.sqrt(Math.pow(y[i].re(), 2) + Math.pow(y[i].im(), 2));
                 if(absSignal[i] > mMaxFFTSample)
                 {
                     mMaxFFTSample = absSignal[i];
                     mPeakPos = i;
                 } 
            }

            return absSignal;

        }

我之前使用CalculateFFT方法处理了从AudioRecorder中录制的音频,成功地获取到了值,但是我无法从我的音频文件中获取值... 我不打算播放这段音频... 我只需要用FFT处理它。

我的代码有问题吗?似乎我在使用Asli()方法时出现了问题。但我不知道哪里出错了...

任何帮助都将不胜感激... :) 谢谢


1
你刚刚发布了很多代码,但是对于你的代码失败的方式以及你已经尝试过什么来解决它都没有给出清晰的描述。 - marko
1
@Marko 很抱歉.. :( 因为我看到很多没有通过适当代码的问题,所以人们投票反对..非常不清楚我的代码失败在哪里--> 我已经写了我无法从我的音频文件中获取值...用FFT处理它。我已经尝试过的是-->方法asli()我尝试使用InputStream inputStream =getResources().openRawResource(R.raw.b1); DataInputStream dataInputStream = new DataInputStream(inputStream); 然后我用缓冲区大小读取数组数据,但似乎我失败了。 - raisa_
2
RIFF/WAV文件中的样本通常以16位小端有符号整数的形式存储,而不是字节或双精度浮点数。因此,您可能会缺少类型转换。 - hotpaw2
@BjornRoche 是的..非常感谢您的页面推荐.. :) 我正在学习它... - raisa_
@hotpaw2 我已经编辑了我的代码...我添加了转换方法,现在结果出来了,但是返回值总是0.0...有什么建议吗?:( - raisa_
显示剩余3条评论
1个回答

13

我花了大部分上午的时间用一些FFT java代码片段编写了一个解决方案,但后来我偶然发现了这个非常棒的Google Code项目,它有一堆用于处理WAV和MP3文件信号处理任务的实用类。

https://github.com/Uriopass/audio-analysis 以前在Google Code上的SVN导出在此处:https://storage.googleapis.com/google-code-archive-source/v2/code.google.com/audio-analysis/source-archive.zip

现在它变得非常容易:

WaveDecoder decoder = new WaveDecoder(new FileInputStream(wavFile));
FFT fft = new FFT(1024, wavFileObj.getSampleRate());

现在,您可以使用fft对象进行各种计算。它们有很多很棒的示例,例如生成包含频谱通量的列表:

    float[] samples = new float[1024];
    float[] spectrum = new float[1024 / 2 + 1];
    float[] lastSpectrum = new float[1024 / 2 + 1];
    List<Float> spectralFlux = new ArrayList<Float>();

    while (decoder.readSamples(samples) > 0) {
        fft.forward(samples);
        System.arraycopy(spectrum, 0, lastSpectrum, 0, spectrum.length);
        System.arraycopy(fft.getSpectrum(), 0, spectrum, 0, spectrum.length);

        float flux = 0;
        for (int i = 0; i < spectrum.length; i++)
            flux += (spectrum[i] - lastSpectrum[i]);
        spectralFlux.add(flux);
    }

我的公司需要我分析一些音频,以查看是否已经播放了预期的等待音乐。因此,首先我为一个包含等待音乐的示例生成了WAV文件。然后,我捕获了一个没有等待音乐的示例的某些音频。现在剩下的就是对WAV的谱通量求平均值,我就可以开始了。

注意:我不能简单地取振幅...但傅里叶变换具有正确使用的频率,可以用来进行比较。

我热爱数学。


他们的结帐链接不起作用,即http://audio-analysis.googlecode.com/svn/trunk/audio-analysis-read-only。 - berserk
4
你从哪里获得了 wavFileObj 对象? 你从哪里获得了 WaveDecoder 对象? 你写了一些代码,但似乎没有用处。 请详细解释一下... - Vahid Forghani
什么混蛋给我这个答案打了-1分? - Nicholas DiPiazza
这一切都包含在那个Google库中。在测试类中查找FFT示例。 - Nicholas DiPiazza
1
为了以后参考,答案中的原始链接已失效,因为谷歌关闭了几乎所有的googlecode项目。幸运的是,在此之前我克隆了这个库:https://github.com/Uriopass/audio-analysis - Pâris Douady
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接