溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android中怎么利用流播放聲音

發(fā)布時(shí)間:2021-06-26 15:42:00 來源:億速云 閱讀:166 作者:Leah 欄目:移動(dòng)開發(fā)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)Android中怎么利用流播放聲音,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

(1)創(chuàng)建AudioRecord和AudioTrack類對(duì)象:創(chuàng)建這兩個(gè)類的對(duì)象比較復(fù)雜,通過對(duì)文檔的反復(fù)和仔細(xì)理解,并通過多次失敗的嘗試,并在 北理工的某個(gè)Android大牛的網(wǎng)上的文章啟發(fā)下,我們也最終成功地創(chuàng)建了這兩個(gè)類的對(duì)象。創(chuàng)建AudioRecord和AudioTrack類對(duì)象的 代碼如下:

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)關(guān)于AudioRecord和AudioTrack類的監(jiān)聽函數(shù),不用也行。

(3)調(diào)試方面,包括初始化后看logcat信息,以確定類的工作狀態(tài),初始化是否成功等。

編寫好代碼,沒有語法錯(cuò)誤,調(diào)用模擬器運(yùn)行、調(diào)試代碼時(shí),logcat發(fā)揮了很好的功用。剛調(diào)試時(shí),經(jīng)常會(huì)出現(xiàn)模擬器顯示出現(xiàn)異常,這時(shí)我們可以在代碼 的一些關(guān)鍵語句后添加如Log.d(“test1″,”O(jiān)K”);這樣的語句進(jìn)行標(biāo)識(shí),出現(xiàn)異常時(shí)我們就可以在logcat窗口觀察代碼執(zhí)行到哪里出現(xiàn)異 常,然后進(jìn)行相應(yīng)的修改、調(diào)試。模擬器不會(huì)出現(xiàn)異常時(shí),又遇到了錄放音的問題。錄音方面,剛開始選擇將語音編碼數(shù)據(jù)存放在多個(gè)固定大小的文件中進(jìn)行傳送, 但是這種情況下會(huì)出現(xiàn)聲音斷續(xù)的現(xiàn)象,而且要反復(fù)的建立文件,比較麻煩,后來想到要進(jìn)行網(wǎng)上傳輸,直接將語音編碼數(shù)據(jù)以數(shù)據(jù)流的形式傳送,經(jīng)過驗(yàn)證,這種 方法可行并且使代碼更加簡(jiǎn)潔。放音方面,將接收到的數(shù)據(jù)流存放在一個(gè)數(shù)組中,然后將數(shù)組中數(shù)據(jù)寫到AudioTrack中。剛開始只是“嘟”幾聲,經(jīng)過檢 查發(fā)現(xiàn)只是把數(shù)據(jù)寫一次,加入循環(huán),讓數(shù)據(jù)反復(fù)寫到AudioTrack中,就可以聽到正常的語音了。接下來的工作主要是改善話音質(zhì)量與話音延遲,在進(jìn)行 通話的過程中,觀察logcat窗口,發(fā)現(xiàn)向數(shù)組中寫數(shù)據(jù)時(shí)會(huì)出現(xiàn)Bufferflow的情況,于是把重心轉(zhuǎn)移到數(shù)組大小的影響上,經(jīng)過試驗(yàn),發(fā)現(xiàn)  AudioRecord一次會(huì)讀640個(gè)數(shù)據(jù),然后就對(duì)錄音和放音中有數(shù)組的地方進(jìn)行實(shí)驗(yàn)修改。AudioRecord和AudioTrack進(jìn)行實(shí)例化 時(shí),參數(shù)中各有一個(gè)數(shù)組大小,經(jīng)過試驗(yàn)這個(gè)數(shù)組大小和AudioRecord和AudioTrack能正常實(shí)例化所需的最小Buffer大小(即上面實(shí)例 化時(shí)的m_in_buf_size和m_out_buf_size參數(shù))相等且服務(wù)器方進(jìn)行緩存數(shù)據(jù)的數(shù)組尺寸是上述數(shù)值的2倍時(shí),語音質(zhì)量***。由于錄 音和放音的速度不一致,受到北理工大牛的啟發(fā),在錄音方面,將存放錄音數(shù)據(jù)的數(shù)組放到LinkedList中,當(dāng)LinkedList中數(shù)組個(gè)數(shù)達(dá)到 2(這個(gè)也是經(jīng)過試驗(yàn)驗(yàn)證話音質(zhì)量***時(shí)的數(shù)據(jù))時(shí),將先錄好的數(shù)組中數(shù)據(jù)傳送出去。經(jīng)過上述反復(fù)試驗(yàn)和修改,最終使雙方通話質(zhì)量較好,且延時(shí)較短。

(4)通過套接字傳輸和接收數(shù)據(jù)

數(shù)據(jù)傳送部分,使用的是套接字。通信雙方,通過不同的端口向服務(wù)器發(fā)送請(qǐng)求,與服務(wù)器連接上后,開始通話向服務(wù)器發(fā)送數(shù)據(jù),服務(wù)器通過一個(gè)套接字接收到一 方的數(shù)據(jù)后,先存在一個(gè)數(shù)組中,然后將該數(shù)組中數(shù)據(jù)以數(shù)據(jù)流的形式再通過另一個(gè)套接字傳送到另一方。這樣就實(shí)現(xiàn)了雙方數(shù)據(jù)的傳送。

(5)代碼架構(gòu)

為避免反復(fù)錄入和讀取數(shù)據(jù)占用較多資源,使程序在進(jìn)行錄放音時(shí)不能執(zhí)行其他命令,故將錄音和放音各寫成一個(gè)線程類,然后在主程序中,通過MENU控制通話的開始、停止、結(jié)束。

***說明,AudioRecord和AudioTrack類可以用,只是稍微復(fù)雜些。以下貼出雙方通信的源碼,希望對(duì)大家有所幫助:

主程序

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中怎么利用流播放聲音了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI