audio.c 44.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
 * Wine Driver for NAS Network Audio System
 *   http://radscan.com/nas.html
 *
 * Copyright 1994 Martin Ayotte
 *           1999 Eric Pouech (async playing in waveOut/waveIn)
 *	     2000 Eric Pouech (loops in waveOut)
 *	     2002 Chris Morgan (aRts version of this file)
 *           2002 Nicolas Escuder (NAS version of this file)
 *
 * Copyright 2002 Nicolas Escuder <n.escuder@alineanet.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
26
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 28 29 30 31 32 33 34 35 36 37 38 39
 */
/* NOTE:
 *    with nas we cannot stop the audio that is already in
 *    the servers buffer.
 *
 * FIXME:
 *	pause in waveOut does not work correctly in loop mode
 *
 */

#include "config.h"

#include <stdlib.h>
40
#include <stdarg.h>
41 42
#include <stdio.h>
#include <string.h>
43 44 45 46 47 48
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
49
#include <fcntl.h>
50
#include <math.h>
51

52 53
#define FRAG_SIZE  1024
#define FRAG_COUNT 10
54 55

/* avoid type conflicts */
56
#define INT8 X_INT8
57
#define INT16 X_INT16
58
#define INT32 X_INT32
59
#define INT64 X_INT64
60 61 62 63 64 65 66 67
#define BOOL X_BOOL
#define BYTE X_BYTE
#ifdef HAVE_AUDIO_AUDIOLIB_H
#include <audio/audiolib.h>
#endif
#ifdef HAVE_AUDIO_SOUNDLIB_H
#include <audio/soundlib.h>
#endif
68
#undef INT8
69
#undef INT16
70
#undef INT32
71 72
#undef INT64
#undef LONG64
73 74 75 76
#undef BOOL
#undef BYTE

#include "windef.h"
77
#include "winbase.h"
78
#include "wingdi.h"
79
#include "winuser.h"
80 81 82 83 84
#include "winerror.h"
#include "mmddk.h"
#include "dsound.h"
#include "dsdriver.h"
#include "nas.h"
85
#include "wine/unicode.h"
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(wave);

/* Allow 1% deviation for sample rates (some ES137x cards) */
#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)

#ifdef HAVE_NAS

static AuServer         *AuServ;

#define MAX_WAVEOUTDRV 	(1)

/* state diagram for waveOut writing:
 *
 * +---------+-------------+---------------+---------------------------------+
 * |  state  |  function   |     event     |            new state	     |
 * +---------+-------------+---------------+---------------------------------+
 * |	     | open()	   |		   | STOPPED		       	     |
 * | PAUSED  | write()	   | 		   | PAUSED		       	     |
 * | STOPPED | write()	   | <thrd create> | PLAYING		  	     |
 * | PLAYING | write()	   | HEADER        | PLAYING		  	     |
 * | (other) | write()	   | <error>       |		       		     |
 * | (any)   | pause()	   | PAUSING	   | PAUSED		       	     |
 * | PAUSED  | restart()   | RESTARTING    | PLAYING (if no thrd => STOPPED) |
 * | (any)   | reset()	   | RESETTING     | STOPPED		      	     |
 * | (any)   | close()	   | CLOSING	   | CLOSED		      	     |
 * +---------+-------------+---------------+---------------------------------+
 */

/* states of the playing device */
#define	WINE_WS_PLAYING		0
#define	WINE_WS_PAUSED		1
#define	WINE_WS_STOPPED		2
#define WINE_WS_CLOSED		3

/* events to be send to device */
enum win_wm_message {
    WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
    WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING
};

typedef struct {
    enum win_wm_message 	msg;	/* message identifier */
    DWORD	                param;  /* parameter for this message */
    HANDLE	                hEvent;	/* if message is synchronous, handle of event for synchro */
} RING_MSG;

/* implement an in-process message ring for better performance
 * (compared to passing thru the server)
 * this ring will be used by the input (resp output) record (resp playback) routine
 */
138
#define NAS_RING_BUFFER_INCREMENT      64
139
typedef struct {
140 141
    RING_MSG			* messages;
    int                         ring_buffer_size;
142 143 144 145 146 147 148 149 150 151 152
    int				msg_tosave;
    int				msg_toget;
    HANDLE			msg_event;
    CRITICAL_SECTION		msg_crst;
} MSG_RING;

typedef struct {
    volatile int		state;			/* one of the WINE_WS_ manifest constants */
    WAVEOPENDESC		waveDesc;
    WORD			wFlags;
    PCMWAVEFORMAT		format;
153
    WAVEOUTCAPSW		caps;
154
    int				Id;
155 156

    int                         open;
157 158 159
    AuServer                    *AuServ;
    AuDeviceID                  AuDev;
    AuFlowID                    AuFlow;
160 161 162 163 164
    BOOL                        FlowStarted;

    DWORD                       writeBytes;
    DWORD                       freeBytes;
    DWORD                       sendBytes;
165

166
    DWORD                       BufferSize;           /* size of whole buffer in bytes */
167

168 169
    char*                       SoundBuffer;
    long                        BufferUsed;
170 171 172 173 174 175 176 177 178 179

    DWORD			volume_left;		/* volume control information */
    DWORD			volume_right;

    LPWAVEHDR			lpQueuePtr;		/* start of queued WAVEHDRs (waiting to be notified) */
    LPWAVEHDR			lpPlayPtr;		/* start of not yet fully played buffers */

    LPWAVEHDR			lpLoopPtr;              /* pointer of first buffer in loop, if any */
    DWORD			dwLoops;		/* private copy of loop counter */

180 181
    DWORD			PlayedTotal;		/* number of bytes actually played since opening */
    DWORD                       WrittenTotal;         /* number of bytes written to the audio device since opening */
182 183 184 185 186

    /* synchronization stuff */
    HANDLE			hStartUpEvent;
    HANDLE			hThread;
    DWORD			dwThreadID;
187
    MSG_RING			msgRing;
188 189 190 191 192
} WINE_WAVEOUT;

