-
Notifications
You must be signed in to change notification settings - Fork 0
/
FlacFile.cs
335 lines (292 loc) · 12.2 KB
/
FlacFile.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
#define DIAGNOSTICS
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NAudio.Wave;
namespace NAudio.Flac
{
/// <summary>
/// Provides a decoder for decoding flac (Free Lostless Audio Codec) data.
/// </summary>
public class FlacReader : WaveStream, IDisposable, ISampleProvider, IWaveProvider
{
private readonly Stream _stream;
private readonly WaveFormat _waveFormat;
private readonly FlacMetadataStreamInfo _streamInfo;
private readonly FlacPreScan _scan;
private readonly object _bufferLock = new object();
//overflow:
private byte[] _overflowBuffer;
private int _overflowCount;
private int _overflowOffset;
private int _frameIndex;
/// <summary>
/// Gets a list with all found metadata fields.
/// </summary>
public List<FlacMetadata> Metadata { get; protected set; }
/// <summary>
/// Gets the output <see cref="CSCore.WaveFormat" /> of the decoder.
/// </summary>
public override WaveFormat WaveFormat
{
get { return _waveFormat; }
}
/// <summary>
/// Gets a value which indicates whether the seeking is supported. True means that seeking is supported; False means
/// that seeking is not supported.
/// </summary>
public override bool CanSeek
{
get { return _scan != null; }
}
private FlacFrame _frame;
private FlacFrame Frame
{
get { return _frame ?? (_frame = FlacFrame.FromStream(_stream, _streamInfo)); }
}
/// <summary>
/// Initializes a new instance of the <see cref="FlacReader" /> class.
/// </summary>
/// <param name="fileName">Filename which of a flac file which should be decoded.</param>
public FlacReader(string fileName)
: this(File.OpenRead(fileName))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FlacReader" /> class.
/// </summary>
/// <param name="stream">Stream which contains flac data which should be decoded.</param>
public FlacReader(Stream stream)
: this(stream, FlacPreScanMethodMode.Default)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FlacReader" /> class.
/// </summary>
/// <param name="stream">Stream which contains flac data which should be decoded.</param>
/// <param name="scanFlag">Scan mode which defines how to scan the flac data for frames.</param>
public FlacReader(Stream stream, FlacPreScanMethodMode scanFlag)
: this(stream, scanFlag, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FlacReader" /> class.
/// </summary>
/// <param name="stream">Stream which contains flac data which should be decoded.</param>
/// <param name="scanFlag">Scan mode which defines how to scan the flac data for frames.</param>
/// <param name="onscanFinished">
/// Callback which gets called when the pre scan processes finished. Should be used if the
/// <paramref name="scanFlag" /> argument is set the <see cref="FlacPreScanMethodMode.Async" />.
/// </param>
public FlacReader(Stream stream, FlacPreScanMethodMode scanFlag,
Action<FlacPreScanFinishedEventArgs> onscanFinished)
{
if (stream == null)
throw new ArgumentNullException();
if (!stream.CanRead)
throw new ArgumentException("Stream is not readable.", "stream");
_stream = stream;
//skip ID3v2
NAudio.Flac.ID3v2.SkipTag(stream);
//read fLaC sync
var beginSync = new byte[4];
int read = stream.Read(beginSync, 0, beginSync.Length);
if (read < beginSync.Length)
throw new EndOfStreamException("Can not read \"fLaC\" sync.");
if (beginSync[0] == 0x66 && beginSync[1] == 0x4C && //Check for 'fLaC' signature
beginSync[2] == 0x61 && beginSync[3] == 0x43)
{
//read metadata
List<FlacMetadata> metadata = FlacMetadata.ReadAllMetadataFromStream(stream);
Metadata = metadata;
if (metadata == null || metadata.Count <= 0)
throw new FlacException("No Metadata found.", FlacLayer.Metadata);
var streamInfo =
metadata.First(x => x.MetaDataType == FlacMetaDataType.StreamInfo) as FlacMetadataStreamInfo;
if (streamInfo == null)
throw new FlacException("No StreamInfo-Metadata found.", FlacLayer.Metadata);
_streamInfo = streamInfo;
_waveFormat = new WaveFormat(streamInfo.SampleRate, (short) streamInfo.BitsPerSample,
(short) streamInfo.Channels);
Debug.WriteLine("Flac StreamInfo found -> WaveFormat: " + _waveFormat);
Debug.WriteLine("Flac-File-Metadata read.");
}
else
throw new FlacException("Invalid Flac-File. \"fLaC\" Sync not found.", FlacLayer.Top);
//prescan stream
if (scanFlag != FlacPreScanMethodMode.None)
{
var scan = new FlacPreScan(stream);
scan.ScanFinished += (s, e) =>
{
if (onscanFinished != null)
onscanFinished(e);
};
scan.ScanStream(_streamInfo, scanFlag);
_scan = scan;
}
}
/// <summary>
/// Reads a sequence of bytes from the <see cref="FlacReader" /> and advances the position within the stream by the
/// number of bytes read.
/// </summary>
/// <param name="buffer">
/// An array of bytes. When this method returns, the <paramref name="buffer" /> contains the specified
/// byte array with the values between <paramref name="offset" /> and (<paramref name="offset" /> +
/// <paramref name="count" /> - 1) replaced by the bytes read from the current source.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in the <paramref name="buffer" /> at which to begin storing the data
/// read from the current stream.
/// </param>
/// <param name="count">The maximum number of bytes to read from the current source.</param>
/// <returns>The total number of bytes read into the buffer.</returns>
public override int Read(byte[] buffer, int offset, int count)
{
int read = 0;
count -= (count % WaveFormat.BlockAlign);
lock (_bufferLock)
{
read += GetOverflows(buffer, ref offset, count);
while (read < count)
{
FlacFrame frame = Frame;
if (frame == null)
return 0;
while (!frame.NextFrame())
{
if (CanSeek) //go to next frame
{
if (++_frameIndex >= _scan.Frames.Count)
return 0;
_stream.Position = _scan.Frames[_frameIndex].StreamOffset;
}
}
_frameIndex++;
int bufferlength = frame.GetBuffer(ref _overflowBuffer, 0);
int bytesToCopy = Math.Min(count - read, bufferlength);
Array.Copy(_overflowBuffer, 0, buffer, offset, bytesToCopy);
read += bytesToCopy;
offset += bytesToCopy;
_overflowCount = ((bufferlength > bytesToCopy) ? (bufferlength - bytesToCopy) : 0);
_overflowOffset = ((bufferlength > bytesToCopy) ? (bytesToCopy) : 0);
}
}
#if DIAGNOSTICS
_position += read;
#endif
return read;
}
private int GetOverflows(byte[] buffer, ref int offset, int count)
{
if (_overflowCount != 0 && _overflowBuffer != null && count > 0)
{
int bytesToCopy = Math.Min(count, _overflowCount);
Array.Copy(_overflowBuffer, _overflowOffset, buffer, offset, bytesToCopy);
_overflowCount -= bytesToCopy;
_overflowOffset += bytesToCopy;
offset += bytesToCopy;
return bytesToCopy;
}
return 0;
}
#if DIAGNOSTICS
private long _position;
#endif
/// <summary>
/// Gets or sets the position of the <see cref="FlacReader" /> in bytes.
/// </summary>
public override long Position
{
get
{
if (!CanSeek)
return 0;
lock (_bufferLock)
{
#if !DIAGNOSTICS
if (_frameIndex == _scan.Frames.Count)
return Length;
return _scan.Frames[_frameIndex].SampleOffset * WaveFormat.BlockAlign + _overflowOffset;
#else
return _position;
#endif
}
}
set
{
if (!CanSeek)
return;
lock (_bufferLock)
{
value = Math.Min(value, Length);
value = value > 0 ? value : 0;
for (int i = 0; i < _scan.Frames.Count; i++)
{
if ((value / WaveFormat.BlockAlign) <= _scan.Frames[i].SampleOffset)
{
_stream.Position = _scan.Frames[i].StreamOffset;
_frameIndex = i;
if (_stream.Position >= _stream.Length)
throw new EndOfStreamException("Stream got EOF.");
#if DIAGNOSTICS
_position = _scan.Frames[i].SampleOffset * WaveFormat.BlockAlign;
#endif
_overflowCount = 0;
_overflowOffset = 0;
break;
}
}
}
}
}
/// <summary>
/// Gets the length of the <see cref="FlacReader" /> in bytes.
/// </summary>
public override long Length
{
get {return (long) _scan.TotalSamples * WaveFormat.BlockAlign;}
}
/// <summary>
/// Disposes the <see cref="FlacReader" /> instance and disposes the underlying stream.
/// </summary>
public new void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the <see cref="FlacReader" /> instance and disposes the underlying stream.
/// </summary>
/// <param name="disposing">
/// True to release both managed and unmanaged resources; false to release only unmanaged
/// resources.
/// </param>
protected override void Dispose(bool disposing)
{
lock (_bufferLock)
{
if (_frame != null)
{
_frame.FreeBuffers();
_frame = null;
}
if (_stream != null)
_stream.Dispose();
}
}
/// <summary>
/// Destructor which calls the <see cref="Dispose(bool)" /> method.
/// </summary>
~FlacReader()
{
Dispose(false);
}
public int Read(float[] buffer, int offset, int count)
{
return -1;
}
}
}