1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Wine Driver for NAS Network Audio System
4 * http://radscan.com/nas.html
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)
10 * 2002 Nicolas Escuder (NAS version of this file)
12 * Copyright 2002 Nicolas Escuder <n.escuder@alineanet.com>
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * with nas we cannot stop the audio that is already in
33 * pause in waveOut does not work correctly in loop mode
47 //#define EMULATE_SB16
48 #define FRAG_SIZE 1024
51 /* avoid type conflicts */
56 #ifdef HAVE_AUDIO_AUDIOLIB_H
57 #include <audio/audiolib.h>
59 #ifdef HAVE_AUDIO_SOUNDLIB_H
60 #include <audio/soundlib.h>
70 #include "wine/winuser16.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(wave);
79 /* Allow 1% deviation for sample rates (some ES137x cards) */
80 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
84 static AuServer *AuServ;
86 #define MAX_WAVEOUTDRV (1)
88 /* state diagram for waveOut writing:
90 * +---------+-------------+---------------+---------------------------------+
91 * | state | function | event | new state |
92 * +---------+-------------+---------------+---------------------------------+
93 * | | open() | | STOPPED |
94 * | PAUSED | write() | | PAUSED |
95 * | STOPPED | write() | <thrd create> | PLAYING |
96 * | PLAYING | write() | HEADER | PLAYING |
97 * | (other) | write() | <error> | |
98 * | (any) | pause() | PAUSING | PAUSED |
99 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
100 * | (any) | reset() | RESETTING | STOPPED |
101 * | (any) | close() | CLOSING | CLOSED |
102 * +---------+-------------+---------------+---------------------------------+
105 /* states of the playing device */
106 #define WINE_WS_PLAYING 0
107 #define WINE_WS_PAUSED 1
108 #define WINE_WS_STOPPED 2
109 #define WINE_WS_CLOSED 3
111 /* events to be send to device */
112 enum win_wm_message {
113 WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
114 WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
118 enum win_wm_message msg; /* message identifier */
119 DWORD param; /* parameter for this message */
120 HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
123 /* implement an in-process message ring for better performance
124 * (compared to passing thru the server)
125 * this ring will be used by the input (resp output) record (resp playback) routine
128 #define NAS_RING_BUFFER_SIZE 30
129 RING_MSG messages[NAS_RING_BUFFER_SIZE];
133 CRITICAL_SECTION msg_crst;
137 volatile int state; /* one of the WINE_WS_ manifest constants */
138 WAVEOPENDESC waveDesc;
140 PCMWAVEFORMAT format;
154 DWORD BufferSize; /* size of whole buffer in bytes */
159 DWORD volume_left; /* volume control information */
162 LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
163 LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
165 LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
166 DWORD dwLoops; /* private copy of loop counter */
168 DWORD PlayedTotal; /* number of bytes actually played since opening */
169 DWORD WrittenTotal; /* number of bytes written to the audio device since opening */
171 /* synchronization stuff */
172 HANDLE hStartUpEvent;
178 static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
180 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
184 static AuBool event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd);
185 static int nas_init(void);
186 static int nas_end(void);
188 static int nas_finddev(WINE_WAVEOUT* wwo);
189 static int nas_open(WINE_WAVEOUT* wwo);
190 static int nas_free(WINE_WAVEOUT* wwo);
191 static int nas_close(WINE_WAVEOUT* wwo);
192 static void buffer_resize(WINE_WAVEOUT* wwo, int len);
193 static int nas_add_buffer(WINE_WAVEOUT* wwo);
194 static int nas_send_buffer(WINE_WAVEOUT* wwo);
196 /* These strings used only for tracing */
197 static const char *wodPlayerCmdString[] = {
199 "WINE_WM_RESTARTING",
207 static char *nas_event_types[] = {
217 static char *nas_elementnotify_kinds[] = {
224 static char *nas_states[] = {
231 static char *nas_reasons[] = {
241 static char* nas_reason(unsigned int reason)
243 if (reason > 6) reason = 6;
244 return nas_reasons[reason];
247 static char* nas_elementnotify_kind(unsigned int kind)
249 if (kind > 2) kind = 3;
250 return nas_elementnotify_kinds[kind];
254 static char* nas_event_type(unsigned int type)
256 if (type > 6) type = 0;
257 return nas_event_types[type];
261 static char* nas_state(unsigned int state)
263 if (state > 3) state = 3;
264 return nas_states[state];
267 /*======================================================================*
268 * Low level WAVE implementation *
269 *======================================================================*/
271 /* Volume functions derived from Alsaplayer source */
272 /* length is the number of 16 bit samples */
273 void volume_effect16(void *bufin, void* bufout, int length, int left,
274 int right, int nChannels)
276 short *d_out = (short *)bufout;
277 short *d_in = (short *)bufin;
281 TRACE("length == %d, nChannels == %d\n", length, nChannels);
284 if (right == -1) right = left;
286 for(i = 0; i < length; i+=(nChannels))
288 v = (int) ((*(d_in++) * left) / 100);
289 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
292 v = (int) ((*(d_in++) * right) / 100);
293 *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
298 /* length is the number of 8 bit samples */
299 void volume_effect8(void *bufin, void* bufout, int length, int left,
300 int right, int nChannels)
302 char *d_out = (char *)bufout;
303 char *d_in = (char *)bufin;
307 TRACE("length == %d, nChannels == %d\n", length, nChannels);
310 if (right == -1) right = left;
312 for(i = 0; i < length; i+=(nChannels))
314 v = (char) ((*(d_in++) * left) / 100);
315 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
318 v = (char) ((*(d_in++) * right) / 100);
319 *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
324 /******************************************************************
328 void NAS_CloseDevice(WINE_WAVEOUT* wwo)
330 TRACE("NAS_CloseDevice\n");
334 /******************************************************************
337 LONG NAS_WaveClose(void)
339 nas_end(); /* free up nas server */
343 /******************************************************************
346 * Initialize internal structures from NAS server info
348 LONG NAS_WaveInit(void)
353 /* initialize all device handles to -1 */
354 for (i = 0; i < MAX_WAVEOUTDRV; ++i)
356 memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */
358 WOutDev[i].AuServ = AuServ;
359 WOutDev[i].AuDev = AuNone;
361 /* FIXME: some programs compare this string against the content of the registry
362 * for MM drivers. The names have to match in order for the program to work
363 * (e.g. MS win9x mplayer.exe)
366 WOutDev[i].caps.wMid = 0x0002;
367 WOutDev[i].caps.wPid = 0x0104;
368 strcpy(WOutDev[i].caps.szPname, "SB16 Wave Out");
370 WOutDev[i].caps.wMid = 0x00FF; /* Manufac ID */
371 WOutDev[i].caps.wPid = 0x0001; /* Product ID */
372 /* strcpy(WOutDev[i].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/
373 strcpy(WOutDev[i].caps.szPname, "CS4236/37/38");
375 WOutDev[i].AuFlow = 0;
376 WOutDev[i].caps.vDriverVersion = 0x0100;
377 WOutDev[i].caps.dwFormats = 0x00000000;
378 WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
380 WOutDev[i].caps.wChannels = 2;
381 WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;
383 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
384 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
385 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
386 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
387 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
388 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
389 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
390 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
391 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
392 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
393 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
394 WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
401 /******************************************************************
402 * NAS_InitRingMessage
404 * Initialize the ring of messages for passing between driver's caller and playback/record
407 static int NAS_InitRingMessage(MSG_RING* mr)
411 mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
412 memset(mr->messages, 0, sizeof(RING_MSG) * NAS_RING_BUFFER_SIZE);
413 InitializeCriticalSection(&mr->msg_crst);
417 /******************************************************************
418 * NAS_DestroyRingMessage
421 static int NAS_DestroyRingMessage(MSG_RING* mr)
423 CloseHandle(mr->msg_event);
424 DeleteCriticalSection(&mr->msg_crst);
428 /******************************************************************
431 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
433 static int NAS_AddRingMessage(MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
435 HANDLE hEvent = INVALID_HANDLE_VALUE;
437 EnterCriticalSection(&mr->msg_crst);
438 if ((mr->msg_toget == ((mr->msg_tosave + 1) % NAS_RING_BUFFER_SIZE))) /* buffer overflow? */
440 ERR("buffer overflow !?\n");
441 LeaveCriticalSection(&mr->msg_crst);
446 hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
447 if (hEvent == INVALID_HANDLE_VALUE)
449 ERR("can't create event !?\n");
450 LeaveCriticalSection(&mr->msg_crst);
453 if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
454 FIXME("two fast messages in the queue!!!!\n");
456 /* fast messages have to be added at the start of the queue */
457 mr->msg_toget = (mr->msg_toget + NAS_RING_BUFFER_SIZE - 1) % NAS_RING_BUFFER_SIZE;
459 mr->messages[mr->msg_toget].msg = msg;
460 mr->messages[mr->msg_toget].param = param;
461 mr->messages[mr->msg_toget].hEvent = hEvent;
465 mr->messages[mr->msg_tosave].msg = msg;
466 mr->messages[mr->msg_tosave].param = param;
467 mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
468 mr->msg_tosave = (mr->msg_tosave + 1) % NAS_RING_BUFFER_SIZE;
471 LeaveCriticalSection(&mr->msg_crst);
473 SetEvent(mr->msg_event); /* signal a new message */
477 /* wait for playback/record thread to have processed the message */
478 WaitForSingleObject(hEvent, INFINITE);
485 /******************************************************************
486 * NAS_RetrieveRingMessage
488 * Get a message from the ring. Should be called by the playback/record thread.
490 static int NAS_RetrieveRingMessage(MSG_RING* mr,
491 enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
493 EnterCriticalSection(&mr->msg_crst);
495 if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
497 LeaveCriticalSection(&mr->msg_crst);
501 *msg = mr->messages[mr->msg_toget].msg;
502 mr->messages[mr->msg_toget].msg = 0;
503 *param = mr->messages[mr->msg_toget].param;
504 *hEvent = mr->messages[mr->msg_toget].hEvent;
505 mr->msg_toget = (mr->msg_toget + 1) % NAS_RING_BUFFER_SIZE;
506 LeaveCriticalSection(&mr->msg_crst);
510 /*======================================================================*
511 * Low level WAVE OUT implementation *
512 *======================================================================*/
514 /**************************************************************************
515 * wodNotifyClient [internal]
517 static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
519 TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
525 if (wwo->wFlags != DCB_NULL &&
526 !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave,
527 wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
528 WARN("can't notify client !\n");
529 return MMSYSERR_ERROR;
533 FIXME("Unknown callback message %u\n", wMsg);
534 return MMSYSERR_INVALPARAM;
536 return MMSYSERR_NOERROR;
539 /**************************************************************************
540 * wodUpdatePlayedTotal [internal]
543 static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
545 wwo->PlayedTotal = wwo->WrittenTotal;
549 /**************************************************************************
550 * wodPlayer_BeginWaveHdr [internal]
552 * Makes the specified lpWaveHdr the currently playing wave header.
553 * If the specified wave header is a begin loop and we're not already in
554 * a loop, setup the loop.
556 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
558 wwo->lpPlayPtr = lpWaveHdr;
560 if (!lpWaveHdr) return;
562 if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
563 if (wwo->lpLoopPtr) {
564 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
565 TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
567 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
568 wwo->lpLoopPtr = lpWaveHdr;
569 /* Windows does not touch WAVEHDR.dwLoops,
570 * so we need to make an internal copy */
571 wwo->dwLoops = lpWaveHdr->dwLoops;
576 /**************************************************************************
577 * wodPlayer_PlayPtrNext [internal]
579 * Advance the play pointer to the next waveheader, looping if required.
581 static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
583 LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
585 if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
586 /* We're at the end of a loop, loop if required */
587 if (--wwo->dwLoops > 0) {
588 wwo->lpPlayPtr = wwo->lpLoopPtr;
590 /* Handle overlapping loops correctly */
591 if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
592 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
593 /* shall we consider the END flag for the closing loop or for
594 * the opening one or for both ???
595 * code assumes for closing loop only
598 lpWaveHdr = lpWaveHdr->lpNext;
600 wwo->lpLoopPtr = NULL;
601 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
604 /* We're not in a loop. Advance to the next wave header */
605 wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
610 /**************************************************************************
611 * wodPlayer_NotifyCompletions [internal]
613 * Notifies and remove from queue all wavehdrs which have been played to
614 * the speaker (ie. they have cleared the audio device). If force is true,
615 * we notify all wavehdrs and remove them all from the queue even if they
616 * are unplayed or part of a loop.
618 static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
622 /* Start from lpQueuePtr and keep notifying until:
623 * - we hit an unwritten wavehdr
624 * - we hit the beginning of a running loop
625 * - we hit a wavehdr which hasn't finished playing
627 wodUpdatePlayedTotal(wwo);
629 while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr &&
630 lpWaveHdr != wwo->lpLoopPtr && lpWaveHdr->reserved <= wwo->PlayedTotal))) {
632 wwo->lpQueuePtr = lpWaveHdr->lpNext;
634 lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
635 lpWaveHdr->dwFlags |= WHDR_DONE;
637 wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
639 return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
643 /**************************************************************************
644 * wodPlayer_Reset [internal]
646 * wodPlayer helper. Resets current output stream.
648 static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
650 wodUpdatePlayedTotal(wwo);
651 wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
653 /* we aren't able to flush any data that has already been written */
654 /* to nas, otherwise we would do the flushing here */
659 enum win_wm_message msg;
663 /* remove any buffer */
664 wodPlayer_NotifyCompletions(wwo, TRUE);
666 wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
667 wwo->state = WINE_WS_STOPPED;
668 wwo->PlayedTotal = wwo->WrittenTotal = 0;
670 /* remove any existing message in the ring */
671 EnterCriticalSection(&wwo->msgRing.msg_crst);
673 /* return all pending headers in queue */
674 while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
676 TRACE("flushing msg\n");
677 if (msg != WINE_WM_HEADER)
679 FIXME("shouldn't have headers left\n");
683 ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
684 ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
686 wodNotifyClient(wwo, WOM_DONE, param, 0);
688 ResetEvent(wwo->msgRing.msg_event);
689 LeaveCriticalSection(&wwo->msgRing.msg_crst);
691 if (wwo->lpLoopPtr) {
692 /* complicated case, not handled yet (could imply modifying the loop counter */
693 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
694 wwo->lpPlayPtr = wwo->lpLoopPtr;
695 wwo->WrittenTotal = wwo->PlayedTotal; /* this is wrong !!! */
697 /* the data already written is going to be played, so take */
698 /* this fact into account here */
699 wwo->PlayedTotal = wwo->WrittenTotal;
701 wwo->state = WINE_WS_PAUSED;
705 /**************************************************************************
706 * wodPlayer_ProcessMessages [internal]
708 static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
711 enum win_wm_message msg;
715 while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
716 TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
718 case WINE_WM_PAUSING:
719 wodPlayer_Reset(wwo, FALSE);
722 case WINE_WM_RESTARTING:
723 wwo->state = WINE_WS_PLAYING;
727 lpWaveHdr = (LPWAVEHDR)param;
729 /* insert buffer at the end of queue */
732 for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
736 wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
737 if (wwo->state == WINE_WS_STOPPED)
738 wwo->state = WINE_WS_PLAYING;
740 case WINE_WM_RESETTING:
741 wodPlayer_Reset(wwo, TRUE);
745 wodUpdatePlayedTotal(wwo);
748 case WINE_WM_BREAKLOOP:
749 if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
750 /* ensure exit at end of current loop */
755 case WINE_WM_CLOSING:
756 // sanity check: this should not happen since the device must have been reset before
757 if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
759 wwo->state = WINE_WS_CLOSED;
764 FIXME("unknown message %d\n", msg);
770 /**************************************************************************
771 * wodPlayer [internal]
773 static DWORD CALLBACK wodPlayer(LPVOID pmt)
775 WORD uDevID = (DWORD)pmt;
776 WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
778 wwo->state = WINE_WS_STOPPED;
779 SetEvent(wwo->hStartUpEvent);
783 if (wwo->FlowStarted) {
784 AuHandleEvents(wwo->AuServ);
786 if (wwo->state == WINE_WS_PLAYING && wwo->freeBytes && wwo->BufferUsed)
787 nas_send_buffer(wwo);
790 if (wwo->BufferUsed <= FRAG_SIZE && wwo->writeBytes > 0)
791 wodPlayer_NotifyCompletions(wwo, FALSE);
793 WaitForSingleObject(wwo->msgRing.msg_event, 20);
794 wodPlayer_ProcessMessages(wwo);
796 while(wwo->lpPlayPtr) {
797 wwo->lpPlayPtr->reserved = wwo->WrittenTotal + wwo->lpPlayPtr->dwBufferLength;
799 wodPlayer_PlayPtrNext(wwo);
805 /**************************************************************************
806 * wodGetDevCaps [internal]
808 static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
810 TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
812 if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
814 if (wDevID >= MAX_WAVEOUTDRV) {
815 TRACE("MAX_WAVOUTDRV reached !\n");
816 return MMSYSERR_BADDEVICEID;
819 memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
820 return MMSYSERR_NOERROR;
823 /**************************************************************************
826 static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
830 TRACE("wodOpen (%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
832 if (lpDesc == NULL) {
833 WARN("Invalid Parameter !\n");
834 return MMSYSERR_INVALPARAM;
836 if (wDevID >= MAX_WAVEOUTDRV) {
837 TRACE("MAX_WAVOUTDRV reached !\n");
838 return MMSYSERR_BADDEVICEID;
841 /* if this device is already open tell the app that it is allocated */
843 wwo = &WOutDev[wDevID];
847 TRACE("device already allocated\n");
848 return MMSYSERR_ALLOCATED;
852 /* only PCM format is supported so far... */
853 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
854 lpDesc->lpFormat->nChannels == 0 ||
855 lpDesc->lpFormat->nSamplesPerSec == 0) {
856 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
857 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
858 lpDesc->lpFormat->nSamplesPerSec);
859 return WAVERR_BADFORMAT;
862 if (dwFlags & WAVE_FORMAT_QUERY) {
863 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
864 lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
865 lpDesc->lpFormat->nSamplesPerSec);
866 return MMSYSERR_NOERROR;
869 /* direct sound not supported, ignore the flag */
870 dwFlags &= ~WAVE_DIRECTSOUND;
872 wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
874 memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
875 memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
877 if (wwo->format.wBitsPerSample == 0) {
878 WARN("Resetting zeroed wBitsPerSample\n");
879 wwo->format.wBitsPerSample = 8 *
880 (wwo->format.wf.nAvgBytesPerSec /
881 wwo->format.wf.nSamplesPerSec) /
882 wwo->format.wf.nChannels;
886 return MMSYSERR_ALLOCATED;
888 NAS_InitRingMessage(&wwo->msgRing);
890 /* create player thread */
891 if (!(dwFlags & WAVE_DIRECTSOUND)) {
892 wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
893 wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
894 WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
895 CloseHandle(wwo->hStartUpEvent);
897 wwo->hThread = INVALID_HANDLE_VALUE;
900 wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
902 TRACE("stream=0x%lx, BufferSize=%ld\n", (long)wwo->AuServ, wwo->BufferSize);
904 TRACE("wBitsPerSample=%u nAvgBytesPerSec=%lu nSamplesPerSec=%lu nChannels=%u nBlockAlign=%u\n",
905 wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
906 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
907 wwo->format.wf.nBlockAlign);
909 return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
912 /**************************************************************************
913 * wodClose [internal]
915 static DWORD wodClose(WORD wDevID)
917 DWORD ret = MMSYSERR_NOERROR;
920 TRACE("(%u);\n", wDevID);
922 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
924 WARN("bad device ID !\n");
925 return MMSYSERR_BADDEVICEID;
928 wwo = &WOutDev[wDevID];
929 if (wwo->lpQueuePtr) {
930 WARN("buffers still playing !\n");
931 ret = WAVERR_STILLPLAYING;
933 TRACE("imhere[3-close]\n");
934 if (wwo->hThread != INVALID_HANDLE_VALUE) {
935 NAS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
938 NAS_DestroyRingMessage(&wwo->msgRing);
940 NAS_CloseDevice(wwo); /* close the stream and clean things up */
942 ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
947 /**************************************************************************
948 * wodWrite [internal]
951 static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
953 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
955 /* first, do the sanity checks... */
956 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
958 WARN("bad dev ID !\n");
959 return MMSYSERR_BADDEVICEID;
962 if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
964 TRACE("unprepared\n");
965 return WAVERR_UNPREPARED;
968 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
970 TRACE("still playing\n");
971 return WAVERR_STILLPLAYING;
974 lpWaveHdr->dwFlags &= ~WHDR_DONE;
975 lpWaveHdr->dwFlags |= WHDR_INQUEUE;
976 lpWaveHdr->lpNext = 0;
978 TRACE("adding ring message\n");
979 NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
981 return MMSYSERR_NOERROR;
984 /**************************************************************************
985 * wodPrepare [internal]
987 static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
989 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
991 if (wDevID >= MAX_WAVEOUTDRV) {
992 WARN("bad device ID !\n");
993 return MMSYSERR_BADDEVICEID;
996 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
997 return WAVERR_STILLPLAYING;
999 lpWaveHdr->dwFlags |= WHDR_PREPARED;
1000 lpWaveHdr->dwFlags &= ~WHDR_DONE;
1001 return MMSYSERR_NOERROR;
1004 /**************************************************************************
1005 * wodUnprepare [internal]
1007 static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
1009 TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
1011 if (wDevID >= MAX_WAVEOUTDRV) {
1012 WARN("bad device ID !\n");
1013 return MMSYSERR_BADDEVICEID;
1016 if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
1017 return WAVERR_STILLPLAYING;
1019 lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
1020 lpWaveHdr->dwFlags |= WHDR_DONE;
1022 return MMSYSERR_NOERROR;
1025 /**************************************************************************
1026 * wodPause [internal]
1028 static DWORD wodPause(WORD wDevID)
1030 TRACE("(%u);!\n", wDevID);
1032 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1034 WARN("bad device ID !\n");
1035 return MMSYSERR_BADDEVICEID;
1038 TRACE("imhere[3-PAUSING]\n");
1039 NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
1041 return MMSYSERR_NOERROR;
1044 /**************************************************************************
1045 * wodRestart [internal]
1047 static DWORD wodRestart(WORD wDevID)
1049 TRACE("(%u);\n", wDevID);
1051 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1053 WARN("bad device ID !\n");
1054 return MMSYSERR_BADDEVICEID;
1057 if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
1058 TRACE("imhere[3-RESTARTING]\n");
1059 NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
1062 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1063 /* FIXME: Myst crashes with this ... hmm -MM
1064 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1067 return MMSYSERR_NOERROR;
1070 /**************************************************************************
1071 * wodReset [internal]
1073 static DWORD wodReset(WORD wDevID)
1075 TRACE("(%u);\n", wDevID);
1077 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1079 WARN("bad device ID !\n");
1080 return MMSYSERR_BADDEVICEID;
1083 TRACE("imhere[3-RESET]\n");
1084 NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
1086 return MMSYSERR_NOERROR;
1089 /**************************************************************************
1090 * wodGetPosition [internal]
1092 static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
1098 TRACE("%u, %p, %lu);\n", wDevID, lpTime, uSize);
1100 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1102 WARN("bad device ID !\n");
1103 return MMSYSERR_BADDEVICEID;
1106 if (lpTime == NULL) return MMSYSERR_INVALPARAM;
1108 wwo = &WOutDev[wDevID];
1109 // NAS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
1110 val = wwo->WrittenTotal;
1112 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1113 lpTime->wType, wwo->format.wBitsPerSample,
1114 wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
1115 wwo->format.wf.nAvgBytesPerSec);
1116 TRACE("PlayedTotal=%lu\n", val);
1118 switch (lpTime->wType) {
1121 TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
1124 lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample / wwo->format.wf.nChannels;
1125 TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
1128 time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1129 lpTime->u.smpte.hour = time / 108000;
1130 time -= lpTime->u.smpte.hour * 108000;
1131 lpTime->u.smpte.min = time / 1800;
1132 time -= lpTime->u.smpte.min * 1800;
1133 lpTime->u.smpte.sec = time / 30;
1134 time -= lpTime->u.smpte.sec * 30;
1135 lpTime->u.smpte.frame = time;
1136 lpTime->u.smpte.fps = 30;
1137 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1138 lpTime->u.smpte.hour, lpTime->u.smpte.min,
1139 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
1142 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
1143 lpTime->wType = TIME_MS;
1145 lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
1146 TRACE("TIME_MS=%lu\n", lpTime->u.ms);
1149 return MMSYSERR_NOERROR;
1152 /**************************************************************************
1153 * wodBreakLoop [internal]
1155 static DWORD wodBreakLoop(WORD wDevID)
1157 TRACE("(%u);\n", wDevID);
1159 if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
1161 WARN("bad device ID !\n");
1162 return MMSYSERR_BADDEVICEID;
1164 NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
1165 return MMSYSERR_NOERROR;
1168 /**************************************************************************
1169 * wodGetVolume [internal]
1171 static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
1175 left = WOutDev[wDevID].volume_left;
1176 right = WOutDev[wDevID].volume_right;
1178 TRACE("(%u, %p);\n", wDevID, lpdwVol);
1180 *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
1182 return MMSYSERR_NOERROR;
1185 /**************************************************************************
1186 * wodSetVolume [internal]
1188 static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
1192 left = (LOWORD(dwParam) * 100) / 0xFFFFl;
1193 right = (HIWORD(dwParam) * 100) / 0xFFFFl;
1195 TRACE("(%u, %08lX);\n", wDevID, dwParam);
1197 WOutDev[wDevID].volume_left = left;
1198 WOutDev[wDevID].volume_right = right;
1200 return MMSYSERR_NOERROR;
1203 /**************************************************************************
1204 * wodGetNumDevs [internal]
1206 static DWORD wodGetNumDevs(void)
1208 return MAX_WAVEOUTDRV;
1211 /**************************************************************************
1212 * wodMessage (WINENAS.@)
1214 DWORD WINAPI NAS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
1215 DWORD dwParam1, DWORD dwParam2)
1217 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1224 /* FIXME: Pretend this is supported */
1226 case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
1227 case WODM_CLOSE: return wodClose (wDevID);
1228 case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1229 case WODM_PAUSE: return wodPause (wDevID);
1230 case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
1231 case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
1232 case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1233 case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
1234 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
1235 case WODM_GETNUMDEVS: return wodGetNumDevs ();
1236 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
1237 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
1238 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1239 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
1240 case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
1241 case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
1242 case WODM_RESTART: return wodRestart (wDevID);
1243 case WODM_RESET: return wodReset (wDevID);
1245 case DRV_QUERYDSOUNDIFACE: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
1247 FIXME("unknown message %d!\n", wMsg);
1249 return MMSYSERR_NOTSUPPORTED;
1252 /*======================================================================*
1253 * Low level DSOUND implementation *
1254 *======================================================================*/
1255 static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
1257 /* we can't perform memory mapping as we don't have a file stream
1258 interface with nas like we do with oss */
1259 MESSAGE("This sound card s driver does not support direct access\n");
1260 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
1261 return MMSYSERR_NOTSUPPORTED;
1264 static int nas_init(void) {
1265 TRACE("NAS INIT\n");
1266 if (!(AuServ = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL)))
1272 static int nas_finddev(WINE_WAVEOUT* wwo) {
1275 for (i = 0; i < AuServerNumDevices(wwo->AuServ); i++) {
1276 if ((AuDeviceKind(AuServerDevice(wwo->AuServ, i)) ==
1277 AuComponentKindPhysicalOutput) &&
1278 AuDeviceNumTracks(AuServerDevice(wwo->AuServ, i)) == wwo->format.wf.nChannels)
1280 wwo->AuDev = AuDeviceIdentifier(AuServerDevice(wwo->AuServ, i));
1285 if (wwo->AuDev == AuNone)
1290 static int nas_open(WINE_WAVEOUT* wwo) {
1291 AuElement elements[3];
1296 if (!nas_finddev(wwo))
1299 if (!(wwo->AuFlow = AuCreateFlow(wwo->AuServ, NULL)))
1302 wwo->BufferSize = FRAG_SIZE * FRAG_COUNT;
1304 AuMakeElementImportClient(&elements[0], wwo->format.wf.nSamplesPerSec,
1305 wwo->format.wBitsPerSample == 16 ? AuFormatLinearSigned16LSB : AuFormatLinearUnsigned8,
1306 wwo->format.wf.nChannels, AuTrue, wwo->BufferSize, wwo->BufferSize / 2, 0, NULL);
1308 AuMakeElementExportDevice(&elements[1], 0, wwo->AuDev, wwo->format.wf.nSamplesPerSec,
1309 AuUnlimitedSamples, 0, NULL);
1311 AuSetElements(wwo->AuServ, wwo->AuFlow, AuTrue, 2, elements, NULL);
1313 AuRegisterEventHandler(wwo->AuServ, AuEventHandlerIDMask, 0, wwo->AuFlow,
1314 event_handler, (AuPointer) wwo);
1317 wwo->PlayedTotal = 0;
1318 wwo->WrittenTotal = 0;
1321 wwo->BufferUsed = 0;
1322 wwo->writeBytes = 0;
1325 wwo->SoundBuffer = NULL;
1326 wwo->FlowStarted = 0;
1328 AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
1329 AuPauseFlow(wwo->AuServ, wwo->AuFlow, NULL);
1330 wwo->FlowStarted = 1;
1336 event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
1338 WINE_WAVEOUT *wwo = (WINE_WAVEOUT *)hnd->data;
1341 case AuEventTypeElementNotify: {
1342 AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
1345 switch (event->kind) {
1346 case AuElementNotifyKindLowWater:
1347 wwo->freeBytes += event->num_bytes;
1348 if (wwo->writeBytes > 0)
1349 wwo->sendBytes += event->num_bytes;
1350 if (wwo->freeBytes && wwo->BufferUsed)
1351 nas_send_buffer(wwo);
1354 case AuElementNotifyKindState:
1355 TRACE("ev: kind %s state %s->%s reason %s numbytes %ld freeB %lu\n",
1356 nas_elementnotify_kind(event->kind),
1357 nas_state(event->prev_state),
1358 nas_state(event->cur_state),
1359 nas_reason(event->reason),
1360 event->num_bytes, wwo->freeBytes);
1362 if (event->cur_state == AuStatePause && event->reason != AuReasonUser) {
1363 wwo->freeBytes += event->num_bytes;
1364 if (wwo->writeBytes > 0)
1365 wwo->sendBytes += event->num_bytes;
1366 if (wwo->sendBytes > wwo->writeBytes)
1367 wwo->sendBytes = wwo->writeBytes;
1368 if (wwo->freeBytes && wwo->BufferUsed)
1369 nas_send_buffer(wwo);
1379 buffer_resize(WINE_WAVEOUT* wwo, int len)
1381 void *newbuf = malloc(wwo->BufferUsed + len);
1382 void *oldbuf = wwo->SoundBuffer;
1383 memcpy(newbuf, oldbuf, wwo->BufferUsed);
1384 wwo->SoundBuffer = newbuf;
1389 static int nas_add_buffer(WINE_WAVEOUT* wwo) {
1390 int len = wwo->lpPlayPtr->dwBufferLength;
1392 buffer_resize(wwo, len);
1393 memcpy(wwo->SoundBuffer + wwo->BufferUsed, wwo->lpPlayPtr->lpData, len);
1394 wwo->BufferUsed += len;
1395 wwo->WrittenTotal += len;
1399 static int nas_send_buffer(WINE_WAVEOUT* wwo) {
1401 char *ptr, *newdata;
1405 if (wwo->freeBytes <= 0)
1408 if (wwo->SoundBuffer == NULL || wwo->BufferUsed == 0) {
1412 if (wwo->BufferUsed <= wwo->freeBytes) {
1413 len = wwo->BufferUsed;
1414 ptr = wwo->SoundBuffer;
1416 len = wwo->freeBytes;
1418 memcpy(ptr,wwo->SoundBuffer,len);
1419 newdata = malloc(wwo->BufferUsed - len);
1420 memcpy(newdata, wwo->SoundBuffer + len, wwo->BufferUsed - len);
1423 TRACE("envoye de %d bytes / %lu bytes / freeBytes %lu\n", len, wwo->BufferUsed, wwo->freeBytes);
1425 AuWriteElement(wwo->AuServ, wwo->AuFlow, 0, len, ptr, AuFalse, NULL);
1427 wwo->BufferUsed -= len;
1428 wwo->freeBytes -= len;
1429 wwo->writeBytes += len;
1433 wwo->SoundBuffer = NULL;
1435 if (newdata != NULL)
1436 wwo->SoundBuffer = newdata;
1441 static int nas_free(WINE_WAVEOUT* wwo)
1444 if (!wwo->FlowStarted && wwo->BufferUsed) {
1445 AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
1446 wwo->FlowStarted = 1;
1449 while (wwo->BufferUsed || wwo->writeBytes != wwo->sendBytes) {
1451 nas_send_buffer(wwo);
1452 AuHandleEvents(wwo->AuServ);
1455 AuFlush(wwo->AuServ);
1459 static int nas_close(WINE_WAVEOUT* wwo)
1465 AuStopFlow(wwo->AuServ, wwo->AuFlow, NULL);
1466 AuDestroyFlow(wwo->AuServ, wwo->AuFlow, NULL);
1467 AuFlush(wwo->AuServ);
1468 AuNextEvent(wwo->AuServ, AuTrue, &ev);
1469 AuDispatchEvent(wwo->AuServ, &ev);
1473 wwo->BufferUsed = 0;
1475 wwo->SoundBuffer = NULL;
1479 static int nas_end(void)
1481 AuCloseServer(AuServ);
1486 #else /* !HAVE_NAS */
1488 /**************************************************************************
1489 * wodMessage (WINENAS.@)
1491 DWORD WINAPI NAS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
1493 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1494 return MMSYSERR_NOTENABLED;