AudioRecord和AudioTrack类的使用(转)

AudioRecord和AudioTrack类是Android获取和播放音频流的重要类,放置在android.media包中。与该包中的MediaRecorder和MediaPlayer类不同,AudioRecord和AudioTrack类在获取和播放音频数据流时无需通过文件保存和文件读取,可以动态地直接获取和播放音频流,在实时处理音频数据流时非常有用。

当然,如果用户只想录音后写入文件或从文件中取得音频流进行播放,那么直接使用MediaRecorder和MediaPlayer类是首选方案,因为这两个类使用非常方便,而且成功率很高。而AudioRecord和AudioTrack类的使用却比较复杂,我们发现很多人都不能成功地使用这两个类,甚至认为Android的这两个类是不能工作的。

其实,AudioRecord和AudioTrack类的使用虽然比较复杂,但是可以工作,我们不仅可以很好地使用了这两个类,而且还通过套接字(Socket)实现了音频数据的网络传输,做到了一端使用AudioRecord获取音频流然后通过套接字传输出去,而另一端通过套接字接收后使用AudioTrack类播放。

下面是我们对AudioRecord和AudioTrack类在使用方面的经验总结:

(1)创建AudioRecord和AudioTrack类对象:创建这两个类的对象比较复杂,通过对文档的反复和仔细理解,并通过多次失败的尝试,并在北理工的某个Android大牛的网上的文章启发下,我们也最终成功地创建了这两个类的对象。创建AudioRecord和AudioTrack类对象的代码如下:

AudioRecord类:

m_in_buf_size =AudioRecord.getMinBufferSize(8000,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
   
         m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC,
         8000,
         AudioFormat.CHANNEL_CONFIGURATION_MONO,
         AudioFormat.ENCODING_PCM_16BIT,
         m_in_buf_size) ;

AudioTrack类:

m_out_buf_size = android.media.AudioTrack.getMinBufferSize(8000,
                          AudioFormat.CHANNEL_CONFIGURATION_MONO,
                          AudioFormat.ENCODING_PCM_16BIT);

m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
                                       AudioFormat.CHANNEL_CONFIGURATION_MONO,
                                       AudioFormat.ENCODING_PCM_16BIT,
                                       m_out_buf_size,
                                       AudioTrack.MODE_STREAM);

(2)关于AudioRecord和AudioTrack类的监听函数,不用也行。

(3)调试方面,包括初始化后看logcat信息,以确定类的工作状态,初始化是否成功等。

编写好代码,没有语法错误,调用模拟器运行、调试代码时,logcat发挥了很好的功用。刚调试时,经常会出现模拟器显示出现异常,这时我们可以在代码的一些关键语句后添加如Log.d("test1","OK");这样的语句进行标识,出现异常时我们就可以在logcat窗口观察代码执行到哪里出现异常,然后进行相应的修改、调试。模拟器不会出现异常时,又遇到了录放音的问题。录音方面,刚开始选择将语音编码数据存放在多个固定大小的文件中进行传送,但是这种情况下会出现声音断续的现象,而且要反复的建立文件,比较麻烦,后来想到要进行网上传输,直接将语音编码数据以数据流的形式传送,经过验证,这种方法可行并且使代码更加简洁。放音方面,将接收到的数据流存放在一个数组中,然后将数组中数据写到AudioTrack中。刚开始只是“嘟”几声,经过检查发现只是把数据写一次,加入循环,让数据反复写到AudioTrack中,就可以听到正常的语音了。接下来的工作主要是改善话音质量与话音延迟,在进行通话的过程中,观察logcat窗口,发现向数组中写数据时会出现Bufferflow的情况,于是把重心转移到数组大小的影响上,经过试验,发现 AudioRecord一次会读640个数据,然后就对录音和放音中有数组的地方进行实验修改。AudioRecord和AudioTrack进行实例化时,参数中各有一个数组大小,经过试验这个数组大小和AudioRecord和AudioTrack能正常实例化所需的最小Buffer大小(即上面实例化时的m_in_buf_size和m_out_buf_size参数)相等且服务器方进行缓存数据的数组尺寸是上述数值的2倍时,语音质量最好。由于录音和放音的速度不一致,受到北理工大牛的启发,在录音方面,将存放录音数据的数组放到LinkedList中,当LinkedList中数组个数达到2(这个也是经过试验验证话音质量最好时的数据)时,将先录好的数组中数据传送出去。经过上述反复试验和修改,最终使双方通话质量较好,且延时较短(大概有2秒钟)。

(4)通过套接字传输和接收数据

数据传送部分,使用的是套接字。通信双方,通过不同的端口向服务器发送请求,与服务器连接上后,开始通话向服务器发送数据,服务器通过一个套接字接收到一方的数据后,先存在一个数组中,然后将该数组中数据以数据流的形式再通过另一个套接字传送到另一方。这样就实现了双方数据的传送。

