+++ /dev/null
-/* -*- tab-width: 8; c-basic-offset: 4 -*- */
-/*
- * Sample Wine Driver for Advanced Linux Sound System (ALSA)
- * Based on version 0.5 of the ALSA API
- *
- * Copyright 2002 Eric Pouech
- * 2002 David Hammerton
- *
- * 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
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "config.h"
-
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
-#endif
-#ifdef HAVE_SYS_MMAN_H
-# include <sys/mman.h>
-#endif
-#include "windef.h"
-#include "winbase.h"
-#include "wingdi.h"
-#include "winerror.h"
-#include "winuser.h"
-#include "mmddk.h"
-#include "dsound.h"
-#include "dsdriver.h"
-#include "alsa.h"
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(wave);
-
-
-#if defined(HAVE_ALSA) && (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)
-
-#define MAX_WAVEOUTDRV (1)
-#define MAX_WAVEINDRV (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 */
-} ALSA_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
- */
-typedef struct {
- /* FIXME: this could be made a dynamically growing array (if needed) */
-#define ALSA_RING_BUFFER_SIZE 30
- ALSA_MSG messages[ALSA_RING_BUFFER_SIZE];
- int msg_tosave;
- int msg_toget;
- HANDLE msg_event;
- CRITICAL_SECTION msg_crst;
-} ALSA_MSG_RING;
-
-typedef struct {
- /* Windows information */
- volatile int state; /* one of the WINE_WS_ manifest constants */
- WAVEOPENDESC waveDesc;
- WORD wFlags;
- PCMWAVEFORMAT format;
- WAVEOUTCAPSW caps;
-
- /* ALSA information */
- snd_pcm_t* handle; /* handle to ALSA device */
- DWORD dwFragmentSize; /* size of ALSA buffer fragment */
- DWORD dwBufferSize; /* size of whole ALSA buffer in bytes */
- LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
- LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
- DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
-
- LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
- DWORD dwLoops; /* private copy of loop counter */
-
- DWORD dwPlayedTotal; /* number of bytes actually played since opening */
- DWORD dwWrittenTotal; /* number of bytes written to ALSA buffer since opening */
-
- /* synchronization stuff */
- HANDLE hStartUpEvent;
- HANDLE hThread;
- DWORD dwThreadID;
- ALSA_MSG_RING msgRing;
-
- /* DirectSound stuff */
- void* mmap_buffer;
- snd_pcm_mmap_control_t* mmap_control;
- unsigned mmap_block_size;
- unsigned mmap_block_number;
-} WINE_WAVEOUT;
-
-static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
-static DWORD ALSA_WodNumDevs;
-
-static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
-
-/* These strings used only for tracing */
-static const char *wodPlayerCmdString[] = {
- "WINE_WM_PAUSING",
- "WINE_WM_RESTARTING",
- "WINE_WM_RESETTING",
- "WINE_WM_HEADER",
- "WINE_WM_UPDATE",
- "WINE_WM_BREAKLOOP",
- "WINE_WM_CLOSING",
-};
-
-/*======================================================================*
- * Low level WAVE implementation *
- *======================================================================*/
-
-/******************************************************************
- * ALSA_WaveInit
- *
- * Initialize internal structures from ALSA information
- */
-LONG ALSA_WaveInit(void)
-{
- snd_pcm_t* h;
- snd_pcm_info_t info;
- snd_pcm_channel_info_t chn_info;
-
- static const WCHAR ini_out[] = {'A','L','S','A',' ','W','a','v','e','O','u','t',' ','D','r','i','v','e','r',0};
-
- TRACE("There are %d cards\n", snd_cards());
-
- ALSA_WodNumDevs = 0;
- if (snd_pcm_open(&h, 0, 0, SND_PCM_OPEN_DUPLEX|SND_PCM_OPEN_NONBLOCK))
- {
- ERR("Error open: %s\n", snd_strerror(errno));
- return -1;
- }
- if (snd_pcm_info(h, &info))
- {
- ERR("Error info: %s\n", snd_strerror(errno));
- return -1;
- }
- ALSA_WodNumDevs++;
- TRACE("type=%u, flags=%s%s%s name=%s #pb=%d cp=%d\n",
- info.type, (info.flags & SND_PCM_INFO_PLAYBACK) ? "playback " : "",
- (info.flags & SND_PCM_INFO_PLAYBACK) ? "capture " : "",
- (info.flags & SND_PCM_INFO_DUPLEX) ? "duplex " : "",
- info.name, info.playback, info.capture);
- memset(&chn_info, 0, sizeof(chn_info));
- if (snd_pcm_channel_info(h, &chn_info))
- {
- ERR("Error chn info: %s\n", snd_strerror(errno));
- return -1;
- }
-#define X(f,s) ((chn_info.flags & (f)) ? #s " " : "")
-#define Y(f,s) ((chn_info.rates & (f)) ? #s " " : "")
- TRACE("subdevice=%d name=%s chn=%d mode=%d\n"
- "\tflags=%s%s%s%s%s%s%s%s%s%s%s\n"
- "\tfmts=%u rates=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
- "\trates=[%d,%d] voices=[%d,%d] buf_size=%d fg_size=[%d,%d] fg_align=%u\n",
- chn_info.subdevice, chn_info.subname, chn_info.channel,
- chn_info.mode,
- X(SND_PCM_CHNINFO_MMAP,MMAP),
- X(SND_PCM_CHNINFO_STREAM,STREAM),
- X(SND_PCM_CHNINFO_BLOCK,BLOCK),
- X(SND_PCM_CHNINFO_BATCH,BATCH),
- X(SND_PCM_CHNINFO_INTERLEAVE,INTERLEAVE),
- X(SND_PCM_CHNINFO_NONINTERLEAVE,NONINTERLEAVE),
- X(SND_PCM_CHNINFO_BLOCK_TRANSFER,BLOCK_TRANSFER),
- X(SND_PCM_CHNINFO_OVERRANGE,OVERRANGE),
- X(SND_PCM_CHNINFO_MMAP_VALID,MMAP_VALID),
- X(SND_PCM_CHNINFO_PAUSE,PAUSE),
- X(SND_PCM_CHNINFO_GLOBAL_PARAMS,GLOBAL_PARAMS),
- chn_info.formats,
- Y(SND_PCM_RATE_CONTINUOUS,CONTINUOUS),
- Y(SND_PCM_RATE_KNOT,KNOT),
- Y(SND_PCM_RATE_8000,8000),
- Y(SND_PCM_RATE_11025,11025),
- Y(SND_PCM_RATE_16000,16000),
- Y(SND_PCM_RATE_22050,22050),
- Y(SND_PCM_RATE_32000,32000),
- Y(SND_PCM_RATE_44100,44100),
- Y(SND_PCM_RATE_48000,48000),
- Y(SND_PCM_RATE_88200,88200),
- Y(SND_PCM_RATE_96000,96000),
- Y(SND_PCM_RATE_176400,176400),
- Y(SND_PCM_RATE_192000,192000),
- chn_info.min_rate, chn_info.max_rate,
- chn_info.min_voices, chn_info.max_voices,
- chn_info.buffer_size,
- chn_info.min_fragment_size, chn_info.max_fragment_size,
- chn_info.fragment_align);
-#undef X
-#undef Y
-
- /* FIXME: use better values */
- WOutDev[0].caps.wMid = 0x0002;
- WOutDev[0].caps.wPid = 0x0104;
- strcpyW(WOutDev[0].caps.szPname, ini);
- WOutDev[0].caps.vDriverVersion = 0x0100;
- WOutDev[0].caps.dwFormats = 0x00000000;
- WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;
-#define X(r,v) \
- if (chn_info.rates & SND_PCM_RATE_##r) \
- { \
- if (chn_info.formats & SND_PCM_FMT_U8) \
- { \
- if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
- WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
- if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
- WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \
- } \
- if (chn_info.formats & SND_PCM_FMT_S16_LE) \
- { \
- if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \
- WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
- if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \
- WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \
- } \
- }
- X(11025,1);
- X(22050,2);
- X(44100,4);
-#undef X
- if (chn_info.min_voices > 1) FIXME("-\n");
- WOutDev[0].caps.wChannels = (chn_info.max_voices >= 2) ? 2 : 1;
- if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices)
- WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;
-
- /* FIXME: always true ? */
- WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
-
- /* FIXME: is test sufficient ? */
- if (chn_info.flags & SND_PCM_CHNINFO_MMAP)
- WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;
-
- TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",
- WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);
-
- snd_pcm_close(h);
-
- return 0;
-}
-
-/******************************************************************
- * ALSA_InitRingMessage
- *
- * Initialize the ring of messages for passing between driver's caller and playback/record
- * thread
- */
-static int ALSA_InitRingMessage(ALSA_MSG_RING* omr)
-{
- omr->msg_toget = 0;
- omr->msg_tosave = 0;
- omr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
- memset(omr->messages, 0, sizeof(ALSA_MSG) * ALSA_RING_BUFFER_SIZE);
- InitializeCriticalSection(&omr->msg_crst);
- return 0;
-}
-
-/******************************************************************
- * ALSA_DestroyRingMessage
- *
- */
-static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr)
-{
- CloseHandle(omr->msg_event);
- DeleteCriticalSection(&omr->msg_crst);
- return 0;
-}
-
-/******************************************************************
- * ALSA_AddRingMessage
- *
- * Inserts a new message into the ring (should be called from DriverProc derivated routines)
- */
-static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
-{
- HANDLE hEvent = INVALID_HANDLE_VALUE;
-
- EnterCriticalSection(&omr->msg_crst);
- if ((omr->msg_toget == ((omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE))) /* buffer overflow ? */
- {
- ERR("buffer overflow !?\n");
- LeaveCriticalSection(&omr->msg_crst);
- return 0;
- }
- if (wait)
- {
- hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
- if (hEvent == INVALID_HANDLE_VALUE)
- {
- ERR("can't create event !?\n");
- LeaveCriticalSection(&omr->msg_crst);
- return 0;
- }
- if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->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 */
- omr->msg_toget = (omr->msg_toget + ALSA_RING_BUFFER_SIZE - 1) % ALSA_RING_BUFFER_SIZE;
-
- omr->messages[omr->msg_toget].msg = msg;
- omr->messages[omr->msg_toget].param = param;
- omr->messages[omr->msg_toget].hEvent = hEvent;
- }
- else
- {
- omr->messages[omr->msg_tosave].msg = msg;
- omr->messages[omr->msg_tosave].param = param;
- omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
- omr->msg_tosave = (omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE;
- }
- LeaveCriticalSection(&omr->msg_crst);
- /* signal a new message */
- SetEvent(omr->msg_event);
- if (wait)
- {
- /* wait for playback/record thread to have processed the message */
- WaitForSingleObject(hEvent, INFINITE);
- CloseHandle(hEvent);
- }
- return 1;
-}
-
-/******************************************************************
- * ALSA_RetrieveRingMessage
- *
- * Get a message from the ring. Should be called by the playback/record thread.
- */
-static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,
- enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
-{
- EnterCriticalSection(&omr->msg_crst);
-
- if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
- {
- LeaveCriticalSection(&omr->msg_crst);
- return 0;
- }
-
- *msg = omr->messages[omr->msg_toget].msg;
- omr->messages[omr->msg_toget].msg = 0;
- *param = omr->messages[omr->msg_toget].param;
- *hEvent = omr->messages[omr->msg_toget].hEvent;
- omr->msg_toget = (omr->msg_toget + 1) % ALSA_RING_BUFFER_SIZE;
- LeaveCriticalSection(&omr->msg_crst);
- return 1;
-}
-
-/*======================================================================*
- * Low level WAVE OUT implementation *
- *======================================================================*/
-
-/**************************************************************************
- * wodNotifyClient [internal]
- */
-static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
-{
- TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);
-
- switch (wMsg) {
- case WOM_OPEN:
- case WOM_CLOSE:
- case WOM_DONE:
- if (wwo->wFlags != DCB_NULL &&
- !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
- (HDRVR)wwo->waveDesc.hWave,
- 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, snd_pcm_channel_status_t* ps)
-{
- snd_pcm_channel_status_t s;
- snd_pcm_channel_status_t* status = (ps) ? ps : &s;
-
- if (snd_pcm_channel_status(wwo->handle, status))
- {
- ERR("Can't get channel status: %s\n", snd_strerror(errno));
- return FALSE;
- }
- wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - status->count);
- if (wwo->dwPlayedTotal != status->scount)
- {
- FIXME("Ooch: %u played by ALSA, %lu counted by driver\n",
- status->scount, wwo->dwPlayedTotal);
- if (wwo->dwPlayedTotal & 0x8000000) wwo->dwPlayedTotal = 0;
- }
- 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);
- } else {
- TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
- wwo->lpLoopPtr = lpWaveHdr;
- /* Windows does not touch WAVEHDR.dwLoops,
- * so we need to make an internal copy */
- wwo->dwLoops = lpWaveHdr->dwLoops;
- }
- }
- wwo->dwPartialOffset = 0;
-}
-
-/**************************************************************************
- * 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;
-
- wwo->dwPartialOffset = 0;
- 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_DSPWait [internal]
- * Returns the number of milliseconds to wait for the DSP buffer to write
- * one fragment.
- */
-static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
-{
- /* time for one fragment to be played */
- return wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec;
-}
-
-/**************************************************************************
- * wodPlayer_NotifyWait [internal]
- * Returns the number of milliseconds to wait before attempting to notify
- * completion of the specified wavehdr.
- * This is based on the number of bytes remaining to be written in the
- * wave.
- */
-static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
-{
- DWORD dwMillis;
-
- if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
- dwMillis = 1;
- } else {
- dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
- if (!dwMillis) dwMillis = 1;
- }
-
- return dwMillis;
-}
-
-
-/**************************************************************************
- * wodPlayer_WriteMaxFrags [internal]
- * Writes the maximum number of bytes palsaible to the DSP and returns
- * the number of bytes written.
- */
-static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
-{
- /* Only attempt to write to free bytes */
- DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
- int toWrite = min(dwLength, *bytes);
- int written;
-
- TRACE("Writing wavehdr %p.%lu[%lu]\n",
- wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
- written = snd_pcm_write(wwo->handle, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
- if (written <= 0)
- {
- ERR("Wrote: %d bytes (%s)\n", written, snd_strerror(errno));
- return written;
- }
-
- if (written >= dwLength) {
- /* If we wrote all current wavehdr, skip to the next one */
- wodPlayer_PlayPtrNext(wwo);
- } else {
- /* Remove the amount written */
- wwo->dwPartialOffset += written;
- }
- *bytes -= written;
- wwo->dwWrittenTotal += written;
-
- return written;
-}
-
-
-/**************************************************************************
- * wodPlayer_NotifyCompletions [internal]
- *
- * Notifies and remove from queue all wavehdrs which have been played to
- * the speaker (ie. they have cleared the ALSA buffer). 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
- */
- while ((lpWaveHdr = wwo->lpQueuePtr) &&
- (force ||
- (lpWaveHdr != wwo->lpPlayPtr &&
- lpWaveHdr != wwo->lpLoopPtr &&
- lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
-
- 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) ?
- wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
-}
-
-/**************************************************************************
- * wodPlayer_Reset [internal]
- *
- * wodPlayer helper. Resets current output stream.
- */
-static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
-{
- wodUpdatePlayedTotal(wwo, NULL);
- /* updates current notify list */
- wodPlayer_NotifyCompletions(wwo, FALSE);
-
- if (snd_pcm_playback_flush(wwo->handle) != 0) {
- FIXME("flush: %s\n", snd_strerror(errno));
- wwo->hThread = 0;
- wwo->state = WINE_WS_STOPPED;
- ExitThread(-1);
- }
-
- 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;
- wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
- /* Clear partial wavehdr */
- wwo->dwPartialOffset = 0;
-
- /* remove any existing message in the ring */
- EnterCriticalSection(&wwo->msgRing.msg_crst);
- /* return all pending headers in queue */
- while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
- {
- 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;
- wwo->dwPartialOffset = 0;
- wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
- } else {
- LPWAVEHDR ptr;
- DWORD sz = wwo->dwPartialOffset;
-
- /* reset all the data as if we had written only up to lpPlayedTotal bytes */
- /* compute the max size playable from lpQueuePtr */
- for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
- sz += ptr->dwBufferLength;
- }
- /* because the reset lpPlayPtr will be lpQueuePtr */
- if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
- wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
- wwo->dwWrittenTotal = wwo->dwPlayedTotal;
- wwo->lpPlayPtr = wwo->lpQueuePtr;
- }
- 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 (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
- TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
-
- switch (msg) {
- case WINE_WM_PAUSING:
- wodPlayer_Reset(wwo, FALSE);
- SetEvent(ev);
- break;
- case WINE_WM_RESTARTING:
- if (wwo->state == WINE_WS_PAUSED)
- {
- snd_pcm_playback_prepare(wwo->handle);
- 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, NULL);
- 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:
- /* sanity check: this should not happen since the device must have been reset before */
- if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
- wwo->hThread = 0;
- wwo->state = WINE_WS_CLOSED;
- SetEvent(ev);
- ExitThread(0);
- /* shouldn't go here */
- default:
- FIXME("unknown message %d\n", msg);
- break;
- }
- }
-}
-
-/**************************************************************************
- * wodPlayer_FeedDSP [internal]
- * Feed as much sound data as we can into the DSP and return the number of
- * milliseconds before it will be necessary to feed the DSP again.
- */
-static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
-{
- snd_pcm_channel_status_t status;
- DWORD availInQ;
-
- wodUpdatePlayedTotal(wwo, &status);
- availInQ = status.free;
-
-#if 0
- TODO;
- TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
- dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);
-#endif
-
- /* input queue empty and output buffer with less than one fragment to play */
- /* FIXME: we should be able to catch OVERRUN errors */
- if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize) {
- TRACE("Run out of wavehdr:s... flushing\n");
- snd_pcm_playback_drain(wwo->handle);
- wwo->dwPlayedTotal = wwo->dwWrittenTotal;
- return INFINITE;
- }
-
- /* no more room... no need to try to feed */
- if (status.free > 0) {
- /* Feed from partial wavehdr */
- if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
- wodPlayer_WriteMaxFrags(wwo, &availInQ);
- }
-
- /* Feed wavehdrs until we run out of wavehdrs or DSP space */
- if (wwo->dwPartialOffset == 0) {
- while (wwo->lpPlayPtr && availInQ > 0) {
- /* note the value that dwPlayedTotal will return when this wave finishes playing */
- wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
- wodPlayer_WriteMaxFrags(wwo, &availInQ);
- }
- }
- }
- return wodPlayer_DSPWait(wwo);
-}
-
-
-/**************************************************************************
- * wodPlayer [internal]
- */
-static DWORD CALLBACK wodPlayer(LPVOID pmt)
-{
- WORD uDevID = (DWORD)pmt;
- WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
- DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
- DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
- DWORD dwSleepTime;
-
- wwo->state = WINE_WS_STOPPED;
- SetEvent(wwo->hStartUpEvent);
-
- for (;;) {
- /** Wait for the shortest time before an action is required. If there
- * are no pending actions, wait forever for a command.
- */
- dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
- TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
- WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);
- wodPlayer_ProcessMessages(wwo);
- if (wwo->state == WINE_WS_PLAYING) {
- dwNextFeedTime = wodPlayer_FeedDSP(wwo);
- dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
- } else {
- dwNextFeedTime = dwNextNotifyTime = INFINITE;
- }
- }
-}
-
-/**************************************************************************
- * wodGetDevCaps [internal]
- */
-static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
-{
- TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
-
- 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;
- snd_pcm_channel_params_t params;
-
- TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
- if (lpDesc == NULL) {
- WARN("Invalid Parameter !\n");
- return MMSYSERR_INVALPARAM;
- }
- if (wDevID >= MAX_WAVEOUTDRV) {
- TRACE("MAX_WAVOUTDRV reached !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- /* only PCM format is supported so far... */
- if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
- lpDesc->lpFormat->nChannels == 0 ||
- lpDesc->lpFormat->nSamplesPerSec == 0) {
- WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
- lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
- lpDesc->lpFormat->nSamplesPerSec);
- return WAVERR_BADFORMAT;
- }
-
- if (dwFlags & WAVE_FORMAT_QUERY) {
- TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
- lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
- lpDesc->lpFormat->nSamplesPerSec);
- return MMSYSERR_NOERROR;
- }
-
- wwo = &WOutDev[wDevID];
-
- if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))
- /* not supported, ignore it */
- dwFlags &= ~WAVE_DIRECTSOUND;
-
- wwo->handle = 0;
- if (snd_pcm_open(&wwo->handle, wDevID, 0, SND_PCM_OPEN_DUPLEX|SND_PCM_OPEN_NONBLOCK))
- {
- ERR("Error open: %s\n", snd_strerror(errno));
- return MMSYSERR_NOTENABLED;
- }
-
- memset(¶ms, 0, sizeof(params));
- params.channel = SND_PCM_CHANNEL_PLAYBACK;
- params.start_mode = SND_PCM_START_DATA;
- params.stop_mode = SND_PCM_STOP_STOP;
- params.mode = SND_PCM_MODE_STREAM;
- params.buf.stream.queue_size = 0x1000;
- params.buf.stream.fill = SND_PCM_FILL_SILENCE;
- params.buf.stream.max_fill = 0x800;
-
- wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
-
- memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
- 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;
- }
- params.format.interleave = 1;
- params.format.format = (wwo->format.wBitsPerSample == 16) ?
- SND_PCM_SFMT_S16_LE : SND_PCM_SFMT_U8;
- params.format.rate = wwo->format.wf.nSamplesPerSec;
- params.format.voices = (wwo->format.wf.nChannels > 1) ? 2 : 1;
- params.format.special = 0;
-
- if (snd_pcm_channel_params(wwo->handle, ¶ms))
- {
- ERR("Can't set params: %s\n", snd_strerror(errno));
- snd_pcm_close(wwo->handle);
- wwo->handle = NULL;
- return MMSYSERR_INVALPARAM;
- }
-#if 0
- TODO;
- if (params.format.rate != format != ((wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8))
- ERR("Can't set format to %d (%d)\n",
- (wwo->format.wBitsPerSample == 16) ? AFMT_S16_LE : AFMT_U8, format);
- if (dsp_stereo != (wwo->format.wf.nChannels > 1) ? 1 : 0)
- ERR("Can't set stereo to %u (%d)\n",
- (wwo->format.wf.nChannels > 1) ? 1 : 0, dsp_stereo);
- if (!NEAR_MATCH(sample_rate, wwo->format.wf.nSamplesPerSec))
- ERR("Can't set sample_rate to %lu (%d)\n",
- wwo->format.wf.nSamplesPerSec, sample_rate);
-#endif
-
- snd_pcm_playback_prepare(wwo->handle);
-
- /* Remember fragsize and total buffer size for future use */
- wwo->dwBufferSize = params.buf.stream.queue_size;
- /* FIXME: should get rid off fragment size */
- wwo->dwFragmentSize = wwo->dwBufferSize >> 4; /* why not */
- wwo->dwPlayedTotal = 0;
- wwo->dwWrittenTotal = 0;
- wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
-
- ALSA_InitRingMessage(&wwo->msgRing);
-
- if (!(dwFlags & WAVE_DIRECTSOUND)) {
- wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
- wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
- WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
- CloseHandle(wwo->hStartUpEvent);
- } else {
- wwo->hThread = INVALID_HANDLE_VALUE;
- wwo->dwThreadID = 0;
- }
- wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
-
- TRACE("handle=%08lx fragmentSize=%ld\n",
- (DWORD)wwo->handle, wwo->dwFragmentSize);
- if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
- ERR("Fragment doesn't contain an integral number of data blocks\n");
-
- TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
- 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 || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- wwo = &WOutDev[wDevID];
- if (wwo->lpQueuePtr) {
- WARN("buffers still playing !\n");
- ret = WAVERR_STILLPLAYING;
- } else {
- if (wwo->hThread != INVALID_HANDLE_VALUE) {
- ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
- }
- if (wwo->mmap_buffer) {
- snd_pcm_munmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK);
- wwo->mmap_buffer = wwo->mmap_control = NULL;
- }
-
- ALSA_DestroyRingMessage(&wwo->msgRing);
-
- snd_pcm_close(wwo->handle);
- wwo->handle = NULL;
- wwo->dwFragmentSize = 0;
- ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
- }
- return ret;
-}
-
-/**************************************************************************
- * wodWrite [internal]
- *
- */
-static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
-{
- TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
-
- /* first, do the sanity checks... */
- if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
- WARN("bad dev ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
- return WAVERR_UNPREPARED;
-
- if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
- return WAVERR_STILLPLAYING;
-
- lpWaveHdr->dwFlags &= ~WHDR_DONE;
- lpWaveHdr->dwFlags |= WHDR_INQUEUE;
- lpWaveHdr->lpNext = 0;
-
- ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
-
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodPrepare [internal]
- */
-static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
-{
- TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
-
- if (wDevID >= MAX_WAVEOUTDRV) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
- return WAVERR_STILLPLAYING;
-
- lpWaveHdr->dwFlags |= WHDR_PREPARED;
- lpWaveHdr->dwFlags &= ~WHDR_DONE;
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodUnprepare [internal]
- */
-static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
-{
- TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
-
- if (wDevID >= MAX_WAVEOUTDRV) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
- return WAVERR_STILLPLAYING;
-
- lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
- lpWaveHdr->dwFlags |= WHDR_DONE;
-
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodPause [internal]
- */
-static DWORD wodPause(WORD wDevID)
-{
- TRACE("(%u);!\n", wDevID);
-
- if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- ALSA_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 || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
- ALSA_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 || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
-
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodGetPosition [internal]
- */
-static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
-{
- int time;
- DWORD val;
- WINE_WAVEOUT* wwo;
-
- TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
-
- if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
-
- if (lpTime == NULL) return MMSYSERR_INVALPARAM;
-
- wwo = &WOutDev[wDevID];
- ALSA_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
- val = wwo->dwPlayedTotal;
-
- TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
- lpTime->wType, wwo->format.wBitsPerSample,
- wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
- wwo->format.wf.nAvgBytesPerSec);
- TRACE("dwPlayedTotal=%lu\n", val);
-
- switch (lpTime->wType) {
- case TIME_BYTES:
- lpTime->u.cb = val;
- TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
- break;
- case TIME_SAMPLES:
- lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
- TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
- break;
- case TIME_SMPTE:
- time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
- lpTime->u.smpte.hour = time / 108000;
- time -= lpTime->u.smpte.hour * 108000;
- lpTime->u.smpte.min = time / 1800;
- time -= lpTime->u.smpte.min * 1800;
- lpTime->u.smpte.sec = time / 30;
- time -= lpTime->u.smpte.sec * 30;
- lpTime->u.smpte.frame = time;
- lpTime->u.smpte.fps = 30;
- 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:
- FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
- lpTime->wType = TIME_MS;
- case TIME_MS:
- lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
- TRACE("TIME_MS=%lu\n", lpTime->u.ms);
- break;
- }
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodBreakLoop [internal]
- */
-static DWORD wodBreakLoop(WORD wDevID)
-{
- TRACE("(%u);\n", wDevID);
-
- if (wDevID >= MAX_WAVEOUTDRV || WOutDev[wDevID].handle == NULL) {
- WARN("bad device ID !\n");
- return MMSYSERR_BADDEVICEID;
- }
- ALSA_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodGetVolume [internal]
- */
-static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
-{
-#if 0
- int mixer;
-#endif
- int volume;
- DWORD left, right;
-
- TRACE("(%u, %p);\n", wDevID, lpdwVol);
-
- if (lpdwVol == NULL)
- return MMSYSERR_NOTENABLED;
-#if 0
- TODO;
- if ((mixer = open(MIXER_DEV, O_RDONLY|O_NDELAY)) < 0) {
- WARN("mixer device not available !\n");
- return MMSYSERR_NOTENABLED;
- }
- if (ioctl(mixer, SOUND_MIXER_READ_PCM, &volume) == -1) {
- WARN("unable to read mixer !\n");
- return MMSYSERR_NOTENABLED;
- }
- close(mixer);
-#else
- volume = 0x2020;
-#endif
- left = LOBYTE(volume);
- right = HIBYTE(volume);
- TRACE("left=%ld right=%ld !\n", left, right);
- *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) << 16);
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodSetVolume [internal]
- */
-static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
-{
-#if 0
- int mixer;
-#endif
- int volume;
- DWORD left, right;
-
- TRACE("(%u, %08lX);\n", wDevID, dwParam);
-
- left = (LOWORD(dwParam) * 100) / 0xFFFFl;
- right = (HIWORD(dwParam) * 100) / 0xFFFFl;
- volume = left + (right << 8);
-
-#if 0
- TODO;
- if ((mixer = open(MIXER_DEV, O_WRONLY|O_NDELAY)) < 0) {
- WARN("mixer device not available !\n");
- return MMSYSERR_NOTENABLED;
- }
- if (ioctl(mixer, SOUND_MIXER_WRITE_PCM, &volume) == -1) {
- WARN("unable to set mixer !\n");
- return MMSYSERR_NOTENABLED;
- } else {
- TRACE("volume=%04x\n", (unsigned)volume);
- }
- close(mixer);
-#endif
- return MMSYSERR_NOERROR;
-}
-
-/**************************************************************************
- * wodGetNumDevs [internal]
- */
-static DWORD wodGetNumDevs(void)
-{
- return ALSA_WodNumDevs;
-}
-
-/**************************************************************************
- * wodMessage (WINEALSA.@)
- */
-DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
- DWORD dwParam1, DWORD dwParam2)
-{
- TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
- wDevID, wMsg, dwUser, dwParam1, dwParam2);
-
- 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);
- case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
- case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
- case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
- 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);
-
- case DRV_QUERYDSOUNDIFACE: return wodDsCreate(wDevID, (PIDSDRIVER*)dwParam1);
- default:
- FIXME("unknown message %d!\n", wMsg);
- }
- return MMSYSERR_NOTSUPPORTED;
-}
-
-/*======================================================================*
- * Low level DSOUND implementation *
- *======================================================================*/
-
-typedef struct IDsDriverImpl IDsDriverImpl;
-typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
-
-struct IDsDriverImpl
-{
- /* IUnknown fields */
- IDsDriverVtbl *lpVtbl;
- DWORD ref;
- /* IDsDriverImpl fields */
- UINT wDevID;
- IDsDriverBufferImpl*primary;
-};
-
-struct IDsDriverBufferImpl
-{
- /* IUnknown fields */
- IDsDriverBufferVtbl *lpVtbl;
- DWORD ref;
- /* IDsDriverBufferImpl fields */
- IDsDriverImpl* drv;
- DWORD buflen;
-};
-
-static HRESULT DSDB_UnmapPrimary(IDsDriverBufferImpl *dsdb)
-{
- WINE_WAVEOUT *wwo = &(WOutDev[dsdb->drv->wDevID]);
- if (wwo->mmap_buffer) {
- if (snd_pcm_munmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK) < 0) {
- ERR("(%p): Could not unmap sound device (errno=%d)\n", dsdb, errno);
- return DSERR_GENERIC;
- }
- wwo->mmap_buffer = wwo->mmap_control = NULL;
- TRACE("(%p): sound device unmapped\n", dsdb);
- }
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
- FIXME("(): stub!\n");
- return DSERR_UNSUPPORTED;
-}
-
-static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
-{
- IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
- This->ref++;
- return This->ref;
-}
-
-static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
-{
- IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
- if (--This->ref)
- return This->ref;
- if (This == This->drv->primary)
- This->drv->primary = NULL;
- DSDB_UnmapPrimary(This);
- HeapFree(GetProcessHeap(),0,This);
- return 0;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
- LPVOID*ppvAudio1,LPDWORD pdwLen1,
- LPVOID*ppvAudio2,LPDWORD pdwLen2,
- DWORD dwWritePosition,DWORD dwWriteLen,
- DWORD dwFlags)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
- /* FIXME: we need to implement it */
- TRACE("(%p)\n",iface);
- return DSERR_UNSUPPORTED;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
- LPVOID pvAudio1,DWORD dwLen1,
- LPVOID pvAudio2,DWORD dwLen2)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
- TRACE("(%p)\n",iface);
- return DSERR_UNSUPPORTED;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
- LPWAVEFORMATEX pwfx)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
-
- TRACE("(%p,%p)\n",iface,pwfx);
- /* On our request (GetDriverDesc flags), DirectSound has by now used
- * waveOutClose/waveOutOpen to set the format...
- * unfortunately, this means our mmap() is now gone...
- * so we need to somehow signal to our DirectSound implementation
- * that it should completely recreate this HW buffer...
- * this unexpected error code should do the trick... */
- return DSERR_BUFFERLOST;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
- TRACE("(%p,%ld): stub\n",iface,dwFreq);
- return DSERR_UNSUPPORTED;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
-{
- /* IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface; */
- FIXME("(%p,%p): stub!\n",iface,pVolPan);
- return DSERR_UNSUPPORTED;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
-{
- /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
- TRACE("(%p,%ld): stub\n",iface,dwNewPos);
- return DSERR_UNSUPPORTED;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
- LPDWORD lpdwPlay, LPDWORD lpdwWrite)
-{
-#if 0
- IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
- TODO;
- count_info info;
- DWORD ptr;
-
- TRACE("(%p)\n",iface);
- if (WOutDev[This->drv->wDevID].handle == NULL) {
- ERR("device not open, but accessing?\n");
- return DSERR_UNINITIALIZED;
- }
- if (ioctl(WOutDev[This->drv->wDevID].unixdev, SNDCTL_DSP_GETOPTR, &info) < 0) {
- ERR("ioctl failed (%d)\n", errno);
- return DSERR_GENERIC;
- }
- ptr = info.ptr & ~3; /* align the pointer, just in case */
- if (lpdwPlay) *lpdwPlay = ptr;
- if (lpdwWrite) {
- /* add some safety margin (not strictly necessary, but...) */
- if (WOutDev[This->drv->wDevID].caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
- *lpdwWrite = ptr + 32;
- else
- *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
- while (*lpdwWrite > This->buflen)
- *lpdwWrite -= This->buflen;
-
- }
-#endif
- TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
-{
- IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
-
- TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
-
- /* FIXME: error handling */
- snd_pcm_playback_go(WOutDev[This->drv->wDevID].handle);
-
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
-{
- IDsDriverBufferImpl *This = (IDsDriverBufferImpl *)iface;
-
- TRACE("(%p)\n",iface);
-
- /* no more playing */
- /* FIXME: error handling */
- snd_pcm_playback_drain(WOutDev[This->drv->wDevID].handle);
-
- /* Most ALSA drivers just can't stop the playback without closing the device...
- * so we need to somehow signal to our DirectSound implementation
- * that it should completely recreate this HW buffer...
- * this unexpected error code should do the trick... */
- return DSERR_BUFFERLOST;
-}
-
-static IDsDriverBufferVtbl dsdbvt =
-{
- IDsDriverBufferImpl_QueryInterface,
- IDsDriverBufferImpl_AddRef,
- IDsDriverBufferImpl_Release,
- IDsDriverBufferImpl_Lock,
- IDsDriverBufferImpl_Unlock,
- IDsDriverBufferImpl_SetFormat,
- IDsDriverBufferImpl_SetFrequency,
- IDsDriverBufferImpl_SetVolumePan,
- IDsDriverBufferImpl_SetPosition,
- IDsDriverBufferImpl_GetPosition,
- IDsDriverBufferImpl_Play,
- IDsDriverBufferImpl_Stop
-};
-
-static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
-{
- /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
- FIXME("(%p): stub!\n",iface);
- return DSERR_UNSUPPORTED;
-}
-
-static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
- This->ref++;
- return This->ref;
-}
-
-static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
- if (--This->ref)
- return This->ref;
- HeapFree(GetProcessHeap(),0,This);
- return 0;
-}
-
-static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
- TRACE("(%p,%p)\n",iface,pDesc);
- pDesc->dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
- DSDDESC_USESYSTEMMEMORY;
- strcpy(pDesc->szDesc,"WineALSA DirectSound Driver");
- strcpy(pDesc->szDrvName,"winealsa.drv");
- pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
- pDesc->wVxdId = 0;
- pDesc->wReserved = 0;
- pDesc->ulDeviceNum = This->wDevID;
- pDesc->dwHeapType = DSDHEAP_NOHEAP;
- pDesc->pvDirectDrawHeap = NULL;
- pDesc->dwMemStartAddress = 0;
- pDesc->dwMemEndAddress = 0;
- pDesc->dwMemAllocExtra = 0;
- pDesc->pvReserved1 = NULL;
- pDesc->pvReserved2 = NULL;
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
-
- TRACE("(%p)\n",iface);
- /* FIXME: error handling */
- snd_pcm_channel_prepare(WOutDev[This->wDevID].handle, SND_PCM_CHANNEL_PLAYBACK);
-
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
- TRACE("(%p)\n",iface);
- if (This->primary) {
- ERR("problem with DirectSound: primary not released\n");
- return DSERR_GENERIC;
- }
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
-{
- /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
- TRACE("(%p,%p)\n",iface,pCaps);
- memset(pCaps, 0, sizeof(*pCaps));
- /* FIXME: need to check actual capabilities */
- pCaps->dwFlags = DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO |
- DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARY16BIT;
- pCaps->dwPrimaryBuffers = 1;
- /* the other fields only apply to secondary buffers, which we don't support
- * (unless we want to mess with wavetable synthesizers and MIDI) */
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
- LPWAVEFORMATEX pwfx,
- DWORD dwFlags, DWORD dwCardAddress,
- LPDWORD pdwcbBufferSize,
- LPBYTE *ppbBuffer,
- LPVOID *ppvObj)
-{
- IDsDriverImpl *This = (IDsDriverImpl *)iface;
- IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
- WINE_WAVEOUT *wwo = &(WOutDev[This->wDevID]);
- struct snd_pcm_channel_setup setup;
-
- TRACE("(%p,%p,%lx,%lx)\n",iface,pwfx,dwFlags,dwCardAddress);
- /* we only support primary buffers */
- if (!(dwFlags & DSBCAPS_PRIMARYBUFFER))
- return DSERR_UNSUPPORTED;
- if (This->primary)
- return DSERR_ALLOCATED;
- if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
- return DSERR_CONTROLUNAVAIL;
-
- *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverBufferImpl));
- if (*ippdsdb == NULL)
- return DSERR_OUTOFMEMORY;
- (*ippdsdb)->lpVtbl = &dsdbvt;
- (*ippdsdb)->ref = 1;
- (*ippdsdb)->drv = This;
-
- if (!wwo->mmap_buffer) {
- if (snd_pcm_mmap(wwo->handle, SND_PCM_CHANNEL_PLAYBACK, &wwo->mmap_control, &wwo->mmap_buffer))
- {
- ERR("(%p): Could not map sound device for direct access (%s)\n", *ippdsdb, snd_strerror(errno));
- return DSERR_GENERIC;
- }
-
- setup.mode = SND_PCM_MODE_BLOCK;
- setup.channel = SND_PCM_CHANNEL_PLAYBACK;
- if (snd_pcm_channel_setup(wwo->handle, &setup) < 0) {
- ERR("Unable to obtain setup\n");
- /* FIXME: resource cleanup */
- return DSERR_GENERIC;
- }
- wwo->mmap_block_size = setup.buf.block.frag_size;
- wwo->mmap_block_number = setup.buf.block.frags;
-
- TRACE("(%p): sound device has been mapped for direct access at %p, size=%d\n",
- *ippdsdb, wwo->mmap_buffer, setup.buf.block.frags * setup.buf.block.frag_size);
-#if 0
- /* for some reason, es1371 and sblive! sometimes have junk in here.
- * clear it, or we get junk noise */
- /* some libc implementations are buggy: their memset reads from the buffer...
- * to work around it, we have to zero the block by hand. We don't do the expected:
- * memset(wwo->mapping, 0, wwo->maplen);
- */
- {
- char* p1 = wwo->mapping;
- unsigned len = wwo->maplen;
-
- if (len >= 16) /* so we can have at least a 4 long area to store... */
- {
- /* the mmap:ed value is (at least) dword aligned
- * so, start filling the complete unsigned long:s
- */
- int b = len >> 2;
- unsigned long* p4 = (unsigned long*)p1;
-
- while (b--) *p4++ = 0;
- /* prepare for filling the rest */
- len &= 3;
- p1 = (unsigned char*)p4;
- }
- /* in all cases, fill the remaining bytes */
- while (len-- != 0) *p1++ = 0;
- }
-#endif
- }
-
- /* primary buffer is ready to go */
- *pdwcbBufferSize = wwo->mmap_block_size * wwo->mmap_block_number;
- *ppbBuffer = wwo->mmap_buffer;
-
- This->primary = *ippdsdb;
-
- return DS_OK;
-}
-
-static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
- PIDSDRIVERBUFFER pBuffer,
- LPVOID *ppvObj)
-{
- /* IDsDriverImpl *This = (IDsDriverImpl *)iface; */
- TRACE("(%p,%p): stub\n",iface,pBuffer);
- return DSERR_INVALIDCALL;
-}
-
-static IDsDriverVtbl dsdvt =
-{
- IDsDriverImpl_QueryInterface,
- IDsDriverImpl_AddRef,
- IDsDriverImpl_Release,
- IDsDriverImpl_GetDriverDesc,
- IDsDriverImpl_Open,
- IDsDriverImpl_Close,
- IDsDriverImpl_GetCaps,
- IDsDriverImpl_CreateSoundBuffer,
- IDsDriverImpl_DuplicateSoundBuffer
-};
-
-static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
-{
- IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
-
- /* the HAL isn't much better than the HEL if we can't do mmap() */
- if (!(WOutDev[wDevID].caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
- ERR("DirectSound flag not set\n");
- 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;
- }
-
- *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDsDriverImpl));
- if (!*idrv)
- return MMSYSERR_NOMEM;
- (*idrv)->lpVtbl = &dsdvt;
- (*idrv)->ref = 1;
-
- (*idrv)->wDevID = wDevID;
- (*idrv)->primary = NULL;
- return MMSYSERR_NOERROR;
-}
-
-/* we don't need a default wodMessage for audio when we don't have ALSA, the
- * audio.c file will provide it for us
- */
-#endif /* HAVE_ALSA && interface == 0.5 */