-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
WaveOutBuffer.cs
151 lines (136 loc) · 4.61 KB
/
WaveOutBuffer.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
using System;
using System.Runtime.InteropServices;
namespace NAudio.Wave
{
/// <summary>
/// A buffer of Wave samples for streaming to a Wave Output device
/// </summary>
public class WaveOutBuffer : IDisposable
{
private readonly WaveHeader header;
private readonly Int32 bufferSize; // allocated bytes, may not be the same as bytes read
private readonly byte[] buffer;
private readonly IWaveProvider waveStream;
private readonly object waveOutLock;
private GCHandle hBuffer;
private IntPtr hWaveOut;
private GCHandle hHeader; // we need to pin the header structure
private GCHandle hThis; // for the user callback
/// <summary>
/// creates a new wavebuffer
/// </summary>
/// <param name="hWaveOut">WaveOut device to write to</param>
/// <param name="bufferSize">Buffer size in bytes</param>
/// <param name="bufferFillStream">Stream to provide more data</param>
/// <param name="waveOutLock">Lock to protect WaveOut API's from being called on >1 thread</param>
public WaveOutBuffer(IntPtr hWaveOut, Int32 bufferSize, IWaveProvider bufferFillStream, object waveOutLock)
{
this.bufferSize = bufferSize;
buffer = new byte[bufferSize];
hBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
this.hWaveOut = hWaveOut;
waveStream = bufferFillStream;
this.waveOutLock = waveOutLock;
header = new WaveHeader();
hHeader = GCHandle.Alloc(header, GCHandleType.Pinned);
header.dataBuffer = hBuffer.AddrOfPinnedObject();
header.bufferLength = bufferSize;
header.loops = 1;
hThis = GCHandle.Alloc(this);
header.userData = (IntPtr)hThis;
lock (waveOutLock)
{
MmException.Try(WaveInterop.waveOutPrepareHeader(hWaveOut, header, Marshal.SizeOf(header)), "waveOutPrepareHeader");
}
}
#region Dispose Pattern
/// <summary>
/// Finalizer for this wave buffer
/// </summary>
~WaveOutBuffer()
{
Dispose(false);
System.Diagnostics.Debug.Assert(true, "WaveBuffer was not disposed");
}
/// <summary>
/// Releases resources held by this WaveBuffer
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
/// <summary>
/// Releases resources held by this WaveBuffer
/// </summary>
protected void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources
}
// free unmanaged resources
if (hHeader.IsAllocated)
hHeader.Free();
if (hBuffer.IsAllocated)
hBuffer.Free();
if (hThis.IsAllocated)
hThis.Free();
if (hWaveOut != IntPtr.Zero)
{
lock (waveOutLock)
{
WaveInterop.waveOutUnprepareHeader(hWaveOut, header, Marshal.SizeOf(header));
}
hWaveOut = IntPtr.Zero;
}
}
#endregion
/// this is called by the WAVE callback and should be used to refill the buffer
public bool OnDone()
{
int bytes;
lock (waveStream)
{
bytes = waveStream.Read(buffer, 0, buffer.Length);
}
if (bytes == 0)
{
return false;
}
for (int n = bytes; n < buffer.Length; n++)
{
buffer[n] = 0;
}
WriteToWaveOut();
return true;
}
/// <summary>
/// Whether the header's in queue flag is set
/// </summary>
public bool InQueue
{
get
{
return (header.flags & WaveHeaderFlags.InQueue) == WaveHeaderFlags.InQueue;
}
}
/// <summary>
/// The buffer size in bytes
/// </summary>
public int BufferSize => bufferSize;
private void WriteToWaveOut()
{
MmResult result;
lock (waveOutLock)
{
result = WaveInterop.waveOutWrite(hWaveOut, header, Marshal.SizeOf(header));
}
if (result != MmResult.NoError)
{
throw new MmException(result, "waveOutWrite");
}
GC.KeepAlive(this);
}
}
}