Android-音频收罗

打印 上一主题 下一主题

主题 2302|帖子 2302|积分 6906

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
前言

音视频这块,起首是要先收罗音频。今天我们就来深入探讨一下 Android 音频收罗的两大类型:Mic 音频收罗和系统音频收罗。

Mic音频收罗

Android提供了两个API用于实现录音功能:android.media.AudioRecord、android.media.MediaRecorder。

AudioRecord和MediaRecorder两种都可以录制音频,MediaRecorder已实现大量的封装,操作起来更加简单,而AudioRecord使用起来更加灵活,能实现更多的功能。

AudioRecord


主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的及时处置处罚(如会说话的汤姆猫、语音)
优点:语音的及时处置处罚,可以用代码实现各种音频的封装
缺点:输出是PCM语音数据,如果生存成音频文件,是不能够被播放器播放的,以是必须先写代码实现数据编码以及压缩
示例:
使用AudioRecord类录音,并实现WAV格式封装。录音20s,输出的音频文件大概为3.5M左(已写测试代码)

特点:


  • 基于字节省录音;
  • 可以实现语音的及时处置处罚,举行边录边播,对音频的及时处置处罚;
  • AudioRecord是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据举行处置处罚;
  • 输出的是PCM的语音数据,如果生存成音频文件是不能被播放器播放的。要用到AudioTrack这个去举行处置处罚;
MediaRecorder


已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp
优点:大部分以及集成,直接调用相干接口即可,代码量小
缺点:无法及时处置处罚音频;输出的音频格式不是很多,例如没有输出mp3格式文件
示例:
使用MediaRecorder类录音,输出amr格式文件。录音20s,输出的音频文件大概为33K(已写测试代码)
特点:


  • 基于文件录音;
  • MediaRecorder 是基于 AudioRecorder 的 API(终极还是会创建AudioRecord用来与AudioFlinger举行交互) ,它可以直接将收罗到的音频数据转化为执行的编码格式,并生存;
  • 已集成了录音,编码,压缩等,支持少量的音频格式文件;
  • 封装度很高,操作简单;
音频格式比较


WAV格式:录音质量高,但是压缩率小,文件大。

AAC格式:相对于mp3,AAC格式的音质更佳,文件更小;有损压缩;一般苹果或者Android SDK4.1.2(API 16)及以上版本支持播放 。

AMR格式:压缩比比较大,但相对其他的压缩格式质量比较差,多用于人声,通话录音
mp3格式:使用MediaRecorder没有该视频格式输出。一些人的做法是使用AudioRecord录音,然后编码成wav格式,再转换成mp3格式。

测试代码

