ReactOS  0.4.14-dev-55-g2da92ac
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  {
30  return MMSYSERR_INVALPARAM;
31  }
32 
33  if ( ! wave_header->lpData )
34  {
35  return MMSYSERR_INVALPARAM;
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");
49  return WAVERR_STILLPLAYING;
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 
93 VOID
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  {
103  DWORD message;
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 
135 DWORD
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  {
175  result = SetDeviceData(session_info->kernel_device_handle,
177  (PBYTE) session_info->thread.parameter,
178  sizeof(DWORD));
179  break;
180  }
181 
182  case WODM_GETPITCH :
183  {
184  result = GetDeviceData(session_info->kernel_device_handle,
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
199  result = GetDeviceData(session_info->kernel_device_handle,
201  (PBYTE) session_info->thread.parameter,);
202 #endif
203  break;
204  }
205 
206  case WODM_SETPLAYBACKRATE :
207  {
208  result = SetDeviceData(session_info->kernel_device_handle,
210  (PBYTE) session_info->thread.parameter,
211  sizeof(DWORD));
212  break;
213  }
214 
215  case WODM_GETPLAYBACKRATE :
216  {
217  result = GetDeviceData(session_info->kernel_device_handle,
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 
265 DWORD
266 WaveThread(LPVOID parameter)
267 {
269  SessionInfo* session_info = (SessionInfo*) parameter;
270  BOOL terminate = FALSE;
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 
319 DWORD
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 
334 DWORD
337  PVOID lpFormat)
338 {
339  /* TODO */
340  return WAVERR_BADFORMAT;
341 }
342 
343 
344 /*
345  Set the format to be used.
346 */
347 
348 BOOL
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 
371 DWORD
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 WAVERR_BADFORMAT
Definition: mmsystem.h:176
DWORD function
Definition: mmdrv.h:117
PWAVEHDR wave_queue
Definition: mmdrv.h:158
#define WOM_DONE
Definition: mmsystem.h:183
DWORD WINAPI WaitForSingleObjectEx(IN HANDLE hHandle, IN DWORD dwMilliseconds, IN BOOL bAlertable)
Definition: synch.c:94
#define WODM_PAUSE
Definition: mmddk.h:115
Definition: tftpd.h:59
#define TRUE
Definition: types.h:120
HANDLE ready_event
Definition: mmdrv.h:113
#define DWORD_PTR
Definition: treelist.c:76
MMRESULT QueueWaveBuffer(SessionInfo *session_info, LPWAVEHDR wave_header)
Definition: wave.c:21
BOOL GetDeviceData(LPD3D9_DEVICEDATA pDeviceData)
Definition: d3d9_caps.c:126
#define WODM_GETPLAYBACKRATE
Definition: mmddk.h:123
DeviceType
Definition: mmdrv.h:41
device_type
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: glext.h:5579
#define WODM_RESTART
Definition: mmddk.h:116
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:733
UINT MMRESULT
Definition: mmsystem.h:962
struct tWAVEFORMATEX WAVEFORMATEX
Definition: austream.idl:23
#define IOCTL_WAVE_SET_PITCH
Definition: mmdef.h:58
LPSTR lpData
Definition: mmsystem.h:1014
#define WODM_GETPITCH
Definition: mmddk.h:119
struct pcmwaveformat_tag PCMWAVEFORMAT
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
BOOL SetWaveFormat(HANDLE device_handle, PWAVEFORMATEX format)
Definition: wave.c:349
#define WAVE_FORMAT_PCM
Definition: constants.h:425
BOOL NotifyClient(SessionInfo *session_info, DWORD message, DWORD_PTR parameter1, DWORD_PTR parameter2)
Definition: mme.c:25
HANDLE WINAPI GetCurrentThread(VOID)
Definition: proc.c:1148
unsigned int BOOL
Definition: ntddk_ex.h:94
#define WAVERR_STILLPLAYING
Definition: mmsystem.h:177
DWORD dwFlags
Definition: mmsystem.h:1018
VOID PerformWaveIO(SessionInfo *session_info)
Definition: wave_io.c:39
MMRESULT result
Definition: mmdrv.h:120
smooth NULL
Definition: ftsmooth.c:416
void DPRINT(...)
Definition: polytest.cpp:61
#define WODM_GETVOLUME
Definition: mmddk.h:121
#define WAIT_IO_COMPLETION
Definition: winbase.h:392
BOOL WINAPI SetThreadPriority(IN HANDLE hThread, IN int nPriority)
Definition: thread.c:699
VOID ReturnCompletedBuffers(SessionInfo *session_info)
Definition: wave.c:94
#define DRVM_TERMINATE
Definition: mmdrv.h:86
void MSVCRT() terminate()
GLsizeiptr size
Definition: glext.h:5919
#define MMSYSERR_NOERROR
Definition: mmsystem.h:96
#define MMSYSERR_ERROR
Definition: mmsystem.h:97
#define IOCTL_WAVE_SET_FORMAT
Definition: mmdef.h:51
DeviceType device_type
Definition: mmdrv.h:133
#define WODM_WRITE
Definition: mmddk.h:114
unsigned long DWORD
Definition: ntddk_ex.h:95
#define WODM_SETPITCH
Definition: mmddk.h:120
#define IOCTL_WAVE_GET_VOLUME
Definition: mmdef.h:57
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
#define THREAD_PRIORITY_TIME_CRITICAL
Definition: winbase.h:278
#define WODM_SETVOLUME
Definition: mmddk.h:122
DWORD ProcessSessionThreadRequest(SessionInfo *session_info)
Definition: wave.c:136
HANDLE kernel_device_handle
Definition: mmdrv.h:136
LPCWSTR lpFormat
Definition: trayclock.cpp:32
uint32_t DWORD_PTR
Definition: typedefs.h:63
#define WODM_CLOSE
Definition: mmddk.h:111
struct wavehdr_tag * lpNext
Definition: mmsystem.h:1020
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 MMSYSERR_INVALPARAM
Definition: mmsystem.h:107
DWORD WaveThread(LPVOID parameter)
Definition: wave.c:266
#define WHDR_INQUEUE
Definition: mmsystem.h:197
ThreadInfo thread
Definition: mmdrv.h:172
#define IOCTL_WAVE_SET_PLAYBACK_RATE
Definition: mmdef.h:60
#define WHDR_PREPARED
Definition: mmsystem.h:194
#define IOCTL_WAVE_GET_PLAYBACK_RATE
Definition: mmdef.h:61
#define IOCTL_WAVE_GET_PITCH
Definition: mmdef.h:59
MMRESULT SetDeviceData(HANDLE device_handle, DWORD ioctl, PBYTE input_buffer, DWORD buffer_size)
Definition: kernel.c:139
#define WAVERR_UNPREPARED
Definition: mmsystem.h:178
#define WODM_SETPLAYBACKRATE
Definition: mmddk.h:124
DWORD GetWaveFormatExSize(PWAVEFORMATEX format)
Definition: wave.c:320
#define WODM_GETPOS
Definition: mmddk.h:118
#define WHDR_DONE
Definition: mmsystem.h:193
DWORD_PTR reserved
Definition: mmsystem.h:1021
#define WIM_DATA
Definition: mmsystem.h:186
MMRESULT CallSessionThread(SessionInfo *session_info, ThreadFunction function, PVOID thread_parameter)
Definition: session.c:219
HANDLE go_event
Definition: mmdrv.h:114
DWORD buffer_position
Definition: mmdrv.h:166
DWORD WriteWaveBuffer(DWORD_PTR private_handle, PWAVEHDR wave_header, DWORD wave_header_size)
Definition: wave.c:372
#define INFINITE
Definition: serial.h:102
DWORD QueryWaveFormat(DeviceType device_type, PVOID lpFormat)
Definition: wave.c:335
GLuint64EXT * result
Definition: glext.h:11304
BYTE * PBYTE
Definition: pedump.c:66
struct CFHEADER header
Definition: fdi.c:109
DWORD dwBytesRecorded
Definition: mmsystem.h:1016
PVOID parameter
Definition: mmdrv.h:118
#define WODM_RESET
Definition: mmddk.h:117