1.WAV音频文件结构
WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。
而每个Chunk都有自己的id做为该chunk开始的标志.下面我们来看下一个wav文件的具体信息:
Data Chunk是真正保存wav数据的地方,以"data"作为该Chunk的标示。然后是数据的大小。紧接着就是wav数据。
2.制作WAV文件
最后.读取CaptureBuffer中的数据保存为wav文件.
WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。WAVE文件是由若干个Chunk组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(可选), Data Chunk。
而每个Chunk都有自己的id做为该chunk开始的标志.下面我们来看下一个wav文件的具体信息:
标识 | 大小 | 值 | |
RIFF Chunk | ID | 4 Bytes | "RIFF" |
Size | 4 Bytes | File.length - 8 | |
Type | 4 Bytes | "WAVE" | |
Format Chunk | ID | 4 Bytes | "fmt " |
Size | 4 Bytes | 数值为16或18,18则最后又附加信息 | |
FormatTag | 2 Bytes | 编码方式,一般为0x0001 | |
Channels | 2 Bytes | 声道数目,1--单声道;2--双声道 | |
SamplesPerSecond | 4 Bytes | 采样频率 8000, 44100, 等 | |
AvgBytesPerSec | 4 Bytes | 每秒所需字节数 (BlockAlign * SamplesPerSecond) | |
BlockAlign | 2 Bytes | 数据块对齐单位(每个采样需要的字节数) Channels * (BitsPerSample / 8) | |
BitsPerSample | 2 Bytes | 每个采样需要的bit数 8或者是16 | |
2 Bytes | 附加信息(可选,通过Size来判断有无) | ||
data Chunk | ID | 4 Bytes | "data" |
SIZE | 4 Bytes | ||
data | 音频数据 |
2.制作WAV文件
有了wav文件的结构加上音频数据那么我们很快就能制作出一个wav文件. 这里我们使用DirectSound来获取麦克风上录入的音频数据,然后制作wav文件(即DirectSound录音); 这里使用winform来编写一个简单的录音程序. 准备工作: vs2008 DirectX SDK(这样才能引入程序集Microsoft.DirectX.DirectSound,Microsoft.DirectX) 那么接下来的问题是我们如何获取音频数据. 使用CaptureBuffer中的Read方法就可以获得: byte[] data = (byte[])CaptureBuffer.Read(); 这个流程也就很明显了. 首先.创建CaptureBuffer. 创建CaptureBuffer之前需要创建两个实例即WaveFormat(Wav文件的Format Chunk的描述结构), Capture(声卡设备的抽象描述);
//创建Capture protected Capture CreateCapture() { Capture capture = null; try { capture = new Capture(DSoundHelper.DefaultVoiceCaptureDevice); //DSoundHelper.DefaultVoiceCaptureDevice 使用默认声卡的GUID } catch (DirectXException e) { MessageBox.Show(e.ErrorString); } return capture; } //创建WaveFormat protected WaveFormat CreateFormat() { WaveFormat format = new WaveFormat(); format.FormatTag = WaveFormatTag.Pcm; format.Channels = 1; format.SamplesPerSecond = 16000; format.BitsPerSample = 16; format.BlockAlign = (short)(format.Channels * format.BitsPerSample / 8); format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign; return format; } //创建CaptureBuffer protected CaptureBuffer CreateBuffer(WaveFormat format, Capture capture) { CaptureBufferDescription desc = new CaptureBufferDescription(); desc.Format = format; desc.BufferBytes = BUFFER_SIZE;//设置CaptureBuffer的大小,这里BUFFER_SIZE=2m CaptureBuffer buffer = new CaptureBuffer(desc, capture); return buffer; } //buffer.start(true); 意味着录音开始
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
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
//读取缓冲区中的音频数据 protected byte[] GetRecordBuffer(CaptureBuffer buffer) { byte[] data = null; int readPos; int capturePos; buffer.GetCurrentPosition(out capturePos, out readPos); if (readPos < 0) readPos += BUFFER_SIZE; if (readPos == 0) return null; data = (byte[])buffer.Read(0, typeof(byte), LockFlag.None, readPos); return data; } private void StopRecord() { byte[] data = GetRecordBuffer(buffer); buffer.Stop();//录音结束 if (data != null) { SaveFileDialog dlg = new SaveFileDialog(); dlg.Title = "Save Audio File"; dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); dlg.FileName = "test.wav"; if (dlg.ShowDialog() != DialogResult.OK) return; string path = dlg.FileName; dlg.Dispose(); FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); char[] chunkRiff = { "R", "I", "F", "F" }; char[] chunkType = { "W", "A", "V", "E" }; char[] chunkFmt = { "f", "m", "t", " " }; char[] chunkData = { "d", "a", "t", "a" }; //RIFF WAVE Chunk BinaryWriter writer = new BinaryWriter(file); writer.Write(chunkRiff); writer.Write((int)(data.Length + 36)); writer.Write(chunkType); //Format Chunk writer.Write(chunkFmt); writer.Write((int)16); writer.Write((ushort)1); writer.Write(format.Channels); writer.Write(format.SamplesPerSecond); writer.Write(format.AverageBytesPerSecond); writer.Write(format.BlockAlign); writer.Write(format.BitsPerSample); //data Chunk writer.Write(chunkData); writer.Write((int)data.Length); writer.Write(data, 0, data.Length); writer.Write((int)0); writer.Close(); file.Close(); file.Dispose(); } }
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
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
哈哈,大功告成了,也许你注意到了,我们设置的缓冲区的大小是2M,如果我们录制的文件大于2M呢,那岂不是在缓冲区中放不下吗?是的,没错.DirectSound的解决方案是,当缓冲区内的数据满时,通知你,你转移到别的地方去. 详细的可以参看这里; 或者是查看类 Microsoft.DirectX.DirectSound.BufferPositionNotify Microsoft.DirectX.DirectSound.Notify