zoukankan      html  css  js  c++  java
  • 8.2.2 可视化频率

        人们通常用来分析音频的方法是可视化其中存在的频率。通常这些类型的可视化采用均衡器,均衡器允许调整各种频率范围的级别。

        将音频信号转换成分量频率(component frequency)的技术采用了一个数学变换,称为离散傅里叶变换(Discrete Fourier Transform,DFT)。DFT通常用来将基于时间的数据转换成基于频率的数据。一种用来执行DFT的算法是快速傅里叶变换(FFT),它非常有效,但是,非常复杂。

        幸运的是,存在许多FFT算法的实现,他们位于公共领域中,或者开放源代码,因此可以直接使用他们。其中的一个版本是FFTPACK库的java端口,这个库最初由国家大气研究中心的Paul Swarztrauber开发。其java端口由加拿大亚伯达省Lethbridge大学的Baoshe Zhang实现。www.netlib.org/fftpack/在线提供了各种实现。我们将要使用的文件称为jfftpack.tgz的存档,它由该页面提供链接。可以直接通过www.netlib,org/jffpack.tgz下载该文件。

        为了在一个Eclipse Android项目中使用这个程序包或任何其他包含java源代码的程序包,需要将源代码导入到项目中。由于这个存档包含了程序包的正确的目录结构,因此只要将位于javasource目录(ca)的顶层文件夹拖动到项目的src目录中。

        下面是一个示例,其绘制了图像均衡器的图形部分。

     1 package com.nthm.androidtestActivity;
     2 
     3 import com.nthm.androidtest.R;
     4 import android.app.Activity;
     5 import android.graphics.Bitmap;
     6 import android.graphics.Canvas;
     7 import android.graphics.Color;
     8 import android.graphics.Paint;
     9 import android.media.AudioFormat;
    10 import android.media.AudioRecord;
    11 import android.media.MediaRecorder;
    12 import android.os.AsyncTask;
    13 import android.os.Bundle;
    14 import android.view.View;
    15 import android.view.View.OnClickListener;
    16 import android.widget.Button;
    17 import android.widget.ImageView;

        我们将从fftpack包中导入RealDoubleFFT类。

    1 import ca.uol.aig.fftpack.RealDoubleFFT;
    2 public class AudioProcessing extends Activity implements OnClickListener {

        我们将在AudioRecord对象中使用8kHz的频率,单音频通道和16位的样本。

    1     private int frequency=8000;
    2     private int channelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_MONO;
    3     private int audioEncoding=AudioFormat.ENCODING_PCM_16BIT;

        transformer将是我们的FFT对象,通过该FFT对象,可以一次性处理来自AudioRecord对象的256个样本。使用的样本数量将对应于通过FFT对象运行他们之后获得的分量频率的数量。虽然可以自由选择不同的大小,但是确实需要考虑内存和性能的问题,因为需要计算的算术操作时处理器密集型的。

    1     private RealDoubleFFT transformer;
    2     private int blockSize=256;
    3     private Button startStopButton;
    4     private boolean started=false;

        下面定义的RecordAudio是内部类,其扩展了AsyncTask。

    1     private RecordAudio recordTask;

        我们将使用ImageView显示一幅位图图像,其表示当前音频流中各种频率的级别。为了绘制这些级别,可以使用通过该位图构造的Canvas和Paint对象。

    1     private ImageView imageView;
    2     private Bitmap bitmap;
    3     private Canvas canvas;
    4     private Paint paint;
    5     @Override
    6     protected void onCreate(Bundle savedInstanceState) {
    7         super.onCreate(savedInstanceState);
    8         startStopButton=(Button) findViewById(R.id.StartStopButton);
    9         startStopButton.setOnClickListener(this);

        RealDoubleFFT类的构造函数接受每次处理的样品数量作为参数。这也代表了将要输出的不同频率范围的数量。

    1         transformer=new RealDoubleFFT(blockSize);

        下面是ImageView的设置和用于绘图的相关对象。

    1         imageView=(ImageView) findViewById(R.id.ImageView01);
    2         bitmap=Bitmap.createBitmap((int)256, (int)100, Bitmap.Config.ARGB_8888);
    3         canvas=new Canvas(bitmap);
    4         paint=new Paint();
    5         paint.setColor(Color.GREEN);
    6         imageView.setImageBitmap(bitmap);
    7     }

        这个活动中的大部分工作在下面的RecordAudio类中完成,其扩展了AsyncTask。通过使用AsyncTask,可以在一个单独的线程上运行绑定用户界面的方法。任何放置在doInBackground方法中的代码都将以这种方式运行。

    1     private class RecordAudio extends AsyncTask<Void, double[], Void>{
    2 
    3         @Override
    4         protected Void doInBackground(Void... params) {
    5             try{

        我们将以通常的方式建立和使用AudioRecord。

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

        short类型的数组buffer将接受来自AudioRecord对象的原始PCM样本。Double类型的数组toTransform将以双精度的形式存储相同的数据,因为这是FFT类所需要的类型。

    1                 short [] buffer=new short[blockSize];
    2                 double[] toTransform=new double[blockSize];
    3                 audioRecord.startRecording();
    4                 while(started){
    5                     int bufferReadResult=audioRecord.read(buffer, 0,blockSize);

        从AudioRecord对象中读取数据之后进行遍历,并将short值转换成double值。但是,不能直接通过强制类型转换这么做,因为期望值应该在-1.0~1.0之间,而不是整个值范围。将short值除以Short.MAX_VALUE将实现这个目的,因为该值是short类型的最大值。

    1                     for(int i=0;i<blockSize&&i<bufferReadResult;i++){
    2                         toTransform[i]=(double)buffer[i]/Short.MAX_VALUE;//有符号16位
    3                     }

        接下来将双精度值的数组传递给FFT对象。FFT对象重用这个相同的数组来保存输出值。包含的数据将采用频域(frequency domain)而非时域(time domain),这意味着数组的第一个元素不是根据时间表示第一个样本——相反,它表示第一个频率集合的级别。

        由于使用256个值(或范围)且采样率是8000,因此可以确定数组中的每个元素将近似覆盖15.625Hz。通过将这个采样率除以2(因为可以捕获的最高频率是采样率的一半),然后除以256,就可以得到这个数字。因此,数组中第一个元素表示的数据将代表在0~15.625Hz之间的音频级别。

    1                     transformer.ft(toTransform);

        调用publishProgress方法将会调用onProgressUpdate。

    1                     publishProgress(toTransform);
    2                 }
    3                 audioRecord.stop();
    4             }catch(Exception e){
    5                 e.printStackTrace();
    6             }
    7             return null;
    8         }

        onProgressUpdate子啊活动的主线程上运行,因此可以正常的与用户界面交互。在此实现中,传入了通过FFT对象运行之后的数据。该方法在屏幕上将数据绘制成一系列最大100像素高的线。每一条线表示数组中的一个元素,因此值范围是15.625Hz。第一条线表示频率范围是0~15.625Hz,而最后一条线表示频率范围是3984.375~4000Hz。

     1         @Override
     2         protected void onProgressUpdate(double[]... toTransform) {
     3             super.onProgressUpdate(toTransform);
     4             canvas.drawColor(Color.BLACK);
     5             for(int i=0;i<toTransform.length;i++){
     6                 int x=i;
     7                 int downy=(int)(100-(toTransform[0][i]*10));
     8                 int upy=100;
     9                 canvas.drawLine(x, downy, x, upy, paint);
    10             }
    11             imageView.invalidate();
    12         }
    13     }
    14     @Override
    15     public void onClick(View v) {
    16        if(started){
    17            started=false;
    18            startStopButton.setText("Start");
    19            recordTask.cancel(true);
    20        }else{
    21            started=true; 
    22            startStopButton.setText("Stop");
    23            recordTask=new RecordAudio();
    24            recordTask.execute();
    25        }
    26     }
    27 }

        下面是刚刚定义的AudioProcessing活动所使用的布局XML文件。

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="match_parent"
     3     android:layout_height="match_parent"
     4     android:orientation="vertical"
     5     >
     6  <TextView 
     7      android:id="@+id/StatusTextView"
     8      android:text="hello"
     9      android:layout_width="fill_parent"
    10      android:layout_height="wrap_content"
    11      android:textSize="35dip"></TextView>
    12   <ImageView 
    13       android:id="@+id/ImageView01"
    14       android:layout_width="wrap_content"
    15       android:layout_height="wrap_content"
    16       />
    17  <Button 
    18      android:layout_width="wrap_content"
    19      android:layout_height="wrap_content"
    20      android:id="@+id/StartStopButton"
    21      android:text="Start"/>
    22 
    23 </LinearLayout>
  • 相关阅读:
    转:ITIL的開源軟件
    转:Asp.Net大型项目实践
    转:Ubuntu上apache多端口配置虚拟主机的方法
    转:JS 获取鼠标位置
    转:一切整合分享到新浪网易微博代码
    转:Facebook是如何发布代码的
    转:利用 Amazon Web Services 集成企业应用程序使用 Amazon SQS 发送 XML 消息
    sqlite3 常用操作
    转:4 款消息队列软件产品大比拼
    SQL Server 2008 列转行 实例
  • 原文地址:https://www.cnblogs.com/ZSS-Android/p/3951851.html
Copyright © 2011-2022 走看看