static WINE_WAVEOUT	WOutDev   [MAX_WAVEOUTDRV];

static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
193
static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
194 195


196
/* NASFUNC */
197 198 199 200
static AuBool event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd);
static int nas_init(void);
static int nas_end(void);

201
static int nas_finddev(WINE_WAVEOUT* wwo);
202
static int nas_open(WINE_WAVEOUT* wwo);
203
static int nas_free(WINE_WAVEOUT* wwo);
204 205
static int nas_close(WINE_WAVEOUT* wwo);
static void buffer_resize(WINE_WAVEOUT* wwo, int len);
206 207
static int nas_add_buffer(WINE_WAVEOUT* wwo);
static int nas_send_buffer(WINE_WAVEOUT* wwo);
208 209

/* These strings used only for tracing */
210
static const char * const wodPlayerCmdString[] = {
211 212 213 214 215 216 217 218 219
    "WINE_WM_PAUSING",
    "WINE_WM_RESTARTING",
    "WINE_WM_RESETTING",
    "WINE_WM_HEADER",
    "WINE_WM_UPDATE",
    "WINE_WM_BREAKLOOP",
    "WINE_WM_CLOSING",
};

220
static const char * const nas_elementnotify_kinds[] = {
221 222 223 224 225 226
        "LowWater",
        "HighWater",
        "State",
        "Unknown"
};

227
static const char * const nas_states[] = {
228 229 230 231 232 233
        "Stop",
        "Start",
        "Pause",
        "Any"
};

234
static const char * const nas_reasons[] = {
235 236 237 238 239 240 241 242 243
        "User",
        "Underrun",
        "Overrun",
        "EOF",
        "Watermark",
        "Hardware",
        "Any"
};

244
static const char* nas_reason(unsigned int reason)
245 246 247 248 249
{
        if (reason > 6) reason = 6;
        return nas_reasons[reason];
}

250
static const char* nas_elementnotify_kind(unsigned int kind)
251 252 253 254 255 256
{
        if (kind > 2) kind = 3;
        return nas_elementnotify_kinds[kind];
}


Alexandre Julliard's avatar
Alexandre Julliard committed
257 258
#if 0
static const char* nas_event_type(unsigned int type)
259
{
Alexandre Julliard's avatar
Alexandre Julliard committed
260 261 262 263 264 265 266 267 268 269 270
        static const char * const nas_event_types[] =
        {
            "Undefined",
            "Undefined",
            "ElementNotify",
            "GrabNotify",
            "MonitorNotify",
            "BucketNotify",
            "DeviceNotify"
        };

271 272 273
        if (type > 6) type = 0;
        return nas_event_types[type];
}
Alexandre Julliard's avatar
Alexandre Julliard committed
274
#endif
275 276


277
static const char* nas_state(unsigned int state)
278 279 280 281 282
{
        if (state > 3) state = 3;
        return nas_states[state];
}

283 284 285
static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
                             PCMWAVEFORMAT* format)
{
286
    TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
287 288
          lpTime->wType, format->wBitsPerSample, format->wf.nSamplesPerSec,
          format->wf.nChannels, format->wf.nAvgBytesPerSec);
289
    TRACE("Position in bytes=%u\n", position);
290 291 292 293

    switch (lpTime->wType) {
    case TIME_SAMPLES:
        lpTime->u.sample = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
294
        TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
295 296 297
        break;
    case TIME_MS:
        lpTime->u.ms = 1000.0 * position / (format->wBitsPerSample / 8 * format->wf.nChannels * format->wf.nSamplesPerSec);
298
        TRACE("TIME_MS=%u\n", lpTime->u.ms);
299 300
        break;
    case TIME_SMPTE:
301
        lpTime->u.smpte.fps = 30;
302
        position = position / (format->wBitsPerSample / 8 * format->wf.nChannels);
303
        position += (format->wf.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
304 305 306 307 308 309 310 311 312 313 314 315 316
        lpTime->u.smpte.sec = position / format->wf.nSamplesPerSec;
        position -= lpTime->u.smpte.sec * format->wf.nSamplesPerSec;
        lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
        lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
        lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
        lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
        lpTime->u.smpte.fps = 30;
        lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->wf.nSamplesPerSec;
        TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
              lpTime->u.smpte.hour, lpTime->u.smpte.min,
              lpTime->u.smpte.sec, lpTime->u.smpte.frame);
        break;
    default:
317
        WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
318 319 320 321
        lpTime->wType = TIME_BYTES;
        /* fall through */
    case TIME_BYTES:
        lpTime->u.cb = position;
322
        TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
323 324 325 326 327
        break;
    }
    return MMSYSERR_NOERROR;
}

328 329 330
/*======================================================================*
 *                  Low level WAVE implementation			*
 *======================================================================*/
331
#if 0
332 333
/* Volume functions derived from Alsaplayer source */
/* length is the number of 16 bit samples */
334 335
static void volume_effect16(void *bufin, void* bufout, int length, int left,
                            int right, int nChannels)
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
{
  short *d_out = (short *)bufout;
  short *d_in = (short *)bufin;
  int i, v;

/*
  TRACE("length == %d, nChannels == %d\n", length, nChannels);
*/

  if (right == -1) right = left;

  for(i = 0; i < length; i+=(nChannels))
  {
    v = (int) ((*(d_in++) * left) / 100);
    *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
    if(nChannels == 2)
    {
      v = (int) ((*(d_in++) * right) / 100);
      *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
    }
  }
}

/* length is the number of 8 bit samples */
360 361
static void volume_effect8(void *bufin, void* bufout, int length, int left,
                           int right, int nChannels)
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
{
  char *d_out = (char *)bufout;
  char *d_in = (char *)bufin;
  int i, v;

/*
  TRACE("length == %d, nChannels == %d\n", length, nChannels);
*/

  if (right == -1) right = left;

  for(i = 0; i < length; i+=(nChannels))
  {
    v = (char) ((*(d_in++) * left) / 100);
    *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
    if(nChannels == 2)
    {
      v = (char) ((*(d_in++) * right) / 100);
      *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
    }
  }
}
384
#endif
385 386 387 388 389

/******************************************************************
 *		NAS_CloseDevice
 *
 */
390
static void NAS_CloseDevice(WINE_WAVEOUT* wwo)
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
{
  TRACE("NAS_CloseDevice\n");
  nas_close(wwo);
}

/******************************************************************
 *		NAS_WaveClose
 */
LONG		NAS_WaveClose(void)
{
    nas_end();    /* free up nas server */
    return 1;
}

/******************************************************************
 *		NAS_WaveInit
 *
 * Initialize internal structures from NAS server info
 */
LONG NAS_WaveInit(void)
{
    int 	i;
Eric Pouech's avatar
Eric Pouech committed
413
    if (!nas_init()) return MMSYSERR_ERROR;
414 415 416 417

    /* initialize all device handles to -1 */
    for (i = 0; i < MAX_WAVEOUTDRV; ++i)
    {
418
        static const WCHAR ini[] = {'N','A','S',' ','W','A','V','E','O','U','T',' ','D','r','i','v','e','r',0};
419 420 421 422 423 424 425
	memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */

        WOutDev[i].AuServ = AuServ;
        WOutDev[i].AuDev = AuNone;
	WOutDev[i].Id = i;
    	WOutDev[i].caps.wMid = 0x00FF; 	/* Manufac ID */
    	WOutDev[i].caps.wPid = 0x0001; 	/* Product ID */
426
        strcpyW(WOutDev[i].caps.szPname, ini);
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        WOutDev[i].AuFlow = 0;
    	WOutDev[i].caps.vDriverVersion = 0x0100;
    	WOutDev[i].caps.dwFormats = 0x00000000;
    	WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;

    	WOutDev[i].caps.wChannels = 2;
    	WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME;

    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
    	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
	WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
    }


    return 0;
}

/******************************************************************
 *		NAS_InitRingMessage
 *
 * Initialize the ring of messages for passing between driver's caller and playback/record
 * thread
 */
static int NAS_InitRingMessage(MSG_RING* mr)
{
    mr->msg_toget = 0;
    mr->msg_tosave = 0;
463
    mr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
464 465
    mr->ring_buffer_size = NAS_RING_BUFFER_INCREMENT;
    mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
466
    InitializeCriticalSection(&mr->msg_crst);
467
    mr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MSG_RING.msg_crst");
468 469 470 471 472 473 474 475 476 477
    return 0;
}

/******************************************************************
 *		NAS_DestroyRingMessage
 *
 */
static int NAS_DestroyRingMessage(MSG_RING* mr)
{
    CloseHandle(mr->msg_event);
478
    HeapFree(GetProcessHeap(),0,mr->messages);
479
    mr->msg_crst.DebugInfo->Spare[0] = 0;
480 481 482 483 484 485 486
    DeleteCriticalSection(&mr->msg_crst);
    return 0;
}

/******************************************************************
 *		NAS_AddRingMessage
 *
Austin English's avatar
Austin English committed
487
 * Inserts a new message into the ring (should be called from DriverProc derived routines)
488 489 490 491 492 493
 */
static int NAS_AddRingMessage(MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
{
    HANDLE      hEvent = INVALID_HANDLE_VALUE;

    EnterCriticalSection(&mr->msg_crst);
494
    if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
495
    {
496
	int old_ring_buffer_size = mr->ring_buffer_size;
497 498 499
	mr->ring_buffer_size += NAS_RING_BUFFER_INCREMENT;
	TRACE("omr->ring_buffer_size=%d\n",mr->ring_buffer_size);
	mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
500 501 502 503 504 505 506 507 508 509 510 511
	/* Now we need to rearrange the ring buffer so that the new
	   buffers just allocated are in between mr->msg_tosave and
	   mr->msg_toget.
	*/
	if (mr->msg_tosave < mr->msg_toget)
	{
	    memmove(&(mr->messages[mr->msg_toget + NAS_RING_BUFFER_INCREMENT]),
		    &(mr->messages[mr->msg_toget]),
		    sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
		    );
	    mr->msg_toget += NAS_RING_BUFFER_INCREMENT;
	}
512 513 514
    }
    if (wait)
    {
515
        hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
516 517 518 519 520 521 522 523 524 525
        if (hEvent == INVALID_HANDLE_VALUE)
        {
            ERR("can't create event !?\n");
            LeaveCriticalSection(&mr->msg_crst);
            return 0;
        }
        if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->msg_toget].msg != WINE_WM_HEADER)
            FIXME("two fast messages in the queue!!!!\n");

        /* fast messages have to be added at the start of the queue */
526
        mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
527 528 529 530 531 532 533 534 535 536

        mr->messages[mr->msg_toget].msg = msg;
        mr->messages[mr->msg_toget].param = param;
        mr->messages[mr->msg_toget].hEvent = hEvent;
    }
    else
    {
        mr->messages[mr->msg_tosave].msg = msg;
        mr->messages[mr->msg_tosave].param = param;
        mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
537
        mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
    }

    LeaveCriticalSection(&mr->msg_crst);

    SetEvent(mr->msg_event);    /* signal a new message */

    if (wait)
    {
        /* wait for playback/record thread to have processed the message */
        WaitForSingleObject(hEvent, INFINITE);
        CloseHandle(hEvent);
    }

    return 1;
}

/******************************************************************
 *		NAS_RetrieveRingMessage
 *
 * Get a message from the ring. Should be called by the playback/record thread.
 */
static int NAS_RetrieveRingMessage(MSG_RING* mr,
                                   enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
{
    EnterCriticalSection(&mr->msg_crst);

    if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
    {
        LeaveCriticalSection(&mr->msg_crst);
	return 0;
    }

    *msg = mr->messages[mr->msg_toget].msg;
    mr->messages[mr->msg_toget].msg = 0;
    *param = mr->messages[mr->msg_toget].param;
    *hEvent = mr->messages[mr->msg_toget].hEvent;
574
    mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
575 576 577 578 579 580 581 582 583 584 585 586 587
    LeaveCriticalSection(&mr->msg_crst);
    return 1;
}

/*======================================================================*
 *                  Low level WAVE OUT implementation			*
 *======================================================================*/

/**************************************************************************
 * 			wodNotifyClient			[internal]
 */
static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
{
588
    TRACE("wMsg = 0x%04x dwParm1 = %04X dwParam2 = %04X\n", wMsg, dwParam1, dwParam2);
589 590 591 592 593 594

    switch (wMsg) {
    case WOM_OPEN:
    case WOM_CLOSE:
    case WOM_DONE:
	if (wwo->wFlags != DCB_NULL &&
595
	    !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
			    wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
	    WARN("can't notify client !\n");
	    return MMSYSERR_ERROR;
	}
	break;
    default:
	FIXME("Unknown callback message %u\n", wMsg);
        return MMSYSERR_INVALPARAM;
    }
    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodUpdatePlayedTotal	[internal]
 *
 */
static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
{
614
    wwo->PlayedTotal = wwo->WrittenTotal;
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
    return TRUE;
}

/**************************************************************************
 * 				wodPlayer_BeginWaveHdr          [internal]
 *
 * Makes the specified lpWaveHdr the currently playing wave header.
 * If the specified wave header is a begin loop and we're not already in
 * a loop, setup the loop.
 */
static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
{
    wwo->lpPlayPtr = lpWaveHdr;

    if (!lpWaveHdr) return;

    if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
	if (wwo->lpLoopPtr) {
	    WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
	    TRACE("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
	} else {
636
            TRACE("Starting loop (%dx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
	    wwo->lpLoopPtr = lpWaveHdr;
	    /* Windows does not touch WAVEHDR.dwLoops,
	     * so we need to make an internal copy */
	    wwo->dwLoops = lpWaveHdr->dwLoops;
	}
    }
}

/**************************************************************************
 * 				wodPlayer_PlayPtrNext	        [internal]
 *
 * Advance the play pointer to the next waveheader, looping if required.
 */
static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
{
    LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;

    if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
	/* We're at the end of a loop, loop if required */
	if (--wwo->dwLoops > 0) {
	    wwo->lpPlayPtr = wwo->lpLoopPtr;
	} else {
	    /* Handle overlapping loops correctly */
	    if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
		FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
		/* shall we consider the END flag for the closing loop or for
		 * the opening one or for both ???
		 * code assumes for closing loop only
		 */
	    } else {
                lpWaveHdr = lpWaveHdr->lpNext;
            }
            wwo->lpLoopPtr = NULL;
            wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
	}
    } else {
	/* We're not in a loop.  Advance to the next wave header */
	wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
    }
    return lpWaveHdr;
}

/**************************************************************************
 * 				wodPlayer_NotifyCompletions	[internal]
 *
 * Notifies and remove from queue all wavehdrs which have been played to
 * the speaker (ie. they have cleared the audio device).  If force is true,
 * we notify all wavehdrs and remove them all from the queue even if they
 * are unplayed or part of a loop.
 */
static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
{
    LPWAVEHDR		lpWaveHdr;

    /* Start from lpQueuePtr and keep notifying until:
     * - we hit an unwritten wavehdr
     * - we hit the beginning of a running loop
     * - we hit a wavehdr which hasn't finished playing
     */
696 697
    wodUpdatePlayedTotal(wwo);

698
    while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr &&
699
            lpWaveHdr != wwo->lpLoopPtr && lpWaveHdr->reserved <= wwo->PlayedTotal))) {
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724

	wwo->lpQueuePtr = lpWaveHdr->lpNext;

	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
	lpWaveHdr->dwFlags |= WHDR_DONE;

	wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
    }
    return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
            1 : 1;
}

/**************************************************************************
 * 				wodPlayer_Reset			[internal]
 *
 * wodPlayer helper. Resets current output stream.
 */
static	void	wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
{
    wodUpdatePlayedTotal(wwo);
    wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */

    /* we aren't able to flush any data that has already been written */
    /* to nas, otherwise we would do the flushing here */

725 726
    nas_free(wwo);

727 728 729 730 731 732 733 734 735 736
    if (reset) {
        enum win_wm_message     msg;
        DWORD                   param;
        HANDLE                  ev;

	/* remove any buffer */
	wodPlayer_NotifyCompletions(wwo, TRUE);

	wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
	wwo->state = WINE_WS_STOPPED;
737
	wwo->PlayedTotal = wwo->WrittenTotal = 0;
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763

        /* remove any existing message in the ring */
        EnterCriticalSection(&wwo->msgRing.msg_crst);

        /* return all pending headers in queue */
        while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
        {
	    TRACE("flushing msg\n");
            if (msg != WINE_WM_HEADER)
            {
                FIXME("shouldn't have headers left\n");
                SetEvent(ev);
                continue;
            }
            ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
            ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;

            wodNotifyClient(wwo, WOM_DONE, param, 0);
        }
        ResetEvent(wwo->msgRing.msg_event);
        LeaveCriticalSection(&wwo->msgRing.msg_crst);
    } else {
        if (wwo->lpLoopPtr) {
            /* complicated case, not handled yet (could imply modifying the loop counter */
            FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
            wwo->lpPlayPtr = wwo->lpLoopPtr;
764
            wwo->WrittenTotal = wwo->PlayedTotal; /* this is wrong !!! */
765 766 767
        } else {
	    /* the data already written is going to be played, so take */
	    /* this fact into account here */
768
	    wwo->PlayedTotal = wwo->WrittenTotal;
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
        }
	wwo->state = WINE_WS_PAUSED;
    }
}

/**************************************************************************
 * 		      wodPlayer_ProcessMessages			[internal]
 */
static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
{
    LPWAVEHDR           lpWaveHdr;
    enum win_wm_message	msg;
    DWORD		param;
    HANDLE		ev;

    while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {
785
        TRACE("Received %s %x\n", wodPlayerCmdString[msg - WM_USER - 1], param);
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
	switch (msg) {
	case WINE_WM_PAUSING:
	    wodPlayer_Reset(wwo, FALSE);
	    SetEvent(ev);
	    break;
	case WINE_WM_RESTARTING:
	    wwo->state = WINE_WS_PLAYING;
	    SetEvent(ev);
	    break;
	case WINE_WM_HEADER:
	    lpWaveHdr = (LPWAVEHDR)param;

	    /* insert buffer at the end of queue */
	    {
		LPWAVEHDR*	wh;
		for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
		*wh = lpWaveHdr;
	    }
            if (!wwo->lpPlayPtr)
                wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
	    if (wwo->state == WINE_WS_STOPPED)
		wwo->state = WINE_WS_PLAYING;
	    break;
	case WINE_WM_RESETTING:
	    wodPlayer_Reset(wwo, TRUE);
	    SetEvent(ev);
	    break;
        case WINE_WM_UPDATE:
            wodUpdatePlayedTotal(wwo);
	    SetEvent(ev);
            break;
        case WINE_WM_BREAKLOOP:
            if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
                /* ensure exit at end of current loop */
                wwo->dwLoops = 1;
            }
	    SetEvent(ev);
            break;
	case WINE_WM_CLOSING:
825
	    /* sanity check: this should not happen since the device must have been reset before */
826 827 828 829 830
	    if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
	    wwo->hThread = 0;
	    wwo->state = WINE_WS_CLOSED;
	    SetEvent(ev);
	    ExitThread(0);
831
	    /* shouldn't go here */
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
	default:
	    FIXME("unknown message %d\n", msg);
	    break;
	}
    }
}

/**************************************************************************
 * 				wodPlayer			[internal]
 */
static	DWORD	CALLBACK	wodPlayer(LPVOID pmt)
{
    WORD	  uDevID = (DWORD)pmt;
    WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];

    wwo->state = WINE_WS_STOPPED;
    SetEvent(wwo->hStartUpEvent);

    for (;;) {
851 852 853 854 855 856 857 858 859 860 861 862

        if (wwo->FlowStarted) {
           AuHandleEvents(wwo->AuServ);

           if (wwo->state == WINE_WS_PLAYING && wwo->freeBytes && wwo->BufferUsed)
              nas_send_buffer(wwo);
        }

        if (wwo->BufferUsed <= FRAG_SIZE && wwo->writeBytes > 0)
           wodPlayer_NotifyCompletions(wwo, FALSE);

        WaitForSingleObject(wwo->msgRing.msg_event, 20);
863
        wodPlayer_ProcessMessages(wwo);
864 865 866 867 868 869 870

        while(wwo->lpPlayPtr) {
           wwo->lpPlayPtr->reserved = wwo->WrittenTotal + wwo->lpPlayPtr->dwBufferLength;
           nas_add_buffer(wwo);
           wodPlayer_PlayPtrNext(wwo);
        }

871 872 873 874 875 876
    }
}

/**************************************************************************
 * 			wodGetDevCaps				[internal]
 */
877
static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
878
{
879
    TRACE("(%u, %p, %u);\n", wDevID, lpCaps, dwSize);
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898

    if (lpCaps == NULL) return MMSYSERR_NOTENABLED;

    if (wDevID >= MAX_WAVEOUTDRV) {
	TRACE("MAX_WAVOUTDRV reached !\n");
	return MMSYSERR_BADDEVICEID;
    }

    memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodOpen				[internal]
 */
static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
{
    WINE_WAVEOUT*	wwo;

899
    TRACE("wodOpen (%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923

    if (lpDesc == NULL) {
	WARN("Invalid Parameter !\n");
	return MMSYSERR_INVALPARAM;
    }
    if (wDevID >= MAX_WAVEOUTDRV) {
	TRACE("MAX_WAVOUTDRV reached !\n");
	return MMSYSERR_BADDEVICEID;
    }

    /* if this device is already open tell the app that it is allocated */

    wwo = &WOutDev[wDevID];

    if(wwo->open)
    {
      TRACE("device already allocated\n");
      return MMSYSERR_ALLOCATED;
    }


    /* only PCM format is supported so far... */
    if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
	lpDesc->lpFormat->nChannels == 0 ||
924 925
	lpDesc->lpFormat->nSamplesPerSec == 0 ||
        (lpDesc->lpFormat->wBitsPerSample!=8 && lpDesc->lpFormat->wBitsPerSample!=16)) {
926
	WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
927 928 929 930 931 932
	     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
	     lpDesc->lpFormat->nSamplesPerSec);
	return WAVERR_BADFORMAT;
    }

    if (dwFlags & WAVE_FORMAT_QUERY) {
933
	TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%d !\n",
934 935 936 937 938 939 940 941 942 943
	     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
	     lpDesc->lpFormat->nSamplesPerSec);
	return MMSYSERR_NOERROR;
    }

    /* direct sound not supported, ignore the flag */
    dwFlags &= ~WAVE_DIRECTSOUND;

    wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);

944
    wwo->waveDesc = *lpDesc;
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
    memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));

    if (wwo->format.wBitsPerSample == 0) {
	WARN("Resetting zeroed wBitsPerSample\n");
	wwo->format.wBitsPerSample = 8 *
	    (wwo->format.wf.nAvgBytesPerSec /
	     wwo->format.wf.nSamplesPerSec) /
	    wwo->format.wf.nChannels;
    }

    if (!nas_open(wwo))
       return MMSYSERR_ALLOCATED;

    NAS_InitRingMessage(&wwo->msgRing);

    /* create player thread */
    if (!(dwFlags & WAVE_DIRECTSOUND)) {
962
	wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
963
	wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
964 965
        if (wwo->hThread)
            SetThreadPriority(wwo->hThread, THREAD_PRIORITY_TIME_CRITICAL);
966 967 968 969 970 971 972 973
	WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
	CloseHandle(wwo->hStartUpEvent);
    } else {
	wwo->hThread = INVALID_HANDLE_VALUE;
	wwo->dwThreadID = 0;
    }
    wwo->hStartUpEvent = INVALID_HANDLE_VALUE;

974
    TRACE("stream=0x%lx, BufferSize=%d\n", (long)wwo->AuServ, wwo->BufferSize);
975

976
    TRACE("wBitsPerSample=%u nAvgBytesPerSec=%u nSamplesPerSec=%u nChannels=%u nBlockAlign=%u\n",
977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024
	  wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
	  wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
	  wwo->format.wf.nBlockAlign);

    return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
}

/**************************************************************************
 * 				wodClose			[internal]
 */
static DWORD wodClose(WORD wDevID)
{
    DWORD		ret = MMSYSERR_NOERROR;
    WINE_WAVEOUT*	wwo;

    TRACE("(%u);\n", wDevID);

    if (wDevID >= MAX_WAVEOUTDRV || AuServ  == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    wwo = &WOutDev[wDevID];
    if (wwo->lpQueuePtr) {
	WARN("buffers still playing !\n");
	ret = WAVERR_STILLPLAYING;
    } else {
	TRACE("imhere[3-close]\n");
	if (wwo->hThread != INVALID_HANDLE_VALUE) {
	    NAS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
	}

        NAS_DestroyRingMessage(&wwo->msgRing);

	NAS_CloseDevice(wwo);	/* close the stream and clean things up */

	ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
    }
    return ret;
}

/**************************************************************************
 * 				wodWrite			[internal]
 *
 */
static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
{
1025
    TRACE("(%u, %p, %08X);\n", wDevID, lpWaveHdr, dwSize);
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126

    /* first, do the sanity checks... */
    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
        WARN("bad dev ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
    {
	TRACE("unprepared\n");
	return WAVERR_UNPREPARED;
    }

    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
    {
	TRACE("still playing\n");
	return WAVERR_STILLPLAYING;
    }

    lpWaveHdr->dwFlags &= ~WHDR_DONE;
    lpWaveHdr->dwFlags |= WHDR_INQUEUE;
    lpWaveHdr->lpNext = 0;

    TRACE("adding ring message\n");
    NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 			wodPause				[internal]
 */
static DWORD wodPause(WORD wDevID)
{
    TRACE("(%u);!\n", wDevID);

    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    TRACE("imhere[3-PAUSING]\n");
    NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 			wodRestart				[internal]
 */
static DWORD wodRestart(WORD wDevID)
{
    TRACE("(%u);\n", wDevID);

    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
	TRACE("imhere[3-RESTARTING]\n");
	NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
    }

    /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
    /* FIXME: Myst crashes with this ... hmm -MM
       return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
    */

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 			wodReset				[internal]
 */
static DWORD wodReset(WORD wDevID)
{
    TRACE("(%u);\n", wDevID);

    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    TRACE("imhere[3-RESET]\n");
    NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodGetPosition			[internal]
 */
static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
{
    WINE_WAVEOUT*	wwo;

1127
    TRACE("%u, %p, %u);\n", wDevID, lpTime, uSize);
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }

    if (lpTime == NULL)	return MMSYSERR_INVALPARAM;

    wwo = &WOutDev[wDevID];
1138 1139 1140
#if 0
    NAS_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
#endif
1141

1142
    return bytes_to_mmtime(lpTime, wwo->WrittenTotal, &wwo->format);
1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
}

/**************************************************************************
 * 				wodBreakLoop			[internal]
 */
static DWORD wodBreakLoop(WORD wDevID)
{
    TRACE("(%u);\n", wDevID);

    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)
    {
	WARN("bad device ID !\n");
	return MMSYSERR_BADDEVICEID;
    }
    NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodGetVolume			[internal]
 */
static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
{
    DWORD left, right;

    left = WOutDev[wDevID].volume_left;
    right = WOutDev[wDevID].volume_right;

    TRACE("(%u, %p);\n", wDevID, lpdwVol);

    *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodSetVolume			[internal]
 */
static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
{
    DWORD left, right;

    left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
    right = (HIWORD(dwParam) * 100) / 0xFFFFl;

1188
    TRACE("(%u, %08X);\n", wDevID, dwParam);
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209

    WOutDev[wDevID].volume_left = left;
    WOutDev[wDevID].volume_right = right;

    return MMSYSERR_NOERROR;
}

/**************************************************************************
 * 				wodGetNumDevs			[internal]
 */
static	DWORD	wodGetNumDevs(void)
{
    return MAX_WAVEOUTDRV;
}

/**************************************************************************
 * 				wodMessage (WINENAS.@)
 */
DWORD WINAPI NAS_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
			    DWORD dwParam1, DWORD dwParam2)
{
1210
    TRACE("(%u, %04X, %08X, %08X, %08X);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224

    switch (wMsg) {
    case DRVM_INIT:
    case DRVM_EXIT:
    case DRVM_ENABLE:
    case DRVM_DISABLE:
	/* FIXME: Pretend this is supported */
	return 0;
    case WODM_OPEN:	 	return wodOpen		(wDevID, (LPWAVEOPENDESC)dwParam1,	dwParam2);
    case WODM_CLOSE:	 	return wodClose		(wDevID);
    case WODM_WRITE:	 	return wodWrite		(wDevID, (LPWAVEHDR)dwParam1,		dwParam2);
    case WODM_PAUSE:	 	return wodPause		(wDevID);
    case WODM_GETPOS:	 	return wodGetPosition	(wDevID, (LPMMTIME)dwParam1, 		dwParam2);
    case WODM_BREAKLOOP: 	return wodBreakLoop     (wDevID);
1225 1226
    case WODM_PREPARE:	 	return MMSYSERR_NOTSUPPORTED;
    case WODM_UNPREPARE: 	return MMSYSERR_NOTSUPPORTED;
1227
    case WODM_GETDEVCAPS:	return wodGetDevCaps	(wDevID, (LPWAVEOUTCAPSW)dwParam1,	dwParam2);
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237
    case WODM_GETNUMDEVS:	return wodGetNumDevs	();
    case WODM_GETPITCH:	 	return MMSYSERR_NOTSUPPORTED;
    case WODM_SETPITCH:	 	return MMSYSERR_NOTSUPPORTED;
    case WODM_GETPLAYBACKRATE:	return MMSYSERR_NOTSUPPORTED;
    case WODM_SETPLAYBACKRATE:	return MMSYSERR_NOTSUPPORTED;
    case WODM_GETVOLUME:	return wodGetVolume	(wDevID, (LPDWORD)dwParam1);
    case WODM_SETVOLUME:	return wodSetVolume	(wDevID, dwParam1);
    case WODM_RESTART:		return wodRestart	(wDevID);
    case WODM_RESET:		return wodReset		(wDevID);

1238 1239
    case DRV_QUERYDSOUNDIFACE:	return wodDsCreate	(wDevID, (PIDSDRIVER*)dwParam1);
    case DRV_QUERYDSOUNDDESC:	return wodDsDesc	(wDevID, (PDSDRIVERDESC)dwParam1);
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
    default:
	FIXME("unknown message %d!\n", wMsg);
    }
    return MMSYSERR_NOTSUPPORTED;
}

/*======================================================================*
 *                  Low level DSOUND implementation			*
 *======================================================================*/
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
{
    /* we can't perform memory mapping as we don't have a file stream
	interface with nas like we do with oss */
    MESSAGE("This sound card s driver does not support direct access\n");
    MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
    return MMSYSERR_NOTSUPPORTED;
}

1258 1259 1260 1261
static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
{
    memset(desc, 0, sizeof(*desc));
    strcpy(desc->szDesc, "Wine NAS DirectSound Driver");
1262
    strcpy(desc->szDrvname, "winenas.drv");
1263 1264 1265
    return MMSYSERR_NOERROR;
}

1266 1267 1268 1269 1270 1271 1272 1273
static int nas_init(void) {
    TRACE("NAS INIT\n");
    if (!(AuServ = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL)))
       return 0;

    return 1;
}

1274
static int nas_finddev(WINE_WAVEOUT* wwo) {
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
   int i;

    for (i = 0; i < AuServerNumDevices(wwo->AuServ); i++) {
        if ((AuDeviceKind(AuServerDevice(wwo->AuServ, i)) ==
             AuComponentKindPhysicalOutput) &&
             AuDeviceNumTracks(AuServerDevice(wwo->AuServ, i)) == wwo->format.wf.nChannels)
        {
            wwo->AuDev = AuDeviceIdentifier(AuServerDevice(wwo->AuServ, i));
            break;
        }
    }

    if (wwo->AuDev == AuNone)
       return 0;
    return 1;
}

static int nas_open(WINE_WAVEOUT* wwo) {
    AuElement elements[3];

    if (!wwo->AuServ)
       return 0;

1298
    if (!nas_finddev(wwo))
1299 1300 1301 1302 1303
       return 0;

    if (!(wwo->AuFlow = AuCreateFlow(wwo->AuServ, NULL)))
       return 0;

1304
    wwo->BufferSize = FRAG_SIZE * FRAG_COUNT;
1305 1306 1307

    AuMakeElementImportClient(&elements[0], wwo->format.wf.nSamplesPerSec,
           wwo->format.wBitsPerSample == 16 ? AuFormatLinearSigned16LSB : AuFormatLinearUnsigned8,
1308
           wwo->format.wf.nChannels, AuTrue, wwo->BufferSize, wwo->BufferSize / 2, 0, NULL);
1309 1310 1311 1312 1313 1314

    AuMakeElementExportDevice(&elements[1], 0, wwo->AuDev, wwo->format.wf.nSamplesPerSec,
                              AuUnlimitedSamples, 0, NULL);

    AuSetElements(wwo->AuServ, wwo->AuFlow, AuTrue, 2, elements, NULL);

1315 1316
    AuRegisterEventHandler(wwo->AuServ, AuEventHandlerIDMask, 0, wwo->AuFlow,
                           event_handler, (AuPointer) wwo);
1317 1318


1319 1320
    wwo->PlayedTotal = 0;
    wwo->WrittenTotal = 0;
1321 1322 1323 1324
    wwo->open = 1;

    wwo->BufferUsed = 0;
    wwo->writeBytes = 0;
1325 1326 1327 1328
    wwo->freeBytes = 0;
    wwo->sendBytes = 0;
    wwo->SoundBuffer = NULL;
    wwo->FlowStarted = 0;
1329 1330

    AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
1331 1332
    AuPauseFlow(wwo->AuServ, wwo->AuFlow, NULL);
    wwo->FlowStarted = 1;
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347

    return 1;
}

static AuBool
event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
{
  WINE_WAVEOUT *wwo = (WINE_WAVEOUT *)hnd->data;
        switch (ev->type) {

        case AuEventTypeElementNotify: {
                AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;


                switch (event->kind) {
1348 1349 1350 1351 1352 1353 1354 1355 1356
                   case AuElementNotifyKindLowWater:
                     wwo->freeBytes += event->num_bytes;
                     if (wwo->writeBytes > 0)
                        wwo->sendBytes += event->num_bytes;
                    if (wwo->freeBytes && wwo->BufferUsed)
                        nas_send_buffer(wwo);
                   break;

                   case AuElementNotifyKindState:
1357
                     TRACE("ev: kind %s state %s->%s reason %s numbytes %ld freeB %u\n",
1358 1359 1360 1361 1362 1363 1364 1365 1366
                                     nas_elementnotify_kind(event->kind),
                                     nas_state(event->prev_state),
                                     nas_state(event->cur_state),
                                     nas_reason(event->reason),
                                     event->num_bytes, wwo->freeBytes);

                     if (event->cur_state ==  AuStatePause && event->reason != AuReasonUser) {
                        wwo->freeBytes += event->num_bytes;
                        if (wwo->writeBytes > 0)
1367
                           wwo->sendBytes += event->num_bytes;
1368 1369 1370 1371 1372 1373
                        if (wwo->sendBytes > wwo->writeBytes)
                           wwo->sendBytes = wwo->writeBytes;
                       if (wwo->freeBytes && wwo->BufferUsed)
                           nas_send_buffer(wwo);
                     }
                   break;
1374
                }
1375
           }
1376 1377 1378 1379 1380 1381 1382 1383
        }
        return AuTrue;
}

static void
buffer_resize(WINE_WAVEOUT* wwo, int len)
{
        void *newbuf = malloc(wwo->BufferUsed + len);
1384
        void *oldbuf = wwo->SoundBuffer;
1385
        memcpy(newbuf, oldbuf, wwo->BufferUsed);
1386
        wwo->SoundBuffer = newbuf;
1387
        free(oldbuf);
1388 1389
}

1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
static int nas_add_buffer(WINE_WAVEOUT* wwo) {
    int len = wwo->lpPlayPtr->dwBufferLength;

    buffer_resize(wwo, len);
    memcpy(wwo->SoundBuffer + wwo->BufferUsed, wwo->lpPlayPtr->lpData, len);
    wwo->BufferUsed += len;
    wwo->WrittenTotal += len;
    return len;
}

static int nas_send_buffer(WINE_WAVEOUT* wwo) {
  int oldb , len;
1402 1403
  char *ptr, *newdata;
  newdata = NULL;
1404
  oldb = len = 0;
1405

1406
  if (wwo->freeBytes <= 0)
1407 1408
     return 0;

1409
  if (wwo->SoundBuffer == NULL || wwo->BufferUsed == 0) {
1410
     return 0;
1411
  }
1412 1413 1414

  if (wwo->BufferUsed <= wwo->freeBytes) {
     len = wwo->BufferUsed;
1415
     ptr = wwo->SoundBuffer;
1416 1417 1418
  } else {
     len = wwo->freeBytes;
     ptr = malloc(len);
1419
     memcpy(ptr,wwo->SoundBuffer,len);
1420
     newdata = malloc(wwo->BufferUsed - len);
1421
     memcpy(newdata, wwo->SoundBuffer + len, wwo->BufferUsed - len);
1422 1423
  }

1424
 TRACE("envoye de %d bytes / %lu bytes / freeBytes %u\n", len, wwo->BufferUsed, wwo->freeBytes);
1425 1426 1427

 AuWriteElement(wwo->AuServ, wwo->AuFlow, 0, len, ptr, AuFalse, NULL);

1428
 wwo->BufferUsed -= len;
1429 1430 1431
 wwo->freeBytes -= len;
 wwo->writeBytes += len;

1432
 free(ptr);
1433

1434
 wwo->SoundBuffer = NULL;
1435 1436

 if (newdata != NULL)
1437
    wwo->SoundBuffer = newdata;
1438 1439 1440 1441

 return len;
}

1442
static int nas_free(WINE_WAVEOUT* wwo)
1443 1444
{

1445 1446 1447 1448
  if (!wwo->FlowStarted && wwo->BufferUsed) {
     AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
     wwo->FlowStarted = 1;
  }
1449

1450 1451 1452 1453 1454
  while (wwo->BufferUsed || wwo->writeBytes != wwo->sendBytes) {
    if (wwo->freeBytes)
       nas_send_buffer(wwo);
    AuHandleEvents(wwo->AuServ);
  }
1455

1456 1457
  AuFlush(wwo->AuServ);
  return TRUE;
1458 1459
}

1460
static int nas_close(WINE_WAVEOUT* wwo)
1461
{
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
  AuEvent ev;

  nas_free(wwo);

  AuStopFlow(wwo->AuServ, wwo->AuFlow, NULL);
  AuDestroyFlow(wwo->AuServ, wwo->AuFlow, NULL);
  AuFlush(wwo->AuServ);
  AuNextEvent(wwo->AuServ, AuTrue, &ev);
  AuDispatchEvent(wwo->AuServ, &ev);

  wwo->AuFlow = 0;
  wwo->open = 0;
  wwo->BufferUsed = 0;
  wwo->freeBytes = 0;
  wwo->SoundBuffer = NULL;
  return 1;
1478 1479
}

1480
static int nas_end(void)
1481
{
Eric Pouech's avatar
Eric Pouech committed
1482 1483 1484 1485 1486
  if (AuServ)
  {
    AuCloseServer(AuServ);
    AuServ = 0;
  }
1487
  return 1;
1488 1489 1490 1491 1492 1493 1494 1495 1496
}

#else /* !HAVE_NAS */

/**************************************************************************
 * 				wodMessage (WINENAS.@)
 */
DWORD WINAPI NAS_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
{
1497
    FIXME("(%u, %04X, %08X, %08X, %08X):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
1498 1499 1500
    return MMSYSERR_NOTENABLED;
}
#endif