forked from dzamkov/MD-old
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AudioSource.cs
377 lines (345 loc) · 10.8 KB
/
AudioSource.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
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
using System;
using System.Collections.Generic;
using System.IO;
using OpenTK.Audio.OpenAL;
namespace MD
{
/// <summary>
/// A static one-dimensional sampling of audio.
/// </summary>
public abstract class AudioSource
{
/// <summary>
/// Reads some data into the specified output array.
/// </summary>
/// <param name="Position">The position in samples to start reading at.</param>
/// <param name="Amount">The amount of samples to read.</param>
/// <param name="Output">The byte array to read to.</param>
/// <param name="Offset">The place in the byte array to start reading to.</param>
public abstract void Read(int Position, int Amount, byte[] Output, int Offset);
/// <summary>
/// Reads some data (as double samples) into the specified output array. One double is used for each channel of a sample. Data ranges from -1.0 to 1.0.
/// </summary>
public virtual void ReadDouble(int Position, int Amount, double[] Output, int Offset)
{
switch (this.Format)
{
case ALFormat.Mono16:
byte[] data = new byte[Amount * 2];
this.Read(Position, Amount, data, 0);
for (int t = 0; t < Amount; t++)
{
Output[t + Offset] = (double)BitConverter.ToInt16(data, t * 2) / 32768.0;
}
return;
case ALFormat.Stereo16:
data = new byte[Amount * 4];
this.Read(Position, Amount, data, 0);
for (int t = 0; t < Amount * 2; t++)
{
Output[t + Offset] = (double)BitConverter.ToInt16(data, t * 2) / 32768.0;
}
return;
}
throw new NotImplementedException();
}
/// <summary>
/// Reads some data (as double samples) into the specified output array as in ReadDouble. The read array does not have to be completely in the
/// sample area. Samples requested that are outside the sampling area are left alone.
/// </summary>
public void ReadDoublePad(int Position, int Amount, double[] Output, int Offset)
{
int size = this.Size;
if (Position < 0)
{
Amount += Position;
Offset -= Position * this.Channels;
Position = 0;
}
if (Amount + Position >= size)
{
Amount = size - Position;
}
if (Amount > 0)
{
this.ReadDouble(Position, Amount, Output, Offset);
}
}
/// <summary>
/// Gets the size of the audio source in samples.
/// </summary>
public abstract int Size { get; }
/// <summary>
/// Gets the amount of samples in a second.
/// </summary>
public abstract int SampleRate { get; }
/// <summary>
/// Gets the format of the audio in the audio source.
/// </summary>
public abstract ALFormat Format { get; }
/// <summary>
/// Gets the amount of channels this audio source has.
/// </summary>
public int Channels
{
get
{
return GetChannels(this.Format);
}
}
/// <summary>
/// Gets the amount of bytes in a sample of this audio source.
/// </summary>
public int BytesPerSample
{
get
{
return GetBytesPerSample(this.Format);
}
}
/// <summary>
/// Gets an audio feed that plays this source.
/// </summary>
public AudioFeed Play
{
get
{
return new AudioSourcePlayer(this);
}
}
/// <summary>
/// Gets the amount of channels in a sample of the specified format.
/// </summary>
public static int GetChannels(ALFormat Format)
{
switch (Format)
{
case ALFormat.Stereo16: return 2;
case ALFormat.Stereo8: return 2;
case ALFormat.Mono16: return 1;
case ALFormat.Mono8: return 1;
}
throw new NotImplementedException();
}
/// <summary>
/// Reads silence to the specified byte array.
/// </summary>
public static void ReadSilence(ALFormat Format, int Amount, byte[] Data, int Offset)
{
switch (Format)
{
case ALFormat.Stereo16:
for (int t = 0; t < Amount * 4; t++)
{
Data[t + Offset] = 0;
}
break;
case ALFormat.Mono16:
for (int t = 0; t < Amount * 2; t++)
{
Data[t + Offset] = 0;
}
break;
}
}
/// <summary>
/// Gets the amount of bytes in a sample of the specified format.
/// </summary>
public static int GetBytesPerSample(ALFormat Format)
{
switch (Format)
{
case ALFormat.Stereo16: return 4;
case ALFormat.Stereo8: return 2;
case ALFormat.Mono16: return 2;
case ALFormat.Mono8: return 1;
}
throw new NotImplementedException();
}
}
/// <summary>
/// An audio source stored in RAM in chunks.
/// </summary>
public class MemoryAudioSource : AudioSource
{
public MemoryAudioSource(int SampleRate, ALFormat Format, List<byte[]> Chunks, int ChunkSize, int Size)
{
this._SampleRate = SampleRate;
this._Format = Format;
this._ChunkSize = ChunkSize;
this._Chunks = Chunks;
this._Size = Size;
}
public override void Read(int Position, int Amount, byte[] Output, int Offset)
{
int bps = this.BytesPerSample;
int cpos = Position % this._ChunkSize;
int chunk = Position / this._ChunkSize;
while (Amount > 0)
{
byte[] chunkdata = this._Chunks[chunk];
if (Amount + cpos > this._ChunkSize)
{
int dif = this._ChunkSize - cpos;
for (int t = 0; t < dif * bps; t++)
{
Output[Offset + t] = chunkdata[cpos * bps + t];
}
Amount -= dif;
Offset += dif * bps;
chunk++;
cpos = 0;
}
else
{
for (int t = 0; t < Amount * bps; t++)
{
Output[Offset + t] = chunkdata[cpos * bps + t];
}
break;
}
}
}
/// <summary>
/// Gets the amount of samples stored in a chunk.
/// </summary>
public int ChunkSize
{
get
{
return this._ChunkSize;
}
}
public override int Size
{
get
{
return this._Size;
}
}
public override int SampleRate
{
get
{
return this._SampleRate;
}
}
public override ALFormat Format
{
get
{
return this._Format;
}
}
private int _SampleRate;
private ALFormat _Format;
private int _Size;
private int _ChunkSize;
private List<byte[]> _Chunks;
}
/// <summary>
/// Pads (or truncates) another source to a certain length.
/// </summary>
public class PaddedSource : AudioSource
{
public PaddedSource(AudioSource Source, int PaddedSize)
{
this._Source = Source;
this._PaddedSize = PaddedSize;
}
public override void Read(int Position, int Amount, byte[] Output, int Offset)
{
int pad = (Position + Amount) - this._Source.Size;
if (pad > 0)
{
if (pad < Amount)
{
int amountread = Amount - pad;
this._Source.Read(Position, amountread, Output, Offset);
Offset += amountread;
}
AudioSource.ReadSilence(this._Source.Format, pad, Output, Offset);
}
else
{
this._Source.Read(Position, Amount, Output, Offset);
}
}
public override int SampleRate
{
get
{
return this._Source.SampleRate;
}
}
public override int Size
{
get
{
return this._PaddedSize;
}
}
public override ALFormat Format
{
get
{
return this._Source.Format;
}
}
private AudioSource _Source;
private int _PaddedSize;
}
/// <summary>
/// An audio feed that takes data from an audio source. The source will be looped.
/// </summary>
public class AudioSourcePlayer : AudioFeed
{
public AudioSourcePlayer(AudioSource Source)
{
this._Source = Source;
this._Location = 0;
}
public override int Read(int Amount, byte[] Output, int Offset)
{
int dif = this._Source.Size - this._Location;
if (dif < Amount)
{
this._Source.Read(this._Location, dif, Output, Offset);
this._Source.Read(0, Amount - dif, Output, Offset + dif);
this._Location = Amount - dif;
}
else
{
this._Source.Read(this._Location, Amount, Output, Offset);
this._Location += Amount;
}
return Amount;
}
public override int SampleRate
{
get
{
return this._Source.SampleRate;
}
}
public override ALFormat Format
{
get
{
return this._Source.Format;
}
}
/// <summary>
/// Gets the audio source that is being played.
/// </summary>
public AudioSource Source
{
get
{
return this._Source;
}
}
private int _Location;
private AudioSource _Source;
}
}