zoukankan      html  css  js  c++  java
  • 7.6 捕获和播放原始音频的示例

        下面是一个完整的示例,其中使用AudioRecord录制音频,并使用AudioTrack播放音频。通过使用AsyncTask,每个操作都在他们各自的线程中工作,所以他们并不会导致在主线程中运行的应用程序变得无响应。

     1 package com.nthm.androidtestActivity;
     2 
     3 import java.io.DataInputStream;
     4 import java.io.DataOutputStream;
     5 import java.io.File;
     6 import java.io.FileInputStream;
     7 import java.io.FileNotFoundException;
     8 import java.io.FileOutputStream;
     9 import java.io.IOException;
    10 import com.nthm.androidtest.R;
    11 import android.app.Activity;
    12 import android.media.AudioFormat;
    13 import android.media.AudioManager;
    14 import android.media.AudioRecord;
    15 import android.media.AudioTrack;
    16 import android.media.MediaRecorder;
    17 import android.os.AsyncTask;
    18 import android.os.Bundle;
    19 import android.os.Environment;
    20 import android.view.View;
    21 import android.view.View.OnClickListener;
    22 import android.widget.Button;
    23 import android.widget.TextView;
    24 
    25 public class AltAudioRecorder extends Activity implements OnClickListener {

        我们定义了两个内部类——一个用于录制,另一个用于播放。每个类都扩展了AsyncTask。

    1     private RecordAudio recordTask;
    2     private PlayAudio playTask;
    3     private Button startRecordingButton;
    4     private Button stopRecordingButton;
    5     private Button startPlaybackButton;
    6     private Button stopPlaybackButton;
    7     private TextView statusText;
    8     private File recordingFile;

        我们将使用布尔值来跟踪是否应该录制或播放,这些值将用于录制和播放任务中的循环部分。

    1     private boolean isPlaying=false;
    2     private boolean isRecording=false;

        下面是用来定义AudioRecord和AudioTrack对象配置的变量。

     1     private int frequency=11025;
     2     private int channelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_MONO;
     3     private int audioEncoding=AudioFormat.ENCODING_PCM_16BIT;
     4 
     5     @Override
     6     protected void onCreate(Bundle savedInstanceState) {
     7         super.onCreate(savedInstanceState);
     8         setContentView(R.layout.altaudiorecorder);
     9         statusText=(TextView) findViewById(R.id.StatusTextView);
    10         startRecordingButton=(Button) findViewById(R.id.StartRecordingButton);
    11         stopRecordingButton=(Button) findViewById(R.id.StopRecordingButton);
    12         startPlaybackButton=(Button) findViewById(R.id.StartPlaybackButton);
    13         stopPlaybackButton=(Button) findViewById(R.id.StopPlaybackButton);
    14         
    15         startRecordingButton.setOnClickListener(this);
    16         stopRecordingButton.setOnClickListener(this);
    17         startPlaybackButton.setOnClickListener(this);
    18         stopPlaybackButton.setOnClickListener(this);
    19         
    20         stopRecordingButton.setEnabled(false);
    21         startPlaybackButton.setEnabled(false);
    22         stopPlaybackButton.setEnabled(false);

        最后在构造函数中创建将要录制和播放的文件。在当前情况下,将在SD卡上与应用程序相关联的首选位置中创建文件。

    1         File path=new File(Environment.getExternalStorageDirectory().getAbsoluteFile()+"video");
    2         path.mkdirs();
    3         try{
    4         recordingFile=File.createTempFile("recording", ".pcm",path);
    5         }catch(Exception e){
    6             e.printStackTrace();
    7         }
    8     }

        onClick方法处理由用户生成的按钮单击事件。每个事件都对应一个具体的方法。

     1     @Override
     2     public void onClick(View v) {
     3         if(v==startRecordingButton){
     4             record();
     5         }else if(v==stopRecordingButton){
     6             stopRecording();
     7         }else if(v==startPlaybackButton){
     8             play();
     9         }else if(v==stopPlaybackButton){
    10             stopPlaying();
    11         }
    12     }

        为了启动播放,我们将构造一个新的PlayAudio对象,并调用继承自AsyncTask的execute方法。

    1     private void play(){
    2         startPlaybackButton.setEnabled(true);
    3         
    4         playTask=new PlayAudio();
    5         playTask.execute();
    6         stopPlaybackButton.setEnabled(true);
    7     }

        为了停止播放,只须将isPlaying布尔值设置为false。这将导致PlayAudio对象的循环结束。

    1     private void stopPlaying(){
    2         isPlaying=false;
    3         stopPlaybackButton.setEnabled(false);
    4         startPlaybackButton.setEnabled(true);
    5     }

        为了开始录制,我们将构造一个RecordAudio对象,并调用它的execute方法。

    1     private void record(){
    2         startRecordingButton.setEnabled(false);
    3         stopRecordingButton.setEnabled(true);
    4         startPlaybackButton.setEnabled(true);
    5         recordTask=new RecordAudio();
    6         recordTask.execute();
    7     }

        为了停止录制,只须将isRecording布尔值设定为false。这将使得RecordAudio对象停止循环,并执行任何清除工作。

    1     private void stopRecording(){
    2         isRecording=false;
    3     }

        下面是PlayAudio内部类。这个类扩展了AsyncTask,并使用一个AudioTrack对象来播放音频。

     1     private class PlayAudio extends AsyncTask<Void, Integer, Void>{
     2         @Override
     3         protected Void doInBackground(Void... params) {
     4             isPlaying=true;
     5             int bufferSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
     6             
     7 
     8             short [] audiodata=new short[bufferSize/4];
     9             try {
    10                 DataInputStream dis=new DataInputStream(new FileInputStream(recordingFile));
    11                 AudioTrack audioRtack=new AudioTrack(AudioManager.STREAM_MUSIC, frequency, channelConfiguration, audioEncoding, bufferSize, AudioTrack.MODE_STREAM);
    12                 audioRtack.play();
    13                 try {
    14                     while(isPlaying&&dis.available()>0){
    15                         int i=0;
    16                         while(dis.available()>0&&i<audiodata.length){
    17                             audiodata[i]=dis.readShort();
    18                             i++;
    19                         }
    20                         audioRtack.write(audiodata, 0, audiodata.length);
    21                     }
    22                 } catch (IOException e) {
    23                     e.printStackTrace();
    24                 }
    25                 try {
    26                     dis.close();
    27                 } catch (IOException e) {
    28                     e.printStackTrace();
    29                 }
    30                 startPlaybackButton.setEnabled(false);
    31                 stopPlaybackButton.setEnabled(true);
    32             } catch (FileNotFoundException e) {
    33                 e.printStackTrace();
    34             }
    35             return null;
    36         }
    37         
    38     }

        最后是RecordAudio类,它同样也扩展了AsyncTask。这个类在后台运行一个AudioRecord对象,并调用publishProgress方法来使用录制进度提示更新UI。

     1     private class RecordAudio extends AsyncTask<Void, Integer, Void>{
     2        
     3         @Override
     4         protected Void doInBackground(Void... params) {
     5             isRecording=true;
     6             try {
     7                 DataOutputStream dos=new DataOutputStream(new FileOutputStream(recordingFile));
     8 
     9             int bufferSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding);
    10             AudioRecord audioRecord=new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize);
    11             short[]buffer=new short[bufferSize];
    12             audioRecord.startRecording();
    13             int r=0;
    14             while(isRecording){
    15                 int bufferReadResult=audioRecord.read(buffer, 0,bufferSize);
    16                 for(int i=0;i<bufferReadResult;i++){
    17                     dos.writeShort(buffer[i]);
    18                 }
    19                 publishProgress(new Integer(r));
    20                 r++;
    21             }
    22             audioRecord.stop();
    23             dos.close();
    24         }catch(Exception e){
    25             e.printStackTrace();
    26         }
    27             return null;
    28         }

        当调用publishProgress时,onProgressUpdate方法将会被调用。

    1         @Override
    2         protected void onProgressUpdate(Integer... values) {
    3             super.onProgressUpdate(values);
    4             statusText.setText(values[0].toString());
    5         }

        当完成doInBackground方法时,随后将调用onPostExecute方法。

    1         @Override
    2         protected void onPostExecute(Void result) {
    3             super.onPostExecute(result);
    4             startRecordingButton.setEnabled(true);
    5             stopRecordingButton.setEnabled(false);
    6             startPlaybackButton.setEnabled(true);
    7         }
    8     }
    9 }

       下面是用于上述示例的布局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="Status"
     9      android:layout_width="fill_parent"
    10      android:layout_height="wrap_content"
    11      android:textSize="35dip"></TextView>
    12   
    13  <Button 
    14      android:layout_width="wrap_content"
    15      android:layout_height="wrap_content"
    16      android:id="@+id/StartRecordingButton"
    17      android:text="Start Recording"/>
    18  <Button 
    19      android:layout_width="wrap_content"
    20      android:layout_height="wrap_content"
    21      android:id="@+id/StopRecordingButton"
    22      android:text="Stop Recording"/>
    23  <Button 
    24      android:layout_width="wrap_content"
    25      android:layout_height="wrap_content"
    26      android:id="@+id/StartPlaybackButton"
    27      android:text="Play Recording"/>
    28  <Button 
    29      android:layout_width="wrap_content"
    30      android:layout_height="wrap_content"
    31      android:id="@+id/StopPlaybackButton"
    32      android:text="Stop Playback"/>
    33 </LinearLayout>

        同时需要将下面这些权限添加到AndroidMainfest.xml。

    1     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    2     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

        正如我们已经看到的那样,使用AudioRecord和AudioTrack类来创建捕获和播放应用程序要比使用MediaPlayer和MediaPlayer类更加复杂。但是我们在第8章中将看到,当需要实现任何类型的音频处理或者需要合成音频时,这是值得付出努力的。

  • 相关阅读:
    Linux进程间通信分类 以及 pipe的原理实现
    单链表的快速排序
    再谈二分查找
    数据库范式
    二分查找法浅析
    C# Observer设计模式
    C# 交错数组浅析
    C语言初学 数学中带根号的复杂计算问题
    C语言初学 计算三角形面积问题
    C语言初学 简单定义圆的面积计算问题
  • 原文地址:https://www.cnblogs.com/ZSS-Android/p/3950672.html
Copyright © 2011-2022 走看看