Android通过流播放声音

摘要:
与软件包中的MediaRecorder和MediaPlayer类不同,AudioRecord和AudioTrack类在获取和播放音频数据流时不需要保存和读取文件。它们可以直接动态获取和播放音频流,这在实时处理音频数据流时非常有用。然而,AudioRecord和AudioTrack课程的使用相对复杂。我们发现,许多人无法成功使用这两个类,甚至认为Android的两个类无法工作。

Android通过流播放声音

【转载】http://mobile.51cto.com/amedia-375030.htm

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类:

  1. m_in_buf_size =AudioRecord.getMinBufferSize(8000
  2. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
  3. AudioFormat.ENCODING_PCM_16BIT); 
  4.  
  5.  
  6. m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC,8000
  7. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
  8. AudioFormat.ENCODING_PCM_16BIT, 
  9. m_in_buf_size) ;  

AudioTrack类:

  1. m_out_buf_size = android.media.AudioTrack.getMinBufferSize(8000
  2. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
  3. AudioFormat.ENCODING_PCM_16BIT); 
  4.  
  5.  
  6. m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000
  7. AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT, 
  8. m_out_buf_size, 
  9. 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(这个也是经过试验验证话音质量最好时的数据)时,将先录好的数组中数据传送出去。经过上述反复试验和修改,最终使双方通话质量较好,且延时较短。

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

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

(5)代码架构

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

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

主程序

  1. package eoe.demo; 
  2.  
  3. import android.app.Activity; 
  4. import android.os.Bundle; 
  5. import android.view.Menu; 
  6. import android.view.MenuItem; 
  7.  
  8. public class Daudioclient extends Activity { 
  9.  
  10. public static final int MENU_START_ID = Menu.FIRST ; 
  11. public static final int MENU_STOP_ID = Menu.FIRST + 1 ; 
  12. public static final int MENU_EXIT_ID = Menu.FIRST + 2 ; 
  13.  
  14. protected Saudioserver m_player ; 
  15. protected Saudioclient m_recorder ; 
  16.  
  17. @Override 
  18. public void onCreate(Bundle savedInstanceState) { 
  19. super.onCreate(savedInstanceState); 
  20. setContentView(R.layout.main); 
  21.  
  22. public boolean onCreateOptionsMenu(Menu aMenu) 
  23. boolean res = super.onCreateOptionsMenu(aMenu) ; 
  24.  
  25. aMenu.add(0, MENU_START_ID, 0, “START”) ; 
  26. aMenu.add(0, MENU_STOP_ID, 0, “STOP”) ; 
  27. aMenu.add(0, MENU_EXIT_ID, 0, “EXIT”) ; 
  28.  
  29. return res ; 
  30. public boolean onOptionsItemSelected(MenuItem aMenuItem) 
  31. switch (aMenuItem.getItemId()) { 
  32. case MENU_START_ID: 
  33. m_player = new Saudioserver() ; 
  34. m_recorder = new Saudioclient() ; 
  35.  
  36. m_player.init() ; 
  37. m_recorder.init() ; 
  38.  
  39. m_recorder.start() ; 
  40. m_player.start() ; 
  41.  
  42. break ; 
  43. case MENU_STOP_ID: 
  44. m_recorder.free() ; 
  45. m_player.free() ; 
  46.  
  47. m_player = null ; 
  48. m_recorder = null ; 
  49. break ; 
  50. case MENU_EXIT_ID: 
  51. int pid = android.os.Process.myPid() ; 
  52. android.os.Process.killProcess(pid) ; 
  53. break ; 
  54. default
  55. break ; 
  56.  
  57. return super.onOptionsItemSelected(aMenuItem); 

录音程序Saudioclient:

  1. package eoe.demo; 
  2.  
  3. import java.io.DataOutputStream; 
  4. import java.io.IOException; 
  5. import java.net.Socket; 
  6. import java.net.UnknownHostException; 
  7. import java.util.LinkedList; 
  8.  
  9. import android.media.AudioFormat; 
  10. import android.media.AudioRecord; 
  11. import android.media.MediaRecorder; 
  12. import android.util.Log; 
  13.  
  14. public class Saudioclient extends Thread 
  15.  
  16. protected AudioRecord m_in_rec ; 
  17. protected int m_in_buf_size ; 
  18. protected byte [] m_in_bytes ; 
  19. protected boolean m_keep_running ; 
  20. protected Socket s; 
  21. protected DataOutputStream dout; 
  22. protected LinkedList<byte[]> m_in_q ; 
  23.  
  24. public void run() 
  25. try 
  26. byte [] bytes_pkg ; 
  27. m_in_rec.startRecording() ; 
  28. while(m_keep_running) 
  29. m_in_rec.read(m_in_bytes, 0, m_in_buf_size) ; 
  30. bytes_pkg = m_in_bytes.clone() ; 
  31. if(m_in_q.size() >= 2
  32. dout.write(m_in_q.removeFirst() , 0, m_in_q.removeFirst() .length); 
  33. m_in_q.add(bytes_pkg) ; 
  34.  
  35. m_in_rec.stop() ; 
  36. m_in_rec = null ; 
  37. m_in_bytes = null ; 
  38. dout.close(); 
  39.  
  40. catch(Exception e) 
  41. e.printStackTrace(); 
  42.  
  43. public void init() 
  44. m_in_buf_size = AudioRecord.getMinBufferSize(8000
  45. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
  46. AudioFormat.ENCODING_PCM_16BIT); 
  47.  
  48. m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC, 
  49. 8000
  50. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
  51. AudioFormat.ENCODING_PCM_16BIT, 
  52. m_in_buf_size) ; 
  53.  
  54. m_in_bytes = new byte [m_in_buf_size] ; 
  55.  
  56. m_keep_running = true ; 
  57. m_in_q=new LinkedList<byte[]>(); 
  58.  
  59. try 
  60. s=new Socket(“192.168.1.100″,4332); 
  61. dout=new DataOutputStream(s.getOutputStream()); 
  62. //new Thread(R1).start(); 
  63. catch (UnknownHostException e) 
  64. // TODO Auto-generated catch block 
  65. e.printStackTrace(); 
  66. catch (IOException e) 
  67. // TODO Auto-generated catch block 
  68. e.printStackTrace(); 
  69.  
  70.  
  71. public void free() 
  72. m_keep_running = false ; 
  73. try { 
  74. Thread.sleep(1000) ; 
  75. catch(Exception e) { 
  76. Log.d(“sleep exceptions… ”,”") ; 

放音程序Saudioserver:

    1. package eoe.demo; 
    2.  
    3. import java.io.DataInputStream; 
    4. import java.io.IOException; 
    5. import java.net.Socket; 
    6.  
    7. import android.media.AudioFormat; 
    8. import android.media.AudioManager; 
    9. import android.media.AudioTrack; 
    10. import android.util.Log; 
    11.  
    12. public class Saudioserver extends Thread 
    13. protected AudioTrack m_out_trk ; 
    14. protected int m_out_buf_size ; 
    15. protected byte [] m_out_bytes ; 
    16. protected boolean m_keep_running ; 
    17. private Socket s; 
    18. private DataInputStream din; 
    19. public void init() 
    20. try 
    21. s=new Socket(“192.168.1.100″,4331); 
    22. din=new DataInputStream(s.getInputStream()); 
    23.  
    24. m_keep_running = true ; 
    25.  
    26. m_out_buf_size = AudioTrack.getMinBufferSize(8000
    27. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
    28. AudioFormat.ENCODING_PCM_16BIT); 
    29.  
    30. m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000
    31. AudioFormat.CHANNEL_CONFIGURATION_MONO, 
    32. AudioFormat.ENCODING_PCM_16BIT, 
    33. m_out_buf_size, 
    34. AudioTrack.MODE_STREAM); 
    35.  
    36. m_out_bytes=new byte[m_out_buf_size]; 
    37.  
    38. // new Thread(R1).start(); 
    39.  
    40. catch(Exception e) 
    41. e.printStackTrace(); 
    42.  
    43. public void free() 
    44. m_keep_running = false ; 
    45. try { 
    46. Thread.sleep(1000) ; 
    47. catch(Exception e) { 
    48. Log.d(“sleep exceptions… ”,”") ; 
    49.  
    50. public void run() 
    51. byte [] bytes_pkg = null ; 
    52. m_out_trk.play() ; 
    53. while(m_keep_running) { 
    54. try 
    55. din.read(m_out_bytes); 
    56. bytes_pkg = m_out_bytes.clone() ; 
    57. m_out_trk.write(bytes_pkg, 0, bytes_pkg.length) ; 
    58. catch(Exception e) 
    59. e.printStackTrace(); 
    60.  
    61.  
    62. m_out_trk.stop() ; 
    63. m_out_trk = null ; 
    64. try { 
    65. din.close(); 
    66. catch (IOException e) { 
    67. // TODO Auto-generated catch block 
    68. e.printStackTrace(); 
    69. }

免责声明:文章转载自《Android通过流播放声音》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇用MATLAB做聚类分析网络安全相关证书有哪些?——就实战型看,OSCP、CISP-PTE (国家注册渗透测试工程师)最好下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

【Python之路】特别篇--ECMA对象、DOM对象、BOM对象

ECMA对象从传统意义上来说,ECMAScript 并不真正具有类。事实上,除了说明不存在类,在 ECMA-262 中根本没有出现“类”这个词。 ECMAScript 定义了“对象定义”,逻辑上等价于其他程序设计语言中的类。 var o = new Object(); 对象的概念与分类: 由ECMAScript定义的本地对象.独立于宿主环境的 ECMAS...

ASP数组全集,多维数组和一维数组

ASP数组是比较好用的装载大量数据的容器。1 定义数组 有两种方式:DIM和REDIM。 DIM定义的是固定个数、数据类型的数组;而REDIM则不同,它可以定义不同类型的数据,也可以定义个数并非固定的数据。比较下面几个例子。 都合法的例子: Dim myarray(5,2) Redim myarray(5,2) 前者错误而后者合法的例子: n=10 n=1...

vb编程代码大全

数值型函数:abs(num): 返回绝对值sgn(num): num>0 1; num=0 0; num<0 -1;判断数值正负hex(num): 返回十六进制值 直接表示:&Hxx 最大8位oct(num): 返回八进制值 直接表示:&Oxx 最大8位sqr(num): 返回平方根 num>0int(num): 取整...

02_编程规约——集合处理

1.【强制】关于hashCode和equals的处理,必须遵循如下规则 1.1 只要重写equals,就必须重写hashCode。 1.2 因为Set存储的是不重复对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。 1.3 如果自定义对象为Map的键,那么必须重写hashCode和equals。 说明:String重...

[C++ STL] vector使用详解

一、概述 vector(向量): 是一种序列式容器,事实上和数组差不多,但它比数组更优越。一般来说数组不能动态拓展,因此在程序运行的时候不是浪费内存,就是造成越界。而vector正好弥补了这个缺陷,它的特征是相当于可分配拓展的数组(动态数组),它的随机访问快,在中间插入和删除慢,但在末端插入和删除快。 二、定义及初始化 使用之前必须加相应容器的头文件: #...

interp1一维数据插值在matlab中的用法

转载:https://ww2.mathworks.cn/help/matlab/ref/interp1.html?s_tid=srchtitle#btwp6lt-2_1 interp1 一维数据插值(表查找) 全页折叠 语法 vq = interp1(x,v,xq) vq = interp1(x,v,xq,method) vq = interp1...