audioRecord 封装wav 格式。

  1. package com.example.audiorecordtest;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import android.media.AudioFormat;
  8. import android.media.AudioRecord;
  9. public class AudioRecordFunc {
  10.     // 缓冲区字节大小  
  11.     private int bufferSizeInBytes = 0;
  12.      
  13.     //AudioName裸音频数据文件 ,麦克风
  14.     private String AudioName = "";  
  15.      
  16.     //NewAudioName可播放的音频文件  
  17.     private String NewAudioName = "";
  18.      
  19.     private AudioRecord audioRecord;  
  20.     private boolean isRecord = false;// 设置正在录制的状态  
  21.      
  22.      
  23.     private static AudioRecordFunc mInstance;
  24.          
  25.     private AudioRecordFunc(){
  26.          
  27.     }   
  28.      
  29.     public synchronized static AudioRecordFunc getInstance()
  30.     {
  31.         if(mInstance == null)
  32.             mInstance = new AudioRecordFunc();
  33.         return mInstance;
  34.     }
  35.      
  36.     public int startRecordAndFile() {
  37.         //判断是否有外部存储设备sdcard
  38.         if(AudioFileFunc.isSdcardExit())
  39.         {
  40.             if(isRecord)
  41.             {
  42.                 return ErrorCode.E_STATE_RECODING;
  43.             }
  44.             else
  45.             {
  46.                 if(audioRecord == null)
  47.                     creatAudioRecord();
  48.                  
  49.                 audioRecord.startRecording();  
  50.                 // 让录制状态为true  
  51.                 isRecord = true;  
  52.                 // 开启音频文件写入线程  
  53.                 new Thread(new AudioRecordThread()).start();  
  54.                  
  55.                 return ErrorCode.SUCCESS;
  56.             }
  57.             
  58.         }      
  59.         else
  60.         {
  61.             return ErrorCode.E_NOSDCARD;            
  62.         }      
  63.     }  
  64.    
  65.     public void stopRecordAndFile() {  
  66.         close();  
  67.     }
  68.      
  69.      
  70.     public long getRecordFileSize(){
  71.         return AudioFileFunc.getFileSize(NewAudioName);
  72.     }
  73.      
  74.    
  75.     private void close() {  
  76.         if (audioRecord != null) {  
  77.             System.out.println("stopRecord");  
  78.             isRecord = false;//停止文件写入  
  79.             audioRecord.stop();  
  80.             audioRecord.release();//释放资源  
  81.             audioRecord = null;  
  82.         }  
  83.     }
  84.      
  85.      
  86.     private void creatAudioRecord() {  
  87.         // 获取音频文件路径
  88.         AudioName = AudioFileFunc.getRawFilePath();
  89.         NewAudioName = AudioFileFunc.getWavFilePath();
  90.          
  91.         // 获得缓冲区字节大小  
  92.         bufferSizeInBytes = AudioRecord.getMinBufferSize(AudioFileFunc.AUDIO_SAMPLE_RATE,  
  93.                 AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);  
  94.          
  95.         // 创建AudioRecord对象  
  96.         audioRecord = new AudioRecord(AudioFileFunc.AUDIO_INPUT, AudioFileFunc.AUDIO_SAMPLE_RATE,  
  97.                 AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);  
  98.     }
  99.      
  100.      
  101.     class AudioRecordThread implements Runnable {  
  102.         @Override
  103.         public void run() {  
  104.             writeDateTOFile();//往文件中写入裸数据  
  105.             copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件  
  106.         }  
  107.     }  
  108.    
  109.     /**
  110.      * 这里将数据写入文件,但是并不能播放,因为AudioRecord获得的音频是原始的裸音频,
  111.      * 如果需要播放就必须加入一些格式或者编码的头信息。但是这样的好处就是你可以对音频的 裸数据进行处理,比如你要做一个爱说话的TOM
  112.      * 猫在这里就进行音频的处理,然后重新封装 所以说这样得到的音频比较容易做一些音频的处理。
  113.      */
  114.     private void writeDateTOFile() {  
  115.         // new一个byte数组用来存一些字节数据,大小为缓冲区大小  
  116.         byte[] audiodata = new byte[bufferSizeInBytes];  
  117.         FileOutputStream fos = null;  
  118.         int readsize = 0;  
  119.         try {  
  120.             File file = new File(AudioName);  
  121.             if (file.exists()) {  
  122.                 file.delete();  
  123.             }  
  124.             fos = new FileOutputStream(file);// 建立一个可存取字节的文件  
  125.         } catch (Exception e) {  
  126.             e.printStackTrace();  
  127.         }  
  128.         while (isRecord == true) {  
  129.             readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);  
  130.             if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos!=null) {  
  131.                 try {  
  132.                     fos.write(audiodata);  
  133.                 } catch (IOException e) {  
  134.                     e.printStackTrace();  
  135.                 }  
  136.             }  
  137.         }  
  138.         try {
  139.             if(fos != null)
  140.                 fos.close();// 关闭写入流  
  141.         } catch (IOException e) {  
  142.             e.printStackTrace();  
  143.         }  
  144.     }  
  145.    
  146.     // 这里得到可播放的音频文件  
  147.     private void copyWaveFile(String inFilename, String outFilename) {  
  148.         FileInputStream in = null;  
  149.         FileOutputStream out = null;  
  150.         long totalAudioLen = 0;  
  151.         long totalDataLen = totalAudioLen + 36;  
  152.         long longSampleRate = AudioFileFunc.AUDIO_SAMPLE_RATE;  
  153.         int channels = 2;  
  154.         long byteRate = 16 * AudioFileFunc.AUDIO_SAMPLE_RATE * channels / 8;  
  155.         byte[] data = new byte[bufferSizeInBytes];  
  156.         try {  
  157.             in = new FileInputStream(inFilename);  
  158.             out = new FileOutputStream(outFilename);  
  159.             totalAudioLen = in.getChannel().size();  
  160.             totalDataLen = totalAudioLen + 36;  
  161.             WriteWaveFileHeader(out, totalAudioLen, totalDataLen,  
  162.                     longSampleRate, channels, byteRate);  
  163.             while (in.read(data) != -1) {  
  164.                 out.write(data);  
  165.             }  
  166.             in.close();  
  167.             out.close();  
  168.         } catch (FileNotFoundException e) {  
  169.             e.printStackTrace();  
  170.         } catch (IOException e) {  
  171.             e.printStackTrace();  
  172.         }  
  173.     }  
  174.    
  175.     /**
  176.      * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。
  177.      * 为我为啥插入这44个字节,这个还真没深入研究,不过你随便打开一个wav
  178.      * 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有
  179.      * 自己特有的头文件。
  180.      */
  181.     private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,  
  182.             long totalDataLen, long longSampleRate, int channels, long byteRate)  
  183.             throws IOException {  
  184.         byte[] header = new byte[44];  
  185.         header[0] = 'R'; // RIFF/WAVE header  
  186.         header[1] = 'I';  
  187.         header[2] = 'F';  
  188.         header[3] = 'F';  
  189.         header[4] = (byte) (totalDataLen & 0xff);  
  190.         header[5] = (byte) ((totalDataLen >> 8) & 0xff);  
  191.         header[6] = (byte) ((totalDataLen >> 16) & 0xff);  
  192.         header[7] = (byte) ((totalDataLen >> 24) & 0xff);  
  193.         header[8] = 'W';  
  194.         header[9] = 'A';  
  195.         header[10] = 'V';  
  196.         header[11] = 'E';  
  197.         header[12] = 'f'; // 'fmt ' chunk  
  198.         header[13] = 'm';  
  199.         header[14] = 't';  
  200.         header[15] = ' ';  
  201.         header[16] = 16; // 4 bytes: size of 'fmt ' chunk  
  202.         header[17] = 0;  
  203.         header[18] = 0;  
  204.         header[19] = 0;  
  205.         header[20] = 1; // format = 1  
  206.         header[21] = 0;  
  207.         header[22] = (byte) channels;  
  208.         header[23] = 0;  
  209.         header[24] = (byte) (longSampleRate & 0xff);  
  210.         header[25] = (byte) ((longSampleRate >> 8) & 0xff);  
  211.         header[26] = (byte) ((longSampleRate >> 16) & 0xff);  
  212.         header[27] = (byte) ((longSampleRate >> 24) & 0xff);  
  213.         header[28] = (byte) (byteRate & 0xff);  
  214.         header[29] = (byte) ((byteRate >> 8) & 0xff);  
  215.         header[30] = (byte) ((byteRate >> 16) & 0xff);  
  216.         header[31] = (byte) ((byteRate >> 24) & 0xff);  
  217.         header[32] = (byte) (2 * 16 / 8); // block align  
  218.         header[33] = 0;  
  219.         header[34] = 16; // bits per sample  
  220.         header[35] = 0;  
  221.         header[36] = 'd';  
  222.         header[37] = 'a';  
  223.         header[38] = 't';  
  224.         header[39] = 'a';  
  225.         header[40] = (byte) (totalAudioLen & 0xff);  
  226.         header[41] = (byte) ((totalAudioLen >> 8) & 0xff);  
  227.         header[42] = (byte) ((totalAudioLen >> 16) & 0xff);  
  228.         header[43] = (byte) ((totalAudioLen >> 24) & 0xff);  
  229.         out.write(header, 0, 44);  
  230.     }  
  231. }
