Added wave-in support and fixed a few bugs in the wave-out code.
[wine/multimedia.git] / dlls / winmm / winearts / audio.c
blobd3eaae5976465f2ce8b34e499c00946e3588114b
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3 * Wine Driver for aRts Sound Server
4 * http://www.arts-project.org
6 * Copyright 1994 Martin Ayotte
7 * 1999 Eric Pouech (async playing in waveOut/waveIn)
8 * 2000 Eric Pouech (loops in waveOut)
9 * 2002 Chris Morgan (aRts version of this file)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 /* NOTE:
26 * with arts we cannot stop the audio that is already in
27 * the servers buffer, so to reduce delays during starting
28 * and stoppping of audio streams adjust the
29 * audio buffer size in the kde control center or in the
30 * artsd startup script
32 * FIXME:
33 * pause in waveOut does not work correctly in loop mode
35 * does something need to be done in for WaveIn DirectSound?
38 /*#define EMULATE_SB16*/
40 #include "config.h"
42 #include <stdlib.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <string.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <fcntl.h>
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winerror.h"
54 #include "wine/winuser16.h"
55 #include "mmddk.h"
56 #include "dsound.h"
57 #include "dsdriver.h"
58 #include "arts.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wave);
63 #ifdef HAVE_ARTS
65 #include <artsc.h>
67 /* The following four #defines allow you to fine-tune the packet
68 * settings in arts for better low-latency support. You must also
69 * adjust the latency in the KDE arts control panel. I recommend 4
70 * fragments, 1024 bytes.
72 * The following is from the ARTS documentation and explains what CCCC
73 * and SSSS mean:
75 * @li ARTS_P_PACKET_SETTINGS (rw) This is a way to configure packet
76 * size & packet count at the same time. The format is 0xCCCCSSSS,
77 * where 2^SSSS is the packet size, and CCCC is the packet count. Note
78 * that when writing this, you don't necessarily get the settings you
79 * requested.
81 #define WAVEOUT_PACKET_CCCC 0x000C
82 #define WAVEOUT_PACKET_SSSS 0x0008
83 #define WAVEIN_PACKET_CCCC 0x000C
84 #define WAVEIN_PACKET_SSSS 0x0008
86 #define BUFFER_REFILL_THRESHOLD 4
88 #define WAVEOUT_PACKET_SETTINGS ((WAVEOUT_PACKET_CCCC << 16) | (WAVEOUT_PACKET_SSSS))
89 #define WAVEIN_PACKET_SETTINGS ((WAVEIN_PACKET_CCCC << 16) | (WAVEIN_PACKET_SSSS))
91 #define MAX_WAVEOUTDRV (10)
92 #define MAX_WAVEINDRV (10)
94 /* state diagram for waveOut writing:
96 * +---------+-------------+---------------+---------------------------------+
97 * | state | function | event | new state |
98 * +---------+-------------+---------------+---------------------------------+
99 * | | open() | | STOPPED |
100 * | PAUSED | write() | | PAUSED |
101 * | STOPPED | write() | <thrd create> | PLAYING |
102 * | PLAYING | write() | HEADER | PLAYING |
103 * | (other) | write() | <error> | |
104 * | (any) | pause() | PAUSING | PAUSED |
105 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
106 * | (any) | reset() | RESETTING | STOPPED |
107 * | (any) | close() | CLOSING | CLOSED |
108 * +---------+-------------+---------------+---------------------------------+
111 /* states of the playing device */
112 #define WINE_WS_PLAYING 0
113 #define WINE_WS_PAUSED 1
114 #define WINE_WS_STOPPED 2
115 #define WINE_WS_CLOSED 3
117 /* events to be send to device */
118 enum win_wm_message {
119 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
120 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
123 typedef struct {
124 enum win_wm_message msg; /* message identifier */
125 DWORD param; /* parameter for this message */
126 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
127 } RING_MSG;
129 /* implement an in-process message ring for better performance
130 * (compared to passing thru the server)
131 * this ring will be used by the input (resp output) record (resp playback) routine
133 #define ARTS_RING_BUFFER_INCREMENT 64
134 typedef struct {
135 RING_MSG * messages;
136 int ring_buffer_size;
137 int msg_tosave;
138 int msg_toget;
139 HANDLE msg_event;
140 CRITICAL_SECTION msg_crst;
141 } ARTS_MSG_RING;
143 typedef struct {
144 volatile int state; /* one of the WINE_WS_ manifest constants */
145 WAVEOPENDESC waveDesc;
146 WORD wFlags;
147 PCMWAVEFORMAT format;
148 WAVEOUTCAPSA caps;
150 DWORD dwSleepTime; /* Num of milliseconds to sleep between filling the dsp buffers */
152 /* arts information */
153 arts_stream_t play_stream; /* the stream structure we get from arts when opening a stream for playing */
154 DWORD dwBufferSize; /* size of whole buffer in bytes */
155 int packetSettings;
157 char* sound_buffer;
158 long buffer_size;
160 DWORD volume_left; /* volume control information */
161 DWORD volume_right;
163 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
164 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
165 DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
167 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
168 DWORD dwLoops; /* private copy of loop counter */
170 DWORD dwPlayedTotal; /* number of bytes actually played since opening */
171 DWORD dwWrittenTotal; /* number of bytes written to the audio device since opening */
173 /* synchronization stuff */
174 HANDLE hStartUpEvent;
175 HANDLE hThread;
176 DWORD dwThreadID;
177 ARTS_MSG_RING msgRing;
178 } WINE_WAVEOUT;
180 typedef struct {
181 volatile int state; /* one of the WINE_WS_ manifest constants */
182 WAVEOPENDESC waveDesc;
183 WORD wFlags;
184 PCMWAVEFORMAT format;
185 WAVEINCAPSA caps;
187 /* arts information */
188 arts_stream_t record_stream; /* the stream structure we get from arts when opening a stream for recording */
189 int packetSettings;
191 LPWAVEHDR lpQueuePtr;
192 DWORD dwRecordedTotal;
194 /* synchronization stuff */
195 HANDLE hStartUpEvent;
196 HANDLE hThread;
197 DWORD dwThreadID;
198 ARTS_MSG_RING msgRing;
199 } WINE_WAVEIN;
201 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
202 static WINE_WAVEIN WInDev [MAX_WAVEINDRV];
204 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
205 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
206 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
208 /* These strings used only for tracing */
209 static const char *wodPlayerCmdString[] = {
210 "WINE_WM_PAUSING",
211 "WINE_WM_RESTARTING",
212 "WINE_WM_RESETTING",
213 "WINE_WM_HEADER",
214 "WINE_WM_UPDATE",
215 "WINE_WM_BREAKLOOP",
216 "WINE_WM_CLOSING",
217 "WINE_WM_STARTING",
218 "WINE_WM_STOPPING",
221 /*======================================================================*
222 * Low level WAVE implementation *
223 *======================================================================*/
225 /* Volume functions derived from Alsaplayer source */
226 /* length is the number of 16 bit samples */
227 void volume_effect16(void *bufin, void* bufout, int length, int left,
228 int right, int nChannels)
230 short *d_out = (short *)bufout;
231 short *d_in = (short *)bufin;
232 int i, v;
235 TRACE("length == %d, nChannels == %d\n", length, nChannels);
238 if (right == -1) right = left;
240 for(i = 0; i < length; i+=(nChannels))
242 v = (int) ((*(d_in++) * left) / 100);
243 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
244 if(nChannels == 2)
246 v = (int) ((*(d_in++) * right) / 100);
247 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
252 /* length is the number of 8 bit samples */
253 void volume_effect8(void *bufin, void* bufout, int length, int left,
254 int right, int nChannels)
256 BYTE *d_out = (BYTE *)bufout;
257 BYTE *d_in = (BYTE *)bufin;
258 int i, v;
261 TRACE("length == %d, nChannels == %d\n", length, nChannels);
264 if (right == -1) right = left;
266 for(i = 0; i < length; i+=(nChannels))
268 v = (BYTE) ((*(d_in++) * left) / 100);
269 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
270 if(nChannels == 2)
272 v = (BYTE) ((*(d_in++) * right) / 100);
273 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
278 /******************************************************************
279 * ARTS_CloseWaveOutDevice
282 void ARTS_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
284 arts_close_stream(wwo->play_stream); /* close the arts stream */
285 wwo->play_stream = (arts_stream_t*)-1;
287 /* free up the buffer we use for volume and reset the size */
288 if(wwo->sound_buffer)
290 HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);
291 wwo->sound_buffer = NULL;
294 wwo->buffer_size = 0;
297 /******************************************************************
298 * ARTS_CloseWaveInDevice
301 void ARTS_CloseWaveInDevice(WINE_WAVEIN* wwi)
303 arts_close_stream(wwi->record_stream); /* close the arts stream */
304 wwi->record_stream = (arts_stream_t*)-1;
307 /******************************************************************
308 * ARTS_Init
310 static int ARTS_Init(void)
312 return arts_init(); /* initialize arts and return errorcode */
315 /******************************************************************
316 * ARTS_WaveClose
318 LONG ARTS_WaveClose(void)
320 int iDevice;
322 /* close all open devices */
323 for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
325 if(WOutDev[iDevice].play_stream != (arts_stream_t*)-1)
327 ARTS_CloseWaveOutDevice(&WOutDev[iDevice]);
331 for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
333 if(WInDev[iDevice].record_stream != (arts_stream_t*)-1)
335 ARTS_CloseWaveInDevice(&WInDev[iDevice]);
339 arts_free(); /* free up arts */
340 return 1;
343 /******************************************************************
344 * ARTS_WaveInit
346 * Initialize internal structures from ARTS server info
348 LONG ARTS_WaveInit(void)
350 int i;
351 int errorcode;
353 TRACE("called\n");
355 if ((errorcode = ARTS_Init()) < 0)
357 ERR("arts_init() failed (%d)\n", errorcode);
358 return -1;
361 /* initialize all device handles to -1 */
362 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
364 WOutDev[i].play_stream = (arts_stream_t*)-1;
365 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out
366 caps values */
367 /* FIXME: some programs compare this string against the content of the registry
368 * for MM drivers. The names have to match in order for the program to work
369 * (e.g. MS win9x mplayer.exe)
371 #ifdef EMULATE_SB16
372 WOutDev[i].caps.wMid = 0x0002;
373 WOutDev[i].caps.wPid = 0x0104;
374 strcpy(WOutDev[i].caps.szPname, "SB16 Wave Out");
375 #else
376 WOutDev[i].caps.wMid = 0x00FF; /* Manufac ID */
377 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
378 /* strcpy(WOutDev[i].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
379 strcpy(WOutDev[i].caps.szPname, "CS4236/37/38");
380 #endif
381 WOutDev[i].caps.vDriverVersion = 0x0100;
382 WOutDev[i].caps.dwFormats = 0x00000000;
383 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
385 WOutDev[i].caps.wChannels = 2;
386 WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
388 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
389 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
390 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
391 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
392 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
393 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
394 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
395 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
396 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
397 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
398 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
399 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
402 for (i = 0; i < MAX_WAVEINDRV; ++i)
404 WInDev[i].record_stream = (arts_stream_t*)-1;
405 memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out
406 caps values */
407 /* FIXME: some programs compare this string against the content of the registry
408 * for MM drivers. The names have to match in order for the program to work
409 * (e.g. MS win9x mplayer.exe)
411 #ifdef EMULATE_SB16
412 WInDev[i].caps.wMid = 0x0002;
413 WInDev[i].caps.wPid = 0x0104;
414 strcpy(WInDev[i].caps.szPname, "SB16 Wave In");
415 #else
416 WInDev[i].caps.wMid = 0x00FF;
417 WInDev[i].caps.wPid = 0x0001;
418 strcpy(WInDev[i].caps.szPname,"CS4236/37/38");
419 #endif
420 WInDev[i].caps.vDriverVersion = 0x0100;
421 WInDev[i].caps.dwFormats = 0x00000000;
423 WInDev[i].caps.wChannels = 2;
425 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
426 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
427 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
428 WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
429 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
430 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
431 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
432 WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
433 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
434 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
435 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
436 WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
438 WInDev[i].caps.wReserved1 = 0;
440 return 0;
443 /******************************************************************
444 * ARTS_InitRingMessage
446 * Initialize the ring of messages for passing between driver's caller and playback/record
447 * thread
449 static int ARTS_InitRingMessage(ARTS_MSG_RING* mr)
451 mr->msg_toget = 0;
452 mr->msg_tosave = 0;
453 mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
454 mr->ring_buffer_size = ARTS_RING_BUFFER_INCREMENT;
455 mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
456 InitializeCriticalSection(&mr->msg_crst);
457 return 0;
460 /******************************************************************
461 * ARTS_DestroyRingMessage
464 static int ARTS_DestroyRingMessage(ARTS_MSG_RING* mr)
466 CloseHandle(mr->msg_event);
467 HeapFree(GetProcessHeap(),0,mr->messages);
468 mr->messages=NULL;
469 DeleteCriticalSection(&mr->msg_crst);
470 return 0;
473 /******************************************************************
474 * ARTS_AddRingMessage
476 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
478 static int ARTS_AddRingMessage(ARTS_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
480 HANDLE hEvent = INVALID_HANDLE_VALUE;
482 EnterCriticalSection(&mr->msg_crst);
483 if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
485 int old_ring_buffer_size = mr->ring_buffer_size;
486 mr->ring_buffer_size += ARTS_RING_BUFFER_INCREMENT;
487 TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
488 mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
489 /* Now we need to rearrange the ring buffer so that the new
490 buffers just allocated are in between mr->msg_tosave and
491 mr->msg_toget.
493 if (mr->msg_tosave < mr->msg_toget)
495 memmove(&(mr->messages[mr->msg_toget + ARTS_RING_BUFFER_INCREMENT]),
496 &(mr->messages[mr->msg_toget]),
497 sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
499 mr->msg_toget += ARTS_RING_BUFFER_INCREMENT;
502 if (wait)
504 hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
505 if (hEvent == INVALID_HANDLE_VALUE)
507 ERR("can't create event !?\n");
508 LeaveCriticalSection(&mr->msg_crst);
509 return 0;
511 if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
512 FIXME("two fast messages in the queue!!!!\n");
514 /* fast messages have to be added at the start of the queue */
515 mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
517 mr->messages[mr->msg_toget].msg = msg;
518 mr->messages[mr->msg_toget].param = param;
519 mr->messages[mr->msg_toget].hEvent = hEvent;
521 else
523 mr->messages[mr->msg_tosave].msg = msg;
524 mr->messages[mr->msg_tosave].param = param;
525 mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
526 mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
529 LeaveCriticalSection(&mr->msg_crst);
531 SetEvent(mr->msg_event); /* signal a new message */
533 if (wait)
535 /* wait for playback/record thread to have processed the message */
536 WaitForSingleObject(hEvent, INFINITE);
537 CloseHandle(hEvent);
540 return 1;
543 /******************************************************************
544 * ARTS_RetrieveRingMessage
546 * Get a message from the ring. Should be called by the playback/record thread.
548 static int ARTS_RetrieveRingMessage(ARTS_MSG_RING* mr,
549 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
551 EnterCriticalSection(&mr->msg_crst);
553 if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
555 LeaveCriticalSection(&mr->msg_crst);
556 return 0;
559 *msg = mr->messages[mr->msg_toget].msg;
560 mr->messages[mr->msg_toget].msg = 0;
561 *param = mr->messages[mr->msg_toget].param;
562 *hEvent = mr->messages[mr->msg_toget].hEvent;
563 mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
564 LeaveCriticalSection(&mr->msg_crst);
565 return 1;
568 /*======================================================================*
569 * Low level WAVE OUT implementation *
570 *======================================================================*/
572 /**************************************************************************
573 * wodNotifyClient [internal]
575 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
577 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
579 switch (wMsg) {
580 case WOM_OPEN:
581 case WOM_CLOSE:
582 case WOM_DONE:
583 if (wwo->wFlags != DCB_NULL &&
584 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
585 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
586 WARN("can't notify client !\n");
587 return MMSYSERR_ERROR;
589 break;
590 default:
591 FIXME("Unknown callback message %u\n", wMsg);
592 return MMSYSERR_INVALPARAM;
594 return MMSYSERR_NOERROR;
597 /**************************************************************************
598 * wodUpdatePlayedTotal [internal]
601 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
603 /* total played is the bytes written less the bytes to write ;-) */
604 wwo->dwPlayedTotal = wwo->dwWrittenTotal -
605 (wwo->dwBufferSize -
606 arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE));
608 return TRUE;
611 /**************************************************************************
612 * wodPlayer_BeginWaveHdr [internal]
614 * Makes the specified lpWaveHdr the currently playing wave header.
615 * If the specified wave header is a begin loop and we're not already in
616 * a loop, setup the loop.
618 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
620 wwo->lpPlayPtr = lpWaveHdr;
622 if (!lpWaveHdr) return;
624 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
625 if (wwo->lpLoopPtr) {
626 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
627 TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
628 } else {
629 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
630 wwo->lpLoopPtr = lpWaveHdr;
631 /* Windows does not touch WAVEHDR.dwLoops,
632 * so we need to make an internal copy */
633 wwo->dwLoops = lpWaveHdr->dwLoops;
636 wwo->dwPartialOffset = 0;
639 /**************************************************************************
640 * wodPlayer_PlayPtrNext [internal]
642 * Advance the play pointer to the next waveheader, looping if required.
644 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
646 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
648 wwo->dwPartialOffset = 0;
649 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
650 /* We're at the end of a loop, loop if required */
651 if (--wwo->dwLoops > 0) {
652 wwo->lpPlayPtr = wwo->lpLoopPtr;
653 } else {
654 /* Handle overlapping loops correctly */
655 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
656 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
657 /* shall we consider the END flag for the closing loop or for
658 * the opening one or for both ???
659 * code assumes for closing loop only
661 } else {
662 lpWaveHdr = lpWaveHdr->lpNext;
664 wwo->lpLoopPtr = NULL;
665 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
667 } else {
668 /* We're not in a loop. Advance to the next wave header */
669 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
672 return lpWaveHdr;
675 /**************************************************************************
676 * wodPlayer_NotifyWait [internal]
677 * Returns the number of milliseconds to wait before attempting to notify
678 * completion of the specified wavehdr.
679 * This is based on the number of bytes remaining to be written in the
680 * wave.
682 static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
684 DWORD dwMillis;
686 if(lpWaveHdr->reserved < wwo->dwPlayedTotal)
688 dwMillis = 1;
690 else
692 dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
693 if(!dwMillis) dwMillis = 1;
696 TRACE("dwMillis = %ld\n", dwMillis);
698 return dwMillis;
702 /**************************************************************************
703 * wodPlayer_WriteMaxFrags [internal]
704 * Writes the maximum number of bytes possible to the DSP and returns
705 * the number of bytes written.
707 static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
709 /* Only attempt to write to free bytes */
710 DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
711 int toWrite = min(dwLength, *bytes);
712 int written;
714 TRACE("Writing wavehdr %p.%lu[%lu]\n",
715 wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
717 /* see if our buffer isn't large enough for the data we are writing */
718 if(wwo->buffer_size < toWrite)
720 if(wwo->sound_buffer)
722 HeapRealloc(GetProcessHeap(), 0, wwo->sound_buffer, toWrite);
723 wwo->buffer_size = toWrite;
727 /* if we don't have a buffer then get one */
728 if(!wwo->sound_buffer)
730 /* allocate some memory for the buffer */
731 wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);
732 wwo->buffer_size = toWrite;
735 /* if we don't have a buffer then error out */
736 if(!wwo->sound_buffer)
738 ERR("error allocating sound_buffer memory\n");
739 return 0;
742 TRACE("toWrite == %d\n", toWrite);
744 /* apply volume to the bits */
745 /* for single channel audio streams we only use the LEFT volume */
746 if(wwo->format.wBitsPerSample == 16)
748 /* apply volume to the buffer we are about to send */
749 /* divide toWrite(bytes) by 2 as volume processes by 16 bits */
750 volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
751 wwo->sound_buffer, toWrite>>1, wwo->volume_left,
752 wwo->volume_right, wwo->format.wf.nChannels);
753 } else if(wwo->format.wBitsPerSample == 8)
755 /* apply volume to the buffer we are about to send */
756 volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
757 wwo->sound_buffer, toWrite, wwo->volume_left,
758 wwo->volume_right, wwo->format.wf.nChannels);
759 } else
761 FIXME("unsupported wwo->format.wBitsPerSample of %d\n",
762 wwo->format.wBitsPerSample);
765 /* send the audio data to arts for playing */
766 written = arts_write(wwo->play_stream, wwo->sound_buffer, toWrite);
768 TRACE("written = %d\n", written);
770 if (written <= 0)
772 *bytes = 0; /* apparently arts is actually full */
773 return written; /* if we wrote nothing just return */
776 if (written >= dwLength)
777 wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
778 else
779 wwo->dwPartialOffset += written; /* Remove the amount written */
781 if (written < toWrite)
782 *bytes = 0;
783 else
784 *bytes -= written;
786 wwo->dwWrittenTotal += written; /* update stats on this wave device */
788 return written; /* return the number of bytes written */
792 /**************************************************************************
793 * wodPlayer_NotifyCompletions [internal]
795 * Notifies and remove from queue all wavehdrs which have been played to
796 * the speaker (ie. they have cleared the audio device). If force is true,
797 * we notify all wavehdrs and remove them all from the queue even if they
798 * are unplayed or part of a loop.
800 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
802 LPWAVEHDR lpWaveHdr;
804 if (wwo->lpQueuePtr) {
805 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n",
806 wwo->lpQueuePtr,
807 wwo->lpPlayPtr,
808 wwo->lpLoopPtr,
809 wwo->lpQueuePtr->reserved,
810 wwo->dwWrittenTotal,
811 force);
812 } else {
813 TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), dwWrittenTotal=(%ld), force=(%d)\n",
814 wwo->lpQueuePtr,
815 wwo->lpPlayPtr,
816 wwo->lpLoopPtr,
817 wwo->dwWrittenTotal,
818 force);
821 /* Start from lpQueuePtr and keep notifying until:
822 * - we hit an unwritten wavehdr
823 * - we hit the beginning of a running loop
824 * - we hit a wavehdr which hasn't finished playing
826 while ((lpWaveHdr = wwo->lpQueuePtr) &&
827 (force ||
828 (lpWaveHdr != wwo->lpPlayPtr &&
829 lpWaveHdr != wwo->lpLoopPtr &&
830 lpWaveHdr->reserved <= wwo->dwWrittenTotal))) {
832 wwo->lpQueuePtr = lpWaveHdr->lpNext;
834 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
835 lpWaveHdr->dwFlags |= WHDR_DONE;
837 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
839 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
840 wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
843 /**************************************************************************
844 * wodPlayer_Reset [internal]
846 * wodPlayer helper. Resets current output stream.
848 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
850 wodUpdatePlayedTotal(wwo);
852 wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
854 /* we aren't able to flush any data that has already been written */
855 /* to arts, otherwise we would do the flushing here */
857 if (reset) {
858 enum win_wm_message msg;
859 DWORD param;
860 HANDLE ev;
862 /* remove any buffer */
863 wodPlayer_NotifyCompletions(wwo, TRUE);
865 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
866 wwo->state = WINE_WS_STOPPED;
867 wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
869 wwo->dwPartialOffset = 0; /* Clear partial wavehdr */
871 /* remove any existing message in the ring */
872 EnterCriticalSection(&wwo->msgRing.msg_crst);
874 /* return all pending headers in queue */
875 while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
877 TRACE("flushing msg\n");
878 if (msg != WINE_WM_HEADER)
880 FIXME("shouldn't have headers left\n");
881 SetEvent(ev);
882 continue;
884 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
885 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
887 wodNotifyClient(wwo, WOM_DONE, param, 0);
889 ResetEvent(wwo->msgRing.msg_event);
890 LeaveCriticalSection(&wwo->msgRing.msg_crst);
891 } else {
892 if (wwo->lpLoopPtr) {
893 /* complicated case, not handled yet (could imply modifying the loop counter */
894 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
895 wwo->lpPlayPtr = wwo->lpLoopPtr;
896 wwo->dwPartialOffset = 0;
897 wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
898 } else {
899 /* the data already written is going to be played, so take */
900 /* this fact into account here */
901 wwo->dwPlayedTotal = wwo->dwWrittenTotal;
903 wwo->state = WINE_WS_PAUSED;
907 /**************************************************************************
908 * wodPlayer_ProcessMessages [internal]
910 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
912 LPWAVEHDR lpWaveHdr;
913 enum win_wm_message msg;
914 DWORD param;
915 HANDLE ev;
917 while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
918 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
919 switch (msg) {
920 case WINE_WM_PAUSING:
921 wodPlayer_Reset(wwo, FALSE);
922 SetEvent(ev);
923 break;
924 case WINE_WM_RESTARTING:
925 wwo->state = WINE_WS_PLAYING;
926 SetEvent(ev);
927 break;
928 case WINE_WM_HEADER:
929 lpWaveHdr = (LPWAVEHDR)param;
931 /* insert buffer at the end of queue */
933 LPWAVEHDR* wh;
934 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
935 *wh = lpWaveHdr;
937 if (!wwo->lpPlayPtr)
938 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
939 if (wwo->state == WINE_WS_STOPPED)
940 wwo->state = WINE_WS_PLAYING;
941 break;
942 case WINE_WM_RESETTING:
943 wodPlayer_Reset(wwo, TRUE);
944 SetEvent(ev);
945 break;
946 case WINE_WM_UPDATE:
947 wodUpdatePlayedTotal(wwo);
948 SetEvent(ev);
949 break;
950 case WINE_WM_BREAKLOOP:
951 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
952 /* ensure exit at end of current loop */
953 wwo->dwLoops = 1;
955 SetEvent(ev);
956 break;
957 case WINE_WM_CLOSING:
958 /* sanity check: this should not happen since the device must have been reset before */
959 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
960 wwo->hThread = 0;
961 wwo->state = WINE_WS_CLOSED;
962 SetEvent(ev);
963 ExitThread(0);
964 /* shouldn't go here */
965 default:
966 FIXME("unknown message %d\n", msg);
967 break;
972 /**************************************************************************
973 * wodPlayer_FeedDSP [internal]
974 * Feed as much sound data as we can into the DSP and return the number of
975 * milliseconds before it will be necessary to feed the DSP again.
977 static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
979 DWORD availInQ;
981 wodUpdatePlayedTotal(wwo);
982 availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);
983 TRACE("availInQ = %ld\n", availInQ);
985 /* input queue empty */
986 if (!wwo->lpPlayPtr) {
987 TRACE("Run out of wavehdr:s... flushing\n");
988 return INFINITE;
991 /* no more room... no need to try to feed */
992 if(!availInQ)
994 TRACE("no more room, no need to try to feed\n");
995 return wwo->dwSleepTime;
998 /* Feed from partial wavehdr */
999 if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
1001 TRACE("feeding from partial wavehdr\n");
1002 wodPlayer_WriteMaxFrags(wwo, &availInQ);
1005 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1006 if (!wwo->dwPartialOffset)
1008 while(wwo->lpPlayPtr && availInQ)
1010 TRACE("feeding waveheaders until we run out of space\n");
1011 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1012 wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
1013 TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
1014 wwo->lpPlayPtr->reserved,
1015 wwo->dwWrittenTotal,
1016 wwo->lpPlayPtr->dwBufferLength
1018 wodPlayer_WriteMaxFrags(wwo, &availInQ);
1022 if (!wwo->lpPlayPtr) {
1023 TRACE("Ran out of wavehdrs\n");
1024 return INFINITE;
1027 return wwo->dwSleepTime;
1031 /**************************************************************************
1032 * wodPlayer [internal]
1034 static DWORD CALLBACK wodPlayer(LPVOID pmt)
1036 WORD uDevID = (DWORD)pmt;
1037 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
1038 DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
1039 DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
1040 DWORD dwSleepTime;
1042 wwo->state = WINE_WS_STOPPED;
1043 SetEvent(wwo->hStartUpEvent);
1045 for (;;) {
1046 /** Wait for the shortest time before an action is required. If there
1047 * are no pending actions, wait forever for a command.
1049 dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
1050 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
1051 WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
1052 wodPlayer_ProcessMessages(wwo);
1053 if (wwo->state == WINE_WS_PLAYING) {
1054 dwNextFeedTime = wodPlayer_FeedDSP(wwo);
1055 dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
1056 } else {
1057 dwNextFeedTime = dwNextNotifyTime = INFINITE;
1062 /**************************************************************************
1063 * wodGetDevCaps [internal]
1065 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
1067 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1069 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1071 if (wDevID >= MAX_WAVEOUTDRV) {
1072 TRACE("MAX_WAVOUTDRV reached !\n");
1073 return MMSYSERR_BADDEVICEID;
1076 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1077 return MMSYSERR_NOERROR;
1080 /**************************************************************************
1081 * wodOpen [internal]
1083 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1085 WINE_WAVEOUT* wwo;
1087 TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
1088 if (lpDesc == NULL) {
1089 WARN("Invalid Parameter !\n");
1090 return MMSYSERR_INVALPARAM;
1092 if (wDevID >= MAX_WAVEOUTDRV) {
1093 TRACE("MAX_WAVOUTDRV reached !\n");
1094 return MMSYSERR_BADDEVICEID;
1097 /* if this device is already open tell the app that it is allocated */
1098 if(WOutDev[wDevID].play_stream != (arts_stream_t*)-1)
1100 TRACE("device already allocated\n");
1101 return MMSYSERR_ALLOCATED;
1104 /* only PCM format is supported so far... */
1105 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1106 lpDesc->lpFormat->nChannels == 0 ||
1107 lpDesc->lpFormat->nSamplesPerSec == 0) {
1108 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1109 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1110 lpDesc->lpFormat->nSamplesPerSec);
1111 return WAVERR_BADFORMAT;
1114 if (dwFlags & WAVE_FORMAT_QUERY) {
1115 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1116 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1117 lpDesc->lpFormat->nSamplesPerSec);
1118 return MMSYSERR_NOERROR;
1121 wwo = &WOutDev[wDevID];
1123 /* direct sound not supported, ignore the flag */
1124 dwFlags &= ~WAVE_DIRECTSOUND;
1126 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1128 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1129 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1131 if (wwo->format.wBitsPerSample == 0) {
1132 WARN("Resetting zeroed wBitsPerSample\n");
1133 wwo->format.wBitsPerSample = 8 *
1134 (wwo->format.wf.nAvgBytesPerSec /
1135 wwo->format.wf.nSamplesPerSec) /
1136 wwo->format.wf.nChannels;
1139 wwo->play_stream = arts_play_stream(wwo->format.wf.nSamplesPerSec,
1140 wwo->format.wBitsPerSample, wwo->format.wf.nChannels, "winearts");
1142 /* clear these so we don't have any confusion ;-) */
1143 wwo->sound_buffer = 0;
1144 wwo->buffer_size = 0;
1146 arts_stream_set(wwo->play_stream, ARTS_P_BLOCKING, 0); /* disable blocking on this stream */
1148 if(!wwo->play_stream) return MMSYSERR_ALLOCATED;
1150 /* Try to set the packet settings from constant and store the value that it
1151 was actually set to for future use */
1152 wwo->packetSettings = arts_stream_set(wwo->play_stream, ARTS_P_PACKET_SETTINGS, WAVEOUT_PACKET_SETTINGS);
1153 TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEOUT_PACKET_SETTINGS, wwo->packetSettings);
1155 wwo->dwBufferSize = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SIZE);
1156 TRACE("Buffer size is now (%ld)\n",wwo->dwBufferSize);
1158 wwo->dwPlayedTotal = 0;
1159 wwo->dwWrittenTotal = 0;
1161 wwo->dwSleepTime = ((1 << (wwo->packetSettings & 0xFFFF)) * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->format.wf.nAvgBytesPerSec;
1163 /* Initialize volume to full level */
1164 wwo->volume_left = 100;
1165 wwo->volume_right = 100;
1167 ARTS_InitRingMessage(&wwo->msgRing);
1169 /* create player thread */
1170 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1171 wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1172 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
1173 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
1174 CloseHandle(wwo->hStartUpEvent);
1175 } else {
1176 wwo->hThread = INVALID_HANDLE_VALUE;
1177 wwo->dwThreadID = 0;
1179 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
1181 TRACE("stream=0x%lx, dwBufferSize=%ld\n",
1182 (long)wwo->play_stream, wwo->dwBufferSize);
1184 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1185 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
1186 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1187 wwo->format.wf.nBlockAlign);
1189 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
1192 /**************************************************************************
1193 * wodClose [internal]
1195 static DWORD wodClose(WORD wDevID)
1197 DWORD ret = MMSYSERR_NOERROR;
1198 WINE_WAVEOUT* wwo;
1200 TRACE("(%u);\n", wDevID);
1202 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1203 (arts_stream_t*)-1)
1205 WARN("bad device ID !\n");
1206 return MMSYSERR_BADDEVICEID;
1209 wwo = &WOutDev[wDevID];
1210 if (wwo->lpQueuePtr) {
1211 WARN("buffers still playing !\n");
1212 ret = WAVERR_STILLPLAYING;
1213 } else {
1214 TRACE("imhere[3-close]\n");
1215 if (wwo->hThread != INVALID_HANDLE_VALUE) {
1216 ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
1219 ARTS_DestroyRingMessage(&wwo->msgRing);
1221 ARTS_CloseWaveOutDevice(wwo); /* close the stream and clean things up */
1223 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
1225 return ret;
1228 /**************************************************************************
1229 * wodWrite [internal]
1232 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1234 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1236 /* first, do the sanity checks... */
1237 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1238 (arts_stream_t*)-1)
1240 WARN("bad dev ID !\n");
1241 return MMSYSERR_BADDEVICEID;
1244 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
1246 TRACE("unprepared\n");
1247 return WAVERR_UNPREPARED;
1250 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1252 TRACE("still playing\n");
1253 return WAVERR_STILLPLAYING;
1256 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1257 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1258 lpWaveHdr->lpNext = 0;
1260 TRACE("adding ring message\n");
1261 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1263 return MMSYSERR_NOERROR;
1266 /**************************************************************************
1267 * wodPrepare [internal]
1269 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1271 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1273 if (wDevID >= MAX_WAVEOUTDRV) {
1274 WARN("bad device ID !\n");
1275 return MMSYSERR_BADDEVICEID;
1278 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1279 return WAVERR_STILLPLAYING;
1281 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1282 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1283 return MMSYSERR_NOERROR;
1286 /**************************************************************************
1287 * wodUnprepare [internal]
1289 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1291 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1293 if (wDevID >= MAX_WAVEOUTDRV) {
1294 WARN("bad device ID !\n");
1295 return MMSYSERR_BADDEVICEID;
1298 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1299 return WAVERR_STILLPLAYING;
1301 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1302 lpWaveHdr->dwFlags |= WHDR_DONE;
1304 return MMSYSERR_NOERROR;
1307 /**************************************************************************
1308 * wodPause [internal]
1310 static DWORD wodPause(WORD wDevID)
1312 TRACE("(%u);!\n", wDevID);
1314 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1315 (arts_stream_t*)-1)
1317 WARN("bad device ID !\n");
1318 return MMSYSERR_BADDEVICEID;
1321 TRACE("imhere[3-PAUSING]\n");
1322 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1324 return MMSYSERR_NOERROR;
1327 /**************************************************************************
1328 * wodRestart [internal]
1330 static DWORD wodRestart(WORD wDevID)
1332 TRACE("(%u);\n", wDevID);
1334 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1335 (arts_stream_t*)-1)
1337 WARN("bad device ID !\n");
1338 return MMSYSERR_BADDEVICEID;
1341 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1342 TRACE("imhere[3-RESTARTING]\n");
1343 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1346 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1347 /* FIXME: Myst crashes with this ... hmm -MM
1348 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1351 return MMSYSERR_NOERROR;
1354 /**************************************************************************
1355 * wodReset [internal]
1357 static DWORD wodReset(WORD wDevID)
1359 TRACE("(%u);\n", wDevID);
1361 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1362 (arts_stream_t*)-1)
1364 WARN("bad device ID !\n");
1365 return MMSYSERR_BADDEVICEID;
1368 TRACE("imhere[3-RESET]\n");
1369 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1371 return MMSYSERR_NOERROR;
1374 /**************************************************************************
1375 * wodGetPosition [internal]
1377 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1379 int time;
1380 DWORD val;
1381 WINE_WAVEOUT* wwo;
1383 TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
1385 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1386 (arts_stream_t*)-1)
1388 WARN("bad device ID !\n");
1389 return MMSYSERR_BADDEVICEID;
1392 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1394 wwo = &WOutDev[wDevID];
1395 ARTS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1396 val = wwo->dwPlayedTotal;
1398 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1399 lpTime->wType, wwo->format.wBitsPerSample,
1400 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1401 wwo->format.wf.nAvgBytesPerSec);
1402 TRACE("dwPlayedTotal=%lu\n", val);
1404 switch (lpTime->wType) {
1405 case TIME_BYTES:
1406 lpTime->u.cb = val;
1407 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1408 break;
1409 case TIME_SAMPLES:
1410 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
1411 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1412 break;
1413 case TIME_SMPTE:
1414 time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1415 lpTime->u.smpte.hour = time / 108000;
1416 time -= lpTime->u.smpte.hour * 108000;
1417 lpTime->u.smpte.min = time / 1800;
1418 time -= lpTime->u.smpte.min * 1800;
1419 lpTime->u.smpte.sec = time / 30;
1420 time -= lpTime->u.smpte.sec * 30;
1421 lpTime->u.smpte.frame = time;
1422 lpTime->u.smpte.fps = 30;
1423 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1424 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1425 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1426 break;
1427 default:
1428 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1429 lpTime->wType = TIME_MS;
1430 case TIME_MS:
1431 lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1432 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1433 break;
1435 return MMSYSERR_NOERROR;
1438 /**************************************************************************
1439 * wodBreakLoop [internal]
1441 static DWORD wodBreakLoop(WORD wDevID)
1443 TRACE("(%u);\n", wDevID);
1445 if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].play_stream ==
1446 (arts_stream_t*)-1)
1448 WARN("bad device ID !\n");
1449 return MMSYSERR_BADDEVICEID;
1451 ARTS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1452 return MMSYSERR_NOERROR;
1455 /**************************************************************************
1456 * wodGetVolume [internal]
1458 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1460 DWORD left, right;
1462 left = WOutDev[wDevID].volume_left;
1463 right = WOutDev[wDevID].volume_right;
1465 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1467 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) <<
1468 16);
1470 return MMSYSERR_NOERROR;
1473 /**************************************************************************
1474 * wodSetVolume [internal]
1476 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1478 DWORD left, right;
1480 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1481 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1483 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1485 WOutDev[wDevID].volume_left = left;
1486 WOutDev[wDevID].volume_right = right;
1488 return MMSYSERR_NOERROR;
1491 /**************************************************************************
1492 * wodGetNumDevs [internal]
1494 static DWORD wodGetNumDevs(void)
1496 return MAX_WAVEOUTDRV;
1499 /**************************************************************************
1500 * wodMessage (WINEARTS.@)
1502 DWORD WINAPI ARTS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1503 DWORD dwParam1, DWORD dwParam2)
1505 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1506 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1508 switch (wMsg) {
1509 case DRVM_INIT:
1510 case DRVM_EXIT:
1511 case DRVM_ENABLE:
1512 case DRVM_DISABLE:
1513 /* FIXME: Pretend this is supported */
1514 return 0;
1515 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1516 case WODM_CLOSE: return wodClose (wDevID);
1517 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1518 case WODM_PAUSE: return wodPause (wDevID);
1519 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1520 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
1521 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1522 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1523 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1524 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1525 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1526 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1527 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1528 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1529 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1530 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1531 case WODM_RESTART: return wodRestart (wDevID);
1532 case WODM_RESET: return wodReset (wDevID);
1534 case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
1535 case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
1536 case DRV_QUERYDSOUNDGUID: return wodDsGuid (wDevID, (LPGUID)dwParam1);
1537 default:
1538 FIXME("unknown message %d!\n", wMsg);
1540 return MMSYSERR_NOTSUPPORTED;
1543 /*======================================================================*
1544 * Low level WAVE IN implementation *
1545 *======================================================================*/
1547 /**************************************************************************
1548 * widGetNumDevs [internal]
1550 static DWORD widGetNumDevs(void)
1552 TRACE("%d \n",MAX_WAVEINDRV);
1553 return MAX_WAVEINDRV;
1556 /**************************************************************************
1557 * widNotifyClient [internal]
1559 static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
1561 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
1563 switch (wMsg) {
1564 case WIM_OPEN:
1565 case WIM_CLOSE:
1566 case WIM_DATA:
1567 if (wwi->wFlags != DCB_NULL &&
1568 !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
1569 (HDRVR)wwi->waveDesc.hWave, wMsg,
1570 wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
1571 WARN("can't notify client !\n");
1572 return MMSYSERR_ERROR;
1574 break;
1575 default:
1576 FIXME("Unknown callback message %u\n", wMsg);
1577 return MMSYSERR_INVALPARAM;
1579 return MMSYSERR_NOERROR;
1582 /**************************************************************************
1583 * widGetDevCaps [internal]
1585 static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
1587 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
1589 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
1591 if (wDevID >= MAX_WAVEINDRV) {
1592 TRACE("MAX_WAVINDRV reached !\n");
1593 return MMSYSERR_BADDEVICEID;
1596 memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
1597 return MMSYSERR_NOERROR;
1600 /**************************************************************************
1601 * widRecorder [internal]
1603 static DWORD CALLBACK widRecorder(LPVOID pmt)
1605 WORD uDevID = (DWORD)pmt;
1606 WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
1607 WAVEHDR* lpWaveHdr;
1608 DWORD dwSleepTime;
1609 DWORD bytesRead;
1610 int dwBufferSpace;
1611 enum win_wm_message msg;
1612 DWORD param;
1613 HANDLE ev;
1615 SetEvent(wwi->hStartUpEvent);
1617 /* make sleep time to be # of ms to record one packet */
1618 dwSleepTime = ((1 << (wwi->packetSettings & 0xFFFF)) * 1000) / wwi->format.wf.nAvgBytesPerSec;
1619 TRACE("sleeptime=%ld ms\n", dwSleepTime);
1621 for(;;) {
1622 /* Oddly enough, dwBufferSpace is sometimes negative....
1624 * NOTE: If you remove this call to arts_stream_get() and
1625 * remove the && (dwBufferSpace > 0) the code will still
1626 * function correctly. I don't know which way is
1627 * faster/better.
1629 dwBufferSpace = arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SPACE);
1630 TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d), dwBufferSpace=(%d)\n",wwi->lpQueuePtr,wwi->state,dwBufferSpace);
1632 /* read all data is arts input buffer. */
1633 if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING) && (dwBufferSpace > 0))
1635 lpWaveHdr = wwi->lpQueuePtr;
1637 TRACE("read as much as we can\n");
1638 while(wwi->lpQueuePtr)
1640 TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1641 bytesRead = arts_read(wwi->record_stream,
1642 lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
1643 lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
1644 TRACE("bytesRead=%ld\n",bytesRead);
1645 if (bytesRead==0) break;
1647 lpWaveHdr->dwBytesRecorded += bytesRead;
1648 wwi->dwRecordedTotal += bytesRead;
1650 /* buffer full. notify client */
1651 if (lpWaveHdr->dwBytesRecorded >= lpWaveHdr->dwBufferLength)
1653 /* must copy the value of next waveHdr, because we have no idea of what
1654 * will be done with the content of lpWaveHdr in callback
1656 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1658 TRACE("waveHdr full.\n");
1660 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1661 lpWaveHdr->dwFlags |= WHDR_DONE;
1663 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1664 lpWaveHdr = wwi->lpQueuePtr = lpNext;
1669 /* wait for dwSleepTime or an event in thread's queue */
1670 WaitForSingleObject(wwi->msgRing.msg_event, dwSleepTime);
1672 while (ARTS_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &ev))
1674 TRACE("msg=%s param=0x%lx\n",wodPlayerCmdString[msg - WM_USER - 1], param);
1675 switch(msg) {
1676 case WINE_WM_PAUSING:
1677 wwi->state = WINE_WS_PAUSED;
1679 /* Put code here to "pause" arts recording
1682 SetEvent(ev);
1683 break;
1684 case WINE_WM_STARTING:
1685 wwi->state = WINE_WS_PLAYING;
1687 /* Put code here to "start" arts recording
1690 SetEvent(ev);
1691 break;
1692 case WINE_WM_HEADER:
1693 lpWaveHdr = (LPWAVEHDR)param;
1694 /* insert buffer at end of queue */
1696 LPWAVEHDR* wh;
1697 int num_headers = 0;
1698 for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
1700 num_headers++;
1703 *wh=lpWaveHdr;
1705 break;
1706 case WINE_WM_STOPPING:
1707 if (wwi->state != WINE_WS_STOPPED)
1710 /* Put code here to "stop" arts recording
1713 /* return current buffer to app */
1714 lpWaveHdr = wwi->lpQueuePtr;
1715 if (lpWaveHdr)
1717 LPWAVEHDR lpNext = lpWaveHdr->lpNext;
1718 TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1719 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1720 lpWaveHdr->dwFlags |= WHDR_DONE;
1721 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1722 wwi->lpQueuePtr = lpNext;
1725 wwi->state = WINE_WS_STOPPED;
1726 SetEvent(ev);
1727 break;
1728 case WINE_WM_RESETTING:
1729 wwi->state = WINE_WS_STOPPED;
1730 wwi->dwRecordedTotal = 0;
1732 /* return all buffers to the app */
1733 for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
1734 TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
1735 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
1736 lpWaveHdr->dwFlags |= WHDR_DONE;
1738 widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
1740 wwi->lpQueuePtr = NULL;
1741 SetEvent(ev);
1742 break;
1743 case WINE_WM_CLOSING:
1744 wwi->hThread = 0;
1745 wwi->state = WINE_WS_CLOSED;
1746 SetEvent(ev);
1747 ExitThread(0);
1748 /* shouldn't go here */
1749 default:
1750 FIXME("unknown message %d\n", msg);
1751 break;
1755 ExitThread(0);
1756 /* just for not generating compilation warnings... should never be executed */
1757 return 0;
1760 /**************************************************************************
1761 * widOpen [internal]
1763 static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
1765 WINE_WAVEIN* wwi;
1767 TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
1768 if (lpDesc == NULL) {
1769 WARN("Invalid Parametr (lpDesc == NULL)!\n");
1770 return MMSYSERR_INVALPARAM;
1773 if (wDevID >= MAX_WAVEINDRV) {
1774 TRACE ("MAX_WAVEINDRV reached !\n");
1775 return MMSYSERR_BADDEVICEID;
1778 /* if this device is already open tell the app that it is allocated */
1779 if(WInDev[wDevID].record_stream != (arts_stream_t*)-1)
1781 TRACE("device already allocated\n");
1782 return MMSYSERR_ALLOCATED;
1785 /* only PCM format is support so far... */
1786 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
1787 lpDesc->lpFormat->nChannels == 0 ||
1788 lpDesc->lpFormat->nSamplesPerSec == 0) {
1789 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1790 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1791 lpDesc->lpFormat->nSamplesPerSec);
1792 return WAVERR_BADFORMAT;
1795 if (dwFlags & WAVE_FORMAT_QUERY) {
1796 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1797 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
1798 lpDesc->lpFormat->nSamplesPerSec);
1799 return MMSYSERR_NOERROR;
1802 wwi = &WInDev[wDevID];
1804 /* direct sound not supported, ignore the flag */
1805 dwFlags &= ~WAVE_DIRECTSOUND;
1807 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1809 memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
1810 memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
1812 if (wwi->format.wBitsPerSample == 0) {
1813 WARN("Resetting zerod wBitsPerSample\n");
1814 wwi->format.wBitsPerSample = 8 *
1815 (wwi->format.wf.nAvgBytesPerSec /
1816 wwi->format.wf.nSamplesPerSec) /
1817 wwi->format.wf.nChannels;
1820 wwi->record_stream = arts_record_stream(wwi->format.wf.nSamplesPerSec,
1821 wwi->format.wBitsPerSample,
1822 wwi->format.wf.nChannels,
1823 "winearts");
1824 TRACE("(wwi->record_stream=%p)\n",wwi->record_stream);
1825 wwi->state = WINE_WS_STOPPED;
1827 wwi->packetSettings = arts_stream_set(wwi->record_stream, ARTS_P_PACKET_SETTINGS, WAVEIN_PACKET_SETTINGS);
1828 TRACE("Tried to set ARTS_P_PACKET_SETTINGS to (%x), actually set to (%x)\n", WAVEIN_PACKET_SETTINGS, wwi->packetSettings);
1829 TRACE("Buffer size is now (%d)\n", arts_stream_get(wwi->record_stream, ARTS_P_BUFFER_SIZE));
1831 if (wwi->lpQueuePtr) {
1832 WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
1833 wwi->lpQueuePtr = NULL;
1835 arts_stream_set(wwi->record_stream, ARTS_P_BLOCKING, 0); /* disable blocking on this stream */
1837 if(!wwi->record_stream) return MMSYSERR_ALLOCATED;
1839 wwi->dwRecordedTotal = 0;
1840 wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
1842 ARTS_InitRingMessage(&wwi->msgRing);
1844 /* create recorder thread */
1845 if (!(dwFlags & WAVE_DIRECTSOUND)) {
1846 wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
1847 wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
1848 WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
1849 CloseHandle(wwi->hStartUpEvent);
1850 } else {
1851 wwi->hThread = INVALID_HANDLE_VALUE;
1852 wwi->dwThreadID = 0;
1854 wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
1856 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1857 wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
1858 wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
1859 wwi->format.wf.nBlockAlign);
1860 return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
1863 /**************************************************************************
1864 * widClose [internal]
1866 static DWORD widClose(WORD wDevID)
1868 WINE_WAVEIN* wwi;
1870 TRACE("(%u);\n", wDevID);
1871 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1872 WARN("can't close !\n");
1873 return MMSYSERR_INVALHANDLE;
1876 wwi = &WInDev[wDevID];
1878 if (wwi->lpQueuePtr != NULL) {
1879 WARN("still buffers open !\n");
1880 return WAVERR_STILLPLAYING;
1883 ARTS_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
1884 ARTS_CloseWaveInDevice(wwi);
1885 wwi->state = WINE_WS_CLOSED;
1886 ARTS_DestroyRingMessage(&wwi->msgRing);
1887 return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
1890 /**************************************************************************
1891 * widAddBuffer [internal]
1893 static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1895 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1897 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1898 WARN("can't do it !\n");
1899 return MMSYSERR_INVALHANDLE;
1901 if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
1902 TRACE("never been prepared !\n");
1903 return WAVERR_UNPREPARED;
1905 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1906 TRACE("header already in use !\n");
1907 return WAVERR_STILLPLAYING;
1910 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
1911 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1912 lpWaveHdr->dwBytesRecorded = 0;
1913 lpWaveHdr->lpNext = NULL;
1915 ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
1916 return MMSYSERR_NOERROR;
1919 /**************************************************************************
1920 * widPrepare [internal]
1922 static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1924 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1926 if (wDevID >= MAX_WAVEINDRV) return MMSYSERR_INVALHANDLE;
1928 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1929 return WAVERR_STILLPLAYING;
1931 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1932 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1933 lpWaveHdr->dwBytesRecorded = 0;
1935 return MMSYSERR_NOERROR;
1938 /**************************************************************************
1939 * widUnprepare [internal]
1941 static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1943 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1944 if (wDevID >= MAX_WAVEINDRV) {
1945 WARN("bad device ID !\n");
1946 return MMSYSERR_INVALHANDLE;
1949 if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
1950 TRACE("Still playing...\n");
1951 return WAVERR_STILLPLAYING;
1954 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1955 lpWaveHdr->dwFlags |= WHDR_DONE;
1957 return MMSYSERR_NOERROR;
1960 /**************************************************************************
1961 * widStart [internal]
1963 static DWORD widStart(WORD wDevID)
1965 TRACE("(%u);\n", wDevID);
1966 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1967 WARN("can't start recording !\n");
1968 return MMSYSERR_INVALHANDLE;
1971 ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
1972 return MMSYSERR_NOERROR;
1975 /**************************************************************************
1976 * widStop [internal]
1978 static DWORD widStop(WORD wDevID)
1980 TRACE("(%u);\n", wDevID);
1981 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1982 WARN("can't stop !\n");
1983 return MMSYSERR_INVALHANDLE;
1986 ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
1988 return MMSYSERR_NOERROR;
1991 /**************************************************************************
1992 * widReset [internal]
1994 static DWORD widReset(WORD wDevID)
1996 TRACE("(%u);\n", wDevID);
1997 if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
1998 WARN("can't reset !\n");
1999 return MMSYSERR_INVALHANDLE;
2001 ARTS_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
2002 return MMSYSERR_NOERROR;
2005 /**************************************************************************
2006 * widMessage (WINEARTS.6)
2008 DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2009 DWORD dwParam1, DWORD dwParam2)
2011 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2012 wDevID, wMsg, dwUser, dwParam1, dwParam2);
2013 switch (wMsg) {
2014 case DRVM_INIT:
2015 case DRVM_EXIT:
2016 case DRVM_ENABLE:
2017 case DRVM_DISABLE:
2018 /* FIXME: Pretend this is supported */
2019 return 0;
2020 case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
2021 case WIDM_CLOSE: return widClose (wDevID);
2022 case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2023 case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2024 case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
2025 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
2026 case WIDM_GETNUMDEVS: return widGetNumDevs ();
2027 case WIDM_RESET: return widReset (wDevID);
2028 case WIDM_START: return widStart (wDevID);
2029 case WIDM_STOP: return widStop (wDevID);
2030 default:
2031 FIXME("unknown message %d!\n", wMsg);
2033 return MMSYSERR_NOTSUPPORTED;
2036 /*======================================================================*
2037 * Low level DSOUND implementation *
2038 *======================================================================*/
2039 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
2041 /* we can't perform memory mapping as we don't have a file stream
2042 interface with arts like we do with oss */
2043 MESSAGE("This sound card's driver does not support direct access\n");
2044 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2045 return MMSYSERR_NOTSUPPORTED;
2048 static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
2050 memset(desc, 0, sizeof(*desc));
2051 strcpy(desc->szDesc, "Wine aRts DirectSound Driver");
2052 strcpy(desc->szDrvName, "winearts.drv");
2053 return MMSYSERR_NOERROR;
2056 static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
2058 memcpy(pGuid, &DSDEVID_DefaultPlayback, sizeof(GUID));
2059 return MMSYSERR_NOERROR;
2062 #else /* !HAVE_ARTS */
2064 /**************************************************************************
2065 * wodMessage (WINEARTS.@)
2067 DWORD WINAPI ARTS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
2068 DWORD dwParam1, DWORD dwParam2)
2070 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2071 return MMSYSERR_NOTENABLED;
2074 /**************************************************************************
2075 * widMessage (WINEARTS.6)
2077 DWORD WINAPI ARTS_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
2078 DWORD dwParam1, DWORD dwParam2)
2080 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
2081 return MMSYSERR_NOTENABLED;
2084 #endif /* HAVE_ARTS */