winealsa.drv: Implement IAudioSessionControl::GetState.
[wine/multimedia.git] / dlls / winealsa.drv / wavein.c
blob86e8c75c0dfe0827036163af425bd614a324f87e
1 /*
2 * Sample Wine Driver for Advanced Linux Sound System (ALSA)
3 * Based on version <final> of the ALSA API
5 * Copyright 2002 Eric Pouech
6 * 2002 Marco Pietrobono
7 * 2003 Christian Costa : WaveIn support
8 * 2006-2007 Maarten Lankhorst
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 /*======================================================================*
26 * Low level WAVE IN implementation *
27 *======================================================================*/
29 #include "config.h"
30 #include "wine/port.h"
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #include <errno.h>
40 #include <limits.h>
41 #include <fcntl.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 # include <sys/ioctl.h>
44 #endif
45 #ifdef HAVE_SYS_MMAN_H
46 # include <sys/mman.h>
47 #endif
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wingdi.h"
51 #include "winuser.h"
52 #include "winnls.h"
53 #include "mmddk.h"
54 #include "mmreg.h"
55 #include "dsound.h"
56 #include "dsdriver.h"
57 #include "ks.h"
58 #include "ksmedia.h"
60 #include "alsa.h"
61 #include "wine/library.h"
62 #include "wine/unicode.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(wave);
67 WINE_WAVEDEV *WInDev;
68 DWORD ALSA_WidNumMallocedDevs;
69 DWORD ALSA_WidNumDevs;
71 /**************************************************************************
72 * widNotifyClient [internal]
74 static void widNotifyClient(WINE_WAVEDEV* wwi, WORD wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
76 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
78 switch (wMsg) {
79 case WIM_OPEN:
80 case WIM_CLOSE:
81 case WIM_DATA:
82 DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags, (HDRVR)wwi->waveDesc.hWave,
83 wMsg, wwi->waveDesc.dwInstance, dwParam1, dwParam2);
84 break;
85 default:
86 FIXME("Unknown callback message %u\n", wMsg);
90 /**************************************************************************
91 * widGetDevCaps [internal]
93 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSW lpCaps, DWORD dwSize)
95 TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
97 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
99 if (wDevID >= ALSA_WidNumDevs) {
100 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
101 return MMSYSERR_BADDEVICEID;
104 memcpy(lpCaps, &WInDev[wDevID].incaps, min(dwSize, sizeof(*lpCaps)));
105 return MMSYSERR_NOERROR;
108 /**************************************************************************
109 * widRecorder_ReadHeaders [internal]
111 static void widRecorder_ReadHeaders(WINE_WAVEDEV * wwi)
113 enum win_wm_message tmp_msg;
114 DWORD_PTR tmp_param;
115 HANDLE tmp_ev;
116 WAVEHDR* lpWaveHdr;
118 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
119 if (tmp_msg == WINE_WM_HEADER) {
120 LPWAVEHDR* wh;
121 lpWaveHdr = (LPWAVEHDR)tmp_param;
122 lpWaveHdr->lpNext = 0;
124 if (wwi->lpQueuePtr == 0)
125 wwi->lpQueuePtr = lpWaveHdr;
126 else {
127 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
128 *wh = lpWaveHdr;
130 } else {
131 ERR("should only have headers left\n");
136 /**************************************************************************
137 * widRecorder [internal]
139 static DWORD CALLBACK widRecorder(LPVOID pmt)
141 WORD uDevID = (DWORD_PTR)pmt;
142 WINE_WAVEDEV* wwi = &WInDev[uDevID];
143 WAVEHDR* lpWaveHdr;
144 DWORD dwSleepTime;
145 DWORD bytesRead;
146 enum win_wm_message msg;
147 DWORD_PTR param;
148 HANDLE ev;
150 wwi->state = WINE_WS_STOPPED;
151 InterlockedExchange((LONG*)&wwi->dwTotalRecorded, 0);
152 wwi->lpQueuePtr = NULL;
154 SetEvent(wwi->hStartUpEvent);
156 /* make sleep time to be # of ms to output a period */
157 dwSleepTime = (wwi->dwPeriodSize * 1000) / wwi->format.Format.nAvgBytesPerSec;
158 TRACE("sleeptime=%d ms, total buffer length=%d ms (%d bytes)\n", dwSleepTime, wwi->dwBufferSize * 1000 / wwi->format.Format.nAvgBytesPerSec, wwi->dwBufferSize);
160 for (;;) {
161 /* wait for dwSleepTime or an event in thread's queue */
162 if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
164 DWORD frames;
165 DWORD bytes;
166 DWORD read;
168 lpWaveHdr = wwi->lpQueuePtr;
169 /* read all the fragments accumulated so far */
170 frames = snd_pcm_avail_update(wwi->pcm);
171 bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames);
173 TRACE("frames = %d bytes = %d state=%d\n", frames, bytes, snd_pcm_state(wwi->pcm));
174 if (snd_pcm_state(wwi->pcm) == SND_PCM_STATE_XRUN)
176 FIXME("Recovering from XRUN!\n");
177 snd_pcm_prepare(wwi->pcm);
178 frames = snd_pcm_avail_update(wwi->pcm);
179 bytes = snd_pcm_frames_to_bytes(wwi->pcm, frames);
180 snd_pcm_start(wwi->pcm);
181 snd_pcm_forward(wwi->pcm, frames - snd_pcm_bytes_to_frames(wwi->pcm, wwi->dwPeriodSize));
182 continue;
184 while (frames > 0 && wwi->lpQueuePtr)
186 TRACE("bytes = %d\n", bytes);
187 if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded < bytes)
189 bytes = lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded;
190 frames = snd_pcm_bytes_to_frames(wwi->pcm, bytes);
192 /* directly read fragment in wavehdr */
193 read = wwi->read(wwi->pcm, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, frames);
194 bytesRead = snd_pcm_frames_to_bytes(wwi->pcm, read);
196 TRACE("bytesRead=(%d(%d)/(%d)) -> (%d/%d)\n", bytesRead, read, frames, lpWaveHdr->dwBufferLength, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
197 if (read != (DWORD) -1)
199 /* update number of bytes recorded in current buffer and by this device */
200 lpWaveHdr->dwBytesRecorded += bytesRead;
201 InterlockedExchangeAdd((LONG*)&wwi->dwTotalRecorded, bytesRead);
202 frames -= read;
203 bytes -= bytesRead;
205 /* buffer is full. notify client */
206 if (!snd_pcm_bytes_to_frames(wwi->pcm, lpWaveHdr->dwBytesRecorded - lpWaveHdr->dwBufferLength))
208 /* must copy the value of next waveHdr, because we have no idea of what
209 * will be done with the content of lpWaveHdr in callback
211 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
213 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
214 lpWaveHdr->dwFlags |= WHDR_DONE;
216 wwi->lpQueuePtr = lpNext;
217 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
218 lpWaveHdr = lpNext;
220 } else {
221 WARN("read(%s, %p, %d) failed (%d/%s)\n", wwi->pcmname,
222 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
223 frames, frames, snd_strerror(read));
228 ALSA_WaitRingMessage(&wwi->msgRing, dwSleepTime);
230 while (ALSA_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
232 TRACE("msg=%s param=0x%lx\n", ALSA_getCmdString(msg), param);
233 switch (msg) {
234 case WINE_WM_PAUSING:
235 wwi->state = WINE_WS_PAUSED;
236 /*FIXME("Device should stop recording\n");*/
237 SetEvent(ev);
238 break;
239 case WINE_WM_STARTING:
240 wwi->state = WINE_WS_PLAYING;
241 snd_pcm_start(wwi->pcm);
242 SetEvent(ev);
243 break;
244 case WINE_WM_HEADER:
245 lpWaveHdr = (LPWAVEHDR)param;
246 lpWaveHdr->lpNext = 0;
248 /* insert buffer at the end of queue */
250 LPWAVEHDR* wh;
251 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
252 *wh = lpWaveHdr;
254 break;
255 case WINE_WM_STOPPING:
256 if (wwi->state != WINE_WS_STOPPED)
258 snd_pcm_drain(wwi->pcm);
260 /* read any headers in queue */
261 widRecorder_ReadHeaders(wwi);
263 /* return current buffer to app */
264 lpWaveHdr = wwi->lpQueuePtr;
265 if (lpWaveHdr)
267 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
268 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
269 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
270 lpWaveHdr->dwFlags |= WHDR_DONE;
271 wwi->lpQueuePtr = lpNext;
272 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
275 wwi->state = WINE_WS_STOPPED;
276 SetEvent(ev);
277 break;
278 case WINE_WM_RESETTING:
279 if (wwi->state != WINE_WS_STOPPED)
281 snd_pcm_drain(wwi->pcm);
283 wwi->state = WINE_WS_STOPPED;
284 wwi->dwTotalRecorded = 0;
286 /* read any headers in queue */
287 widRecorder_ReadHeaders(wwi);
289 /* return all buffers to the app */
290 while (wwi->lpQueuePtr) {
291 lpWaveHdr = wwi->lpQueuePtr;
292 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
293 wwi->lpQueuePtr = lpWaveHdr->lpNext;
294 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
295 lpWaveHdr->dwFlags |= WHDR_DONE;
296 widNotifyClient(wwi, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
299 SetEvent(ev);
300 break;
301 case WINE_WM_CLOSING:
302 wwi->hThread = 0;
303 wwi->state = WINE_WS_CLOSED;
304 SetEvent(ev);
305 ExitThread(0);
306 /* shouldn't go here */
307 default:
308 FIXME("unknown message %d\n", msg);
309 break;
313 ExitThread(0);
314 /* just for not generating compilation warnings... should never be executed */
315 return 0;
318 /**************************************************************************
319 * widOpen [internal]
321 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
323 WINE_WAVEDEV* wwi;
324 snd_pcm_hw_params_t * hw_params;
325 snd_pcm_sw_params_t * sw_params;
326 snd_pcm_access_t access;
327 snd_pcm_format_t format;
328 unsigned int rate;
329 unsigned int buffer_time = 500000;
330 unsigned int period_time = 10000;
331 snd_pcm_uframes_t buffer_size;
332 snd_pcm_uframes_t period_size;
333 int flags;
334 snd_pcm_t * pcm;
335 int err;
336 int dir;
337 DWORD ret;
339 /* JPW TODO - review this code */
340 TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
341 if (lpDesc == NULL) {
342 WARN("Invalid Parameter !\n");
343 return MMSYSERR_INVALPARAM;
345 if (wDevID >= ALSA_WidNumDevs) {
346 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
347 return MMSYSERR_BADDEVICEID;
350 /* only PCM format is supported so far... */
351 if (!ALSA_supportedFormat(lpDesc->lpFormat)) {
352 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
353 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
354 lpDesc->lpFormat->nSamplesPerSec);
355 return WAVERR_BADFORMAT;
358 if (dwFlags & WAVE_FORMAT_QUERY) {
359 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
360 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
361 lpDesc->lpFormat->nSamplesPerSec);
362 return MMSYSERR_NOERROR;
365 wwi = &WInDev[wDevID];
367 if (wwi->pcm != NULL) {
368 WARN("already allocated\n");
369 return MMSYSERR_ALLOCATED;
372 flags = SND_PCM_NONBLOCK;
374 if ( (err=snd_pcm_open(&pcm, wwi->pcmname, SND_PCM_STREAM_CAPTURE, flags)) < 0 )
376 ERR("Error open: %s\n", snd_strerror(err));
377 return MMSYSERR_NOTENABLED;
380 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
382 wwi->waveDesc = *lpDesc;
383 ALSA_copyFormat(lpDesc->lpFormat, &wwi->format);
385 if (wwi->format.Format.wBitsPerSample == 0) {
386 WARN("Resetting zeroed wBitsPerSample\n");
387 wwi->format.Format.wBitsPerSample = 8 *
388 (wwi->format.Format.nAvgBytesPerSec /
389 wwi->format.Format.nSamplesPerSec) /
390 wwi->format.Format.nChannels;
393 hw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_hw_params_sizeof() );
394 sw_params = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, snd_pcm_sw_params_sizeof() );
395 if (!hw_params || !sw_params)
397 ret = MMSYSERR_NOMEM;
398 goto error;
400 snd_pcm_hw_params_any(pcm, hw_params);
402 #define EXIT_ON_ERROR(f,e,txt) do \
404 int err; \
405 if ( (err = (f) ) < 0) \
407 WARN(txt ": %s\n", snd_strerror(err)); \
408 ret = (e); \
409 goto error; \
411 } while(0)
413 access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
414 if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {
415 WARN("mmap not available. switching to standard write.\n");
416 access = SND_PCM_ACCESS_RW_INTERLEAVED;
417 EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");
418 wwi->read = snd_pcm_readi;
420 else
421 wwi->read = snd_pcm_mmap_readi;
423 EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwi->format.Format.nChannels), WAVERR_BADFORMAT, "unable to set required channels");
425 if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_PCM) ||
426 ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
427 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) {
428 format = (wwi->format.Format.wBitsPerSample == 8) ? SND_PCM_FORMAT_U8 :
429 (wwi->format.Format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE :
430 (wwi->format.Format.wBitsPerSample == 24) ? SND_PCM_FORMAT_S24_3LE :
431 (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_S32_LE : -1;
432 } else if ((wwi->format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) &&
433 IsEqualGUID(&wwi->format.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
434 format = (wwi->format.Format.wBitsPerSample == 32) ? SND_PCM_FORMAT_FLOAT_LE : -1;
435 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_MULAW) {
436 FIXME("unimplemented format: WAVE_FORMAT_MULAW\n");
437 ret = WAVERR_BADFORMAT;
438 goto error;
439 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ALAW) {
440 FIXME("unimplemented format: WAVE_FORMAT_ALAW\n");
441 ret = WAVERR_BADFORMAT;
442 goto error;
443 } else if (wwi->format.Format.wFormatTag == WAVE_FORMAT_ADPCM) {
444 FIXME("unimplemented format: WAVE_FORMAT_ADPCM\n");
445 ret = WAVERR_BADFORMAT;
446 goto error;
447 } else {
448 ERR("invalid format: %0x04x\n", wwi->format.Format.wFormatTag);
449 ret = WAVERR_BADFORMAT;
450 goto error;
453 EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), WAVERR_BADFORMAT, "unable to set required format");
455 rate = wwi->format.Format.nSamplesPerSec;
456 dir = 0;
457 err = snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, &dir);
458 if (err < 0) {
459 WARN("Rate %d Hz not available for playback: %s\n", wwi->format.Format.nSamplesPerSec, snd_strerror(rate));
460 ret = WAVERR_BADFORMAT;
461 goto error;
463 if (!ALSA_NearMatch(rate, wwi->format.Format.nSamplesPerSec)) {
464 WARN("Rate doesn't match (requested %d Hz, got %d Hz)\n", wwi->format.Format.nSamplesPerSec, rate);
465 ret = WAVERR_BADFORMAT;
466 goto error;
469 dir=0;
470 EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_time, &dir), MMSYSERR_INVALPARAM, "unable to set buffer time");
471 dir=0;
472 EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir), MMSYSERR_INVALPARAM, "unable to set period time");
474 EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");
476 dir=0;
477 err = snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
478 err = snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
480 snd_pcm_sw_params_current(pcm, sw_params);
481 EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, 1), MMSYSERR_ERROR, "unable to set start threshold");
482 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");
483 EXIT_ON_ERROR( snd_pcm_sw_params_set_avail_min(pcm, sw_params, period_size), MMSYSERR_ERROR, "unable to set avail min");
484 EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_threshold(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence threshold");
485 EXIT_ON_ERROR( snd_pcm_sw_params(pcm, sw_params), MMSYSERR_ERROR, "unable to set sw params for playback");
486 #undef EXIT_ON_ERROR
488 snd_pcm_prepare(pcm);
490 if (TRACE_ON(wave))
491 ALSA_TraceParameters(hw_params, sw_params, FALSE);
493 /* now, we can save all required data for later use... */
495 wwi->dwBufferSize = snd_pcm_frames_to_bytes(pcm, buffer_size);
496 wwi->lpQueuePtr = wwi->lpPlayPtr = wwi->lpLoopPtr = NULL;
498 ALSA_InitRingMessage(&wwi->msgRing);
500 wwi->dwPeriodSize = snd_pcm_frames_to_bytes(pcm, period_size);
501 TRACE("dwPeriodSize=%u\n", wwi->dwPeriodSize);
502 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%u, nSamplesPerSec=%u, nChannels=%u nBlockAlign=%u!\n",
503 wwi->format.Format.wBitsPerSample, wwi->format.Format.nAvgBytesPerSec,
504 wwi->format.Format.nSamplesPerSec, wwi->format.Format.nChannels,
505 wwi->format.Format.nBlockAlign);
507 wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
508 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD_PTR)wDevID, 0, &(wwi->dwThreadID));
509 if (!wwi->hThread) {
510 ERR("Thread creation for the widRecorder failed!\n");
511 CloseHandle(wwi->hStartUpEvent);
512 ret = MMSYSERR_NOMEM;
513 goto error;
515 SetThreadPriority(wwi->hThread, THREAD_PRIORITY_TIME_CRITICAL);
516 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
517 CloseHandle(wwi->hStartUpEvent);
518 wwi->hStartUpEvent = NULL;
520 HeapFree( GetProcessHeap(), 0, sw_params );
521 wwi->hw_params = hw_params;
522 wwi->pcm = pcm;
524 widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
525 return MMSYSERR_NOERROR;
527 error:
528 snd_pcm_close(pcm);
529 HeapFree( GetProcessHeap(), 0, hw_params );
530 HeapFree( GetProcessHeap(), 0, sw_params );
531 if (wwi->msgRing.ring_buffer_size > 0)
532 ALSA_DestroyRingMessage(&wwi->msgRing);
533 return ret;
537 /**************************************************************************
538 * widClose [internal]
540 static DWORD widClose(WORD wDevID)
542 WINE_WAVEDEV* wwi;
544 TRACE("(%u);\n", wDevID);
546 if (wDevID >= ALSA_WidNumDevs) {
547 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
548 return MMSYSERR_BADDEVICEID;
551 wwi = &WInDev[wDevID];
552 if (wwi->pcm == NULL) {
553 WARN("Requested to close already closed device %d!\n", wDevID);
554 return MMSYSERR_BADDEVICEID;
557 if (wwi->lpQueuePtr) {
558 WARN("buffers still playing !\n");
559 return WAVERR_STILLPLAYING;
560 } else {
561 if (wwi->hThread) {
562 ALSA_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
564 ALSA_DestroyRingMessage(&wwi->msgRing);
566 HeapFree( GetProcessHeap(), 0, wwi->hw_params );
567 wwi->hw_params = NULL;
569 snd_pcm_close(wwi->pcm);
570 wwi->pcm = NULL;
572 widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
575 return MMSYSERR_NOERROR;
578 /**************************************************************************
579 * widAddBuffer [internal]
582 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
584 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
586 /* first, do the sanity checks... */
587 if (wDevID >= ALSA_WidNumDevs) {
588 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
589 return MMSYSERR_BADDEVICEID;
592 if (WInDev[wDevID].pcm == NULL) {
593 WARN("Requested to add buffer to already closed device %d!\n", wDevID);
594 return MMSYSERR_BADDEVICEID;
597 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
598 return WAVERR_UNPREPARED;
600 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
601 return WAVERR_STILLPLAYING;
603 lpWaveHdr->dwFlags &= ~WHDR_DONE;
604 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
605 lpWaveHdr->dwBytesRecorded = 0;
606 lpWaveHdr->lpNext = 0;
608 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD_PTR)lpWaveHdr, FALSE);
610 return MMSYSERR_NOERROR;
613 /**************************************************************************
614 * widStart [internal]
617 static DWORD widStart(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
619 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
621 /* first, do the sanity checks... */
622 if (wDevID >= ALSA_WidNumDevs) {
623 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
624 return MMSYSERR_BADDEVICEID;
627 if (WInDev[wDevID].pcm == NULL) {
628 WARN("Requested to start closed device %d!\n", wDevID);
629 return MMSYSERR_BADDEVICEID;
632 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
634 return MMSYSERR_NOERROR;
637 /**************************************************************************
638 * widStop [internal]
641 static DWORD widStop(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
643 TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
645 /* first, do the sanity checks... */
646 if (wDevID >= ALSA_WidNumDevs) {
647 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
648 return MMSYSERR_BADDEVICEID;
651 if (WInDev[wDevID].pcm == NULL) {
652 WARN("Requested to stop closed device %d!\n", wDevID);
653 return MMSYSERR_BADDEVICEID;
656 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
658 return MMSYSERR_NOERROR;
661 /**************************************************************************
662 * widReset [internal]
664 static DWORD widReset(WORD wDevID)
666 TRACE("(%u);\n", wDevID);
667 if (wDevID >= ALSA_WidNumDevs) {
668 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
669 return MMSYSERR_BADDEVICEID;
672 if (WInDev[wDevID].pcm == NULL) {
673 WARN("Requested to reset closed device %d!\n", wDevID);
674 return MMSYSERR_BADDEVICEID;
677 ALSA_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
678 return MMSYSERR_NOERROR;
681 /**************************************************************************
682 * widGetPosition [internal]
684 static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
686 WINE_WAVEDEV* wwi;
688 TRACE("(%u, %p, %u);\n", wDevID, lpTime, uSize);
690 if (wDevID >= ALSA_WidNumDevs) {
691 TRACE("Requested device %d, but only %d are known!\n", wDevID, ALSA_WidNumDevs);
692 return MMSYSERR_BADDEVICEID;
695 if (WInDev[wDevID].state == WINE_WS_CLOSED) {
696 WARN("Requested position of closed device %d!\n", wDevID);
697 return MMSYSERR_BADDEVICEID;
700 if (lpTime == NULL) {
701 WARN("invalid parameter: lpTime = NULL\n");
702 return MMSYSERR_INVALPARAM;
705 wwi = &WInDev[wDevID];
706 return ALSA_bytes_to_mmtime(lpTime, wwi->dwTotalRecorded, &wwi->format);
709 /**************************************************************************
710 * widGetNumDevs [internal]
712 static DWORD widGetNumDevs(void)
714 return ALSA_WidNumDevs;
717 /**************************************************************************
718 * widDevInterfaceSize [internal]
720 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
722 TRACE("(%u, %p)\n", wDevID, dwParam1);
724 *dwParam1 = MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
725 NULL, 0 ) * sizeof(WCHAR);
726 return MMSYSERR_NOERROR;
729 /**************************************************************************
730 * widDevInterface [internal]
732 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
734 if (dwParam2 >= MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
735 NULL, 0 ) * sizeof(WCHAR))
737 MultiByteToWideChar(CP_UNIXCP, 0, WInDev[wDevID].interface_name, -1,
738 dwParam1, dwParam2 / sizeof(WCHAR));
739 return MMSYSERR_NOERROR;
741 return MMSYSERR_INVALPARAM;
744 /**************************************************************************
745 * widMessage (WINEALSA.@)
747 DWORD WINAPI ALSA_widMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
748 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
750 TRACE("(%u, %s, %08lX, %08lX, %08lX);\n",
751 wDevID, ALSA_getMessage(wMsg), dwUser, dwParam1, dwParam2);
753 switch (wMsg) {
754 case DRVM_INIT:
755 ALSA_WaveInit();
756 case DRVM_EXIT:
757 case DRVM_ENABLE:
758 case DRVM_DISABLE:
759 /* FIXME: Pretend this is supported */
760 return 0;
761 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
762 case WIDM_CLOSE: return widClose (wDevID);
763 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
764 case WIDM_PREPARE: return MMSYSERR_NOTSUPPORTED;
765 case WIDM_UNPREPARE: return MMSYSERR_NOTSUPPORTED;
766 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
767 case WIDM_GETNUMDEVS: return widGetNumDevs ();
768 case WIDM_GETPOS: return widGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
769 case WIDM_RESET: return widReset (wDevID);
770 case WIDM_START: return widStart (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
771 case WIDM_STOP: return widStop (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
772 case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
773 case DRV_QUERYDEVICEINTERFACE: return widDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
774 case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
775 case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
776 default:
777 FIXME("unknown message %d!\n", wMsg);
779 return MMSYSERR_NOTSUPPORTED;