(5)代码架构

为避免反复录入和读取数据占用较多资源,使程序在进行录放音时不能执行其他命令,故将录音和放音各写成一个线程类,然后在主程序中,通过MENU控制通话的开始、停止、结束。

最后说明,AudioRecord和AudioTrack类可以用,只是稍微复杂些。以下贴出双方通信的源码,希望对大家有所帮助:

主程序Daudioclient:

package cn.Daudioclient;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class Daudioclient extends Activity {
 
    public static final int MENU_START_ID = Menu.FIRST ;
    public static final int MENU_STOP_ID = Menu.FIRST + 1 ;
    public static final int MENU_EXIT_ID = Menu.FIRST + 2 ;
 
    protected Saudioserver     m_player ;
    protected Saudioclient     m_recorder ;
 
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
   
    public boolean onCreateOptionsMenu(Menu aMenu)
    {
        boolean res = super.onCreateOptionsMenu(aMenu) ;

aMenu.add(0, MENU_START_ID, 0, "START") ;
        aMenu.add(0, MENU_STOP_ID, 0, "STOP") ;
        aMenu.add(0, MENU_EXIT_ID, 0, "EXIT") ;

return res ;
    }

public boolean onOptionsItemSelected(MenuItem aMenuItem)
    {
        switch (aMenuItem.getItemId()) {
        case MENU_START_ID:
            {
             m_player = new Saudioserver() ;
                m_recorder = new Saudioclient() ;

m_player.init() ;
                m_recorder.init() ;

m_recorder.start() ;
                m_player.start() ;
               
            }
            break ;
        case MENU_STOP_ID:
            {  
             m_recorder.free() ;
                m_player.free() ;

m_player = null ;
                m_recorder = null ;
            }
            break ;
        case MENU_EXIT_ID:
            {
                int pid = android.os.Process.myPid() ;
                android.os.Process.killProcess(pid) ;
            }
            break ;
        default:
            break ;
        }

return super.onOptionsItemSelected(aMenuItem);
    }
}

录音程序Saudioclient:

package cn.Daudioclient;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

public class Saudioclient extends Thread
{
 
    protected AudioRecord m_in_rec ; 
    protected int         m_in_buf_size ;
    protected byte []     m_in_bytes ;
    protected boolean     m_keep_running ;
    protected Socket      s;
    protected DataOutputStream dout;
    protected LinkedList<byte[]>  m_in_q ;
  
    public void run()
 {
      try
      {
          byte [] bytes_pkg ;
             m_in_rec.startRecording() ;
             while(m_keep_running)
             {
                 m_in_rec.read(m_in_bytes, 0, m_in_buf_size) ;
                 bytes_pkg = m_in_bytes.clone() ;
                 if(m_in_q.size() >= 2)
                 {
                        dout.write(m_in_q.removeFirst() , 0, m_in_q.removeFirst() .length);
                    }
                    m_in_q.add(bytes_pkg) ;
             }
     
             m_in_rec.stop() ;
             m_in_rec = null ;
             m_in_bytes = null ;
       dout.close();
        
      }
      catch(Exception e)
      {
       e.printStackTrace();
      }
    }
   
