一 什么是音频的采样率和采样大小

自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码。即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。



抽样:在音频采集中叫做采样率。

由于声音其实是一种能量波,因此也有频率和振幅的特征,频率对应于时间轴线,振幅对应于电平轴线。波是无限光滑的,弦线可以看成由无数点组成,由于存储空间是相对有限的,数字编码过程中,必须对弦线的点进行采样。采样的过程就是抽取某点的频率值,很显然,在一秒中内抽取的点越多,获取得频率信息更丰富,为了复原波形,一次振动中,必须有2个点的采样,人耳能够感觉到的最高频率为20kHz,因此要满足人耳的听觉要求,则需要至少每秒进行40k次采样,用40kHz表达,这个40kHz就是采样率。我们常见的CD,采样率为44.1kHz。

量化:我们这里的采样大小就是量化的过程,将该频率的能量值并量化,用于表示信号强度。量化电平数为 2的整数次幂,我们常见的CD位16bit的采样大小,即2的16次方。

编码:

根据采样率和采样大小可以得知,相对自然界的信号,音频编码最多只能做到无限接近,至少目前的技术只能这样了,相对自然界的信号,任何数字音频编码方案都是有损的,因为无法完全还原。在计算机应用中,能够达到最高保真水平的就是PCM编码,被广泛用于素材保存及音乐欣赏,CD、DVD以及我们常见的WAV文件中均有应用。因此,PCM约定俗成了无损编码,因为PCM代表了数字音频中最佳的保真水准,并不意味着PCM就能够确保信号绝对保真,PCM也只能做到最大程度的无限接近。我们而习惯性的把MP3列入有损音频编码范畴,是相对PCM编码的。强调编码的相对性的有损和无损,是为了告诉大家,要做到真正的无损是困难的,就像用数字去表达圆周率,不管精度多高,也只是无限接近,而不是真正等于圆周率的值



为什么要使用音频压缩技术 



要算一个PCM音频流的码率是一件很轻松的事情,采样率值×采样大小值×声道数bps。一个采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的WAV文件,它的数据速率则为 44.1K×16×2 =1411.2 Kbps。我们常说128K的MP3,对应的WAV的参数,就是这个1411.2 Kbps,这个参数也被称为数据带宽,它和ADSL中的带宽是一个概念。将码率除以8,就可以得到这个WAV的数据速率,即176.4KB/s。这表示存储一秒钟采样率为44.1KHz,采样大小为16bit,双声道的PCM编码的音频信号,需要176.4KB的空间,1分钟则约为10.34M,这对大部分用户是不可接受的,尤其是喜欢在电脑上听音乐的朋友,要降低磁盘占用,只有2种方法,降低采样指标或者压缩。降低指标是不可取的,因此专家们研发了各种压缩方案。由于用途和针对的目标市场不一样,各种音频压缩编码所达到的音质和压缩比都不一样,在后面的文章中我们都会一一提到。有一点是可以肯定的,他们都压缩过。 



频率与采样率的关系 



采样率表示了每秒对原始信号采样的次数,我们常见到的音频文件采样率多为44.1KHz,这意味着什么呢?假设我们有2段正弦波信号,分别为20Hz和20KHz,长度均为一秒钟,以对应我们能听到的最低频和最高频,分别对这两段信号进行 40KHz的采样,我们可以得到一个什么样的结果呢?结果是:20Hz的信号每次振动被采样了40K/20=2000次,而20K的信号每次振动只有2次采样。显然,在相同的采样率下,记录低频的信息远比高频的详细。这也是为什么有些音响发烧友指责CD有数码声不够真实的原因,CD的44.1KHz采样也无法保证高频信号被较好记录。要较好的记录高频信号,看来需要更高的采样率,于是有些朋友在捕捉CD音轨的时候使用48KHz的采样率,这是不可取的!这其实对音质没有任何好处,对抓轨软件来说,保持和CD提供的44.1KHz一样的采样率才是最佳音质的保证之一,而不是去提高它。较高的采样率只有相对模拟信号的时候才有用,如果被采样的信号是数字的,请不要去尝试提高采样率。 



