学校大创项目简单的app 实现功能,录制声音存为wav,利用json与服务器通信,上传wav到服务器,服务器转为midi文件,从服务器下载midi和乐谱并播放,同时电子琴改装后也可以与服务器通信,由手机给电子琴辅助参数,电子琴通过arduino从服务器上读取乐曲中间键值文件播放。
midi播放
调用MediaPlayer类播放,因为不可抗因素,只能用android5.1,没有midi库,就做简单的播放 - MediaPlayer可以用外部存储,assert,自建raw文件夹或者uri四种方式访问媒体文件并播放 - 从raw文件夹中读取可以直接用player = MediaPlayer.create(this, R.raw.test1) - Uri或者外部存储读取new->setDataSource->prepare->start
录制声音并重放
参考android中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
40private class RecordTask extends AsyncTask<Void, Integer, Void> {
protected Void doInBackground(Void... arg0) {
isRecording = true;
try {
//开通输出流到指定的文件
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(pcmFile)));
//根据定义好的几个配置,来获取合适的缓冲大小
int bufferSize = AudioRecord.getMinBufferSize(audioRate, channelConfig, audioEncoding);
//实例化AudioRecord
AudioRecord record = new AudioRecord(MediaRecorder.AudioSource.MIC, audioRate, channelConfig, audioEncoding, bufferSize);
//定义缓冲
short[] buffer = new short[bufferSize];
//开始录制
record.startRecording();
int r = 0; //存储录制进度
//定义循环,根据isRecording的值来判断是否继续录制
while (isRecording) {
//从bufferSize中读取字节,返回读取的short个数
//这里老是出现buffer overflow,不知道是什么原因,试了好几个值,都没用,TODO:待解决
int bufferReadResult = record.read(buffer, 0, buffer.length);
//循环将buffer中的音频数据写入到OutputStream中
for (int i = 0; i < bufferReadResult; i++) {
dos.writeShort(buffer[i]);
}
publishProgress(new Integer(r)); //向UI线程报告当前进度
r++; //自增进度值
}
//录制结束
record.stop();
convertWaveFile();
dos.close();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
}
pcm写头文件转成wav
因为录制的是裸文件,pcm格式,需要自己加上wav头 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
59private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate,
int channels, long byteRate) throws IOException {
byte[] header = new byte[45];
header[0] = 'R'; // RIFF
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';//WAVE
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
//FMT Chunk
header[12] = 'f'; // 'fmt '
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;
//编码方式 10H为PCM编码格式
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);
//音频数据传送速率,采样率*通道数*采样深度/8
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) (1 * 16 / 8);
header[33] = 0;
//每个样本的数据位数
header[34] = 16;
header[35] = 0;
//Data chunk
header[36] = 'd';//data
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);
header[44] = 0;
out.write(header, 0, 45);
}
json收发
根据我们的实际情况,发送时使用json,存三个参数和wav内容,因为录音的wav时长较短,可以把整个wav写入json中
json发送两次,第一次发送参数和文件,拿到md5编码的时间戳,第二次把这个时间戳加入json中请求相应的midi文件
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
29private JSONObject makejson(int request, String identifycode, String data) {
if (identifycode == "a") {
try {
JSONObject pack = new JSONObject();
pack.put("request", request);
JSONObject config = new JSONObject();
config.put("n", lowf);
config.put("m", highf);
config.put("w", interval);
pack.put("config", config);
pack.put("data", data);
return pack;
} catch (JSONException e) {
e.printStackTrace();
}
}