1与MediaPlayer关系
AudioTrack是Android中的一个类,它允许播放原始音频样本。利用该类能够播放使用AudioRecord捕获的音频,而他们并不能使用MediaPlayer对象来播放。
MediaPlayer会在framework层创建相应的音频解码器,可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。而AudioTrack仅仅能播放已经解码的PCM流,假设是文件的话仅仅支持wav格式的音频文件,由于wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器。所以仅仅能播放不须要解码的wav文件。
MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack。AudioTrack再传递给AudioFlinger进行混音,传递音频给硬件播放出来。利用AudioTrack播放只是跳过Mediaplayer的解码部分而已。Mediaplayer的解码核心部分是基于OpenCORE来实现的,支持通用的音视频和图像格式,codec使用的是OpenMAX接口来进行扩展。因此使用audiotrack播放mp3文件的话,要自己加入一个音频解码器,如libmad。否则只能播放PCM数据,如大多数WAV格式的音频文件。如果是实时的音频数据,那么只能用AudioTrack进行播放。
所以是MediaPlayer包括了AudioTrack。
2AudioTrack构造过程
每个音频流相应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注冊到 AudioFlinger中。由AudioFlinger把全部的AudioTrack进行混合(Mixer)。然后输送到AudioHardware中进行播放。眼下Android同一时候最多能够创建32个音频流,也就是说。Mixer最多会同一时候处理32个AudioTrack的数据流。
3使用AudioTrack播放
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes, int mode)
第一个参数是流类型。可能的值定义为AudioManager类中的常量。
STREAM_ALARM:警告声
STREAM_MUSCI:音乐声,例如music等
STREAM_RING:铃声
STREAM_SYSTEM:系统声音
STREAM_VOCIE_CALL:电话声音
在示例中将使用AudioManager.STREAM_MUSIC,这是用于正常播放音乐的音频流。
第二个参数是将要播放的音频数据的采样率,以赫兹为单位。在播放时需要和捕获音频时的采样率指定相同的值。
在数字音频领域,常用的采样率有:
* 8,000 Hz - 电话所用采样率, 对于人的说话已经足够;;
* 11,025 Hz;
* 22,050 Hz - 无线电广播所用采样率;
* 32,000 Hz - miniDV 数码视频 camcorder、DAT (LP mode)所用采样率;
* 44,100 Hz - 音频 CD, 也常用于 [MPEG-1](http://baike.baidu.com/view/7739.htm) 音频(VCD,SVCD, MP3)所用采样率;**
* 47,250 Hz - Nippon Columbia (Denon)开发的世界上第一个商用 PCM [录音机](http://baike.baidu.com/view/29010.htm)所用采样 率;
* 48,000 Hz - miniDV、数字电视、DVD、DAT、电影和专业音频所用的数字声音所用采 样率;
* 50,000 Hz - 二十世纪七十年代后期出现的 3M 和 Soundstream 开发的第一款商用数字 录音机所用采样率;
* 50,400 Hz - 三菱 X-80 数字录音机所用所用采样率;
* 96,000 或者 192,000 Hz - [DVD-Audio](http://baike.baidu.com/view/81803.htm)、一些LPCM DVD [音轨](http://baike.baidu.com/view/779000.htm)、BD-ROM(蓝光盘)音 轨、和 [HD-DVD](http://baike.baidu.com/view/143630.htm) (高清晰度DVD)音轨所用所用采样率;
* 2.8224 MHz - SACD、 索尼 和 飞利浦 联合开发的称为 Direct Stream Digital 的 1 位 sigma-delta modulation 过程所用采样率。
参考:Android录音API AudioRecord的参数有一个是采样率,但Android对音频采样率做了限制,默认值是22050Hz,并且不能小于4000Hz,不能大于48000Hz,即采样率必须位于 [4000,48000]hz之间。
API中对AudioReocrd采样率的参数说明如下:
sampleRateInHzthe sample rate expressed in Hertz. 44100Hz is currently the only rate that is guaranteed to work on all devices, but other rates such as 22050, 16000, and 11025 may work on some devices.
44100Hz是一个通用的值,保证在所有设备上可以工作。但还是需要底层对此支持才可 以,只是一个推荐的通用值,对设备的兼容性最好。
第三个参数是通道配置。可能的值与构造AudioRecord对象时使用的值相同,他们定义为AudioFormat类中的常量。他们的名称解释了其作用。
AudioFormat.CHANNEL_CONFIGURATION_MONO 单通道
AudioFormat.CHANNEL_CONFIGURATION_STEREO 双通道
AudioFormat.CHANNEL_CONFIGURATION_INVALID错误的音频通道掩码
AudioFormat.CHANNEL_CONFIGURATION_DEFAULT 默认通道
第四个参数是音频格式,可能的值与构造AudioRecord对象时使用的值相同,他们定义为AudioFormat类中的常量。使用的值应匹配待传入的音频的值。
AudioFormat.ENCODING_DEFAULT
AudioFormat.ENCODING_INVALID
AudioFormat.ENCODING_PCM_16BIT
AudioFormat.ENCODING_PCM_8BIT
第五个参数是将对象中用于存储音频的缓冲区大小。为了确定使用最小的缓冲区大小,可以调用getMinBufferSize方法,同时传入采样率、通道配置和音频格式。
1private int frequency=44100;
2private int channelConfiguration=AudioFormat.CHANNEL_CONFIGURATION_STEREO;
3private int audioEncoding=AudioFormat.ENCODING_PCM_16BIT;
4private int bufferSize=AudioTrack.getMinBufferSize(frequency, channelConfiguration, audioEncoding)
最后一个参数是模式。可能的值定义为AudioTrack类中的常量。
AudioTrack.MODE_STATIC:在播放发生之前将所有的音频数据转移到AudioTrack对象。
AudioTrack.MODE_STREAM:在播放的同时将音频数据持续的转移到AudioTrack对象。
代码:
AudioTrackPlayActivity.java:
import android.app.Activity;
import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.LinkedList;
import zx.com.mytextproject.R;
/**
* Created by Zhang on 2018/10/9.
*
* audioRecord输入的瞬间 用AudioTrack输出
*/
public class AudioTrackPlayActivity extends Activity implements View.OnClickListener {
Context ctx;
TextView tv_audiotrackplay;//分贝
Button btn_audiotrackplay;
//边唱边放
AudioRecord audioRecord;
AudioTrack audioTrackPlayer;
Thread td_record;//边唱线程
Thread td_play;//边放线程
protected LinkedList m_in_q ;
private boolean isSing = false;// 设置正在播放的状态
private int audioSource = MediaRecorder.AudioSource.MIC;// 音频获取源
private static int sampleRateInHz = 44100; // 设置音频采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
private static int channelConfig = AudioFormat.CHANNEL_IN_STEREO;// 设置音频的录制的声道CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;// 音频数据格式:PCM 16位每个样本。保证设备支持。PCM 8位每个样本。不一定能得到设备支持。
private static int audioMode = AudioTrack.MODE_STREAM;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
setContentView(R.layout.activity_audiotrackplay);
initView();
}
private void initView() {
tv_audiotrackplay= (TextView)findViewById(R.id.tv_audiotrackplay);
btn_audiotrackplay= (Button)findViewById(R.id.btn_audiotrackplay);
btn_audiotrackplay.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_audiotrackplay:
if(isSing){
isSing = false;
audioTrackPlayer.stop();
audioTrackPlayer.release();
audioRecord.stop();
audioRecord.release();//释放资源
m_in_q.clear();
tv_audiotrackplay.post(new Runnable() {
@Override
public void run() {
tv_audiotrackplay.setText("等待");
}
});
btn_audiotrackplay.post(new Runnable() {
@Override
public void run() {
btn_audiotrackplay.setText("开始边唱边放");
}
});
}else{
//开始
isSing = true;
btn_audiotrackplay.post(new Runnable() {
@Override
public void run() {
btn_audiotrackplay.setText("停止边唱边放");
}
});
startSing_Play();
}
break;
}
}
private void startSing_Play() {
int RecordbufferSize= AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);// 获得缓冲区字节大小
audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, RecordbufferSize);// 创建AudioRecord对象
int bufferSize = android.media.AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig,audioFormat);// 获得音频缓冲区大小
audioTrackPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, bufferSize,audioMode);
Log.d("zzz","缓冲大小为RecordbufferSize="+RecordbufferSize);//7104
Log.d("zzz","缓冲大小为bufferSize="+bufferSize);//14144
final Long[] time = {System.currentTimeMillis()};
//bufferSize一定大于RecordbufferSize
Log.d("zzz"," time1="+ time[0]);
m_in_q=new LinkedList();
final byte[] Recordbuffer = new byte[bufferSize];//
final int[] readLen = new int[1];
td_record = new Thread(new Runnable() {
@Override
public void run() {
byte [] bytes_pkg;
while(true){
readLen[0] = audioRecord.read(Recordbuffer, 0, Recordbuffer.length);
Log.d("zzz"," readLen="+ readLen[0]);////14144
bytes_pkg = Recordbuffer.clone() ;
//录制
m_in_q.add(bytes_pkg) ;
//分贝
if(System.currentTimeMillis()- time[0] >200) {
time[0] =System.currentTimeMillis();
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < Recordbuffer.length; i++) {
v += Recordbuffer[i] * Recordbuffer[i];
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) readLen[0];
final double volume = 10 * Math.log10(mean);
tv_audiotrackplay.post(new Runnable() {
@Override
public void run() {
tv_audiotrackplay.setText("volume="+volume);
}
});
}
if(isSing==false){
break;
}
}
}
});
td_play= new Thread(new Runnable() {
@Override
public void run() {
while(true) {
//播放
if (m_in_q.size() >= 1) {
try {
audioTrackPlayer.write(m_in_q.removeFirst(), 0, readLen[0]);
} catch (Exception e) {
Log.d("zzz", " audioTrackPlayer.write异常");//
e.printStackTrace();
}
}
if(isSing==false){
break;
}
}
}
});
if(audioRecord.getState()==audioRecord.STATE_INITIALIZED){//audioRecord初始化失败
if(audioTrackPlayer.getState()==audioTrackPlayer.STATE_INITIALIZED){//audioTrack初始化失败
audioRecord.startRecording();
audioTrackPlayer.play();
td_record.start();
td_play.start();
}else{
Log.d("zzz","audioTrack初始化失败");
}
}else{
Log.d("zzz","audioRecord初始化失败,麦克风无法使用");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("zzz","释放资源");//
if(isSing){
isSing = false;
audioTrackPlayer.stop();
audioTrackPlayer.release();
audioRecord.stop();
audioRecord.release();//释放资源
m_in_q.clear();
}
}
}
activity_audiotrackplay.xml:
注:
新的版本优化了代码,记得插上耳机,否则会有噪音。
延迟问题暂定为Android顶层自带的问题,因为有采样时间+编码时间+解码时间的存在造成的,想完美解决的话需要C++直接调用Android底层硬件来实现,这又涉及到适配Android 芯片和蓝牙耳机的相关知识了。不在此讨论。