欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2017年07月19日 17:07:37   作者:Reoger  
這篇文章主要為大家詳細(xì)介紹了android語(yǔ)音即時(shí)通訊之錄音、播放功能的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

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

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

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

文件流模式

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

在正式編碼之前,還是需要對(duì)其進(jìn)行一個(gè)簡(jiǎn)要的說明。一般來說,錄音功能的實(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);

這里的邏輯比較簡(jiǎn)單,但是值得注意的是,因?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)停止錄音的邏輯也很簡(jiǎ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);
    }

  }

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

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論