国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

android語音即時(shí)通訊之錄音、播放功能實(shí)現(xiàn)代碼

2019-12-12 02:26:36
字體:
供稿:網(wǎng)友

在android中,實(shí)現(xiàn)錄音與語音播放的功能算是比較簡單的,但是作為參考,還是很有必要將語音相關(guān)的知識(shí)做一個(gè)簡要的記錄。

首先,在android中,支持錄音支持兩種方式。主要包括:字節(jié)流模式和文件流模式。用文件流模式進(jìn)行錄音操作比較簡單,而且相對(duì)來說,因?yàn)槠浞庋b性比較好,錄制下的文件也會(huì)比較小。但是相對(duì)于文件流模式,就沒有字節(jié)流模式那么靈活,但是想要用好字節(jié)流模式還是需要下一點(diǎn)功夫的。

下面開始介紹文件流模式的語音操作:

文件流模式

我們來看錄音部分的實(shí)現(xiàn),首先我們實(shí)現(xiàn)開始錄音的部分:

在正式編碼之前,還是需要對(duì)其進(jìn)行一個(gè)簡要的說明。一般來說,錄音功能的實(shí)現(xiàn)是在jin層,而在這一層中,是用單線程實(shí)現(xiàn)的。如果我們?cè)诰幋a的對(duì)錄音api進(jìn)行多線程操作,會(huì)導(dǎo)致程序直接閃退,并且我們是無法在java層對(duì)其異常進(jìn)行捕獲的。所以,我們必須使用單線程以保證錄音的正常運(yùn)行。

一般來說,開始錄音的步驟也就三個(gè),代碼如下:

releaseRecord();//釋放可能沒釋放的錄音相關(guān)資源if (!doStartRecord()) {//真正的開始錄音的函數(shù),開始錄音成功返回true,否則返回false  recordFail(); //開始失敗,向用戶提示開始錄音失敗}

接下來我們來看看上述三個(gè)方法的實(shí)現(xiàn):
實(shí)現(xiàn)是釋放相關(guān)資源的方法releaseRecord:

 if (mMediaRecorder != null) {      mMediaRecorder.release();      mMediaRecorder = null;}