流特征 



随着网络的发展,人们对在线收听音乐提出了要求,因此也要求音频文件能够一边读一边播放,而不需要把这个文件全部读出后然后回放,这样就可以做到不用下载就可以实现收听了。也可以做到一边编码一边播放,正是这种特征,可以实现在线的直播,架设自己的数字广播电台成为了现实。    
 

二 android中AudioRecord采集音频的参数说明

在android中采集音频的api是android.media.AudioRecord类

其中构造器的几个参数就是标准的声音采集参数

以下是参数的含义解释

public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

Since: API Level 3

Class constructor.

Parameters

audioSource

the recording source. See MediaRecorder.AudioSource for recording source definitions.

音频源:指的是从哪里采集音频。这里我们当然是从麦克风采集音频,所以此参数的值为MIC

sampleRateInHz

the sample rate expressed in Hertz. Examples of rates are (but not limited to) 44100, 22050 and 11025.

采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。例如要采集低质量的音频就可以使用4000、8000等低采样率。

channelConfig

describes the configuration of the audio channels. SeeCHANNEL_IN_MONO and CHANNEL_IN_STEREO

声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声

audioFormat

the format in which the audio data is represented. SeeENCODING_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()查看。

采集到的数据保存在一个byteBuffer中,可以使用流将其读出。亦可保存成为文件的形式

三 Android 使用AudioRecord录音相关和音频文件的封装

在Android中录音可以用MediaRecord录音,操作比较简单。但是不够专业,就是不能对音频进行处理。如果要进行音频的实时的处理或者音频的一些封装

就可以用AudioRecord来进行录音了。

这里给出一段代码。实现了AudioRecord的录音和WAV格式音频的封装。

用AudioTrack和AudioTrack类可以进行边录边播,可以参考:http://blog.sina.com.cn/s/blog_6309e1ed0100j1rw.html

我们这里的代码没有播放。但是有封装和详解,如下:

