【问题标题】:Get the frequency of an audio file in every 1/4 seconds in android在android中每1/4秒获取一个音频文件的频率
【发布时间】:2012-08-04 09:44:29
【问题描述】:

我有一个声音文件 (.3gp),大约需要 1 分钟。我想每 1/4 秒得到这个声音文件的频率。我的想法是每 1/4 秒从音频文件中接收样本,并使用 FFT 我可能会得到频率值。有没有办法做到这一点?

实际上,我会将声音文件分成 1/4 秒的样本声音文件(总是覆盖以前的文件),然后使用 FFT 算法并检测幅度最大的频率。但是可能有更简单的解决方案,但是我也不知道如何做到这一点。

***更新 2 - 新代码

到目前为止,我使用此代码:

public class RecordAudio extends AsyncTask<Void, double[], Void> {

    @Override
    protected Void doInBackground(Void... arg0) {

        try {
             int bufferSize = AudioRecord.getMinBufferSize(frequency,
             AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);


            //int bufferSize = AudioRecord.getMinBufferSize(frequency, 
                  //  channelConfiguration, audioEncoding); 

            AudioRecord audioRecord = new AudioRecord( 
                    MediaRecorder.AudioSource.MIC, frequency, 
                    channelConfiguration, audioEncoding, bufferSize); 

            short[] buffer = new short[blockSize];
            //double[] toTransform = new double[blockSize];


            audioRecord.startRecording();


            // started = true; hopes this should true before calling
            // following while loop

            while (started) {
               sampling++;

               double[] re = new double[blockSize];
               double[] im = new double[blockSize];

               double[] newArray = new double[blockSize*2];
               double[] magns = new double[blockSize];

               double MaxMagn=0;
               double pitch = 0;

               int bufferReadResult = audioRecord.read(buffer, 0,
                        blockSize);


               for (int i = 0; i < blockSize && i < bufferReadResult; i++) {
                   re[i] = (double) buffer[i] / 32768.0; // signed   16bit
                   im[i] = 0;
               }    

               newArray = FFTbase.fft(re, im,true);

               for (int i = 0; i < newArray.length; i+=2) {

                   re[i/2]=newArray[i];
                   im[i/2]=newArray[i+1];
                   magns[i/2] = Math.sqrt(re[i/2]*re[i/2]+im[i/2]*im[i/2]);
               }

              // I only need the first half      

              for (int i = 0; i < (magns.length)/2; i++) {
                   if (magns[i]>MaxMagn)
                   {
                       MaxMagn = magns[i];
                       pitch=i;
                   }
               }                                           
                 if (sampling > 50) {
                   Log.i("pitch and magnitude", "" + MaxMagn + "   " + pitch*15.625f);
                   sampling=0;
                   MaxMagn=0;pitch=0;
                   }                   


            }

            audioRecord.stop();

        } catch (Throwable t) {
            t.printStackTrace();
            Log.e("AudioRecord", "Recording Failed");
        }
        return null;
    }

我用这个:http://www.wikijava.org/wiki/The_Fast_Fourier_Transform_in_Java_%28part_1%29

吉他弦似乎是正确的,但我自己的声音并不好,因此:

两个峰值的幅度大部分时间都在变化,我总是找到最大的来获得基频。

【问题讨论】:

  • 你好,我也有同样的问题,我需要实时录制语音,每4ms计算一次频率,你是怎么做到的?你有任何示例代码吗?
  • 嗨,我没有成功解决这个问题,但是我的吉他声音是 10 分中有 9 分是合适的,但我的声音可能是 10 分中有 7 分..

标签: android audio fft frequency


【解决方案1】:

在 Stack Overflow 上经常有人问到使用 FFT 进行音高跟踪,我写了一个 blog entry with sample code。代码是 C 语言,但有了解释和链接,您应该能够做您想做的事。

至于将其划分为 1/4 秒的增量,您可以按照您的建议简单地采用 1/4 秒段的 FFT,而不是默认值(我认为约为 1 秒)。如果这不能为您提供所需的频率分辨率,您可能必须使用不同的音高识别方法。您可以做的另一件事是使用超过 1/4 秒的重叠段,但以相隔 1/4 秒的间隔开始。此方法在博客条目中有所提及,但可能不符合您的设计规范。

【讨论】:

  • 感谢您的回答,用我的代码更新了我的问题。我通过启动采样计数器解决了 1/4 秒,当它达到给定值时,它再次启动。但是音高检测在较高频率时并不是那么好。如果我发出响亮的高音,高次谐波会使整个事情出错,如果是 3khz,我会得到 13khz 左右。但是,例如,我得到 1kz 的 600hz,所以我不知道是什么问题。
  • 问题在于,如果你的声音有谐波(即任何乐器,或任何不是纯正弦波的噪声),那么仅仅找到 FFT 的峰值并不能说明问题你的音高。对应于音高的频率可以是比谐波更低的幅度。你需要阅读Pitch Estimation algorithms
  • the_mandrill 说的是真的,但很明显你还有其他问题,因为你得到的频率不是倍数,因此不是谐波。如果以后有机会,我会更仔细地查看您的代码,但是在第一次浏览时,您似乎犯了一些错误:1.查看整个转换后的数据,而不是下半部分,2.没有窗口化你的数据。我的博客条目教程涵盖了所有这些以及更多内容。
  • Bjorn:我刚刚开始了解您的博客条目。这个小项目对我来说非常重要,所以我计划为此做很多工作:)
  • 我查看了您的博客(无论如何我正在学习 EE)。我使用应用的 LPF 编辑了我的代码。这会给我一个低于 4KHz 的频率,但这是错误的。五月吉他 A2 是 110Hz 的第二弦。如果我让声音流畅,我的应用程序会给我 129Hz。声音更大一点,它给了我 129*2= 258Hz。所以我必须以某种方式找到最低谐波。也许我可以设置一个限制:选择幅度超过限制的频率并选择最低频率。但我认为这是一种解决方法,可能有正确的算法可以做到这一点..
【解决方案2】:

试试 AsyncTask

class GetFrequency extends AsyncTask<String, Void, Void> {
   public Void doInBackground(String... params) {
          while (true) {

             // Apply Logic Here

           try {
                Thread.sleep(250);
               } catch (Exception ie) {
                  // TODO Auto-generated catch block
                e.printStackTrace();
               }
       }
   }  
}

在你的 MainActivity 中调用它,

frequencyButtonListener.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

        new GetFrequency.execute(params);

        }
    });

【讨论】:

  • 您好,感谢您的回答。我遇到了一些我无法修复的错误。 onPostExecute、onPreExecute 和 onProgress 更新给了我语法错误。
  • 如果你不想要它们,那就删除它们吧!!
  • 我删除了它们。坦率地说,我不明白它应该如何工作。我在 /sdcard/music.3gp 中有一个 .3gp 文件,我想对其进行分析。所以我用 new GetFrequency.execute(params); 做了一个按钮但它给了我一个错误 GetFrequency.execute 无法解析为类型。
  • 我的另一个问题是我对我需要应用的逻辑很感兴趣。
  • @JaniBela-尝试编辑后的代码...n 抱歉,我无法为您提供有关逻辑的帮助,因为我在 Android 中没有使用频率!
猜你喜欢
  • 1970-01-01
  • 2019-08-02
  • 2013-07-30
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 2011-07-05
  • 2013-12-18
  • 1970-01-01
相关资源
最近更新 更多