最近项目中需要实现手机采集声音频率实现设备律动的效果,整理了下Android与声音相关的知识。
目的
根据声音振幅、频率获取颜色值,通过蓝牙mesh发送指令给灯改变其颜色值。
实现原理
Android声音采集相关Api
快速傅里叶变换公式
Mesh网发送rgb值相关指令
声音概念
响度(dB)
人主观感觉声音的大小(音量),振幅与人离声源的距离决定,振幅越大,离声源的距离越小,响度越大。
LP= 20×lgP/P0
LP:声压级(db)
P:声压(Pa)
P0:基准声压:2*10-5Pa,该值是对800HZ声音人耳刚能听到的最低声压。
音调
声音的高低,由频率决定,频率越高,音调越高。
频率是每秒经过一给定点的声波数量,单位赫兹(Hz)
人耳能听到20~20kHz的声音。
音色
音品,波形决定声音的音色。
Android音频采集
MediaRecorder
MediaRecorder:基于文件录音,已集成录音、编码、压缩
mMediaRecorder.setAudioSource(int audioSource)//设置音频源
——MediaRecorder.AudioSource.DEFAULT//默认音频源
——MediaRecorder.AudioSource.MIC//主麦克风
mMediaRecorder.setOutputFormat(int output_format);//设置输出文件格式
——MediaRecorder.OutputFormat.AMR_NB
mMediaRecorder.setAudioEncoder(int audio_encoder);//设置音频文件的编码类型
——MediaRecorder.AudioEncoder.DEFAULT
mMediaRecorder.setOutputFile(String path);//设置输出文件名
mMediaRecorder.setMaxDuration(int max_duration_ms);//设置录制最大持续时间
mMediaRecorder.prepare();//完成初始话
mMediaRecorder.start();//启动录音
mMediaRecorder.stop();//停止录音
mMediaRecorder.reset();//重置配置
mMediaRecorder.release();//释放资源
mMediaRecorder.getMaxAmplitude()//获取自上次调用该方法以来采样的最大绝对振幅,最大值32768
in db = 20 * Math.log10(MediaRecorder.getMaxAmplitude() / BASE) //BSE = 600;
AudioRecord
采样
把模拟信号数字化的过程
采样频率越高,红色间隔越密集,记录音频所用数据量越大,音频质量越高。
采样定理(奈奎斯特理论):当采样频率大于信号中最高频率的2倍时,采样后的数字信号完整地保留原始信号中的信息。人耳能听到20~20kHz的声音,为了保证声音不失真,采样频率应在40kHz以上。
目前44100Hz是唯一可以保证兼容所有Android手机的采样率。
量化精度(位宽)
指将模拟信号分成几个等级,量化精度越高,声音质量越好,单位Bit。
CD标准量化精度16Bit,DVD标准量化精度24Bit。
16Bit可以保证兼容所有Android手机。
声道数
音频采集、播放可以叠加,可以同时从多个音频源采集声音,例如:单声道/双声道。
音频帧
即采样时间,例如20ms一帧代表20ms为单位的数据量为一帧音频。
一帧音频帧大小 = 采样率 x 位宽 x 采样时间 x 通道数
例:采样率8000,位宽8,通道2,采样间隔20ms
(8000 * 8/8 *2)/ (1000/20 ) = 320Byte //1字节 = 8 bits
AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)
——audioSource:声音源(MediaRecorder.AudioSource.MIC)
——sampleRateInHz:采样率(44100)
——channelConfig:声道配置(AudioFormat.CHANNEL_IN_MONO)
——audioFormat:位宽、音频格式,一般的手机设备可能只支持16位PCM编码(AudioFormat.ENCODING_PCM_16BIT)
——bufferSizeInBytes: AudioRecord 内部的音频缓冲区的大小,不能低于一帧音频帧的大小。
(AudioRecord.getMinBufferSize(44100,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT))
audioRecord.startRecording();//开始采集
audioRecord.stop();//停止采集
audioRecord.release();//释放audioRecord资源
audioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes)//从音频硬件录制缓冲区读取数据
——audioData:写入音频录制数据
——offsetInBytes:audioData的起始偏移值
——sizeInBytes:读取的最大字节数
对audioData进行快速傅里叶变化,时域->频域的变化,可以将信号的频谱提取出来。
傅立叶变换就是多个正余弦波叠加可以用来近似任何一个原始的周期函数,它实质是是频域函数和时域函数的转换。
Visualize
Visualizer:检索当前正在播放的音频,对其进行编码
new Visualizer(int audioSession)//构造函数需要
visualizer.setDataCaptureListener()//设置监听
--onWaveFormDataCapture//返回波形信息
--onFftDataCapture//返回经过fft变换后的信息
根据频率划分color值
以下基于AudioRecord采集的音频数据后进行快速傅里叶变换得到频率值
public static final String[] COLOR_LIST = {
"1d1f8c","02459c","0267b9",
"0286d1","00a1ea", "019fc2",
"029d97","009d69","029a45",
"23ac3a","90c221","d1db01",
"fff002","f9c802","f29700",
"ec6000","e50112","e70036",
"e60251","e50069","e5007f",
"bf017f","930784","611786",
};//渐变色范围,冷色->暖色
toneManager.setOnVoiceChangeListener(new ToneManager.OnVoiceChangeListener() {
@Override
public void onVoiceChange(double tone) {
float cent = (float)22050/ColorUtil.COLOR_LIST.length; //22050频率最大值
int level = (int) (tone/cent); //通过level获取ColorUtil.COLOR_LIST[level])即为当前频率对应的颜色值
}
});
recordManager = new RecordManager(new File("/sdcard/Wdz/aa"));
recordManager.setOnVoiceChangeListener(new RecordManager.OnVoiceChangeListener() {
@Override
public void onVoiceChange(int db) {
float cent = (float)ColorUtil.COLOR_LIST.length/34; //34为最大分贝值
int level = (int) (db*cent);//通过level获取ColorUtil.COLOR_LIST[level])即为当前分贝对应的颜色值
}
});