声音信号的播放与录制

在 Andriod 系统下,一般使用 AudioTrack 和 AudioRecord 来实现声音的播放与录制。

1.声音信号的播放

播放声音一般用 AudioTrack 实现,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import android.content.pm.PackageManager;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.jar.Manifest;
public class MainActivity extends AppCompatActivity implements View.OnClickListener , ActivityCompat.OnRequestPermissionsResultCallback{
//private static final int AUDIO_REQUEST = 0;
public static final String TAG = "PCMSample";
//是否在录制
private boolean isRecording = false;
//开始录音
private Button startAudio;
//结束录音
private Button stopAudio;
//播放录音
private Button playAudio;
//删除文件
private Button deleteAudio;
private ScrollView mScrollView;
private TextView tv_audio_succeess;
//pcm文件
private File file;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
//初始化View
private void initView() {
mScrollView = (ScrollView) findViewById(R.id.mScrollView);
tv_audio_succeess = (TextView) findViewById(R.id.tv_audio_succeess);
printLog("初始化成功");
startAudio = (Button) findViewById(R.id.startAudio);
startAudio.setOnClickListener(this);
stopAudio = (Button) findViewById(R.id.stopAudio);
stopAudio.setOnClickListener(this);
playAudio = (Button) findViewById(R.id.playAudio);
playAudio.setOnClickListener(this);
deleteAudio = (Button) findViewById(R.id.deleteAudio);
deleteAudio.setOnClickListener(this);
}
//点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startAudio:
/*在此处插入运行时权限获取的代码*/
if (ActivityCompat.checkSelfPermission(this,
android.Manifest.permission.RECORD_AUDIO) !=
PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[]{android.Manifest.permission.RECORD_AUDIO,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
StartRecord();
Log.e(TAG,"start");
}
});
thread.start();
printLog("开始录音");
break;
case R.id.stopAudio:
isRecording = false;
printLog("停止录音");
break;
case R.id.playAudio:
PlayRecord();
ButtonEnabled(true, false, false);
printLog("播放录音");
break;
case R.id.deleteAudio:
deleFile();
break;
}
}
//打印log
private void printLog(final String resultString) {
tv_audio_succeess.post(new Runnable() {
@Override
public void run() {
tv_audio_succeess.append(resultString + "\n");
mScrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
});
}
//获取/失去焦点
private void ButtonEnabled(boolean start, boolean stop, boolean play) {
startAudio.setEnabled(start);
stopAudio.setEnabled(stop);
playAudio.setEnabled(play);
}
//开始录音
public void StartRecord() {
Log.i(TAG,"开始录音");
//设置采样频率
int frequency = 48000;
//设置音频采样声道,CHANNEL_IN_STEREO代表双声道,CHANNEL_IN_MONO代表单声道
int channelConfiguration = AudioFormat.CHANNEL_IN_STEREO;
//设置音频数据格式,每个采样数据占16bit
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
//生成PCM文件
file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/myaudio.pcm");
Log.i(TAG,"生成文件");
//如果存在,就先删除再创建
if (file.exists())
file.delete();
Log.i(TAG,"删除文件");
try {
file.createNewFile();
Log.i(TAG,"创建文件");
} catch (IOException e) {
Log.i(TAG,"未能创建");
throw new IllegalStateException("未能创建" + file.toString());
}
try {
//输出流
OutputStream os = new FileOutputStream(file);
int bufferSize = AudioRecord.getMinBufferSize(frequency,
channelConfiguration, audioEncoding);
AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
frequency, channelConfiguration, audioEncoding, bufferSize);
byte[] buffer = new byte[bufferSize];
audioRecord.startRecording();
Log.i(TAG, "开始录音");
isRecording = true;
while (isRecording) {
int bufferReadResult = audioRecord.read(buffer, 0, bufferSize);
os.write(buffer);
}
audioRecord.stop();
os.close();
} catch (Throwable t) {
Log.e(TAG, "录音失败");
}
}
//播放文件
public void PlayRecord() {
if(file == null){
return;
}
int musicLength = (int) (file.length() / 2);
byte[] music = new byte[musicLength];
try {
InputStream is = new FileInputStream(file);
//设置播放参数
AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
48000, AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
musicLength * 2,
AudioTrack.MODE_STREAM);
audioTrack.play();
//从文件中读取并写入audioTrack
while(is.read(music)!= -1)
{
audioTrack.write(music, 0, musicLength);
}
is.close();
audioTrack.stop();
} catch (Throwable t) {
Log.e(TAG, "播放失败");
}
}
//删除文件
private void deleFile() {
if(file == null){
return;
}
file.delete();
printLog("文件删除成功");
}
/*private short[] test_Noise(short[] buf, int nb_sample)
{
int i = 0;
for (i = 0; i < nb_sample; i++)
{
buf[i] >>= 2;
}
return buf;
}*/
}

