-
Notifications
You must be signed in to change notification settings - Fork 2
/
RiffMaster.cs
300 lines (271 loc) · 6.82 KB
/
RiffMaster.cs
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
using System;
using System.IO;
using System.Collections.Generic;
class RiffMaster
{
public RiffMaster() { }
public void WriteFile(string fname)
{
using (FileStream fs = new FileStream(fname,FileMode.Create,FileAccess.Write,FileShare.Read))
WriteStream(fs);
}
public void LoadFile(string fname)
{
using (FileStream fs = File.OpenRead(fname))
LoadStream(fs);
}
private static string ReadTag(BinaryReader br)
{
return "" + br.ReadChar() + br.ReadChar() + br.ReadChar() + br.ReadChar();
}
protected static void WriteTag(BinaryWriter bw, string tag)
{
for(int i=0;i<4;i++)
bw.Write(tag[i]);
bw.Flush();
}
public abstract class RiffChunk
{
public string tag;
public abstract void WriteStream(Stream s);
public abstract long GetSize();
public abstract RiffChunk Morph();
}
public class RiffSubchunk : RiffChunk
{
public byte[] data = new byte[0];
public override void WriteStream(Stream s)
{
BinaryWriter bw = new BinaryWriter(s);
WriteTag(bw,tag);
bw.Write(data.Length);
bw.Write(data);
bw.Flush();
if (data.Length % 2 != 0)
s.WriteByte(0);
}
public override long GetSize()
{
long ret = data.Length;
if (ret % 2 != 0) ret++;
return ret;
}
public override RiffChunk Morph()
{
switch (tag)
{
case "fmt ": return new RiffSubchunk_fmt(data);
case "smpl": return new RiffSubchunk_smpl(data);
}
return this;
}
}
public class RiffSubchunk_smpl : RiffSubchunk
{
public RiffSubchunk_smpl(byte[] data)
{
this.data = data;
tag = "smpl";
BinaryReader br = new BinaryReader(new MemoryStream(data));
br.BaseStream.Position += 7 * 4; //skip unusued fields
int numLoops = br.ReadInt32();
br.BaseStream.Position += 4; //skip sampler data
for (int i = 0; i < numLoops; i++)
{
var sl = new SampleLoop();
sl.CuePointId = br.ReadInt32();
sl.Type = br.ReadInt32();
sl.Start = br.ReadInt32();
sl.End = br.ReadInt32();
sl.Fraction = br.ReadInt32();
sl.PlayCount = br.ReadInt32();
SampleLoops.Add(sl);
}
}
public List<SampleLoop> SampleLoops = new List<SampleLoop>();
public class SampleLoop
{
public int CuePointId, Type, Start, End, Fraction, PlayCount;
}
}
public class RiffSubchunk_fmt : RiffSubchunk
{
public enum FORMAT_TAG : ushort
{
MS_ADPCM = 1,
G711_alaw = 6,
G711_ulaw = 7,
IMA_ADPCM = 17,
G723 = 20,
GSM = 49,
G721 = 64,
MPEG = 80,
EXPERIMENTAL = 0xFFFF,
}
public FORMAT_TAG format_tag;
public ushort channels;
public uint samplesPerSec;
public uint avgBytesPerSec;
public ushort blockAlign;
public ushort bitsPerSample;
public RiffSubchunk_fmt(byte[] data)
{
this.data = data;
tag = "fmt ";
BinaryReader br = new BinaryReader(new MemoryStream(data));
format_tag = (FORMAT_TAG)br.ReadUInt16();
channels = br.ReadUInt16();
samplesPerSec = br.ReadUInt32();
avgBytesPerSec = br.ReadUInt32();
blockAlign = br.ReadUInt16();
bitsPerSample = br.ReadUInt16();
}
public override void WriteStream(Stream s)
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
bw.Write((ushort)format_tag);
bw.Write(channels);
bw.Write(samplesPerSec);
bw.Write(avgBytesPerSec);
bw.Write(blockAlign);
bw.Write(bitsPerSample);
bw.Flush();
data = ms.ToArray();
base.WriteStream(s);
}
}
public class RiffContainer : RiffChunk
{
public RiffChunk GetSubchunk(string tag, string type)
{
foreach (RiffChunk rc in subchunks)
if (rc.tag == tag)
{
if(type == null) return rc;
RiffContainer cont = rc as RiffContainer;
if (cont != null && cont.type == type)
return rc;
}
return null;
}
public RiffContainer()
{
tag = "LIST";
}
public string type;
public List<RiffChunk> subchunks = new List<RiffChunk>();
public override void WriteStream(Stream s)
{
BinaryWriter bw = new BinaryWriter(s);
WriteTag(bw, tag);
long size = GetSize();
if (size > uint.MaxValue) throw new FormatException("File too big to write out");
bw.Write((uint)size);
WriteTag(bw, type);
bw.Flush();
foreach (RiffChunk rc in subchunks)
rc.WriteStream(s);
if (size % 2 != 0)
s.WriteByte(0);
}
public override long GetSize()
{
long len = 4;
foreach (RiffChunk rc in subchunks)
len += rc.GetSize() + 8;
return len;
}
public override RiffChunk Morph()
{
switch (type)
{
case "INFO": return new RiffContainer_INFO(this);
}
return this;
}
}
public class RiffContainer_INFO : RiffContainer
{
public Dictionary<string, string> dictionary = new Dictionary<string,string>();
public RiffContainer_INFO() { type = "INFO"; }
public RiffContainer_INFO(RiffContainer rc)
{
subchunks = rc.subchunks;
type = "INFO";
foreach (RiffChunk chunk in subchunks)
{
RiffSubchunk rsc = chunk as RiffSubchunk;
if (chunk == null)
throw new FormatException("Invalid subchunk of INFO list");
dictionary[rsc.tag] = System.Text.Encoding.ASCII.GetString(rsc.data);
}
}
private void Flush()
{
subchunks.Clear();
foreach (KeyValuePair<string, string> kvp in dictionary)
{
RiffSubchunk rs = new RiffSubchunk();
rs.tag = kvp.Key;
rs.data = System.Text.Encoding.ASCII.GetBytes(kvp.Value);
subchunks.Add(rs);
}
}
public override long GetSize()
{
Flush();
return base.GetSize();
}
public override void WriteStream(Stream s)
{
Flush();
base.WriteStream(s);
}
}
public RiffContainer riff;
private long readCounter;
private RiffChunk ReadChunk(BinaryReader br)
{
RiffChunk ret;
string tag = ReadTag(br); readCounter += 4;
uint size = br.ReadUInt32(); readCounter += 4;
if (size > int.MaxValue)
throw new FormatException("chunk too big");
if (tag == "RIFF" || tag == "LIST")
{
RiffContainer rc = new RiffContainer();
rc.tag = tag;
rc.type = ReadTag(br); readCounter += 4;
long readEnd = readCounter - 4 + size;
while (readEnd > readCounter)
rc.subchunks.Add(ReadChunk(br));
ret = rc.Morph();
}
else
{
RiffSubchunk rsc = new RiffSubchunk();
rsc.tag = tag;
rsc.data = br.ReadBytes((int)size); readCounter += size;
ret = rsc.Morph();
}
if (size % 2 != 0)
{
br.ReadByte();
readCounter += 1;
}
return ret;
}
public void WriteStream(Stream s)
{
riff.WriteStream(s);
}
public void LoadStream(Stream s)
{
readCounter = 0;
BinaryReader br = new BinaryReader(s);
RiffChunk chunk = ReadChunk(br);
if (chunk.tag != "RIFF") throw new FormatException("can't recognize riff chunk");
riff = (RiffContainer)chunk;
}
}