1. 程式人生 > >Android 音訊錄製 的三種方式

Android 音訊錄製 的三種方式

對於錄製音訊,Android系統就都自帶了一個小小的應用,可是使用起來可能不是特別的靈活。所以有提供了另外的倆種。

下邊來介紹下這三種錄製的方式;

1、通過Intent呼叫系統的錄音器功能,然後在錄製完畢儲存以後在onActivityResult中返回錄製的音訊的uri,然後通過Mediaplayer進行播放

呼叫系統的錄音器

 private final static int REQUEST_RECORDER = 100;
    private Uri uri;
    public void recorder_Intent(){
        Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
        startActivityForResult(intent,REQUEST_RECORDER);
    }
獲取返回的資訊
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK && REQUEST_RECORDER == requestCode){
        uri = data.getData();
    }
}
<span style="background-color: rgb(255, 255, 255);"><span style="font-size:24px;">通過Mediaplayer進行播放</span></span>
  if (uri != null){
                            if (mediaPlayer != null) {
                                try {
                                    mediaPlayer.reset();
                                    mediaPlayer.setDataSource(RecorderActivity.this, uri);
                                    mediaPlayer.prepare();

                                } catch (IOException e) {
                                    e.printStackTrace();
                                }

                            }else
                                Toast.makeText(RecorderActivity.this,"沒有成功建立Mediaplayer",Toast.LENGTH_SHORT).show();
                        }

2、通過MediaRecorder來進行音訊的錄製:

MediaRecorder 類可用於音訊和視訊的捕獲。再構造了一個MediaRecorder物件之後,為了捕獲音訊,必須呼叫setAudioEncoder和setAudioSource這倆個方法。

假設不呼叫這些方法,那麼將不會錄製音訊(視訊也相同不會),另外,MediaRecorder在準備錄製之前通常還會呼叫setOutputFormat 和setOutputFile,

在例項化MediaRecorder之後。應該呼叫的第一個方法是setAudioSource。它採用一個AudioSource內部類中定義的常量作為引數,我們通常使用的常量是MediaRecorder。

AudioSource.MIC.

依據順序,下一個呼叫的就是setOutputFormat ,這種方法採用在MediaRecorder.OutputFormat內部類中指定的常量作為引數:

(1)MediaRecorder.OutputFormat.MPEG_4:這個常量指定輸出的檔案將是一個MPEG_4檔案,包括音訊跟視訊軌

(2)MediaRecorder.OutputFormat.RAW_AMR;這個常量表示輸出一個沒有不論什麼容器型別的原始檔案,僅僅有在捕獲沒有視訊的音訊且音訊編碼器是AMR_NB時才會使用這個常量。

(3)MediaRecorder.OutputFormat.THREE_GPP:這個常量指定輸出的檔案將是一個3gpp檔案(.3gp)。它可能同一時候包括音訊跟視訊軌

MediaRecorder音訊編碼。在設定輸出格式之後,能夠呼叫setAudioEncoder方法來設定應該使用編碼器,可能的值指定為MediaRecorder.AudioEncoder類中的常量。出來使用DEFAULT之外,僅僅存在一個其它的值:MediaRecorder.AudioEncoder.AMR_NB,這是自適應多速率窄帶編解碼器。

這樣的編解碼器針對語音進行了優化,因此不適應於語音之外的其它內容。預設情況下他的取樣率是8kHz,位元速率在 4.75~12.2kbps之間。這個倆個數據對於錄製語音之外的其它內容而言很低。可是,這是當前可用於MediaRecorder的唯一選擇。

下來就看程式碼的實現:

這是錄製的程式碼:

  private MediaRecorder mediaRecorder = new MediaRecorder();
    private File audioFile;
    public void recorder_Media(){
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Android/data/com.example.chengpengfei_d.recorderdemo/files");
        path.mkdirs();
        amplitude = new RecordAmplitude();
        try {
            audioFile = File.createTempFile("recording",".3gp",path);
            mediaRecorder.setOutputFile(audioFile.getAbsolutePath());
            mediaRecorder.prepare();
            mediaRecorder.start();
            isRecording = true;
            amplitude.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

這是播放的程式碼

   case FLAG_MEDIA:
                        isRecording = false;
                        amplitude.cancel(true);
                        mediaRecorder.stop();
                        mediaRecorder.release();
                        mediaPlayer.reset();
                        try {
                            mediaPlayer.setDataSource(audioFile.getAbsolutePath());
                            mediaPlayer.prepare();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
3 使用AudioRecord錄製原始音訊:
這就是第三種捕獲音訊的方法。使用AudioRecord的類。AudioRecord是三個方法中最靈活的(由於他同意訪問原始音訊流),可是他是擁有最少的內建功能。如不會自己主動壓縮音訊等等。
使用AudioRecord的基礎知識很easy。我們僅僅須要構造一個AudioRecord型別的物件,並傳入各種不同配置引數。
須要制定的第一個值就是音訊源。以下使用值與之前用於MediaRecorder的值同樣,其在MediaRecorder.AudioSource 中定義。實際上。這意味著能夠使用MediaRecorder.AudioSource.MIC;
int audiosource = MediaRecorder.AudioSource.MIC;
須要指定的下一個值是錄製的取樣率,應以赫茲為單位,我們知道。MediaRecorder取樣的音訊是8000赫茲。

而CD質量的音訊一般是44100赫茲(44100Hz)。Hz或赫茲是每秒的樣本數量。

不同的Android手機硬體將可以以不同的取樣率進行取樣。對於我的這個樣例將以11025Hz的取樣率來進行取樣,這是一個經常使用的取樣率。

int sampleRateInHz = 11025;
   接下來,須要指定捕獲的音訊通道的數量,在AudioFormat類中指定了用於此引數的常量。並且可依據名稱理解他們。
如今將使用單聲道配置。
  int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
隨後。須要指定音訊格式。

在AudioFormat類中也指定了一下各種可能的常量。

在這四個選擇中,我們選擇PCM_16位和PCM 8位。

PCM代表脈衝編碼調製(Pulse Code Modulation) 他實際上是原始的音訊樣本。

因此能夠設定每一個樣本的解析度為16位或8位。16位將佔用很多其它的控制元件和處理能力,但表示的音訊將更接近真實。

int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
   最後將須要指定緩衝區大小。

時間上能夠查詢AudioRecord類以獲得最小緩衝區大小。查詢方式是呼叫getMinBufferSize的靜態方法,同一時候傳入取樣率,通道配置以及音訊格式。

int bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,channelConfig
,audioFormat);
下邊就看實際的程式碼吧:
主要分倆個方法,還有些初始化的程式碼:
  private boolean isPlaying = false;
    private int frequency = 11025;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    int audiosource = MediaRecorder.AudioSource.MIC;
    int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    File recordingFile = null;
    RecordAudio recordAudio = null;
    PlayAudio playAudio = null;
    public void recorder_Audio() throws IOException {
        //AudioRecord不會直接儲存音訊。須要自己儲存
        File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
        path.mkdirs();

        try {
            recordingFile = File.createTempFile("recording", ".pcm", path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        recordAudio = new RecordAudio();
        recordAudio.execute();
    }

    public void playRecorder() {
        isRecording = false;
        playAudio = new PlayAudio();
        playAudio.execute();
    }
//播放錄製音訊的非同步任務
    private class PlayAudio extends AsyncTask<Void,Integer,Void>{
        @Override
        protected Void doInBackground(Void... params) {
            isPlaying = true;
            int bufferSize = AudioTrack.getMinBufferSize(frequency,channelConfig,audioFormat);
            short[] buffer = new short[bufferSize / 4];
            DataInputStream dis= null;
            try {
                dis  = new DataInputStream(new BufferedInputStream(new FileInputStream(recordingFile)));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,frequency,channelConfig,audioFormat,bufferSize,AudioTrack.MODE_STREAM);
            audioTrack.play();
            try {
                while(isPlaying && dis.available() > 0 ){
                    int i = 0;
                    while(dis.available() > 0 && i < buffer.length){
                        buffer[i] = dis.readShort();
                        i++;
                    }
                    audioTrack.write(buffer,0,buffer.length);
                }
                dis.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
<span style="white-space:pre">	</span>
//錄製音訊的一個非同步任務;
    private class RecordAudio extends AsyncTask<Void,Integer,Void>{
        @Override
        protected Void doInBackground(Void... params) {
            isRecording = true;
            DataOutputStream dos = null;
            try {
                dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(recordingFile)));
                int bufferSize = AudioRecord.getMinBufferSize(frequency,channelConfig,audioFormat);
                AudioRecord audioRecord = new AudioRecord(audiosource,frequency,channelConfig,audioFormat,bufferSize);
                short [] buffer = new short[bufferSize];
                audioRecord.startRecording();
                int r = 0;
                while(isRecording){
                    int bufferReadResult = audioRecord.read(buffer,0,bufferSize);
                    for (int i = 0; i<bufferReadResult; i++){
                        try {
                            dos.writeShort(buffer[i]);

                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    publishProgress(new Integer(r));
                    r++;
                }
                audioRecord.stop();
                dos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            mTv_progress.setText(values[0] + "");
        }
    }

至此三種方式也就完了,這是一個關於音訊錄製的例項。通過三種不同方式的錄製來達到同樣的目的。可是要了解。三種的優缺點。假設想看實際效果,下載以後執行下看看吧。本例項是在Android studio中開發,假設Ecplise的話那麼直接移植程式碼,以及清單檔案的內容,應該就能夠,假設不行能夠聯絡我。