复制代码

MediaRecorder录音,输出amr格式音频


  1. package com.example.audiorecordtest;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import android.media.MediaRecorder;
  5. public class MediaRecordFunc {  
  6.     private boolean isRecord = false;
  7.      
  8.     private MediaRecorder mMediaRecorder;
  9.     private MediaRecordFunc(){
  10.     }
  11.      
  12.     private static MediaRecordFunc mInstance;
  13.     public synchronized static MediaRecordFunc getInstance(){
  14.         if(mInstance == null)
  15.             mInstance = new MediaRecordFunc();
  16.         return mInstance;
  17.     }
  18.      
  19.     public int startRecordAndFile(){
  20.         //判断是否有外部存储设备sdcard
  21.         if(AudioFileFunc.isSdcardExit())
  22.         {
  23.             if(isRecord)
  24.             {
  25.                 return ErrorCode.E_STATE_RECODING;
  26.             }
  27.             else
  28.             {
  29.                 if(mMediaRecorder == null)
  30.                     createMediaRecord();
  31.                  
  32.                 try{
  33.                     mMediaRecorder.prepare();
  34.                     mMediaRecorder.start();
  35.                     // 让录制状态为true  
  36.                     isRecord = true;
  37.                     return ErrorCode.SUCCESS;
  38.                 }catch(IOException ex){
  39.                     ex.printStackTrace();
  40.                     return ErrorCode.E_UNKOWN;
  41.                 }
  42.             }
  43.             
  44.         }      
  45.         else
  46.         {
  47.             return ErrorCode.E_NOSDCARD;            
  48.         }      
  49.     }
  50.      
  51.      
  52.     public void stopRecordAndFile(){
  53.          close();
  54.     }
  55.      
  56.     public long getRecordFileSize(){
  57.         return AudioFileFunc.getFileSize(AudioFileFunc.getAMRFilePath());
  58.     }
  59.      
  60.      
  61.     private void createMediaRecord(){
  62.          /* ①Initial:实例化MediaRecorder对象 */
  63.         mMediaRecorder = new MediaRecorder();
  64.          
  65.         /* setAudioSource/setVedioSource*/
  66.         mMediaRecorder.setAudioSource(AudioFileFunc.AUDIO_INPUT);//设置麦克风
  67.          
  68.         /* 设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default
  69.          * THREE_GPP(3gp格式,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
  70.          */
  71.          mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
  72.          
  73.          /* 设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default */
  74.          mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
  75.          
  76.          /* 设置输出文件的路径 */
  77.          File file = new File(AudioFileFunc.getAMRFilePath());
  78.          if (file.exists()) {  
  79.              file.delete();  
  80.          }
  81.          mMediaRecorder.setOutputFile(AudioFileFunc.getAMRFilePath());
  82.     }
  83.      
  84.      
  85.     private void close(){
  86.         if (mMediaRecorder != null) {  
  87.             System.out.println("stopRecord");  
  88.             isRecord = false;
  89.             mMediaRecorder.stop();  
  90.             mMediaRecorder.release();  
  91.             mMediaRecorder = null;
  92.         }  
  93.     }
  94. }
