Android 音频
文章目錄
- AudioRecorder與MediaRecorder的比較
- AudioRecorder
- 構造參數
- AudioTrack
- 代碼解析
- 1. 創建一個AudioRecorder對象及初始化
- 2. 啟動錄音
- 3. 錄音線程
- 4. 獲取錄音裸數據
- 5. 給數據加header,轉換成wav格式
- 6. 停止錄音
- MediaRecorder
- workflow
- Error/Info Listener
- usually method
- ATTENTION
AudioRecorder與MediaRecorder的比較
以下內容來自博客:https://www.cnblogs.com/Amandaliu/archive/2013/02/04/2891604.html 若有侵權,立即刪除
1. AudioRecord
-
主要是實現邊錄邊播(AudioRecord+AudioTrack)以及對音頻的實時處理(如會說話的湯姆貓、語音)
-
優點:語音的實時處理,可以用代碼實現各種音頻的封裝
-
缺點:輸出是PCM語音數據,如果保存成音頻文件,是不能夠被播放器播放的,所以必須先寫代碼實現數據編碼以及壓縮
示例:
- 使用AudioRecord類錄音,并實現WAV格式封裝。錄音20s,輸出的音頻文件大概為3.5M左右(已寫測試代碼)
2、MediaRecorder
-
已經集成了錄音、編碼、壓縮等,支持少量的錄音音頻格式,大概有.aac(API = 16) .amr .3gp
-
優點:大部分以及集成,直接調用相關接口即可,代碼量小
-
缺點:無法實時處理音頻;輸出的音頻格式不是很多,例如沒有輸出mp3格式文件
示例:
- 使用MediaRecorder類錄音,輸出amr格式文件。錄音20s,輸出的音頻文件大概為33K(已寫測試代碼)
3、音頻格式比較
-
WAV格式:錄音質量高,但是壓縮率小,文件大
-
AAC格式:相對于mp3,AAC格式的音質更佳,文件更小;有損壓縮;一般蘋果或者Android SDK4.1.2(API 16)及以上版本支持播放
-
AMR格式:壓縮比比較大,但相對其他的壓縮格式質量比較差,多用于人聲,通話錄音
-
至于常用的mp3格式,使用MediaRecorder沒有該視頻格式輸出。一些人的做法是使用AudioRecord錄音,然后編碼成wav格式,再轉換成mp3格式
AudioRecorder
以下內容來自博客:https://blog.csdn.net/hellofeiya/article/details/8968534 若有侵權,立即刪除
構造參數
public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
| audioSource | 音頻源:指的是從哪里采集音頻。這里我們當然是從麥克風采集音頻,所以此參數的值為MIC |
| sampleRateInHz | the sample rate expressed in Hertz. Examples of rates are (but not limited to) 44100, 22050 and 11025. 采樣率越高,音質越高。 |
| channelConfig | describes the configuration of the audio channels. See CHANNEL_IN_MONO and CHANNEL_IN_STEREO , MONO單聲道,STEREO立體聲 |
| audioFormat | the format in which the audio data is represented. See ENCODING_PCM_16BIT and ENCODING_PCM_8BIT. 編碼制式和采樣大小:采集來的數據當然使用PCM編碼(脈沖代碼調制編碼,即PCM編碼。PCM通過抽樣、量化、編碼三個步驟將連續變化的模擬信號轉換為數字編碼。) android支持的采樣大小16bit 或者8bit。當然采樣大小越大,那么信息量越多,音質也越高,現在主流的采樣大小都是16bit,在低質量的語音傳輸的時候8bit足夠了。 |
| bufferSizeInBytes | the total size (in bytes) of the buffer where audio data is written to during the recording. New audio data can be read from this buffer in smaller chunks than this size. SeegetMinBufferSize(int, int, int) to determine the minimum required buffer size for the successful creation of an AudioRecord instance. Using values smaller than getMinBufferSize() will result in an initialization failure. 采集數據需要的緩沖區的大小,如果不知道最小需要的大小可以在getMinBufferSize()查看。 |
AudioTrack
public AudioTrack (int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)
代碼解析
1. 創建一個AudioRecorder對象及初始化
/*** 創建一個AudioRecorder對象及初始化* @param rawAudioName,原始音頻路徑* @param wavAudioName,wav音頻路徑* @return*/private void creatAudioRecord(String rawAudioName, String wavAudioName) {// 獲取音頻文件路徑this.rawAudioName = rawAudioName;this.wavAudioName = wavAudioName;// 獲得緩沖區字節大小bufferSizeInBytes = AudioRecord.getMinBufferSize(AudioFileFunc.AUDIO_SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);// 創建AudioRecord對象audioRecord = new AudioRecord(AudioFileFunc.AUDIO_INPUT, AudioFileFunc.AUDIO_SAMPLE_RATE,AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);}2. 啟動錄音
creatAudioRecord(); audioRecord.startRecording(); // 讓錄制狀態為true isRecord = true; // 開啟音頻文件寫入線程 new Thread(new AudioRecordThread()).start();3. 錄音線程
class AudioRecordThread implements Runnable {@Overridepublic void run() {writeDateTOFile();//往文件中寫入裸數據copyWaveFile(rawAudioName, wavAudioName, AUDIO_SAMPLE_RATE, CHANNELS);//給裸數據加上頭文件}}4. 獲取錄音裸數據
audioRecord.read(audiodata, 0, bufferSizeInBytes);
private void writeDateTOFile() {// new一個byte數組用來存一些字節數據,大小為緩沖區大小byte[] audiodata = new byte[bufferSizeInBytes];FileOutputStream fos = null;int readsize = 0;try {File file = new File(rawAudioName);if (file.exists()) {file.delete();}fos = new FileOutputStream(file);// 建立一個可存取字節的文件} catch (Exception e) {e.printStackTrace();}while (isRecord == true) {readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos!=null) {try {fos.write(audiodata);} catch (IOException e) {e.printStackTrace();}}}try {if(fos != null)fos.close();// 關閉寫入流} catch (IOException e) {e.printStackTrace();}}5. 給數據加header,轉換成wav格式
// 這里得到可播放的音頻文件private void copyWaveFile(String inFilename, String outFilename, long longSampleRate, int channels) {FileInputStream in = null;FileOutputStream out = null;long totalAudioLen = 0;long totalDataLen = totalAudioLen + 36;long byteRate = 16 * AudioFileFunc.AUDIO_SAMPLE_RATE * channels / 8;byte[] data = new byte[bufferSizeInBytes];try {in = new FileInputStream(inFilename);out = new FileOutputStream(outFilename);totalAudioLen = in.getChannel().size();totalDataLen = totalAudioLen + 36;WriteWaveFileHeader(out, totalAudioLen, totalDataLen,longSampleRate, channels, byteRate);while (in.read(data) != -1) {out.write(data);}in.close();out.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 這里提供一個頭信息。插入這些信息就可以得到可以播放的文件。* 為我為啥插入這44個字節,這個還真沒深入研究,不過你隨便打開一個wav* 音頻的文件,可以發現前面的頭文件可以說基本一樣哦。每種格式的文件都有* 自己特有的頭文件。*/private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,long totalDataLen, long longSampleRate, int channels, long byteRate)throws IOException {byte[] header = new byte[44];header[0] = 'R'; // RIFF/WAVE headerheader[1] = 'I';header[2] = 'F';header[3] = 'F';header[4] = (byte) (totalDataLen & 0xff);header[5] = (byte) ((totalDataLen >> 8) & 0xff);header[6] = (byte) ((totalDataLen >> 16) & 0xff);header[7] = (byte) ((totalDataLen >> 24) & 0xff);header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';header[12] = 'f'; // 'fmt ' chunkheader[13] = 'm';header[14] = 't';header[15] = ' ';header[16] = 16; // 4 bytes: size of 'fmt ' chunkheader[17] = 0;header[18] = 0;header[19] = 0;header[20] = 1; // format = 1header[21] = 0;header[22] = (byte) channels;header[23] = 0;header[24] = (byte) (longSampleRate & 0xff);header[25] = (byte) ((longSampleRate >> 8) & 0xff);header[26] = (byte) ((longSampleRate >> 16) & 0xff);header[27] = (byte) ((longSampleRate >> 24) & 0xff);header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);header[32] = (byte) (2 * 16 / 8); // block alignheader[33] = 0;header[34] = 16; // bits per sampleheader[35] = 0;header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);header[43] = (byte) ((totalAudioLen >> 24) & 0xff);out.write(header, 0, 44);}6. 停止錄音
private void stop() {if (audioRecord != null) {System.out.println("stopRecord");isRecord = false;//停止文件寫入audioRecord.stop();audioRecord.release();//釋放資源audioRecord = null;}}MediaRecorder
workflow
A common case of using MediaRecorder to record audio works as follows:
Error/Info Listener
Applications may want to register for informational and error events in order to be informed of some internal update and possible runtime errors during recording. Registration for such events is done by setting the appropriate listeners (via calls (to setOnInfoListener(OnInfoListener) and/or setOnErrorListener(OnErrorListener)). In order to receive the respective callback associated with these listeners, applications are required to create MediaRecorder objects on threads with a Looper running (the main UI thread by default already has a Looper running).
usually method
| setAudioSamplingRate(int) | set sample rate |
| setAudioSource(int audio_source) | 設置聲音來源,一般傳入 MediaRecorder. AudioSource.MIC參數指定錄制來自麥克風的聲音。 |
ATTENTION
- assign null to mediaRecorder after release it
下一次直接new:mMediaRecorder = new MediaRecorder();
總結
以上是生活随笔為你收集整理的Android 音频的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年信息系统管理工程师考试大纲
- 下一篇: Android音频系统之一音频基础