2.声音信号的录制

播放声音一般用 AudioRecord 来实现,实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
package com.Javen;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.text.format.Time;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import com.Javen44100.R;
public class AudioRecordActivity extends Activity {
private static final String TAG = "AudioRecordActivity";
private Button startButton, stopButton, playButton;
private int bufferSizeInBytes = 0;//缓冲区大小
//音频获取来源
private int audioSource = MediaRecorder.AudioSource.MIC;
//设置音频的采样率,44100是目前的标准,但是某些设备仍然支持22050,16000,11025
private static int sampleRateInHz = 44100;
//设置音频的录制声道,CHANNEL_IN_STEREO为双声道,CHANNEL_CONFIGURATION_MONO为单声道
private static int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
//设置音频数据格式:PCM 16位每个样本,保证设备支持。PCM 8位每个样本,不一定能得到设备的支持。
private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
//AudioName裸音频数据文件
private static final String AudioName = "/sdcard/love.raw";
//NewAudioName可播放的音频文件
private String NewAudioName = "/sdcard/new.wav";
private AudioRecord audioRecord;
//播放音频
private MediaPlayer mediaPlayer;
private boolean isRecord = false;//设置录制状态
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//获取当前时间
Time time=new Time();
time.setToNow();
String times= time.year+"."+(time.month+1)+"."+time.monthDay+"_"+time.hour+":"+time.minute+":"+time.second;
NewAudioName = "/sdcard/"+times+".wav";
init();
}
private void init()
{
startButton = (Button)findViewById(R.id.startButton);
stopButton = (Button)findViewById(R.id.stopButton);
playButton = (Button)findViewById(R.id.playButton);
creatAudioRecord();
startButton.setOnClickListener(new AudioRecordLinstener());
stopButton.setOnClickListener(new AudioRecordLinstener());
playButton.setOnClickListener(new AudioRecordLinstener());
}
private void creatAudioRecord()
{
//根据AudioRecord的音频采样率、音频录制声道、音频数据格式获取缓冲区大小
bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
Log.d(TAG, "creatAudioRecord-->bufferSizeInBytes="+bufferSizeInBytes);
//根据音频获取来源、音频采用率、音频录制声道、音频数据格式和缓冲区大小来创建AudioRecord对象
audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
//创建播放实例
mediaPlayer = new MediaPlayer();
}
class AudioRecordLinstener implements OnClickListener
{
@Override
public void onClick(View v)
{
if(v == startButton)
{
startAudioRecord();
}
if(v == stopButton)
{
stopAudioRecord();
}
if(v == playButton)
{
playMusic();
}
}
}
private final class PrepareListener implements OnPreparedListener
{
@Override
public void onPrepared(MediaPlayer mp)
{
// TODO Auto-generated method stub
mediaPlayer.start();//开始播放
}
}
/**
* 播放录制的音频
*/
private void playMusic()
{
File file = new File(NewAudioName);
if(file.exists())
{
try
{
mediaPlayer.reset();
mediaPlayer.setDataSource(NewAudioName);
mediaPlayer.prepare();//进行数据缓冲
mediaPlayer.setOnPreparedListener(new PrepareListener());
} catch (IllegalArgumentException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 开始录制音频
*/
private void startAudioRecord()
{
audioRecord.startRecording();//开始录制
isRecord = true;
new AudioRecordThread().start();//开启线程来把录制的音频数据保留下来
}
/**
* 停止录制音频
*/
private void stopAudioRecord()
{
close();
}
private void close()
{
if (audioRecord != null)
{
System.out.println("stopRecord");
isRecord = false;//停止文件写入
audioRecord.stop();
audioRecord.release();//释放资源
audioRecord = null;
}
}
/**
* 音频数据写入线程
* @author Administrator
*
*/
class AudioRecordThread extends Thread
{
@Override
public void run()
{
super.run();
writeDataToFile();//把录制的音频裸数据写入到文件中去
copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件
}
}
/**
* 把录制的音频裸数据写入到文件中去
* 这里将数据写入文件,但是并不能播放,因为AudioRecord获得的音频是原始的裸音频,
* 如果需要播放就必须加入一些格式或者编码的头信息。但是这样的好处就是你可以对音频的 裸数据进行处理,比如你要做一个爱说话的TOM
* 猫在这里就进行音频的处理,然后重新封装 所以说这样得到的音频比较容易做一些音频的处理。
*/
private void writeDataToFile()
{
// new一个byte数组用来存一些字节数据,大小为缓冲区大小
byte[] audioData = new byte[bufferSizeInBytes];
int readSize = 0;
FileOutputStream fos = null;
File file = new File(AudioName);
if(file.exists())
file.delete();
try
{
fos = new FileOutputStream(file);//获取一个文件的输出流
} catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
while(isRecord == true)
{
readSize = audioRecord.read(audioData, 0, bufferSizeInBytes);
Log.d(TAG, "readSize ="+readSize);
if(AudioRecord.ERROR_INVALID_OPERATION != readSize)
{
try
{
fos.write(audioData);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
try
{
fos.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void copyWaveFile(String inFileName, String outFileName)
{
FileInputStream in = null;
FileOutputStream out = null;
long totalAudioLen = 0;
long totalDataLen = totalAudioLen + 36;
long longSampleRate = sampleRateInHz;
int channels = 2;
long byteRate = 16 * sampleRateInHz * channels / 8;
byte[] data = new byte[bufferSizeInBytes];
try
{
in = new FileInputStream(inFileName);
out = new FileOutputStream(outFileName);
totalAudioLen = in.getChannel().size();
totalDataLen = totalAudioLen + 36;
WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
while(in.read(data) != -1)
{
out.write(data);
}
in.close();
out.close();
} catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。
* 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有
* 自己特有的头文件。
*/
private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels, long byteRate)
throws IOException {
byte[] header = new byte[44];
header[0] = 'R'; // RIFF/WAVE header
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
header[4] = (byte) (totalDataLen & 0xff);
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
header[7] = (byte) ((totalDataLen >> 24) & 0xff);
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
header[12] = 'f'; // 'fmt ' chunk
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
header[16] = 16; // 4 bytes: size of 'fmt ' chunk
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 1; // format = 1
header[21] = 0;
header[22] = (byte) channels;
header[23] = 0;
header[24] = (byte) (longSampleRate & 0xff);
header[25] = (byte) ((longSampleRate >> 8) & 0xff);
header[26] = (byte) ((longSampleRate >> 16) & 0xff);
header[27] = (byte) ((longSampleRate >> 24) & 0xff);
header[28] = (byte) (byteRate & 0xff);
header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff);
header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (2 * 16 / 8); // block align
header[33] = 0;
header[34] = 16; // bits per sample
header[35] = 0;
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);
header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
out.write(header, 0, 44);
}
@Override
protected void onDestroy()
{
close();
if(mediaPlayer != null)
{
mediaPlayer.release();
mediaPlayer = null;
}
super.onDestroy();
}
}