    public void init()
    {
     m_in_buf_size =  AudioRecord.getMinBufferSize(8000,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
   
  m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC,
  8000,
  AudioFormat.CHANNEL_CONFIGURATION_MONO,
  AudioFormat.ENCODING_PCM_16BIT,
  m_in_buf_size) ;
  
  m_in_bytes = new byte [m_in_buf_size] ;
  
  m_keep_running = true ;
  m_in_q=new LinkedList<byte[]>();
  
     try
     {
   s=new Socket("192.168.1.100",4332);
   dout=new DataOutputStream(s.getOutputStream());
   //new Thread(R1).start();
  }
     catch (UnknownHostException e)
     {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
     catch (IOException e)
     {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

}
   
    public void free()
 {
  m_keep_running = false ;
        try {
            Thread.sleep(1000) ;
        } catch(Exception e) {
            Log.d("sleep exceptions...\n","") ;
        }
 }
}
放音程序Saudioserver:

package cn.Daudioclient;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;

public class Saudioserver extends Thread
{  
    protected AudioTrack m_out_trk ;
    protected int        m_out_buf_size ;
    protected byte []    m_out_bytes ;
    protected boolean    m_keep_running ;
 private Socket s;
 private DataInputStream din;
 public void init()
 {
  try
     {
            s=new Socket("192.168.1.100",4331);
            din=new DataInputStream(s.getInputStream());
           
             m_keep_running = true ;
       
           
            m_out_buf_size = AudioTrack.getMinBufferSize(8000,
                             AudioFormat.CHANNEL_CONFIGURATION_MONO,
                             AudioFormat.ENCODING_PCM_16BIT);

m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
                                       AudioFormat.CHANNEL_CONFIGURATION_MONO,
                                       AudioFormat.ENCODING_PCM_16BIT,
                                       m_out_buf_size,
                                       AudioTrack.MODE_STREAM);
         
            m_out_bytes=new byte[m_out_buf_size];
           
           // new Thread(R1).start();
           
     }
     catch(Exception e)
     {
      e.printStackTrace();
     }
 }
   
 public void free()
 {
  m_keep_running = false ;
        try {
            Thread.sleep(1000) ;
        } catch(Exception e) {
            Log.d("sleep exceptions...\n","") ;
        }
 }
 
  public void run()
  {
   byte [] bytes_pkg = null ;
         m_out_trk.play() ;
         while(m_keep_running) {
             try
             {
              din.read(m_out_bytes);
                 bytes_pkg = m_out_bytes.clone() ;
                 m_out_trk.write(bytes_pkg, 0, bytes_pkg.length) ;
             }
             catch(Exception e)
             {
              e.printStackTrace();
             }
            
         }
        
         m_out_trk.stop() ;
         m_out_trk = null ;
         try {
    din.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
}

时间: 2024-10-12 22:28:12

AudioRecord和AudioTrack类的使用(转)的相关文章

android通过数组,流播放声音的方法,音频实时传输

AudioRecord和AudioTrack类是Android获取和播放音频流的重要类,放置在android.media包中.与该包中 的MediaRecorder和MediaPlayer类不同,AudioRecord和AudioTrack类在获取和播放音频数据流时无需通过文件保 存和文件读取,可以动态地直接获取和播放音频流,在实时处理音频数据流时非常有用. 当然,如果用户只想录音后写入文件或从文件中取得音频流进行播放,那么直接使用MediaRecorder和MediaPlayer类是首选方案,

Android深入浅出之 AudioTrack分析

Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等. 分析的流程是: l         先从API层对应的某个类开始,用户层先要有一个简单的使用流程. l         根据这个流程,一步步进入到JNI,服务层.在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释.也就是深度优先的方法. 1.1 分析工

转---Android Audio System 之一:AudioTrack如何与AudioFlinger交换音频数据

引子 Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放,目前Android的Froyo版本设定了同时最多可以创建32个音频流,也就是说,Mixer最多会同时处理32个AudioTrack的数 据流. 如何使用AudioTrack AudioTra

Android Audio System 之一:AudioTrack如何与AudioFlinger

Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinger把所有的AudioTrack进行混合(Mixer),然后输送到AudioHardware中 进行播放 TAG: AudioTrack  音频系统  AudioFlinger 引子 Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个Audi

深入剖析Android音频之AudioTrack

播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用.虽然都可以播放声音,但两者还是有很大的区别的.其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等.MediaPlayer会在framework层创建对应的音频解码器.而AudioTrack只能播放已经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流.AudioTrack不创建解码

Android之AudioTrack实时播放

前言: 其实在Android中录音可以用MediaRecord录音,操作比较简单.但是不能对音频进行处理.考虑到项目中做的是实时语音只能选择AudioRecord进行录音.然后实时播放也只能采用AudioTrack进行播放. 下文为具体实现: public class AudioTrackManager { public static final String TAG = "AudioTrackManager"; private AudioTrack audioTrack; priva

Android 音视频深入 二 AudioTrack播放pcm(附源码下载)

本篇项目地址,名字是录音和播放PCM,求starhttps://github.com/979451341/Audio-and-video-learning-materials 1.AudioTrack官方说明AudioTrack允许PCM音频缓冲器流到音频接收器进行回放.这是通过"推"的数据对象的信号使用 write(byte[], int, int) and write(short[], int, int) 方法.一个信号可以在两种模式下运行:静态或流.在流模式中,应用程序写一个连续

Android多媒体开发介绍(转)

Android多媒体开发介绍 转自:http://blog.csdn.net/reiliu/article/details/9060557 一.       多媒体架构 基于第三方PacketVideo公司的OpenCORE来实现,支持所有通用的音频/视频/静态图像格式,包括:MPEG4.H.264.MP3.AAC.AMR.JPG.PNG.GIF等.从功能上分为两部分,一是音/视频的回放(PlayBack),二是音视频的纪录(Recorder). CODEC(编解码器)使用OpenMAX 1L

Android 实现能够暂停的录音功能

转载请注明出处:http://blog.csdn.net/yegongheng/article/details/40624267 好久没更新博客了,着实有点惭愧,以后不管工作是忙是闲都得坚持更新博客,持之以恒地做下去! 正式进入主题,今天我分享一个在工作中过程中遇到的一个技术难点以及我解决该难点的方案,该问题困扰了我许久,通过不断地研究和翻阅资料,终于在满足工作需求的情况下将该问题解决,希望我的经验能够对读者有所帮助.我们知道Android ApI提供了MediaRecorder和AudioRe