[java] view
plain
copy

  1. package com.ppmeet;
  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.app.Activity;
  8. import android.graphics.PixelFormat;
  9. import android.media.AudioFormat;
  10. import android.media.AudioRecord;
  11. import android.media.MediaRecorder;
  12. import android.os.Bundle;
  13. import android.view.View;
  14. import android.view.View.OnClickListener;
  15. import android.view.Window;
  16. import android.view.WindowManager;
  17. import android.widget.Button;
  18. /**
  19. * class name:TestAudioRecord<BR>
  20. * class description:用AudioRecord来进行录音<BR>
  21. * PS: <BR>
  22. *
  23. * @version 1.00 2011/09/21
  24. * @author CODYY)peijiangping
  25. */
  26. public class TestAudioRecord extends Activity {
  27. // 音频获取源
  28. private int audioSource = MediaRecorder.AudioSource.MIC;
  29. // 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
  30. private static int sampleRateInHz = 44100;
  31. // 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
  32. private static int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
  33. // 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
  34. private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
  35. // 缓冲区字节大小
  36. private int bufferSizeInBytes = 0;
  37. private Button Start;
  38. private Button Stop;
  39. private AudioRecord audioRecord;
  40. private boolean isRecord = false;// 设置正在录制的状态
  41. //AudioName裸音频数据文件
  42. private static final String AudioName = "/sdcard/love.raw";//不推荐这么写,可以用Enviroment.
  43. //NewAudioName可播放的音频文件
  44. private static final String NewAudioName = "/sdcard/new.wav";
  45. public void onCreate(Bundle savedInstanceState) {
  46. super.onCreate(savedInstanceState);
  47. getWindow().setFormat(PixelFormat.TRANSLUCENT);// 让界面横屏
  48. requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉界面标题
  49. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  50. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  51. // 重新设置界面大小
  52. setContentView(R.layout.main);
  53. init();
  54. }
  55. private void init() {
  56. Start = (Button) this.findViewById(R.id.start);
  57. Stop = (Button) this.findViewById(R.id.stop);
  58. Start.setOnClickListener(new TestAudioListener());
  59. Stop.setOnClickListener(new TestAudioListener());
  60. creatAudioRecord();
  61. }
  62. private void creatAudioRecord() {
  63. // 获得缓冲区字节大小
  64. bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,
  65. channelConfig, audioFormat);
  66. // 创建AudioRecord对象
  67. audioRecord = new AudioRecord(audioSource, sampleRateInHz,
  68. channelConfig, audioFormat, bufferSizeInBytes);
  69. }
  70. class TestAudioListener implements OnClickListener {
  71. @Override
  72. public void onClick(View v) {
  73. if (v == Start) {
  74. startRecord();
  75. }
  76. if (v == Stop) {
  77. stopRecord();
  78. }
  79. }
  80. }
  81. private void startRecord() {
  82. audioRecord.startRecording();
  83. // 让录制状态为true
  84. isRecord = true;
  85. // 开启音频文件写入线程
  86. new Thread(new AudioRecordThread()).start();
  87. }
  88. private void stopRecord() {
  89. close();
  90. }
  91. private void close() {
  92. if (audioRecord != null) {
  93. System.out.println("stopRecord");
  94. isRecord = false;//停止文件写入
  95. audioRecord.stop();
  96. audioRecord.release();//释放资源
  97. audioRecord = null;
  98. }
  99. }
  100. class AudioRecordThread implements Runnable {
  101. @Override
  102. public void run() {
  103. writeDateTOFile();//往文件中写入裸数据
  104. copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件
  105. }
  106. }
  107. /**
  108. * 这里将数据写入文件,但是并不能播放,因为AudioRecord获得的音频是原始的裸音频,
  109. * 如果需要播放就必须加入一些格式或者编码的头信息。但是这样的好处就是你可以对音频的 裸数据进行处理,比如你要做一个爱说话的TOM
  110. * 猫在这里就进行音频的处理,然后重新封装 所以说这样得到的音频比较容易做一些音频的处理。
  111. */
  112. private void writeDateTOFile() {
  113. // new一个byte数组用来存一些字节数据,大小为缓冲区大小
  114. byte[] audiodata = new byte[bufferSizeInBytes];
  115. FileOutputStream fos = null;
  116. int readsize = 0;
  117. try {
  118. File file = new File(AudioName);
  119. if (file.exists()) {
  120. file.delete();
  121. }
  122. fos = new FileOutputStream(file);// 建立一个可存取字节的文件
  123. } catch (Exception e) {
  124. e.printStackTrace();
  125. }
  126. while (isRecord == true) {
  127. readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
  128. if (AudioRecord.ERROR_INVALID_OPERATION != readsize) {
  129. try {
  130. fos.write(audiodata);
  131. } catch (IOException e) {
  132. e.printStackTrace();
  133. }
  134. }
  135. }
  136. try {
  137. fos.close();// 关闭写入流
  138. } catch (IOException e) {
  139. e.printStackTrace();
  140. }
  141. }
  142. // 这里得到可播放的音频文件
  143. private void copyWaveFile(String inFilename, String outFilename) {
  144. FileInputStream in = null;
  145. FileOutputStream out = null;
  146. long totalAudioLen = 0;
  147. long totalDataLen = totalAudioLen + 36;
  148. long longSampleRate = sampleRateInHz;
  149. int channels = 2;
  150. long byteRate = 16 * sampleRateInHz * channels / 8;
  151. byte[] data = new byte[bufferSizeInBytes];
  152. try {
  153. in = new FileInputStream(inFilename);
  154. out = new FileOutputStream(outFilename);
  155. totalAudioLen = in.getChannel().size();
  156. totalDataLen = totalAudioLen + 36;
  157. WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
  158. longSampleRate, channels, byteRate);
  159. while (in.read(data) != -1) {
  160. out.write(data);
  161. }
  162. in.close();
  163. out.close();
  164. } catch (FileNotFoundException e) {
  165. e.printStackTrace();
  166. } catch (IOException e) {
  167. e.printStackTrace();
  168. }
  169. }
  170. /**
  171. * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。
  172. * 为我为啥插入这44个字节,这个还真没深入研究,不过你随便打开一个wav
  173. * 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有
  174. * 自己特有的头文件。
  175. */
  176. private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,
  177. long totalDataLen, long longSampleRate, int channels, long byteRate)
  178. throws IOException {
  179. byte[] header = new byte[44];
  180. header[0] = 'R'; // RIFF/WAVE header
  181. header[1] = 'I';
  182. header[2] = 'F';
  183. header[3] = 'F';
  184. header[4] = (byte) (totalDataLen & 0xff);
  185. header[5] = (byte) ((totalDataLen >> 8) & 0xff);
  186. header[6] = (byte) ((totalDataLen >> 16) & 0xff);
  187. header[7] = (byte) ((totalDataLen >> 24) & 0xff);
  188. header[8] = 'W';
  189. header[9] = 'A';
  190. header[10] = 'V';
  191. header[11] = 'E';
  192. header[12] = 'f'; // 'fmt ' chunk
  193. header[13] = 'm';
  194. header[14] = 't';
  195. header[15] = ' ';
  196. header[16] = 16; // 4 bytes: size of 'fmt ' chunk
  197. header[17] = 0;
  198. header[18] = 0;
  199. header[19] = 0;
  200. header[20] = 1; // format = 1
  201. header[21] = 0;
  202. header[22] = (byte) channels;
  203. header[23] = 0;
  204. header[24] = (byte) (longSampleRate & 0xff);
  205. header[25] = (byte) ((longSampleRate >> 8) & 0xff);
  206. header[26] = (byte) ((longSampleRate >> 16) & 0xff);
  207. header[27] = (byte) ((longSampleRate >> 24) & 0xff);
  208. header[28] = (byte) (byteRate & 0xff);
  209. header[29] = (byte) ((byteRate >> 8) & 0xff);
  210. header[30] = (byte) ((byteRate >> 16) & 0xff);
  211. header[31] = (byte) ((byteRate >> 24) & 0xff);
  212. header[32] = (byte) (2 * 16 / 8); // block align
  213. header[33] = 0;
  214. header[34] = 16; // bits per sample
  215. header[35] = 0;
  216. header[36] = 'd';
  217. header[37] = 'a';
  218. header[38] = 't';
  219. header[39] = 'a';
  220. header[40] = (byte) (totalAudioLen & 0xff);
  221. header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
  222. header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
  223. header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
  224. out.write(header, 0, 44);
  225. }
  226. @Override
  227. protected void onDestroy() {
  228. close();
  229. super.onDestroy();
  230. }
  231. }

声音的数字信息可以转化成波形图:貌似是很简单的一些算大

byte[] buffer = new byte[bs]; isRun = true; while (isRun) {
int r = ar.read(buffer, 0, bs);
int v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < buffer.length; i++) {
// 这里没有做运算的优化,为了更加清晰的展示代码
v += buffer[i] * buffer[i];
}
// 平方和除以数据总长度,得到音量大小。可以获取白噪声值,然后对实际采样进行标准化。
// 如果想利用这个数值进行操作,建议用 sendMessage 将其抛出,在 Handler 里进行处理。
Log.d("spl", String.valueOf(v / (float) r));

原帖地址:点击打开


android中AudioRecord使用的更多相关文章

  1. Android 4.4中AudioRecord用例 - 录制系统内置声音

    通过API 19新加的MediaRecorder.AudioSource.REMOTE_SUBMIX參数能够让系统App录制系统内置的声音,也就是扬声器的声音.以下是一个巨简单的样例来演示样例怎样通过 ...

  2. 【Android】【录音】Android录音--AudioRecord、MediaRecorder

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

  3. Android录音--AudioRecord、MediaRecorder

    Android提供了两个API用于实现录音功能:android.media.AudioRecord.android.media.MediaRecorder. 网上有很多谈论这两个类的资料.现在大致总结 ...

  4. 【录音】Android录音--AudioRecord、MediaRecorder

    Android提供了两个API用于实现录音功能:android.media.AudioRecord.android.media.MediaRecorder. 网上有很多谈论这两个类的资料.现在大致总结 ...

  5. Android中的LinearLayout布局

    LinearLayout : 线性布局 在一般情况下,当有很多控件需要在一个界面列出来时,我们就可以使用线性布局(LinearLayout)了,  线性布局是按照垂直方向(vertical)或水平方向 ...

  6. Android中BroadcastReceiver的两种注册方式(静态和动态)详解

    今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...

  7. Android中使用ExpandableListView实现微信通讯录界面(完善仿微信APP)

    之前的博文<Android中使用ExpandableListView实现好友分组>我简单介绍了使用ExpandableListView实现简单的好友分组功能,今天我们针对之前的所做的仿微信 ...

  8. Android中ListView实现图文并列并且自定义分割线(完善仿微信APP)

    昨天的(今天凌晨)的博文<Android中Fragment和ViewPager那点事儿>中,我们通过使用Fragment和ViewPager模仿实现了微信的布局框架.今天我们来通过使用Li ...

  9. Android中Fragment和ViewPager那点事儿(仿微信APP)

    在之前的博文<Android中使用ViewPager实现屏幕页面切换和引导页效果实现>和<Android中Fragment的两种创建方式>以及<Android中Fragm ...

随机推荐

  1. Linux查看并修改mysql的编码

    系统:阿里云 一.查看mysql字符集 输入:show variables like 'character_set_%'; 二.修改某一个数据库的编码 输入:alter database dbname ...

  2. SSDB VS redis

    现在有不少团队开始使用了一个新型高效的 NoSQL数据库 - SSDB,如 京东.唱吧 …… SSDB 官网的定义 一个高性能的支持丰富数据结构的 NoSQL 数据库,用于替代 Redis 官网 ht ...

  3. CentOS7下二进制文件安装MySQL5.6

    1.查看已装包 [root@host2 ~]# rpm -qa | grep mysql mysql-libs-5.1.71-1.el6.x86_64 [root@host2 ~]# [root@ho ...

  4. badblocks 检查磁盘损坏的区块

    Linux badblocks命令用于检查磁盘装置中损坏的区块. 语法: badblocks [-svw][-b <区块大小>][-o <输出文件>][磁盘装置][磁盘区块数] ...

  5. 《精通Spring4.X企业应用开发实战》读后感第五章(FactoryBean)

  6. Apple Swift中文入门教程【转发】

    1   简介 今天凌晨Apple刚刚发布了Swift编程语言,本文从其发布的书籍<The Swift Programming Language>中摘录和提取而成.希望对各位的iOS& ...

  7. POJ 1601 拓展欧几里得算法

    学习链接:http://www.cnblogs.com/frog112111/archive/2012/08/19/2646012.html 先来学习一下什么是欧几里得算法: 欧几里得原理是:两个整数 ...

  8. web.config中authorization下的location中的path的设置 (转)

    项目下 有三个文件夹 A,B,C 验正方式是 Forms 验正 我要设置他们的访问权限为, A,匿名可访问 B,普通用户授权后才能访问 C,只允许管理员访问 <configuration> ...

  9. 连接mysql时报:message from server: "Host '192.168.76.89' is not allowed to connect to this MySQL server 处理方案

    1.先用localhost方式连接到MySQL数据库,然后使用MySQL自带的数据库mysql; use mysql: 2.执行:select host from user where user = ...

  10. 在Android中使用FlatBuffers(上篇)

    本文来自网易云社区. 总览 先来看一下 FlatBuffers 项目已经为我们提供了什么,而我们在将 FlatBuffers 用到我们的项目中时又需要做什么的整体流程.如下图: 在使用 FlatBuf ...