ReactOS 0.4.15-dev-7674-gc0b4db1
wave.c
Go to the documentation of this file.
1/*
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS Multimedia
5 * FILE: dll/win32/mmdrv/wave.c
6 * PURPOSE: Multimedia User Mode Driver (Wave Audio)
7 * PROGRAMMER: Andrew Greenwood
8 * UPDATE HISTORY:
9 * Jan 30, 2004: Imported into ReactOS tree
10 * Jan 14, 2007: Rewritten and tidied up
11 */
12
13#include "mmdrv.h"
14
15#define NDEBUG
16#include <debug.h>
17
18#define MAX_WAVE_BUFFER_SIZE 65536
19
22 SessionInfo* session_info,
23 LPWAVEHDR wave_header)
24{
25 PWAVEHDR queue_node, previous_node;
26 DPRINT("Queueing wave buffer\n");
27
28 if ( ! wave_header )
29 {
31 }
32
33 if ( ! wave_header->lpData )
34 {
36 }
37
38 /* Headers must be prepared first */
39 if ( ! ( wave_header->dwFlags & WHDR_PREPARED ) )
40 {
41 DPRINT("I was given a header which hasn't been prepared yet!\n");
42 return WAVERR_UNPREPARED;
43 }
44
45 /* ...and they must not already be in the playing queue! */
46 if ( wave_header->dwFlags & WHDR_INQUEUE )
47 {
48 DPRINT("I was given a header for a buffer which is already playing\n");
50 }
51
52 /* Initialize */
53 wave_header->dwBytesRecorded = 0;
54
55 /* Clear the DONE bit, and mark the buffer as queued */
56 wave_header->dwFlags &= ~WHDR_DONE;
57 wave_header->dwFlags |= WHDR_INQUEUE;
58
59 /* Save our handle in the header */
60 wave_header->reserved = (DWORD_PTR) session_info;
61
62 /* Locate the end of the queue */
63 previous_node = NULL;
64 queue_node = session_info->wave_queue;
65
66 while ( queue_node )
67 {
68 previous_node = queue_node;
69 queue_node = queue_node->lpNext;
70 }
71
72 /* Go back a step to obtain the previous node (non-NULL) */
73 queue_node = previous_node;
74
75 /* Append our buffer here, and terminate the queue */
76 queue_node->lpNext = wave_header;
77 wave_header->lpNext = NULL;
78
79 /* When no buffers are playing there's no play queue so we start one */
80#if 0
81 if ( ! session_info->next_buffer )
82 {
83 session_info->buffer_position = 0;
84 session_info->next_buffer = wave_header;
85 }
86#endif
87
88 /* Pass to the driver - happens automatically during playback */
89// return PerformWaveIO(session_info);
90 return MMSYSERR_NOERROR;
91}
92
93VOID
95{
97
98 /* Set the current header and test to ensure it's not NULL */
99 while ( ( header = session_info->wave_queue ) )
100 {
101 if ( header->dwFlags & WHDR_DONE )
102 {
104
105 /* Mark as done, and unqueued */
106 header->dwFlags &= ~WHDR_INQUEUE;
107 header->dwFlags |= WHDR_DONE;
108
109 /* Trim it from the start of the queue */
110 session_info->wave_queue = header->lpNext;
111
112 /* Choose appropriate notification */
113 message = (session_info->device_type == WaveOutDevice) ? WOM_DONE :
114 WIM_DATA;
115
116 DPRINT("Notifying client that buffer 0x%p is done\n", header);
117
118 /* Notify the client */
119 NotifyClient(session_info, message, (DWORD_PTR) header, 0);
120 }
121 }
122
123 /* TODO: Perform I/O as a new buffer may have arrived */
124}
125
126
127/*
128 Each thread function/request is packed into the SessionInfo structure
129 using a function ID and a parameter (in some cases.) When the function
130 completes, the function code is set to an "invalid" value. This is,
131 effectively, a hub for operations where sound driver I/O is concerned.
132 It handles MME message codes so is a form of deferred wodMessage().
133*/
134
135DWORD
137{
139
140 switch ( session_info->thread.function )
141 {
142 case WODM_WRITE :
143 {
144 result = QueueWaveBuffer(session_info,
145 (LPWAVEHDR) session_info->thread.parameter);
146 break;
147 }
148
149 case WODM_RESET :
150 {
151 /* TODO */
152 break;
153 }
154
155 case WODM_PAUSE :
156 {
157 /* TODO */
158 break;
159 }
160
161 case WODM_RESTART :
162 {
163 /* TODO */
164 break;
165 }
166
167 case WODM_GETPOS :
168 {
169 /* TODO */
170 break;
171 }
172
173 case WODM_SETPITCH :
174 {
177 (PBYTE) session_info->thread.parameter,
178 sizeof(DWORD));
179 break;
180 }
181
182 case WODM_GETPITCH :
183 {
186 (PBYTE) session_info->thread.parameter,
187 sizeof(DWORD));
188 break;
189 }
190
191 case WODM_SETVOLUME :
192 {
193 break;
194 }
195
196 case WODM_GETVOLUME :
197 {
198#if 0
201 (PBYTE) session_info->thread.parameter,);
202#endif
203 break;
204 }
205
207 {
210 (PBYTE) session_info->thread.parameter,
211 sizeof(DWORD));
212 break;
213 }
214
216 {
219 (PBYTE) session_info->thread.parameter,
220 sizeof(DWORD));
221 break;
222 }
223
224 case WODM_CLOSE :
225 {
226 DPRINT("Thread was asked if OK to close device\n");
227
228 if ( session_info->wave_queue != NULL )
230 else
232
233 break;
234 }
235
236 case DRVM_TERMINATE :
237 {
238 DPRINT("Terminating thread...\n");
240 break;
241 }
242
243 default :
244 {
245 DPRINT("INVALID FUNCTION\n");
247 break;
248 }
249 }
250
251 /* We're done with the function now */
252
253 return result;
254}
255
256
257/*
258 The wave "session". This starts, sets itself as high priority, then waits
259 for the "go" event. When this occurs, it processes the requested function,
260 tidies up any buffers that have finished playing, sends new buffers to the
261 sound driver, then continues handing finished buffers back to the calling
262 application until it's asked to do something else.
263*/
264
265DWORD
267{
269 SessionInfo* session_info = (SessionInfo*) parameter;
271
272 /* All your CPU time are belong to us */
274
275 DPRINT("Wave processing thread setting ready state\n");
276
277 SetEvent(session_info->thread.ready_event);
278
279 while ( ! terminate )
280 {
281 /* Wait for GO event, or IO completion notification */
282 while ( WaitForSingleObjectEx(session_info->thread.go_event,
283 INFINITE,
285 {
286 /* A buffer has been finished with - pass back to the client */
287 ReturnCompletedBuffers(session_info);
288 }
289
290 DPRINT("Wave processing thread woken up\n");
291
292 /* Set the terminate flag if that's what the caller wants */
293 terminate = (session_info->thread.function == DRVM_TERMINATE);
294
295 /* Process the request */
296 DPRINT("Processing thread request\n");
297 result = ProcessSessionThreadRequest(session_info);
298
299 /* Store the result code */
300 session_info->thread.result = result;
301
302 /* Submit new buffers and continue existing ones */
303 DPRINT("Performing wave I/O\n");
304 PerformWaveIO(session_info);
305
306 /* Now we're ready for more action */
307 DPRINT("Wave processing thread sleeping\n");
308 SetEvent(session_info->thread.ready_event);
309 }
310
311 return 0;
312}
313
314
315/*
316 Convenience function for calculating the size of the WAVEFORMATEX struct.
317*/
318
319DWORD
321{
322 if ( format->wFormatTag == WAVE_FORMAT_PCM )
323 return sizeof(PCMWAVEFORMAT);
324 else
325 return sizeof(WAVEFORMATEX) + format->cbSize;
326}
327
328
329/*
330 Query if the driver/device is capable of handling a format. This is called
331 if the device is a wave device, and the QUERYFORMAT flag is set.
332*/
333
334DWORD
338{
339 /* TODO */
340 return WAVERR_BADFORMAT;
341}
342
343
344/*
345 Set the format to be used.
346*/
347
348BOOL
350 HANDLE device_handle,
352{
353 DWORD bytes_returned;
354 DWORD size;
355
357
358 DPRINT("SetWaveFormat\n");
359
360 return DeviceIoControl(device_handle,
362 (PVOID) format,
363 size,
364 NULL,
365 0,
366 &bytes_returned,
367 NULL);
368}
369
370
371DWORD
373 DWORD_PTR private_handle,
374 PWAVEHDR wave_header,
375 DWORD wave_header_size)
376{
377 SessionInfo* session_info = (SessionInfo*) private_handle;
378 ASSERT(session_info);
379
380 /* Let the processing thread know that it has work to do */
381 return CallSessionThread(session_info, WODM_WRITE, wave_header);
382}
#define WAVE_FORMAT_PCM
Definition: constants.h:425
BOOL GetDeviceData(LPD3D9_DEVICEDATA pDeviceData)
Definition: d3d9_caps.c:126
device_type
BOOL WINAPI DeviceIoControl(IN HANDLE hDevice, IN DWORD dwIoControlCode, IN LPVOID lpInBuffer OPTIONAL, IN DWORD nInBufferSize OPTIONAL, OUT LPVOID lpOutBuffer OPTIONAL, IN DWORD nOutBufferSize OPTIONAL, OUT LPDWORD lpBytesReturned OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: deviceio.c:136
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
BOOL WINAPI SetThreadPriority(IN HANDLE hThread, IN int nPriority)
Definition: thread.c:700
MMRESULT SetDeviceData(HANDLE device_handle, DWORD ioctl, PBYTE input_buffer, DWORD buffer_size)
Definition: kernel.c:139
#define WODM_SETVOLUME
Definition: mmddk.h:122
#define WODM_RESET
Definition: mmddk.h:117
#define WODM_GETVOLUME
Definition: mmddk.h:121
#define WODM_WRITE
Definition: mmddk.h:114
#define WODM_RESTART
Definition: mmddk.h:116
#define WODM_GETPITCH
Definition: mmddk.h:119
#define WODM_SETPLAYBACKRATE
Definition: mmddk.h:124
#define WODM_GETPOS
Definition: mmddk.h:118
#define WODM_SETPITCH
Definition: mmddk.h:120
#define WODM_GETPLAYBACKRATE
Definition: mmddk.h:123
#define WODM_CLOSE
Definition: mmddk.h:111
#define WODM_PAUSE
Definition: mmddk.h:115
MMRESULT QueueWaveBuffer(SessionInfo *session_info, LPWAVEHDR wave_header)
Definition: wave.c:21
DWORD GetWaveFormatExSize(PWAVEFORMATEX format)
Definition: wave.c:320
DWORD ProcessSessionThreadRequest(SessionInfo *session_info)
Definition: wave.c:136
BOOL SetWaveFormat(HANDLE device_handle, PWAVEFORMATEX format)
Definition: wave.c:349
VOID ReturnCompletedBuffers(SessionInfo *session_info)
Definition: wave.c:94
DWORD QueryWaveFormat(DeviceType device_type, PVOID lpFormat)
Definition: wave.c:335
DWORD WriteWaveBuffer(DWORD_PTR private_handle, PWAVEHDR wave_header, DWORD wave_header_size)
Definition: wave.c:372
DWORD WaveThread(LPVOID parameter)
Definition: wave.c:266
#define INFINITE
Definition: serial.h:102
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
GLsizeiptr size
Definition: glext.h:5919
GLuint64EXT * result
Definition: glext.h:11304
void MSVCRT() terminate()
#define IOCTL_WAVE_SET_FORMAT
Definition: mmdef.h:51
#define IOCTL_WAVE_SET_PLAYBACK_RATE
Definition: mmdef.h:60
#define IOCTL_WAVE_GET_PITCH
Definition: mmdef.h:59
#define IOCTL_WAVE_GET_VOLUME
Definition: mmdef.h:57
#define IOCTL_WAVE_SET_PITCH
Definition: mmdef.h:58
#define IOCTL_WAVE_GET_PLAYBACK_RATE
Definition: mmdef.h:61
MMRESULT CallSessionThread(SessionInfo *session_info, ThreadFunction function, PVOID thread_parameter)
Definition: session.c:219
DeviceType
Definition: mmdrv.h:42
@ WaveOutDevice
Definition: mmdrv.h:43
VOID PerformWaveIO(SessionInfo *session_info)
Definition: wave_io.c:39
#define DRVM_TERMINATE
Definition: mmdrv.h:86
BOOL NotifyClient(SessionInfo *session_info, DWORD message, DWORD_PTR parameter1, DWORD_PTR parameter2)
Definition: mme.c:25
#define WAVERR_STILLPLAYING
Definition: mmsystem.h:177
#define WOM_DONE
Definition: mmsystem.h:183
UINT MMRESULT
Definition: mmsystem.h:962
#define WIM_DATA
Definition: mmsystem.h:186
#define WHDR_INQUEUE
Definition: mmsystem.h:197
struct pcmwaveformat_tag PCMWAVEFORMAT
#define MMSYSERR_INVALPARAM
Definition: mmsystem.h:107
#define MMSYSERR_NOERROR
Definition: mmsystem.h:96
#define WAVERR_BADFORMAT
Definition: mmsystem.h:176
#define MMSYSERR_ERROR
Definition: mmsystem.h:97
#define WHDR_PREPARED
Definition: mmsystem.h:194
#define WAVERR_UNPREPARED
Definition: mmsystem.h:178
#define WHDR_DONE
Definition: mmsystem.h:193
#define ASSERT(a)
Definition: mode.c:44
BYTE * PBYTE
Definition: pedump.c:66
#define DPRINT
Definition: sndvol32.h:71
ThreadInfo thread
Definition: mmdrv.h:172
DeviceType device_type
Definition: mmdrv.h:133
HANDLE kernel_device_handle
Definition: mmdrv.h:136
DWORD buffer_position
Definition: mmdrv.h:166
PWAVEHDR wave_queue
Definition: mmdrv.h:158
MMRESULT result
Definition: mmdrv.h:120
HANDLE ready_event
Definition: mmdrv.h:113
PVOID parameter
Definition: mmdrv.h:118
DWORD function
Definition: mmdrv.h:117
HANDLE go_event
Definition: mmdrv.h:114
Definition: tftpd.h:60
DWORD dwFlags
Definition: mmsystem.h:1018
DWORD dwBytesRecorded
Definition: mmsystem.h:1016
LPSTR lpData
Definition: mmsystem.h:1014
DWORD_PTR reserved
Definition: mmsystem.h:1021
struct wavehdr_tag * lpNext
Definition: mmsystem.h:1020
DWORD WINAPI WaitForSingleObjectEx(IN HANDLE hHandle, IN DWORD dwMilliseconds, IN BOOL bAlertable)
Definition: synch.c:94
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:733
LPCWSTR lpFormat
Definition: trayclock.cpp:32
#define DWORD_PTR
Definition: treelist.c:76
uint32_t DWORD_PTR
Definition: typedefs.h:65
HANDLE WINAPI GetCurrentThread(void)
Definition: proc.c:1148
#define WAIT_IO_COMPLETION
Definition: winbase.h:411
#define THREAD_PRIORITY_TIME_CRITICAL
Definition: winbase.h:281