具体描述:功能启动后,一直处于录音状态。当玩家对着手机说话(即音量大于某个值),则开始保存玩家说的话;当一段时间之后,玩家不再说话(音量小于某个值),则停止保存音频,并开始播放(即播放玩家说过的话)。
public class MainActivity extends Activity {
private short[] mAudioRecordData; // 录音数据
private short[] mAudioTrackData; // 播放数据
private File mAudioFile; // 录音文件
private AudioRecord mAudioRecord; // 录音对象
private AudioTrack mAudioTrack; // 播放对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
InitDB();
}
//初始化
private void init() {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File file = new File(Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/audio/");
if (!file.exists()) {
file.mkdirs();
}
//创建录音文件
mAudioFile = new File(file, System.currentTimeMillis() + ".pcm");
}
try {
// int sampleRateInHz = 44100;//所有Android系统都支持的频率
int sampleRateInHz = 8000;
//AudioFormat.CHANNEL_IN_MONO单声道输入 AudioFormat.ENCODING_PCM_16BIT这个所有Android系统都支持
// 创建AudioRecord实例,用于录音
int recordBufferSizeInBytes = AudioRecord.getMinBufferSize(
sampleRateInHz, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
Log.d("TAG", "recordBufferSizeInBytes=" + recordBufferSizeInBytes);
mAudioRecordData = new short[recordBufferSizeInBytes];
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
sampleRateInHz, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, recordBufferSizeInBytes);
// 创建mAudioTrack实例,用于播放
int trackBufferSizeInBytes = AudioRecord.getMinBufferSize(
sampleRateInHz, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrackData = new short[trackBufferSizeInBytes];
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRateInHz, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, trackBufferSizeInBytes,
AudioTrack.MODE_STREAM);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
// AudioRecord:实时获取分贝
private static final String TAG = "AudioRecord";
boolean isGetVoiceRun;
Object mLock;
double volume = 0; // 音量,单位为分贝
// 平方和除以数据总长度,得到音量大小。
double mean;
private boolean isNeedRecord = false; // 标记是否需要存储录制数据
private boolean isPlaying = false; // 标记是否正在播放
private boolean isRestart = false;
Timer mTimer1;
private boolean isStartVolumeTimer = false;
public void InitDB() {
mLock = new Object();
getNoiseLevel();
}
// 获取音量,并根据音量来决定是否保存录音到文件和播放已保存的文件
public void getNoiseLevel() {
if (mAudioRecord == null) {
Log.e("sound", "mAudioRecord初始化失败");
}
isGetVoiceRun = true;
new Thread(new Runnable() {
@Override
public void run() {
mAudioRecord.startRecording(); // 开始录音
boolean isFirstStart = true; // 避免第一次的高分贝
try {
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(mAudioFile)));
while (isGetVoiceRun) {
// 如果正在播放,等一小段时间后再继续判断
if(isPlaying){
synchronized (mLock) {
try {
isRestart = true;
mLock.wait(100);
continue;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
if(isRestart){
isRestart = false;
// mAudioRecord.startRecording(); // 开始录音
dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream(mAudioFile)));
}
// r是实际读取的数据长度,一般而言r会小于buffersize
int r = mAudioRecord.read(mAudioRecordData, 0, mAudioRecordData.length); //
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < mAudioRecordData.length; i++) {
v += mAudioRecordData[i] * mAudioRecordData[i];
}
// 避开第一次的高分贝(来源不清楚)
if(isFirstStart){
isFirstStart = false;
continue;
}
// 平方和除以数据总长度,得到音量大小。
mean = v / (double) r;
// 如果正在录音,间隔一段时间再修改volume的值
if(isNeedRecord){
if(!isStartVolumeTimer){
isStartVolumeTimer = true;
mTimer1 = new Timer();
TimerTask mTask1 = new TimerTask() {
@Override
public void run() {
volume = 10 * Math.log10(mean);
}
};
mTimer1.schedule(mTask1, 2000, 1000);
}
}else{
volume = 10 * Math.log10(mean);
}
Log.d(TAG, "分贝值:" + volume);
// 如果音量大于某个值,则把录制的数据存到文件中
if(volume >= 65){
if(!isNeedRecord){
Log.i("apple", "开始存储录制数据StartRecord");
isNeedRecord = true;
}
StartRecord(dos, r, mAudioRecordData);
}
// 如果音量小于某个值,且正在录制,则结束录制并开始播放
if(volume< 65 && isNeedRecord){
Log.i("apple", "结束存储录制数据");
isNeedRecord = false;
// mAudioRecord.stop();
try {
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
mTimer1.cancel();
isStartVolumeTimer = false;
PlayAudio();
}
// // 大概一秒两次
// synchronized (mLock) {
// try {
// mLock.wait(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
}
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
Log.i("apple", "停止-AudioRecord");
}
}).start();
}
//把录制的数据存在mAudioFile文件中
private void StartRecord(DataOutputStream dos,int number, short[] buffer) {
// Log.i("apple", "存储录制数据StartRecord");
try {
for (int i = 0; i < number; i++) {
dos.writeShort(buffer[i]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 播放录制的音频文件
private void PlayAudio() {
Log.i("apple", "开始播放");
isPlaying = true;
new Thread(new Runnable() {
public void run() {
try {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
mAudioTrack.play();
DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream(mAudioFile)));
Log.d("TAG", "dis.available=" + dis.available());
while (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING
&& dis.available() > 0) {
int i = 0;
while (dis.available() > 0
&& i < mAudioTrackData.length) {
mAudioTrackData[i] = dis.readShort();
i++;
}
wipe(mAudioTrackData, 0, mAudioTrackData.length);
// 然后将数据写入到AudioTrack中
mAudioTrack.write(mAudioTrackData, 0,
mAudioTrackData.length);
}
mAudioTrack.stop();
dis.close();
isPlaying = false;
Log.i("apple", "结束播放");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 消除噪音
*/
private void wipe(short[] lin, int off, int len) {
int i, j;
for (i = 0; i < len; i++) {
j = lin[i + off];
lin[i + off] = (short) (j >> 2);
}
}
@Override
protected void onStop() {
if (mAudioFile.exists()) {
mAudioFile.delete();
}
if (mAudioRecord != null) {
mAudioRecord.release();
mAudioRecord = null;
}
if (mAudioTrack != null) {
mAudioTrack.release();
mAudioTrack = null;
}
super.onStop();
}
}
其中,录音与播放部分参考了链接:https://www.2cto.com/kf/201704/636266.html
获取音量部分参考了链接的AudioRecord部分:https://blog.csdn.net/greatpresident/article/details/38402147