Closing of wave output devices is functional and terminates the sound thread
[reactos.git] / reactos / lib / drivers / sound / mmebuddy / wave / streaming.c
1 /*
2 * PROJECT: ReactOS Sound System "MME Buddy" Library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: lib/drivers/sound/mmebuddy/wave/streaming.c
5 *
6 * PURPOSE: Wave streaming
7 *
8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
9 */
10
11 #include <windows.h>
12 #include <mmsystem.h>
13 #include <mmddk.h>
14 #include <ntddsnd.h>
15 #include <mmebuddy.h>
16 #include <sndtypes.h>
17
18
19 /*
20 Restrain ourselves from flooding the kernel device!
21 */
22
23 #define SOUND_KERNEL_BUFFER_COUNT 10
24 #define SOUND_KERNEL_BUFFER_SIZE 16384
25
26
27 /*
28 DoWaveStreaming
29 Check if there is streaming to be done, and if so, do it.
30 */
31
32 VOID
33 DoWaveStreaming(
34 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
35 {
36 MMRESULT Result;
37 MMDEVICE_TYPE DeviceType;
38 PSOUND_DEVICE SoundDevice;
39 PMMFUNCTION_TABLE FunctionTable;
40 PWAVEHDR Header;
41 PWAVEHDR_EXTENSION HeaderExtension;
42
43 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
44 SND_ASSERT( MMSUCCESS(Result) );
45
46 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
47 SND_ASSERT( MMSUCCESS(Result) );
48
49 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
50 SND_ASSERT( MMSUCCESS(Result) );
51 SND_ASSERT( FunctionTable );
52 SND_ASSERT( FunctionTable->CommitWaveBuffer );
53
54 /* No point in doing anything if no resources available to use */
55 if ( SoundDeviceInstance->OutstandingBuffers >= SOUND_KERNEL_BUFFER_COUNT )
56 {
57 SND_TRACE(L"DoWaveStreaming: No available buffers to stream with - doing nothing\n");
58 return;
59 }
60
61 /* Is there any work to do? */
62 Header = SoundDeviceInstance->HeadWaveHeader;
63
64 if ( ! Header )
65 {
66 SND_TRACE(L"DoWaveStreaming: No work to do - doing nothing\n");
67 return;
68 }
69
70 while ( ( SoundDeviceInstance->OutstandingBuffers < SOUND_KERNEL_BUFFER_COUNT ) &&
71 ( Header ) )
72 {
73 HeaderExtension = (PWAVEHDR_EXTENSION) Header->reserved;
74 SND_ASSERT( HeaderExtension );
75
76 /* Can never be *above* the length */
77 SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength );
78
79 /* Is this header entirely committed? */
80 if ( HeaderExtension->BytesCommitted == Header->dwBufferLength )
81 {
82 {
83 /* Move on to the next header */
84 Header = Header->lpNext;
85 }
86 }
87 else
88 {
89 PSOUND_OVERLAPPED Overlap;
90 LPVOID OffsetPtr;
91 DWORD BytesRemaining, BytesToCommit;
92 BOOL OK;
93
94 /* Where within the header buffer to stream from */
95 OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted;
96
97 /* How much of this header has not been committed */
98 BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted;
99
100 /* We can commit anything up to the buffer size limit */
101 BytesToCommit = BytesRemaining > SOUND_KERNEL_BUFFER_SIZE ?
102 SOUND_KERNEL_BUFFER_SIZE :
103 BytesRemaining;
104
105 /* Should always have something to commit by this point */
106 SND_ASSERT( BytesToCommit > 0 );
107
108 /* We need a new overlapped info structure for each buffer */
109 Overlap = AllocateStruct(SOUND_OVERLAPPED);
110
111 if ( Overlap )
112 {
113 ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
114 Overlap->SoundDeviceInstance = SoundDeviceInstance;
115 Overlap->Header = Header;
116
117 /* Don't complete this header if it's part of a loop */
118 Overlap->PerformCompletion = TRUE;
119 // ( SoundDeviceInstance->LoopsRemaining > 0 );
120
121 /* Adjust the commit-related counters */
122 HeaderExtension->BytesCommitted += BytesToCommit;
123 ++ SoundDeviceInstance->OutstandingBuffers;
124
125 OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance,
126 OffsetPtr,
127 BytesToCommit,
128 Overlap,
129 CompleteIO));
130
131 if ( ! OK )
132 {
133 /* Clean-up and try again on the next iteration (is this OK?) */
134 SND_WARN(L"FAILED\n");
135
136 FreeMemory(Overlap);
137 HeaderExtension->BytesCommitted -= BytesToCommit;
138 -- SoundDeviceInstance->OutstandingBuffers;
139 }
140 }
141 }
142 }
143 }
144
145
146 /*
147 CompleteIO
148 An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
149 This will count up the number of bytes which have been dealt with,
150 and when the entire wave header has been dealt with, will call
151 CompleteWaveHeader to have the wave header returned to the client.
152
153 CommitWaveHeaderToKernelDevice
154 Sends portions of the buffer described by the wave header to a kernel
155 device. This must only be called from within the context of the sound
156 thread. The caller supplies either their own commit routine, or uses
157 WriteFileEx_Committer. The committer is called with portions of the
158 buffer specified in the wave header.
159
160 WriteFileEx_Committer
161 Commit buffers using the WriteFileEx API.
162 */
163
164 VOID CALLBACK
165 CompleteIO(
166 IN DWORD dwErrorCode,
167 IN DWORD dwNumberOfBytesTransferred,
168 IN LPOVERLAPPED lpOverlapped)
169 {
170 MMDEVICE_TYPE DeviceType;
171 PSOUND_DEVICE SoundDevice;
172 PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
173 PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
174 PWAVEHDR WaveHdr;
175 PWAVEHDR_EXTENSION HdrExtension;
176 MMRESULT Result;
177
178 WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
179 SND_ASSERT( WaveHdr );
180
181 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
182 SND_ASSERT( HdrExtension );
183
184 SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
185
186 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
187 SND_ASSERT( MMSUCCESS(Result) );
188
189 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
190 SND_ASSERT( MMSUCCESS(Result) );
191
192 HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
193 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
194
195 /* We have an available buffer now */
196 -- SoundDeviceInstance->OutstandingBuffers;
197
198 /* Did we finish a WAVEHDR and aren't looping? */
199 if ( HdrExtension->BytesCompleted == WaveHdr->dwBufferLength &&
200 SoundOverlapped->PerformCompletion )
201 {
202 CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
203 }
204
205 DoWaveStreaming(SoundDeviceInstance);
206
207 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
208
209 FreeMemory(lpOverlapped);
210 }
211
212 MMRESULT
213 WriteFileEx_Committer(
214 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
215 IN PVOID OffsetPtr,
216 IN DWORD Length,
217 IN PSOUND_OVERLAPPED Overlap,
218 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
219 {
220 HANDLE Handle;
221
222 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
223 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
224 VALIDATE_MMSYS_PARAMETER( Overlap );
225 VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
226
227 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
228
229 if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
230 {
231 // TODO
232 }
233
234 return MMSYSERR_NOERROR;
235 }
236
237
238 /*
239 Stream control functions
240 (External/internal thread pairs)
241
242 TODO - Move elsewhere as these shouldn't be wave specific!
243 */
244
245 MMRESULT
246 StopStreamingInSoundThread(
247 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
248 IN PVOID Parameter)
249 {
250 /* TODO */
251 return MMSYSERR_NOTSUPPORTED;
252 }
253
254 MMRESULT
255 StopStreaming(
256 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
257 {
258 MMRESULT Result;
259 PSOUND_DEVICE SoundDevice;
260 MMDEVICE_TYPE DeviceType;
261
262 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
263 return MMSYSERR_INVALHANDLE;
264
265 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
266 if ( ! MMSUCCESS(Result) )
267 return TranslateInternalMmResult(Result);
268
269 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
270 if ( ! MMSUCCESS(Result) )
271 return TranslateInternalMmResult(Result);
272
273 /* FIXME: What about wave input? */
274 if ( DeviceType != WAVE_OUT_DEVICE_TYPE )
275 return MMSYSERR_NOTSUPPORTED;
276
277 return CallSoundThread(SoundDeviceInstance,
278 StopStreamingInSoundThread,
279 NULL);
280 }