其中的mMediaRecorder 的MediaRecorder的全局變量。
接下來是真正實(shí)現(xiàn)開始錄音的實(shí)現(xiàn)邏輯doStartRecord()

 private boolean doStartRecord() {    try {      mMediaRecorder = new MediaRecorder();      mAudioFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/voice/"          + System.currentTimeMillis() + ".m4a");      mAudioFile.getParentFile().mkdirs();      mAudioFile.createNewFile();      //設(shè)置從麥克風(fēng)采集聲音      mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);      //保存文件為mp4的格式      mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);      //設(shè)置所有android系統(tǒng)都支持的采樣頻率      mMediaRecorder.setAudioSamplingRate(44100);      //設(shè)置acc的編碼方式      mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);      //設(shè)置比較好的音質(zhì)      mMediaRecorder.setAudioEncodingBitRate(96000);      mMediaRecorder.setOutputFile(mAudioFile.getAbsolutePath());      mMediaRecorder.prepare();      mMediaRecorder.start();      mStartRecordTime = System.currentTimeMillis();    } catch (IOException | RuntimeException e) {      e.printStackTrace();      return false;    }finally {      if(mAudioRecord != null){        mAudioRecord.release();      }    }    return true;  }

這一部分代碼比較多,但是關(guān)鍵部分我都給出了注釋,相信理解起來也不算難吧。這一部分我們實(shí)現(xiàn)的主要是在sdcrad根目錄下新建一個(gè)voice的目錄,然后在新建一個(gè)以==.m4a==為后綴名的文件。在配置mAudioRecord的相關(guān)參數(shù)后,將收集到的錄音存放到之前的文件中。如果一切都順利的話,就返回true ,表示開始錄音成功。

最后就是提示用戶錄音實(shí)現(xiàn)的邏輯recordFail

mAudioFile = null;    mHandler.postDelayed(new Runnable() {      @Override      public void run() {        Toast.makeText(VioceActivity.this, "錄音失敗", Toast.LENGTH_SHORT).show();      }    }, 100);

這里的邏輯比較簡單,但是值得注意的是,因?yàn)槲覀冮_始錄音方法是在一個(gè)非主線程的線程中執(zhí)行的,所以我們需要借助hander來實(shí)現(xiàn)界面提示的效果。這里的mHander是一個(gè)局部變量,其初始化放在OnCreate()方法中。

mHandler = new Handler(Looper.getMainLooper());

綜上,開始錄音的所有邏輯已經(jīng)全部實(shí)現(xiàn)。接下來實(shí)現(xiàn)的是結(jié)束錄音的實(shí)現(xiàn)邏輯:
主題的邏輯如下:

if (!doStopRecord()) {//實(shí)現(xiàn) 停止錄音的真正邏輯,成功返回true,否則返回false          recordFail();//提示用戶錄音失敗        }

這里的doStopRecord實(shí)現(xiàn)邏輯如下:

 mMediaRecorder.stop();      mEndRecordTime = System.currentTimeMillis();      final int seond = (int) ((mEndRecordTime - mStartRecordTime) / 1000);      if (seond < 3) {        recordFail();        return false;      } else {        mHandler.post(new Runnable() {          @Override          public void run() {            mText.setText("錄音" + seond + "成功!");          }        });      }    } catch (RuntimeException e) {      e.printStackTrace();      return false;    }    return true;

其實(shí)我們實(shí)現(xiàn)停止錄音的邏輯也很簡單,首先調(diào)用mMediaRecorder.stop();停止錄音,然后對(duì)錄音時(shí)間是否大于3s進(jìn)行判斷,若大于3s,則表示錄音有效,提示用戶,錄音成功。

綜上,我們文件流的錄音的所有代碼已經(jīng)實(shí)現(xiàn)完畢。接下來我們實(shí)現(xiàn)對(duì)其進(jìn)行播放。如果需要參考全部的代碼,請(qǐng)戳這里。

private void doPlay(File mAudioFile) {    //配置播放器 MediaPlayer    mMediaPlayer = new MediaPlayer();    try{      //設(shè)置聲音文件      mMediaPlayer.setDataSource(mAudioFile.getAbsolutePath());      //設(shè)置監(jiān)聽回調(diào)      mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {        @Override        public void onCompletion(MediaPlayer mp) {          stopPlay();        }      });      //設(shè)置出錯(cuò)的監(jiān)聽器      mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {        @Override        public boolean onError(MediaPlayer mp, int what, int extra) {          playFail();          //提示用戶          stopPlay();          //釋放播放器          return true;        }      });      //配置音量,是否循環(huán)      mMediaPlayer.setVolume(1,1);      mMediaPlayer.setLooping(false);      mMediaPlayer.prepare();      mMediaPlayer.start();    }catch (Exception e){      e.printStackTrace();      playFail();      stopPlay();    }  }

整體來說,基于文件的錄音是比較容易實(shí)現(xiàn)的。下面介紹如何通過字節(jié)流模式實(shí)現(xiàn)錄音。

字節(jié)流模式錄音

開始錄音:主要邏輯startRecord2()的實(shí)現(xiàn)

private boolean startRecord2() {    try {      mAudioFile2 = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/voice/"          + System.currentTimeMillis() + ".pcm");      mAudioFile2.getParentFile().mkdirs();      mAudioFile2.createNewFile();      mFileOutputStream = new FileOutputStream(mAudioFile2);      //配置AudioRecord      //從麥克風(fēng)采集數(shù)據(jù)      int audioSource = MediaRecorder.AudioSource.MIC;      //采集頻率      int sampleRate = 44100;      //單聲道輸入      int channelConfig = AudioFormat.CHANNEL_IN_MONO;      //設(shè)置pcm(脈沖編碼調(diào)制 Pulse Code Modulation)編碼格式      int audioFormat = AudioFormat.ENCODING_PCM_16BIT;      //計(jì)算AudioRecord 內(nèi)存buffer最小的大小      int minBufferSize = AudioRecord.getMinBufferSize(sampleRate,channelConfig,audioFormat);      //創(chuàng)建AudioRecord對(duì)象      mAudioRecord = new AudioRecord(audioSource,sampleRate,channelConfig,audioFormat,Math.max(BUFFER_SIZE,minBufferSize));      mAudioRecord.startRecording();      mStartRecordTime = System.currentTimeMillis();      //循環(huán)讀取數(shù)據(jù),寫到輸出流中      while(mIsRecord){        int read = mAudioRecord.read(mBuffer,0,BUFFER_SIZE);        if(read >0 ){          //讀取文件寫到文件中          mFileOutputStream.write(mBuffer,0,read);        }else{          return false;        }      }    } catch (IOException | RuntimeException e) {      e.printStackTrace();      return false;    }    //停止錄音    return true;  }

停止錄音的doStopRecord()實(shí)現(xiàn):

private boolean doStopRecord() {    try {      mMediaRecorder.stop();      mEndRecordTime = System.currentTimeMillis();      final int seond = (int) ((mEndRecordTime - mStartRecordTime) / 1000);      if (seond < 3) {        recordFail();        return false;      } else {        mHandler.post(new Runnable() {          @Override          public void run() {            mText.setText("錄音" + seond + "成功!");          }        });      }    } catch (RuntimeException e) {      e.printStackTrace();      return false;    }    return true;  }

對(duì)其中相關(guān)參數(shù)的說名:

 private boolean mIsRecord = false;  private final int BUFFER_SIZE = 2048;//緩存區(qū)的大小  private byte[] mBuffer;  private FileOutputStream mFileOutputStream;  private AudioRecord mAudioRecord;  private File mAudioFile2;

接下來,實(shí)現(xiàn)的是對(duì)其字節(jié)流模式錄制的文件進(jìn)行播放:doPlay2()主題類的實(shí)現(xiàn):

private void doPlay2(File mAudioFile) {    //聲音類型,揚(yáng)聲器播放    int steamType = AudioManager.STREAM_MUSIC;    //采樣頻率    int sampleRate = 44100;    //MONO 表示單聲道 錄音輸入單聲道 播放也使用單聲道    int channelConfig = AudioFormat.CHANNEL_OUT_MONO;    //錄音使用16bit 所以播放也使用同樣的格式    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;    //流模式    int mode = AudioTrack.MODE_STREAM;    //計(jì)算需要最小buffer的大小    int minBufferSize =AudioTrack.getMinBufferSize(sampleRate,channelConfig,audioFormat);    AudioTrack audioTrack = new AudioTrack(steamType,sampleRate,channelConfig,audioFormat,        Math.max(minBufferSize,BUFFER_SIZE),mode);    //從文件流中讀取數(shù)據(jù)    FileInputStream inputStream = null;    try{      inputStream = new FileInputStream(mAudioFile2);      int read;      //循環(huán)讀取數(shù)據(jù),寫到播放器去播放      audioTrack.play();      while((read = inputStream.read(mBuffer)) > 0){        int ret = audioTrack.write(mBuffer,0,read);      switch (ret){        case AudioTrack.ERROR:        case AudioTrack.ERROR_BAD_VALUE:        case AudioTrack.ERROR_INVALID_OPERATION:        case AudioTrack.ERROR_DEAD_OBJECT:            playFail();            return ;        default:          break;      }      }    }catch (Exception e){      e.printStackTrace();    }finally {      mIsPlaying = false;      if(inputStream != null)        closeQuietly(inputStream);      resetQuietly(audioTrack);    }  }

千言萬語肯定不如直接代碼來的直接了當(dāng),所以的代碼實(shí)現(xiàn)點(diǎn)這里

實(shí)現(xiàn)的效果如下:

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 浙江省| 汽车| 平陆县| 澄迈县| 山东| 隆子县| 东台市| 乐昌市| 邢台县| 登封市| 长武县| 霍林郭勒市| 获嘉县| 景泰县| 金山区| 奉贤区| 积石山| 民和| 双柏县| 修文县| 襄樊市| 华安县| 邹城市| 黄浦区| 石渠县| 西平县| 东乡族自治县| 马公市| 凤山市| 巴林左旗| 渑池县| 双流县| 赤壁市| 巢湖市| 衢州市| 宁明县| 梅河口市| 华坪县| 行唐县| 繁峙县| 龙游县|