温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android中怎么利用流播放声音

发布时间:2021-06-26 15:42:00 来源:亿速云 阅读:145 作者:Leah 栏目:移动开发

这期内容当中小编将会给大家带来有关Android中怎么利用流播放声音,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

(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(这个也是经过试验验证话音质量***时的数据)时,将先录好的数组中数据传送出去。经过上述反复试验和修改,最终使双方通话质量较好,且延时较短。

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

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

(5)代码架构

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

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

主程序

package eoe.demo;  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 eoe.demo;  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&Prime;,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&hellip;\n”,”") ; } } }

放音程序Saudioserver:

package eoe.demo;  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&Prime;,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&hellip;\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(); } } }

上述就是小编为大家分享的Android中怎么利用流播放声音了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI