基础知识
PCM(Pulse Code Modulation),脉冲编码调制。人耳听到的是模拟信号,PCM是把声音从模拟信号转化为数字信号的技术。简单来说就是一种无压缩编码
采样频率、量化精度(采样位数)和声道数:
-
采样频率
是设备一秒钟内对模拟信号的采样次数,在主流的采集卡上分为:(8Khz的电话采样率就可以达到人的对话程度)
22.05KHz:无线电广播;
44.1KHz:音频 CD,MP3等;
48KHz:miniDV、数字电视、DVD、电影和专业音频。
-
采样位数
它是用来衡量声音波动变化的一个参数,也可以说是声卡的分辨率。它的数值越大,分辨率也就越高,所发出声音的能力越强。
在计算机中采样位数一般有8位和16位之分,8位不是说把纵坐标分成8份,而是分成2的8次方即256份; 同理16位是把纵坐标分成2的16次方65536份。 -
声道数
即声音的通道的数目。有单声道和立体声之分,单声道的声音只能使用一个喇叭发声(有的也处理成两个喇叭输出同一个声道的声音),立体声的PCM可以使两个喇叭都发声(一般左右声道有分工) ,更能感受到空间效果。
单声道 采样数据为8位的短整数(short);
双声道 采样数据为16位的整数,(int),高八位(左声道)和低八位(右声道)分别代表两个声道。 -
存储量= (采样频率 * 采样位数 * 声道 * 时间)/8 (单位:字节数)。
[时长]s * [采样率]Hz * [采样位数]bit * [声道数] / 8 = [文件大小]byte
某音频信号是采样率为8kHz、声道数、位宽为16bit,时长为1s,则音频数据的大小为:
1 * 8000 * 16 *2 = 256000 bit / 8 = 32000 byte / 1024 = 31.25 KB
AudioRecorder
是 Android 中一种音频采集的方式,另外一种是 MediaRecorder
MediaRecorder:录制的音频文件是经过压缩后的,需要设置编码器。并且录制的音频文件可以用系统自带的Music播放器播放。
优点:官方提供 API
缺点: 不能实时处理音频,输出格式不多,且PCM可以处理生成
AudioRecord: 录制的是PCM格式的音频文件,需要用AudioTrack来播放。
优点:可以实时获取音频的数据做到边录边播放,可以对获取的音频做处理,压缩,传输等
缺点:输出的是原始数据 PCM 所以播放器不能播放,需要通过AudioTrack处理
开始采集
采集音频的步骤:
1.配置 AudioRecorder 构造函数的参数
2.初始化缓冲区
3.开始采集 ,子线程里将缓冲区的数据取出,写入文件流
4.停止采集,释放资源
从 AudioRecord 的构造函数开始
audioSource: 音频采集的输入源,可选的值以常量的形式定义在 MediaRecorder.AudioSource 类中,例如:MIC(由手机麦克风输入),VOICE_COMMUNICATION(用于VoIP应用)等等。
sampleRateInHz:采样率,注意,目前44100Hz是唯一可以保证兼容所有Android手机的采样率。
channelConfig: 通道数的配置,可选的值以常量的形式定义在 AudioFormat 类中,常用的是CHANNEL_IN_MONO(单通道),CHANNEL_IN_STEREO(双通道)
audioFormat: 返回的音频数据的格式,可选的值也是以常量的形式定义在 AudioFormat 类中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保证兼容所有Android手机的。
bufferSizeInBytes: AudioRecord 内部的音频缓冲区的大小,该缓冲区的值不能低于一帧“音频帧”(Frame)的大小
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
throws IllegalArgumentException {
this((new AudioAttributes.Builder())
.setInternalCapturePreset(audioSource)
.build(),
(new AudioFormat.Builder())
.setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
true/*allow legacy configurations*/))
.setEncoding(audioFormat)
.setSampleRate(sampleRateInHz)
.build(),
bufferSizeInBytes,
AudioManager.AUDIO_SESSION_ID_GENERATE);
}
配置
参数配置:
/**
* 采样率。现在能够保证在所有设备上使用的采样率是44100Hz
*/
public static final int SAMPLE_RATE_INHZ = 44100;
/**
* 输入声道数。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是可以保证在所有设备能够使用的。
*/
public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
/**
* 返回的音频数据的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.
*/
public static final int ENCODING_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
缓冲区:
AudioRecord 提供了一个类为我们计算最小缓冲区,参数就是我们上面配置的 采样率,声道, 返回的音频数据的格式
int minBufferSize = AudioRecord.getMinBufferSize(Config.SAMPLE_RATE_INHZ, Config.CHANNEL_CONFIG, Config.ENCODING_FORMAT);
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch (channelConfig) {
case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
case AudioFormat.CHANNEL_IN_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_IN_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
channelCount = 2;
break;
case AudioFormat.CHANNEL_INVALID:
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
}
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size == 0) {
return ERROR_BAD_VALUE;
}
else if (size == -1) {
return ERROR;
}
else {
return size;
}
}
开始采集,子线程读数据写入文件流
直接调用创建好的 AudioRecorder 对象的 startRecording();
//开始采集
mAudioRecorder.startRecording();
//需要再子线程里面调用(读和存)
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);
FileOutputStream 的 write()
停止采集
mAudioTrack.stop();
mAudioRecorder.release();
AudioTrack
开始播放
开始播放步骤:
1.配置参数
2.配置缓冲区
3.开启子线程,把缓冲区读数据转换成输入流,再调用AudioTrack读 write()写入数据,最后调用 play()
4.结束释放资源
配置参数:
参数和 AudioRecorder 差不多,有区别的就是 AudioTrack 是输出声道,还要播放的类型,和播放的模式
streamType:播放的类型,都定义在 AudioManager 类中
mode: 播放的模式, MODE_STATIC, MODE_STREAM 两种
两者的区别
/** 在播放之前必须把数据全部加载完成
* Creation mode where audio data is transferred from Java to the native layer
* only once before the audio starts playing.
*/
public static final int MODE_STATIC = 0;
/**
*可以一边录音一边播放
* Creation mode where audio data is streamed from Java to the native layer
* as the audio is playing.
*/
public static final int MODE_STREAM = 1;
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
this(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}
缓冲区
和上面的 AudioRecorder 的配置一样
开启子线程,把缓冲区数据转换成输入流,再调用AudioTrack读 write()写入数据,最后调用 play()
int readCount = inStream.read(mBuffer);
mAudioTrack.write(mBuffer, 0, readCount);
mAudioTrack.play();
停止释放资源
mAudioTrack.stop();
mAudioTrack.release();
网友评论