复制代码

其他文件 

  1. package com.example.audiorecordtest;
  2. import java.io.File;
  3. import android.media.MediaRecorder;
  4. import android.os.Environment;
  5. public class AudioFileFunc {
  6.     //音频输入-麦克风
  7.     public final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
  8.      
  9.     //采用频率
  10.     //44100是目前的标准,但是某些设备仍然支持22050,16000,11025
  11.     public final static int AUDIO_SAMPLE_RATE = 44100;  //44.1KHz,普遍使用的频率   
  12.     //录音输出文件
  13.     private final static String AUDIO_RAW_FILENAME = "RawAudio.raw";
  14.     private final static String AUDIO_WAV_FILENAME = "FinalAudio.wav";
  15.     public final static String AUDIO_AMR_FILENAME = "FinalAudio.amr";
  16.      
  17.     /**
  18.      * 判断是否有外部存储设备sdcard
  19.      * @return true | false
  20.      */
  21.     public static boolean isSdcardExit(){      
  22.         if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
  23.             return true;
  24.         else
  25.             return false;
  26.     }
  27.          
  28.     /**
  29.      * 获取麦克风输入的原始音频流文件路径
  30.      * @return
  31.      */
  32.     public static String getRawFilePath(){
  33.         String mAudioRawPath = "";
  34.         if(isSdcardExit()){
  35.             String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
  36.             mAudioRawPath = fileBasePath+"/"+AUDIO_RAW_FILENAME;
  37.         }   
  38.          
  39.         return mAudioRawPath;
  40.     }
  41.      
  42.     /**
  43.      * 获取编码后的WAV格式音频文件路径
  44.      * @return
  45.      */
  46.     public static String getWavFilePath(){
  47.         String mAudioWavPath = "";
  48.         if(isSdcardExit()){
  49.             String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
  50.             mAudioWavPath = fileBasePath+"/"+AUDIO_WAV_FILENAME;
  51.         }
  52.         return mAudioWavPath;
  53.     }
  54.      
  55.      
  56.     /**
  57.      * 获取编码后的AMR格式音频文件路径
  58.      * @return
  59.      */
  60.     public static String getAMRFilePath(){
  61.         String mAudioAMRPath = "";
  62.         if(isSdcardExit()){
  63.             String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
  64.             mAudioAMRPath = fileBasePath+"/"+AUDIO_AMR_FILENAME;
  65.         }
  66.         return mAudioAMRPath;
  67.     }   
  68.      
  69.      
  70.     /**
  71.      * 获取文件大小
  72.      * @param path,文件的绝对路径
  73.      * @return
  74.      */
  75.     public static long getFileSize(String path){
  76.         File mFile = new File(path);
  77.         if(!mFile.exists())
  78.             return -1;
  79.         return mFile.length();
  80.     }
  81. }
复制代码

总结

AudioRecord这种方式收罗最为灵活,使开辟者最大限度的处置处罚收罗的音频,同时它捕获到的音频是原始音频PCM格式的!像做变声处置处罚的必要就必须要用它收集音频;

系统音频收罗


系统音频收罗有两种方法,但都有范围性。

2.1 REMOTE_SUBMIX


2.2 AudioPlaybackCapture




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立聪堂德州十三局店

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表