1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
5 * Copyright 1994 Martin Ayotte
6 * 1999 Eric Pouech (async playing in waveOut/waveIn)
7 * 2000 Eric Pouech (loops in waveOut)
8 * 2002 Eric Pouech (full duplex)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * pause in waveOut does not work correctly in loop mode
27 * Direct Sound Capture driver does not work (not complete yet)
30 /*#define EMULATE_SB16*/
32 /* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
35 /* an exact wodGetPosition is usually not worth the extra context switches,
36 * as we're going to have near fragment accuracy anyway */
37 /* #define EXACT_WODPOSITION */
40 #include "wine/port.h"
51 #ifdef HAVE_SYS_IOCTL_H
52 # include <sys/ioctl.h>
54 #ifdef HAVE_SYS_MMAN_H
55 # include <sys/mman.h>
57 #ifdef HAVE_SYS_POLL_H
58 # include <sys/poll.h>
65 #include "wine/winuser16.h"
70 #include "wine/debug.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wave
);
74 /* Allow 1% deviation for sample rates (some ES137x cards) */
75 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
79 #define MAX_WAVEDRV (6)
81 /* state diagram for waveOut writing:
83 * +---------+-------------+---------------+---------------------------------+
84 * | state | function | event | new state |
85 * +---------+-------------+---------------+---------------------------------+
86 * | | open() | | STOPPED |
87 * | PAUSED | write() | | PAUSED |
88 * | STOPPED | write() | <thrd create> | PLAYING |
89 * | PLAYING | write() | HEADER | PLAYING |
90 * | (other) | write() | <error> | |
91 * | (any) | pause() | PAUSING | PAUSED |
92 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
93 * | (any) | reset() | RESETTING | STOPPED |
94 * | (any) | close() | CLOSING | CLOSED |
95 * +---------+-------------+---------------+---------------------------------+
98 /* states of the playing device */
99 #define WINE_WS_PLAYING 0
100 #define WINE_WS_PAUSED 1
101 #define WINE_WS_STOPPED 2
102 #define WINE_WS_CLOSED 3
104 /* events to be send to device */
105 enum win_wm_message
{
106 WINE_WM_PAUSING
= WM_USER
+ 1, WINE_WM_RESTARTING
, WINE_WM_RESETTING
, WINE_WM_HEADER
,
107 WINE_WM_UPDATE
, WINE_WM_BREAKLOOP
, WINE_WM_CLOSING
, WINE_WM_STARTING
, WINE_WM_STOPPING
111 #define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
112 #define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
113 #define RESET_OMR(omr) do { } while (0)
114 #define WAIT_OMR(omr, sleep) \
115 do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
116 pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
118 #define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
119 #define CLEAR_OMR(omr) do { } while (0)
120 #define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
121 #define WAIT_OMR(omr, sleep) \
122 do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
126 enum win_wm_message msg
; /* message identifier */
127 DWORD param
; /* parameter for this message */
128 HANDLE hEvent
; /* if message is synchronous, handle of event for synchro */
131 /* implement an in-process message ring for better performance
132 * (compared to passing thru the server)
133 * this ring will be used by the input (resp output) record (resp playback) routine
135 #define OSS_RING_BUFFER_INCREMENT 64
137 int ring_buffer_size
;
146 CRITICAL_SECTION msg_crst
;
149 typedef struct tagOSS_DEVICE
{
153 WAVEOUTCAPSA out_caps
;
155 DWORD in_caps_support
;
156 unsigned open_access
;
162 unsigned audio_fragment
;
164 BOOL bTriggerSupport
;
167 DSDRIVERDESC ds_desc
;
168 DSDRIVERCAPS ds_caps
;
169 DSCDRIVERCAPS dsc_caps
;
174 static OSS_DEVICE OSS_Devices
[MAX_WAVEDRV
];
178 volatile int state
; /* one of the WINE_WS_ manifest constants */
179 WAVEOPENDESC waveDesc
;
181 PCMWAVEFORMAT format
;
184 /* OSS information */
185 DWORD dwFragmentSize
; /* size of OSS buffer fragment */
186 DWORD dwBufferSize
; /* size of whole OSS buffer in bytes */
187 LPWAVEHDR lpQueuePtr
; /* start of queued WAVEHDRs (waiting to be notified) */
188 LPWAVEHDR lpPlayPtr
; /* start of not yet fully played buffers */
189 DWORD dwPartialOffset
; /* Offset of not yet written bytes in lpPlayPtr */
191 LPWAVEHDR lpLoopPtr
; /* pointer of first buffer in loop, if any */
192 DWORD dwLoops
; /* private copy of loop counter */
194 DWORD dwPlayedTotal
; /* number of bytes actually played since opening */
195 DWORD dwWrittenTotal
; /* number of bytes written to OSS buffer since opening */
196 BOOL bNeedPost
; /* whether audio still needs to be physically started */
198 /* synchronization stuff */
199 HANDLE hStartUpEvent
;
202 OSS_MSG_RING msgRing
;
208 DWORD dwFragmentSize
; /* OpenSound '/dev/dsp' give us that size */
209 WAVEOPENDESC waveDesc
;
211 PCMWAVEFORMAT format
;
212 LPWAVEHDR lpQueuePtr
;
213 DWORD dwTotalRecorded
;
215 /* synchronization stuff */
218 HANDLE hStartUpEvent
;
219 OSS_MSG_RING msgRing
;
222 static WINE_WAVEOUT WOutDev
[MAX_WAVEDRV
];
223 static WINE_WAVEIN WInDev
[MAX_WAVEDRV
];
224 static unsigned numOutDev
;
225 static unsigned numInDev
;
227 static DWORD
wodDsCreate(UINT wDevID
, PIDSDRIVER
* drv
);
228 static DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
);
229 static DWORD
wodDsDesc(UINT wDevID
, PDSDRIVERDESC desc
);
230 static DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
);
231 static DWORD
wodDsGuid(UINT wDevID
, LPGUID pGuid
);
232 static DWORD
widDsGuid(UINT wDevID
, LPGUID pGuid
);
234 /* These strings used only for tracing */
235 static const char *wodPlayerCmdString
[] = {
237 "WINE_WM_RESTARTING",
247 static int getEnables(OSS_DEVICE
*ossdev
)
249 return ( (ossdev
->bOutputEnabled
? PCM_ENABLE_OUTPUT
: 0) |
250 (ossdev
->bInputEnabled
? PCM_ENABLE_INPUT
: 0) );
253 static DWORD
wdDevInterfaceSize(UINT wDevID
, LPDWORD dwParam1
)
255 TRACE("(%u, %p)\n", wDevID
, dwParam1
);
257 *dwParam1
= MultiByteToWideChar(CP_ACP
, 0, OSS_Devices
[wDevID
].dev_name
, -1,
258 NULL
, 0 ) * sizeof(WCHAR
);
259 return MMSYSERR_NOERROR
;
262 static DWORD
wdDevInterface(UINT wDevID
, PWCHAR dwParam1
, DWORD dwParam2
)
264 if (dwParam2
>= MultiByteToWideChar(CP_ACP
, 0, OSS_Devices
[wDevID
].dev_name
, -1,
265 NULL
, 0 ) * sizeof(WCHAR
))
267 MultiByteToWideChar(CP_ACP
, 0, OSS_Devices
[wDevID
].dev_name
, -1,
268 dwParam1
, dwParam2
/ sizeof(WCHAR
));
269 return MMSYSERR_NOERROR
;
272 return MMSYSERR_INVALPARAM
;
275 /*======================================================================*
276 * Low level WAVE implementation *
277 *======================================================================*/
279 /******************************************************************
282 * Low level device opening (from values stored in ossdev)
284 static DWORD
OSS_RawOpenDevice(OSS_DEVICE
* ossdev
, int strict_format
)
287 TRACE("(%p,%d)\n",ossdev
,strict_format
);
289 if ((fd
= open(ossdev
->dev_name
, ossdev
->open_access
|O_NDELAY
, 0)) == -1)
291 WARN("Couldn't open %s (%s)\n", ossdev
->dev_name
, strerror(errno
));
292 return (errno
== EBUSY
) ? MMSYSERR_ALLOCATED
: MMSYSERR_ERROR
;
294 fcntl(fd
, F_SETFD
, 1); /* set close on exec flag */
295 /* turn full duplex on if it has been requested */
296 if (ossdev
->open_access
== O_RDWR
&& ossdev
->full_duplex
) {
297 rc
= ioctl(fd
, SNDCTL_DSP_SETDUPLEX
, 0);
298 /* on *BSD, as full duplex is always enabled by default, this ioctl
299 * will fail with EINVAL
300 * so, we don't consider EINVAL an error here
302 if (rc
!= 0 && errno
!= EINVAL
) {
303 ERR("ioctl(%s, SNDCTL_DSP_SETDUPLEX) failed (%s)\n", ossdev
->dev_name
, strerror(errno
));
308 if (ossdev
->audio_fragment
) {
309 rc
= ioctl(fd
, SNDCTL_DSP_SETFRAGMENT
, &ossdev
->audio_fragment
);
311 ERR("ioctl(%s, SNDCTL_DSP_SETFRAGMENT) failed (%s)\n", ossdev
->dev_name
, strerror(errno
));
316 /* First size and stereo then samplerate */
317 if (ossdev
->format
>=0)
319 val
= ossdev
->format
;
320 rc
= ioctl(fd
, SNDCTL_DSP_SETFMT
, &ossdev
->format
);
321 if (rc
!= 0 || val
!= ossdev
->format
) {
322 TRACE("Can't set format to %d (returned %d)\n", val
, ossdev
->format
);
327 if (ossdev
->stereo
>=0)
329 val
= ossdev
->stereo
;
330 rc
= ioctl(fd
, SNDCTL_DSP_STEREO
, &ossdev
->stereo
);
331 if (rc
!= 0 || val
!= ossdev
->stereo
) {
332 TRACE("Can't set stereo to %u (returned %d)\n", val
, ossdev
->stereo
);
337 if (ossdev
->sample_rate
>=0)
339 val
= ossdev
->sample_rate
;
340 rc
= ioctl(fd
, SNDCTL_DSP_SPEED
, &ossdev
->sample_rate
);
341 if (rc
!= 0 || !NEAR_MATCH(val
, ossdev
->sample_rate
)) {
342 TRACE("Can't set sample_rate to %u (returned %d)\n", val
, ossdev
->sample_rate
);
349 if (ossdev
->bTriggerSupport
) {
351 rc
= ioctl(fd
, SNDCTL_DSP_GETTRIGGER
, &trigger
);
353 ERR("ioctl(%s, SNDCTL_DSP_GETTRIGGER) failed (%s)\n",
354 ossdev
->dev_name
, strerror(errno
));
358 ossdev
->bOutputEnabled
= ((trigger
& PCM_ENABLE_OUTPUT
) == PCM_ENABLE_OUTPUT
);
359 ossdev
->bInputEnabled
= ((trigger
& PCM_ENABLE_INPUT
) == PCM_ENABLE_INPUT
);
361 ossdev
->bOutputEnabled
= TRUE
; /* OSS enables by default */
362 ossdev
->bInputEnabled
= TRUE
; /* OSS enables by default */
365 return MMSYSERR_NOERROR
;
369 return WAVERR_BADFORMAT
;
372 return MMSYSERR_ERROR
;
375 /******************************************************************
378 * since OSS has poor capabilities in full duplex, we try here to let a program
379 * open the device for both waveout and wavein streams...
380 * this is hackish, but it's the way OSS interface is done...
382 static DWORD
OSS_OpenDevice(OSS_DEVICE
* ossdev
, unsigned req_access
,
383 int* frag
, int strict_format
,
384 int sample_rate
, int stereo
, int fmt
)
387 TRACE("(%p,%u,%p,%d,%d,%d,%x)\n",ossdev
,req_access
,frag
,strict_format
,sample_rate
,stereo
,fmt
);
389 if (ossdev
->full_duplex
&& (req_access
== O_RDONLY
|| req_access
== O_WRONLY
))
392 /* FIXME: this should be protected, and it also contains a race with OSS_CloseDevice */
393 if (ossdev
->open_count
== 0)
395 if (access(ossdev
->dev_name
, 0) != 0) return MMSYSERR_NODRIVER
;
397 ossdev
->audio_fragment
= (frag
) ? *frag
: 0;
398 ossdev
->sample_rate
= sample_rate
;
399 ossdev
->stereo
= stereo
;
400 ossdev
->format
= fmt
;
401 ossdev
->open_access
= req_access
;
402 ossdev
->owner_tid
= GetCurrentThreadId();
404 if ((ret
= OSS_RawOpenDevice(ossdev
,strict_format
)) != MMSYSERR_NOERROR
) return ret
;
408 /* check we really open with the same parameters */
409 if (ossdev
->open_access
!= req_access
)
411 ERR("FullDuplex: Mismatch in access. Your sound device is not full duplex capable.\n");
412 return WAVERR_BADFORMAT
;
415 /* check if the audio parameters are the same */
416 if (ossdev
->sample_rate
!= sample_rate
||
417 ossdev
->stereo
!= stereo
||
418 ossdev
->format
!= fmt
)
420 /* This is not a fatal error because MSACM might do the remapping */
421 WARN("FullDuplex: mismatch in PCM parameters for input and output\n"
422 "OSS doesn't allow us different parameters\n"
423 "audio_frag(%x/%x) sample_rate(%d/%d) stereo(%d/%d) fmt(%d/%d)\n",
424 ossdev
->audio_fragment
, frag
? *frag
: 0,
425 ossdev
->sample_rate
, sample_rate
,
426 ossdev
->stereo
, stereo
,
427 ossdev
->format
, fmt
);
428 return WAVERR_BADFORMAT
;
430 /* check if the fragment sizes are the same */
431 if (ossdev
->audio_fragment
!= (frag
? *frag
: 0) ) {
432 ERR("FullDuplex: Playback and Capture hardware acceleration levels are different.\n"
433 "Use: \"HardwareAcceleration\" = \"Emulation\" in the [dsound] section of your config file.\n");
434 return WAVERR_BADFORMAT
;
436 if (GetCurrentThreadId() != ossdev
->owner_tid
)
438 WARN("Another thread is trying to access audio...\n");
439 return MMSYSERR_ERROR
;
443 ossdev
->open_count
++;
445 return MMSYSERR_NOERROR
;
448 /******************************************************************
453 static void OSS_CloseDevice(OSS_DEVICE
* ossdev
)
455 TRACE("(%p)\n",ossdev
);
456 if (ossdev
->open_count
>0) {
457 ossdev
->open_count
--;
459 WARN("OSS_CloseDevice called too many times\n");
461 if (ossdev
->open_count
== 0)
463 /* reset the device before we close it in case it is in a bad state */
464 ioctl(ossdev
->fd
, SNDCTL_DSP_RESET
, 0);
469 /******************************************************************
472 * Resets the device. OSS Commercial requires the device to be closed
473 * after a SNDCTL_DSP_RESET ioctl call... this function implements
475 * FIXME: This causes problems when doing full duplex so we really
476 * only reset when not doing full duplex. We need to do this better
479 static DWORD
OSS_ResetDevice(OSS_DEVICE
* ossdev
)
481 DWORD ret
= MMSYSERR_NOERROR
;
482 int old_fd
= ossdev
->fd
;
483 TRACE("(%p)\n", ossdev
);
485 if (ossdev
->open_count
== 1) {
486 if (ioctl(ossdev
->fd
, SNDCTL_DSP_RESET
, NULL
) == -1)
488 perror("ioctl SNDCTL_DSP_RESET");
492 ret
= OSS_RawOpenDevice(ossdev
, 1);
493 TRACE("Changing fd from %d to %d\n", old_fd
, ossdev
->fd
);
495 WARN("Not resetting device because it is in full duplex mode!\n");
500 const static int win_std_oss_fmts
[2]={AFMT_U8
,AFMT_S16_LE
};
501 const static int win_std_rates
[5]={96000,48000,44100,22050,11025};
502 const static int win_std_formats
[2][2][5]=
503 {{{WAVE_FORMAT_96M08
, WAVE_FORMAT_48M08
, WAVE_FORMAT_4M08
,
504 WAVE_FORMAT_2M08
, WAVE_FORMAT_1M08
},
505 {WAVE_FORMAT_96S08
, WAVE_FORMAT_48S08
, WAVE_FORMAT_4S08
,
506 WAVE_FORMAT_2S08
, WAVE_FORMAT_1S08
}},
507 {{WAVE_FORMAT_96M16
, WAVE_FORMAT_48M16
, WAVE_FORMAT_4M16
,
508 WAVE_FORMAT_2M16
, WAVE_FORMAT_1M16
},
509 {WAVE_FORMAT_96S16
, WAVE_FORMAT_48S16
, WAVE_FORMAT_4S16
,
510 WAVE_FORMAT_2S16
, WAVE_FORMAT_1S16
}},
513 /******************************************************************
518 static BOOL
OSS_WaveOutInit(OSS_DEVICE
* ossdev
)
522 TRACE("(%p) %s\n", ossdev
, ossdev
->dev_name
);
524 if (OSS_OpenDevice(ossdev
, O_WRONLY
, NULL
, 0,-1,-1,-1) != 0) return FALSE
;
525 ioctl(ossdev
->fd
, SNDCTL_DSP_RESET
, 0);
527 #ifdef SOUND_MIXER_INFO
530 if ((mixer
= open(ossdev
->mixer_name
, O_RDONLY
|O_NDELAY
)) >= 0) {
532 if (ioctl(mixer
, SOUND_MIXER_INFO
, &info
) >= 0) {
533 strncpy(ossdev
->ds_desc
.szDesc
, info
.name
, sizeof(info
.name
));
534 strcpy(ossdev
->ds_desc
.szDrvName
, "wineoss.drv");
535 strncpy(ossdev
->out_caps
.szPname
, info
.name
, sizeof(info
.name
));
536 TRACE("%s\n", ossdev
->ds_desc
.szDesc
);
538 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
539 * implement it properly, and there are probably similar issues
540 * on other platforms, so we warn but try to go ahead.
542 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev
->mixer_name
);
546 ERR("%s: %s\n", ossdev
->mixer_name
, strerror( errno
));
547 OSS_CloseDevice(ossdev
);
551 #endif /* SOUND_MIXER_INFO */
553 /* FIXME: some programs compare this string against the content of the
554 * registry for MM drivers. The names have to match in order for the
555 * program to work (e.g. MS win9x mplayer.exe)
558 ossdev
->out_caps
.wMid
= 0x0002;
559 ossdev
->out_caps
.wPid
= 0x0104;
560 strcpy(ossdev
->out_caps
.szPname
, "SB16 Wave Out");
562 ossdev
->out_caps
.wMid
= 0x00FF; /* Manufac ID */
563 ossdev
->out_caps
.wPid
= 0x0001; /* Product ID */
565 ossdev
->out_caps
.vDriverVersion
= 0x0100;
566 ossdev
->out_caps
.wChannels
= 1;
567 ossdev
->out_caps
.dwFormats
= 0x00000000;
568 ossdev
->out_caps
.wReserved1
= 0;
569 ossdev
->out_caps
.dwSupport
= WAVECAPS_VOLUME
;
571 /* direct sound caps */
572 ossdev
->ds_caps
.dwFlags
= 0;
573 ossdev
->ds_caps
.dwPrimaryBuffers
= 1;
574 ossdev
->ds_caps
.dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
575 ossdev
->ds_caps
.dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
577 if (WINE_TRACE_ON(wave
)) {
578 /* Note that this only reports the formats supported by the hardware.
579 * The driver may support other formats and do the conversions in
580 * software which is why we don't use this value
582 int oss_mask
, oss_caps
;
583 ioctl(ossdev
->fd
, SNDCTL_DSP_GETFMTS
, &oss_mask
);
584 TRACE("OSS dsp out mask=%08x ( ", oss_mask
);
585 if (oss_mask
& AFMT_MU_LAW
) TRACE("AFMT_MU_LAW ");
586 if (oss_mask
& AFMT_A_LAW
) TRACE("AFMT_A_LAW ");
587 if (oss_mask
& AFMT_IMA_ADPCM
) TRACE("AFMT_IMA_ADPCM ");
588 if (oss_mask
& AFMT_U8
) TRACE("AFMT_U8 ");
589 if (oss_mask
& AFMT_S16_LE
) TRACE("AFMT_S16_LE ");
590 if (oss_mask
& AFMT_S16_BE
) TRACE("AFMT_S16_BE ");
591 if (oss_mask
& AFMT_S8
) TRACE("AFMT_S8 ");
592 if (oss_mask
& AFMT_U16_LE
) TRACE("AFMT_U16_LE ");
593 if (oss_mask
& AFMT_U16_BE
) TRACE("AFMT_U16_BE ");
594 if (oss_mask
& AFMT_MPEG
) TRACE("AFMT_MPEG ");
596 if (oss_mask
& AFMT_AC3
) TRACE("AFMT_AC3 ");
599 ioctl(ossdev
->fd
, SNDCTL_DSP_GETCAPS
, &oss_caps
);
600 TRACE("Caps=%08x\n",oss_caps
);
601 TRACE("\tRevision: %d\n", oss_caps
&DSP_CAP_REVISION
);
602 TRACE("\tDuplex: %s\n", oss_caps
& DSP_CAP_DUPLEX
? "true" : "false");
603 TRACE("\tRealtime: %s\n", oss_caps
& DSP_CAP_REALTIME
? "true" : "false");
604 TRACE("\tBatch: %s\n", oss_caps
& DSP_CAP_BATCH
? "true" : "false");
605 TRACE("\tCoproc: %s\n", oss_caps
& DSP_CAP_COPROC
? "true" : "false");
606 TRACE("\tTrigger: %s\n", oss_caps
& DSP_CAP_TRIGGER
? "true" : "false");
607 TRACE("\tMmap: %s\n", oss_caps
& DSP_CAP_MMAP
? "true" : "false");
609 TRACE("\tMulti: %s\n", oss_caps
& DSP_CAP_MULTI
? "true" : "false");
612 TRACE("\tBind: %s\n", oss_caps
& DSP_CAP_BIND
? "true" : "false");
616 /* We must first set the format and the stereo mode as some sound cards
617 * may support 44kHz mono but not 44kHz stereo. Also we must
618 * systematically check the return value of these ioctls as they will
619 * always succeed (see OSS Linux) but will modify the parameter to match
620 * whatever they support. The OSS specs also say we must first set the
621 * sample size, then the stereo and then the sample rate.
624 arg
=win_std_oss_fmts
[f
];
625 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_SAMPLESIZE
, &arg
);
626 if (rc
!=0 || arg
!=win_std_oss_fmts
[f
]) {
627 TRACE("DSP_SAMPLESIZE: rc=%d returned %d for %d\n",
628 rc
,arg
,win_std_oss_fmts
[f
]);
632 ossdev
->ds_caps
.dwFlags
|= DSCAPS_PRIMARY8BIT
;
634 ossdev
->ds_caps
.dwFlags
|= DSCAPS_PRIMARY16BIT
;
638 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_STEREO
, &arg
);
639 if (rc
!=0 || arg
!=c
) {
640 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc
,arg
,c
);
644 ossdev
->ds_caps
.dwFlags
|= DSCAPS_PRIMARYMONO
;
646 ossdev
->out_caps
.wChannels
=2;
647 ossdev
->out_caps
.dwSupport
|=WAVECAPS_LRVOLUME
;
648 ossdev
->ds_caps
.dwFlags
|= DSCAPS_PRIMARYSTEREO
;
651 for (r
=0;r
<sizeof(win_std_rates
)/sizeof(*win_std_rates
);r
++) {
652 arg
=win_std_rates
[r
];
653 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_SPEED
, &arg
);
654 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",
655 rc
,arg
,win_std_rates
[r
],win_std_oss_fmts
[f
],c
+1);
656 if (rc
==0 && arg
!=0 && NEAR_MATCH(arg
,win_std_rates
[r
]))
657 ossdev
->out_caps
.dwFormats
|=win_std_formats
[f
][c
][r
];
662 if (ioctl(ossdev
->fd
, SNDCTL_DSP_GETCAPS
, &arg
) == 0) {
663 TRACE("OSS dsp out caps=%08X\n", arg
);
664 if (arg
& DSP_CAP_TRIGGER
)
665 ossdev
->bTriggerSupport
= TRUE
;
666 if ((arg
& DSP_CAP_REALTIME
) && !(arg
& DSP_CAP_BATCH
)) {
667 ossdev
->out_caps
.dwSupport
|= WAVECAPS_SAMPLEACCURATE
;
669 /* well, might as well use the DirectSound cap flag for something */
670 if ((arg
& DSP_CAP_TRIGGER
) && (arg
& DSP_CAP_MMAP
) &&
671 !(arg
& DSP_CAP_BATCH
)) {
672 ossdev
->out_caps
.dwSupport
|= WAVECAPS_DIRECTSOUND
;
674 ossdev
->ds_caps
.dwFlags
|= DSCAPS_EMULDRIVER
;
676 #ifdef DSP_CAP_MULTI /* not every oss has this */
677 /* check for hardware secondary buffer support (multi open) */
678 if ((arg
& DSP_CAP_MULTI
) &&
679 (ossdev
->out_caps
.dwSupport
& WAVECAPS_DIRECTSOUND
)) {
680 TRACE("hardware secondary buffer support available\n");
681 if (ossdev
->ds_caps
.dwFlags
& DSCAPS_PRIMARY8BIT
)
682 ossdev
->ds_caps
.dwFlags
|= DSCAPS_SECONDARY8BIT
;
683 if (ossdev
->ds_caps
.dwFlags
& DSCAPS_PRIMARY16BIT
)
684 ossdev
->ds_caps
.dwFlags
|= DSCAPS_SECONDARY16BIT
;
685 if (ossdev
->ds_caps
.dwFlags
& DSCAPS_PRIMARYMONO
)
686 ossdev
->ds_caps
.dwFlags
|= DSCAPS_SECONDARYMONO
;
687 if (ossdev
->ds_caps
.dwFlags
& DSCAPS_PRIMARYSTEREO
)
688 ossdev
->ds_caps
.dwFlags
|= DSCAPS_SECONDARYSTEREO
;
690 ossdev
->ds_caps
.dwMaxHwMixingAllBuffers
= 16;
691 ossdev
->ds_caps
.dwMaxHwMixingStaticBuffers
= 0;
692 ossdev
->ds_caps
.dwMaxHwMixingStreamingBuffers
= 16;
694 ossdev
->ds_caps
.dwFreeHwMixingAllBuffers
= 16;
695 ossdev
->ds_caps
.dwFreeHwMixingStaticBuffers
= 0;
696 ossdev
->ds_caps
.dwFreeHwMixingStreamingBuffers
= 16;
700 OSS_CloseDevice(ossdev
);
701 TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
702 ossdev
->out_caps
.dwFormats
, ossdev
->out_caps
.dwSupport
);
706 /******************************************************************
711 static BOOL
OSS_WaveInInit(OSS_DEVICE
* ossdev
)
715 TRACE("(%p) %s\n", ossdev
, ossdev
->dev_name
);
717 if (OSS_OpenDevice(ossdev
, O_RDONLY
, NULL
, 0,-1,-1,-1) != 0)
720 ioctl(ossdev
->fd
, SNDCTL_DSP_RESET
, 0);
722 #ifdef SOUND_MIXER_INFO
725 if ((mixer
= open(ossdev
->mixer_name
, O_RDONLY
|O_NDELAY
)) >= 0) {
727 if (ioctl(mixer
, SOUND_MIXER_INFO
, &info
) >= 0) {
728 strncpy(ossdev
->in_caps
.szPname
, info
.name
, sizeof(info
.name
));
729 TRACE("%s\n", ossdev
->ds_desc
.szDesc
);
731 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
732 * implement it properly, and there are probably similar issues
733 * on other platforms, so we warn but try to go ahead.
735 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev
->mixer_name
);
739 ERR("%s: %s\n", ossdev
->mixer_name
, strerror(errno
));
740 OSS_CloseDevice(ossdev
);
744 #endif /* SOUND_MIXER_INFO */
746 /* See comment in OSS_WaveOutInit */
748 ossdev
->in_caps
.wMid
= 0x0002;
749 ossdev
->in_caps
.wPid
= 0x0004;
750 strcpy(ossdev
->in_caps
.szPname
, "SB16 Wave In");
752 ossdev
->in_caps
.wMid
= 0x00FF; /* Manufac ID */
753 ossdev
->in_caps
.wPid
= 0x0001; /* Product ID */
755 ossdev
->in_caps
.dwFormats
= 0x00000000;
756 ossdev
->in_caps
.wChannels
= 1;
757 ossdev
->in_caps
.wReserved1
= 0;
759 /* direct sound caps */
760 ossdev
->dsc_caps
.dwSize
= sizeof(ossdev
->dsc_caps
);
761 ossdev
->dsc_caps
.dwFlags
= 0;
762 ossdev
->dsc_caps
.dwFormats
= 0x00000000;
763 ossdev
->dsc_caps
.dwChannels
= 1;
765 if (WINE_TRACE_ON(wave
)) {
766 /* Note that this only reports the formats supported by the hardware.
767 * The driver may support other formats and do the conversions in
768 * software which is why we don't use this value
771 ioctl(ossdev
->fd
, SNDCTL_DSP_GETFMTS
, &oss_mask
);
772 TRACE("OSS dsp out mask=%08x\n", oss_mask
);
775 /* See the comment in OSS_WaveOutInit */
777 arg
=win_std_oss_fmts
[f
];
778 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_SAMPLESIZE
, &arg
);
779 if (rc
!=0 || arg
!=win_std_oss_fmts
[f
]) {
780 TRACE("DSP_SAMPLESIZE: rc=%d returned 0x%x for 0x%x\n",
781 rc
,arg
,win_std_oss_fmts
[f
]);
787 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_STEREO
, &arg
);
788 if (rc
!=0 || arg
!=c
) {
789 TRACE("DSP_STEREO: rc=%d returned %d for %d\n",rc
,arg
,c
);
793 ossdev
->in_caps
.wChannels
=2;
794 ossdev
->dsc_caps
.dwChannels
=2;
797 for (r
=0;r
<sizeof(win_std_rates
)/sizeof(*win_std_rates
);r
++) {
798 arg
=win_std_rates
[r
];
799 rc
=ioctl(ossdev
->fd
, SNDCTL_DSP_SPEED
, &arg
);
800 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",rc
,arg
,win_std_rates
[r
],win_std_oss_fmts
[f
],c
+1);
801 if (rc
==0 && NEAR_MATCH(arg
,win_std_rates
[r
]))
802 ossdev
->in_caps
.dwFormats
|=win_std_formats
[f
][c
][r
];
803 ossdev
->dsc_caps
.dwFormats
|=win_std_formats
[f
][c
][r
];
808 if (ioctl(ossdev
->fd
, SNDCTL_DSP_GETCAPS
, &arg
) == 0) {
809 TRACE("OSS dsp in caps=%08X\n", arg
);
810 if (arg
& DSP_CAP_TRIGGER
)
811 ossdev
->bTriggerSupport
= TRUE
;
812 if ((arg
& DSP_CAP_TRIGGER
) && (arg
& DSP_CAP_MMAP
) &&
813 !(arg
& DSP_CAP_BATCH
)) {
814 /* FIXME: enable the next statement if you want to work on the driver */
816 ossdev
->in_caps_support
|= WAVECAPS_DIRECTSOUND
;
819 if ((arg
& DSP_CAP_REALTIME
) && !(arg
& DSP_CAP_BATCH
))
820 ossdev
->in_caps_support
|= WAVECAPS_SAMPLEACCURATE
;
822 OSS_CloseDevice(ossdev
);
823 TRACE("in dwFormats = %08lX\n", ossdev
->in_caps
.dwFormats
);
827 /******************************************************************
828 * OSS_WaveFullDuplexInit
832 static void OSS_WaveFullDuplexInit(OSS_DEVICE
* ossdev
)
835 TRACE("(%p)\n",ossdev
);
837 if (OSS_OpenDevice(ossdev
, O_RDWR
, NULL
, 0,-1,-1,-1) != 0) return;
838 if (ioctl(ossdev
->fd
, SNDCTL_DSP_GETCAPS
, &caps
) == 0)
840 ossdev
->full_duplex
= (caps
& DSP_CAP_DUPLEX
);
842 OSS_CloseDevice(ossdev
);
845 #define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
846 guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2; \
847 guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3; \
848 guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6; \
849 guid.Data4[6] = b7; guid.Data4[7] = b8;
850 /******************************************************************
853 * Initialize internal structures from OSS information
855 LONG
OSS_WaveInit(void)
860 for (i
= 0; i
< MAX_WAVEDRV
; ++i
)
863 sprintf((char *)OSS_Devices
[i
].dev_name
, "/dev/dsp");
864 sprintf((char *)OSS_Devices
[i
].mixer_name
, "/dev/mixer");
866 sprintf((char *)OSS_Devices
[i
].dev_name
, "/dev/dsp%d", i
);
867 sprintf((char *)OSS_Devices
[i
].mixer_name
, "/dev/mixer%d", i
);
870 INIT_GUID(OSS_Devices
[i
].ds_guid
, 0xbd6dd71a, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i
);
871 INIT_GUID(OSS_Devices
[i
].dsc_guid
, 0xbd6dd71b, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i
);
874 /* start with output devices */
875 for (i
= 0; i
< MAX_WAVEDRV
; ++i
)
877 if (OSS_WaveOutInit(&OSS_Devices
[i
]))
879 WOutDev
[numOutDev
].state
= WINE_WS_CLOSED
;
880 WOutDev
[numOutDev
].ossdev
= &OSS_Devices
[i
];
881 WOutDev
[numOutDev
].volume
= 0xffffffff;
886 /* then do input devices */
887 for (i
= 0; i
< MAX_WAVEDRV
; ++i
)
889 if (OSS_WaveInInit(&OSS_Devices
[i
]))
891 WInDev
[numInDev
].state
= WINE_WS_CLOSED
;
892 WInDev
[numInDev
].ossdev
= &OSS_Devices
[i
];
897 /* finish with the full duplex bits */
898 for (i
= 0; i
< MAX_WAVEDRV
; i
++)
899 OSS_WaveFullDuplexInit(&OSS_Devices
[i
]);
904 /******************************************************************
905 * OSS_InitRingMessage
907 * Initialize the ring of messages for passing between driver's caller and playback/record
910 static int OSS_InitRingMessage(OSS_MSG_RING
* omr
)
915 if (pipe(omr
->msg_pipe
) < 0) {
916 omr
->msg_pipe
[0] = -1;
917 omr
->msg_pipe
[1] = -1;
918 ERR("could not create pipe, error=%s\n", strerror(errno
));
921 omr
->msg_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
923 omr
->ring_buffer_size
= OSS_RING_BUFFER_INCREMENT
;
924 omr
->messages
= HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,omr
->ring_buffer_size
* sizeof(OSS_MSG
));
925 InitializeCriticalSection(&omr
->msg_crst
);
929 /******************************************************************
930 * OSS_DestroyRingMessage
933 static int OSS_DestroyRingMessage(OSS_MSG_RING
* omr
)
936 close(omr
->msg_pipe
[0]);
937 close(omr
->msg_pipe
[1]);
939 CloseHandle(omr
->msg_event
);
941 HeapFree(GetProcessHeap(),0,omr
->messages
);
942 DeleteCriticalSection(&omr
->msg_crst
);
946 /******************************************************************
949 * Inserts a new message into the ring (should be called from DriverProc derivated routines)
951 static int OSS_AddRingMessage(OSS_MSG_RING
* omr
, enum win_wm_message msg
, DWORD param
, BOOL wait
)
953 HANDLE hEvent
= INVALID_HANDLE_VALUE
;
955 EnterCriticalSection(&omr
->msg_crst
);
956 if ((omr
->msg_toget
== ((omr
->msg_tosave
+ 1) % omr
->ring_buffer_size
)))
958 int old_ring_buffer_size
= omr
->ring_buffer_size
;
959 omr
->ring_buffer_size
+= OSS_RING_BUFFER_INCREMENT
;
960 TRACE("omr->ring_buffer_size=%d\n",omr
->ring_buffer_size
);
961 omr
->messages
= HeapReAlloc(GetProcessHeap(),0,omr
->messages
, omr
->ring_buffer_size
* sizeof(OSS_MSG
));
962 /* Now we need to rearrange the ring buffer so that the new
963 buffers just allocated are in between omr->msg_tosave and
966 if (omr
->msg_tosave
< omr
->msg_toget
)
968 memmove(&(omr
->messages
[omr
->msg_toget
+ OSS_RING_BUFFER_INCREMENT
]),
969 &(omr
->messages
[omr
->msg_toget
]),
970 sizeof(OSS_MSG
)*(old_ring_buffer_size
- omr
->msg_toget
)
972 omr
->msg_toget
+= OSS_RING_BUFFER_INCREMENT
;
977 hEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
978 if (hEvent
== INVALID_HANDLE_VALUE
)
980 ERR("can't create event !?\n");
981 LeaveCriticalSection(&omr
->msg_crst
);
984 if (omr
->msg_toget
!= omr
->msg_tosave
&& omr
->messages
[omr
->msg_toget
].msg
!= WINE_WM_HEADER
)
985 FIXME("two fast messages in the queue!!!!\n");
987 /* fast messages have to be added at the start of the queue */
988 omr
->msg_toget
= (omr
->msg_toget
+ omr
->ring_buffer_size
- 1) % omr
->ring_buffer_size
;
990 omr
->messages
[omr
->msg_toget
].msg
= msg
;
991 omr
->messages
[omr
->msg_toget
].param
= param
;
992 omr
->messages
[omr
->msg_toget
].hEvent
= hEvent
;
996 omr
->messages
[omr
->msg_tosave
].msg
= msg
;
997 omr
->messages
[omr
->msg_tosave
].param
= param
;
998 omr
->messages
[omr
->msg_tosave
].hEvent
= INVALID_HANDLE_VALUE
;
999 omr
->msg_tosave
= (omr
->msg_tosave
+ 1) % omr
->ring_buffer_size
;
1001 LeaveCriticalSection(&omr
->msg_crst
);
1002 /* signal a new message */
1006 /* wait for playback/record thread to have processed the message */
1007 WaitForSingleObject(hEvent
, INFINITE
);
1008 CloseHandle(hEvent
);
1013 /******************************************************************
1014 * OSS_RetrieveRingMessage
1016 * Get a message from the ring. Should be called by the playback/record thread.
1018 static int OSS_RetrieveRingMessage(OSS_MSG_RING
* omr
,
1019 enum win_wm_message
*msg
, DWORD
*param
, HANDLE
*hEvent
)
1021 EnterCriticalSection(&omr
->msg_crst
);
1023 if (omr
->msg_toget
== omr
->msg_tosave
) /* buffer empty ? */
1025 LeaveCriticalSection(&omr
->msg_crst
);
1029 *msg
= omr
->messages
[omr
->msg_toget
].msg
;
1030 omr
->messages
[omr
->msg_toget
].msg
= 0;
1031 *param
= omr
->messages
[omr
->msg_toget
].param
;
1032 *hEvent
= omr
->messages
[omr
->msg_toget
].hEvent
;
1033 omr
->msg_toget
= (omr
->msg_toget
+ 1) % omr
->ring_buffer_size
;
1035 LeaveCriticalSection(&omr
->msg_crst
);
1039 /******************************************************************
1040 * OSS_PeekRingMessage
1042 * Peek at a message from the ring but do not remove it.
1043 * Should be called by the playback/record thread.
1045 static int OSS_PeekRingMessage(OSS_MSG_RING
* omr
,
1046 enum win_wm_message
*msg
,
1047 DWORD
*param
, HANDLE
*hEvent
)
1049 EnterCriticalSection(&omr
->msg_crst
);
1051 if (omr
->msg_toget
== omr
->msg_tosave
) /* buffer empty ? */
1053 LeaveCriticalSection(&omr
->msg_crst
);
1057 *msg
= omr
->messages
[omr
->msg_toget
].msg
;
1058 *param
= omr
->messages
[omr
->msg_toget
].param
;
1059 *hEvent
= omr
->messages
[omr
->msg_toget
].hEvent
;
1060 LeaveCriticalSection(&omr
->msg_crst
);
1064 /*======================================================================*
1065 * Low level WAVE OUT implementation *
1066 *======================================================================*/
1068 /**************************************************************************
1069 * wodNotifyClient [internal]
1071 static DWORD
wodNotifyClient(WINE_WAVEOUT
* wwo
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
)
1073 TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg
,
1074 wMsg
== WOM_OPEN
? "WOM_OPEN" : wMsg
== WOM_CLOSE
? "WOM_CLOSE" :
1075 wMsg
== WOM_DONE
? "WOM_DONE" : "Unknown", dwParam1
, dwParam2
);
1081 if (wwo
->wFlags
!= DCB_NULL
&&
1082 !DriverCallback(wwo
->waveDesc
.dwCallback
, wwo
->wFlags
,
1083 (HDRVR
)wwo
->waveDesc
.hWave
, wMsg
,
1084 wwo
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
1085 WARN("can't notify client !\n");
1086 return MMSYSERR_ERROR
;
1090 FIXME("Unknown callback message %u\n", wMsg
);
1091 return MMSYSERR_INVALPARAM
;
1093 return MMSYSERR_NOERROR
;
1096 /**************************************************************************
1097 * wodUpdatePlayedTotal [internal]
1100 static BOOL
wodUpdatePlayedTotal(WINE_WAVEOUT
* wwo
, audio_buf_info
* info
)
1102 audio_buf_info dspspace
;
1103 if (!info
) info
= &dspspace
;
1105 if (ioctl(wwo
->ossdev
->fd
, SNDCTL_DSP_GETOSPACE
, info
) < 0) {
1106 ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo
->ossdev
->dev_name
, strerror(errno
));
1109 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
- (wwo
->dwBufferSize
- info
->bytes
);
1113 /**************************************************************************
1114 * wodPlayer_BeginWaveHdr [internal]
1116 * Makes the specified lpWaveHdr the currently playing wave header.
1117 * If the specified wave header is a begin loop and we're not already in
1118 * a loop, setup the loop.
1120 static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT
* wwo
, LPWAVEHDR lpWaveHdr
)
1122 wwo
->lpPlayPtr
= lpWaveHdr
;
1124 if (!lpWaveHdr
) return;
1126 if (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
) {
1127 if (wwo
->lpLoopPtr
) {
1128 WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr
);
1130 TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr
->dwLoops
, lpWaveHdr
);
1131 wwo
->lpLoopPtr
= lpWaveHdr
;
1132 /* Windows does not touch WAVEHDR.dwLoops,
1133 * so we need to make an internal copy */
1134 wwo
->dwLoops
= lpWaveHdr
->dwLoops
;
1137 wwo
->dwPartialOffset
= 0;
1140 /**************************************************************************
1141 * wodPlayer_PlayPtrNext [internal]
1143 * Advance the play pointer to the next waveheader, looping if required.
1145 static LPWAVEHDR
wodPlayer_PlayPtrNext(WINE_WAVEOUT
* wwo
)
1147 LPWAVEHDR lpWaveHdr
= wwo
->lpPlayPtr
;
1149 wwo
->dwPartialOffset
= 0;
1150 if ((lpWaveHdr
->dwFlags
& WHDR_ENDLOOP
) && wwo
->lpLoopPtr
) {
1151 /* We're at the end of a loop, loop if required */
1152 if (--wwo
->dwLoops
> 0) {
1153 wwo
->lpPlayPtr
= wwo
->lpLoopPtr
;
1155 /* Handle overlapping loops correctly */
1156 if (wwo
->lpLoopPtr
!= lpWaveHdr
&& (lpWaveHdr
->dwFlags
& WHDR_BEGINLOOP
)) {
1157 FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
1158 /* shall we consider the END flag for the closing loop or for
1159 * the opening one or for both ???
1160 * code assumes for closing loop only
1163 lpWaveHdr
= lpWaveHdr
->lpNext
;
1165 wwo
->lpLoopPtr
= NULL
;
1166 wodPlayer_BeginWaveHdr(wwo
, lpWaveHdr
);
1169 /* We're not in a loop. Advance to the next wave header */
1170 wodPlayer_BeginWaveHdr(wwo
, lpWaveHdr
= lpWaveHdr
->lpNext
);
1176 /**************************************************************************
1177 * wodPlayer_DSPWait [internal]
1178 * Returns the number of milliseconds to wait for the DSP buffer to write
1181 static DWORD
wodPlayer_DSPWait(const WINE_WAVEOUT
*wwo
)
1183 /* time for one fragment to be played */
1184 return wwo
->dwFragmentSize
* 1000 / wwo
->format
.wf
.nAvgBytesPerSec
;
1187 /**************************************************************************
1188 * wodPlayer_NotifyWait [internal]
1189 * Returns the number of milliseconds to wait before attempting to notify
1190 * completion of the specified wavehdr.
1191 * This is based on the number of bytes remaining to be written in the
1194 static DWORD
wodPlayer_NotifyWait(const WINE_WAVEOUT
* wwo
, LPWAVEHDR lpWaveHdr
)
1198 if (lpWaveHdr
->reserved
< wwo
->dwPlayedTotal
) {
1201 dwMillis
= (lpWaveHdr
->reserved
- wwo
->dwPlayedTotal
) * 1000 / wwo
->format
.wf
.nAvgBytesPerSec
;
1202 if (!dwMillis
) dwMillis
= 1;
1209 /**************************************************************************
1210 * wodPlayer_WriteMaxFrags [internal]
1211 * Writes the maximum number of bytes possible to the DSP and returns
1212 * TRUE iff the current playPtr has been fully played
1214 static BOOL
wodPlayer_WriteMaxFrags(WINE_WAVEOUT
* wwo
, DWORD
* bytes
)
1216 DWORD dwLength
= wwo
->lpPlayPtr
->dwBufferLength
- wwo
->dwPartialOffset
;
1217 DWORD toWrite
= min(dwLength
, *bytes
);
1221 TRACE("Writing wavehdr %p.%lu[%lu]/%lu\n",
1222 wwo
->lpPlayPtr
, wwo
->dwPartialOffset
, wwo
->lpPlayPtr
->dwBufferLength
, toWrite
);
1226 written
= write(wwo
->ossdev
->fd
, wwo
->lpPlayPtr
->lpData
+ wwo
->dwPartialOffset
, toWrite
);
1227 if (written
<= 0) return FALSE
;
1232 if (written
>= dwLength
) {
1233 /* If we wrote all current wavehdr, skip to the next one */
1234 wodPlayer_PlayPtrNext(wwo
);
1237 /* Remove the amount written */
1238 wwo
->dwPartialOffset
+= written
;
1241 wwo
->dwWrittenTotal
+= written
;
1242 TRACE("dwWrittenTotal=%lu\n", wwo
->dwWrittenTotal
);
1247 /**************************************************************************
1248 * wodPlayer_NotifyCompletions [internal]
1250 * Notifies and remove from queue all wavehdrs which have been played to
1251 * the speaker (ie. they have cleared the OSS buffer). If force is true,
1252 * we notify all wavehdrs and remove them all from the queue even if they
1253 * are unplayed or part of a loop.
1255 static DWORD
wodPlayer_NotifyCompletions(WINE_WAVEOUT
* wwo
, BOOL force
)
1257 LPWAVEHDR lpWaveHdr
;
1259 /* Start from lpQueuePtr and keep notifying until:
1260 * - we hit an unwritten wavehdr
1261 * - we hit the beginning of a running loop
1262 * - we hit a wavehdr which hasn't finished playing
1264 while ((lpWaveHdr
= wwo
->lpQueuePtr
) &&
1266 (lpWaveHdr
!= wwo
->lpPlayPtr
&&
1267 lpWaveHdr
!= wwo
->lpLoopPtr
&&
1268 lpWaveHdr
->reserved
<= wwo
->dwPlayedTotal
))) {
1270 wwo
->lpQueuePtr
= lpWaveHdr
->lpNext
;
1272 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
1273 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
1275 wodNotifyClient(wwo
, WOM_DONE
, (DWORD
)lpWaveHdr
, 0);
1277 return (lpWaveHdr
&& lpWaveHdr
!= wwo
->lpPlayPtr
&& lpWaveHdr
!= wwo
->lpLoopPtr
) ?
1278 wodPlayer_NotifyWait(wwo
, lpWaveHdr
) : INFINITE
;
1281 /**************************************************************************
1282 * wodPlayer_Reset [internal]
1284 * wodPlayer helper. Resets current output stream.
1286 static void wodPlayer_Reset(WINE_WAVEOUT
* wwo
, BOOL reset
)
1288 wodUpdatePlayedTotal(wwo
, NULL
);
1289 /* updates current notify list */
1290 wodPlayer_NotifyCompletions(wwo
, FALSE
);
1292 /* flush all possible output */
1293 if (OSS_ResetDevice(wwo
->ossdev
) != MMSYSERR_NOERROR
)
1296 wwo
->state
= WINE_WS_STOPPED
;
1301 enum win_wm_message msg
;
1305 /* remove any buffer */
1306 wodPlayer_NotifyCompletions(wwo
, TRUE
);
1308 wwo
->lpPlayPtr
= wwo
->lpQueuePtr
= wwo
->lpLoopPtr
= NULL
;
1309 wwo
->state
= WINE_WS_STOPPED
;
1310 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
= 0;
1311 /* Clear partial wavehdr */
1312 wwo
->dwPartialOffset
= 0;
1314 /* remove any existing message in the ring */
1315 EnterCriticalSection(&wwo
->msgRing
.msg_crst
);
1316 /* return all pending headers in queue */
1317 while (OSS_RetrieveRingMessage(&wwo
->msgRing
, &msg
, ¶m
, &ev
))
1319 if (msg
!= WINE_WM_HEADER
)
1321 FIXME("shouldn't have headers left\n");
1325 ((LPWAVEHDR
)param
)->dwFlags
&= ~WHDR_INQUEUE
;
1326 ((LPWAVEHDR
)param
)->dwFlags
|= WHDR_DONE
;
1328 wodNotifyClient(wwo
, WOM_DONE
, param
, 0);
1330 RESET_OMR(&wwo
->msgRing
);
1331 LeaveCriticalSection(&wwo
->msgRing
.msg_crst
);
1333 if (wwo
->lpLoopPtr
) {
1334 /* complicated case, not handled yet (could imply modifying the loop counter */
1335 FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
1336 wwo
->lpPlayPtr
= wwo
->lpLoopPtr
;
1337 wwo
->dwPartialOffset
= 0;
1338 wwo
->dwWrittenTotal
= wwo
->dwPlayedTotal
; /* this is wrong !!! */
1341 DWORD sz
= wwo
->dwPartialOffset
;
1343 /* reset all the data as if we had written only up to lpPlayedTotal bytes */
1344 /* compute the max size playable from lpQueuePtr */
1345 for (ptr
= wwo
->lpQueuePtr
; ptr
!= wwo
->lpPlayPtr
; ptr
= ptr
->lpNext
) {
1346 sz
+= ptr
->dwBufferLength
;
1348 /* because the reset lpPlayPtr will be lpQueuePtr */
1349 if (wwo
->dwWrittenTotal
> wwo
->dwPlayedTotal
+ sz
) ERR("grin\n");
1350 wwo
->dwPartialOffset
= sz
- (wwo
->dwWrittenTotal
- wwo
->dwPlayedTotal
);
1351 wwo
->dwWrittenTotal
= wwo
->dwPlayedTotal
;
1352 wwo
->lpPlayPtr
= wwo
->lpQueuePtr
;
1354 wwo
->state
= WINE_WS_PAUSED
;
1358 /**************************************************************************
1359 * wodPlayer_ProcessMessages [internal]
1361 static void wodPlayer_ProcessMessages(WINE_WAVEOUT
* wwo
)
1363 LPWAVEHDR lpWaveHdr
;
1364 enum win_wm_message msg
;
1368 while (OSS_RetrieveRingMessage(&wwo
->msgRing
, &msg
, ¶m
, &ev
)) {
1369 TRACE("Received %s %lx\n", wodPlayerCmdString
[msg
- WM_USER
- 1], param
);
1371 case WINE_WM_PAUSING
:
1372 wodPlayer_Reset(wwo
, FALSE
);
1375 case WINE_WM_RESTARTING
:
1376 if (wwo
->state
== WINE_WS_PAUSED
)
1378 wwo
->state
= WINE_WS_PLAYING
;
1382 case WINE_WM_HEADER
:
1383 lpWaveHdr
= (LPWAVEHDR
)param
;
1385 /* insert buffer at the end of queue */
1388 for (wh
= &(wwo
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
1391 if (!wwo
->lpPlayPtr
)
1392 wodPlayer_BeginWaveHdr(wwo
,lpWaveHdr
);
1393 if (wwo
->state
== WINE_WS_STOPPED
)
1394 wwo
->state
= WINE_WS_PLAYING
;
1396 case WINE_WM_RESETTING
:
1397 wodPlayer_Reset(wwo
, TRUE
);
1400 case WINE_WM_UPDATE
:
1401 wodUpdatePlayedTotal(wwo
, NULL
);
1404 case WINE_WM_BREAKLOOP
:
1405 if (wwo
->state
== WINE_WS_PLAYING
&& wwo
->lpLoopPtr
!= NULL
) {
1406 /* ensure exit at end of current loop */
1411 case WINE_WM_CLOSING
:
1412 /* sanity check: this should not happen since the device must have been reset before */
1413 if (wwo
->lpQueuePtr
|| wwo
->lpPlayPtr
) ERR("out of sync\n");
1415 wwo
->state
= WINE_WS_CLOSED
;
1418 /* shouldn't go here */
1420 FIXME("unknown message %d\n", msg
);
1426 /**************************************************************************
1427 * wodPlayer_FeedDSP [internal]
1428 * Feed as much sound data as we can into the DSP and return the number of
1429 * milliseconds before it will be necessary to feed the DSP again.
1431 static DWORD
wodPlayer_FeedDSP(WINE_WAVEOUT
* wwo
)
1433 audio_buf_info dspspace
;
1436 wodUpdatePlayedTotal(wwo
, &dspspace
);
1437 availInQ
= dspspace
.bytes
;
1438 TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
1439 dspspace
.fragments
, dspspace
.fragstotal
, dspspace
.fragsize
, dspspace
.bytes
);
1441 /* input queue empty and output buffer with less than one fragment to play
1442 * actually some cards do not play the fragment before the last if this one is partially feed
1443 * so we need to test for full the availability of 2 fragments
1445 if (!wwo
->lpPlayPtr
&& wwo
->dwBufferSize
< availInQ
+ 2 * wwo
->dwFragmentSize
&&
1447 TRACE("Run out of wavehdr:s...\n");
1451 /* no more room... no need to try to feed */
1452 if (dspspace
.fragments
!= 0) {
1453 /* Feed from partial wavehdr */
1454 if (wwo
->lpPlayPtr
&& wwo
->dwPartialOffset
!= 0) {
1455 wodPlayer_WriteMaxFrags(wwo
, &availInQ
);
1458 /* Feed wavehdrs until we run out of wavehdrs or DSP space */
1459 if (wwo
->dwPartialOffset
== 0 && wwo
->lpPlayPtr
) {
1461 TRACE("Setting time to elapse for %p to %lu\n",
1462 wwo
->lpPlayPtr
, wwo
->dwWrittenTotal
+ wwo
->lpPlayPtr
->dwBufferLength
);
1463 /* note the value that dwPlayedTotal will return when this wave finishes playing */
1464 wwo
->lpPlayPtr
->reserved
= wwo
->dwWrittenTotal
+ wwo
->lpPlayPtr
->dwBufferLength
;
1465 } while (wodPlayer_WriteMaxFrags(wwo
, &availInQ
) && wwo
->lpPlayPtr
&& availInQ
> 0);
1468 if (wwo
->bNeedPost
) {
1469 /* OSS doesn't start before it gets either 2 fragments or a SNDCTL_DSP_POST;
1470 * if it didn't get one, we give it the other */
1471 if (wwo
->dwBufferSize
< availInQ
+ 2 * wwo
->dwFragmentSize
)
1472 ioctl(wwo
->ossdev
->fd
, SNDCTL_DSP_POST
, 0);
1473 wwo
->bNeedPost
= FALSE
;
1477 return wodPlayer_DSPWait(wwo
);
1481 /**************************************************************************
1482 * wodPlayer [internal]
1484 static DWORD CALLBACK
wodPlayer(LPVOID pmt
)
1486 WORD uDevID
= (DWORD
)pmt
;
1487 WINE_WAVEOUT
* wwo
= (WINE_WAVEOUT
*)&WOutDev
[uDevID
];
1488 DWORD dwNextFeedTime
= INFINITE
; /* Time before DSP needs feeding */
1489 DWORD dwNextNotifyTime
= INFINITE
; /* Time before next wave completion */
1492 wwo
->state
= WINE_WS_STOPPED
;
1493 SetEvent(wwo
->hStartUpEvent
);
1496 /** Wait for the shortest time before an action is required. If there
1497 * are no pending actions, wait forever for a command.
1499 dwSleepTime
= min(dwNextFeedTime
, dwNextNotifyTime
);
1500 TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime
, dwNextFeedTime
, dwNextNotifyTime
);
1501 WAIT_OMR(&wwo
->msgRing
, dwSleepTime
);
1502 wodPlayer_ProcessMessages(wwo
);
1503 if (wwo
->state
== WINE_WS_PLAYING
) {
1504 dwNextFeedTime
= wodPlayer_FeedDSP(wwo
);
1505 dwNextNotifyTime
= wodPlayer_NotifyCompletions(wwo
, FALSE
);
1506 if (dwNextFeedTime
== INFINITE
) {
1507 /* FeedDSP ran out of data, but before flushing, */
1508 /* check that a notification didn't give us more */
1509 wodPlayer_ProcessMessages(wwo
);
1510 if (!wwo
->lpPlayPtr
) {
1511 TRACE("flushing\n");
1512 ioctl(wwo
->ossdev
->fd
, SNDCTL_DSP_SYNC
, 0);
1513 wwo
->dwPlayedTotal
= wwo
->dwWrittenTotal
;
1514 dwNextNotifyTime
= wodPlayer_NotifyCompletions(wwo
, FALSE
);
1516 TRACE("recovering\n");
1517 dwNextFeedTime
= wodPlayer_FeedDSP(wwo
);
1521 dwNextFeedTime
= dwNextNotifyTime
= INFINITE
;
1526 /**************************************************************************
1527 * wodGetDevCaps [internal]
1529 static DWORD
wodGetDevCaps(WORD wDevID
, LPWAVEOUTCAPSA lpCaps
, DWORD dwSize
)
1531 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
1533 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
1535 if (wDevID
>= numOutDev
) {
1536 TRACE("numOutDev reached !\n");
1537 return MMSYSERR_BADDEVICEID
;
1540 memcpy(lpCaps
, &WOutDev
[wDevID
].ossdev
->out_caps
, min(dwSize
, sizeof(*lpCaps
)));
1541 return MMSYSERR_NOERROR
;
1544 /**************************************************************************
1545 * wodOpen [internal]
1547 static DWORD
wodOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
1551 audio_buf_info info
;
1554 TRACE("(%u, %p[cb=%08lx], %08lX);\n", wDevID
, lpDesc
, lpDesc
->dwCallback
, dwFlags
);
1555 if (lpDesc
== NULL
) {
1556 WARN("Invalid Parameter !\n");
1557 return MMSYSERR_INVALPARAM
;
1559 if (wDevID
>= numOutDev
) {
1560 TRACE("MAX_WAVOUTDRV reached !\n");
1561 return MMSYSERR_BADDEVICEID
;
1564 /* only PCM format is supported so far... */
1565 if (lpDesc
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
||
1566 lpDesc
->lpFormat
->nChannels
== 0 ||
1567 lpDesc
->lpFormat
->nSamplesPerSec
== 0) {
1568 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1569 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
1570 lpDesc
->lpFormat
->nSamplesPerSec
);
1571 return WAVERR_BADFORMAT
;
1574 if (dwFlags
& WAVE_FORMAT_QUERY
) {
1575 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
1576 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
1577 lpDesc
->lpFormat
->nSamplesPerSec
);
1578 return MMSYSERR_NOERROR
;
1581 wwo
= &WOutDev
[wDevID
];
1583 if ((dwFlags
& WAVE_DIRECTSOUND
) &&
1584 !(wwo
->ossdev
->out_caps
.dwSupport
& WAVECAPS_DIRECTSOUND
))
1585 /* not supported, ignore it */
1586 dwFlags
&= ~WAVE_DIRECTSOUND
;
1588 if (dwFlags
& WAVE_DIRECTSOUND
) {
1589 if (wwo
->ossdev
->out_caps
.dwSupport
& WAVECAPS_SAMPLEACCURATE
)
1590 /* we have realtime DirectSound, fragments just waste our time,
1591 * but a large buffer is good, so choose 64KB (32 * 2^11) */
1592 audio_fragment
= 0x0020000B;
1594 /* to approximate realtime, we must use small fragments,
1595 * let's try to fragment the above 64KB (256 * 2^8) */
1596 audio_fragment
= 0x01000008;
1598 /* A wave device must have a worst case latency of 10 ms so calculate
1599 * the largest fragment size less than 10 ms long.
1601 int fsize
= lpDesc
->lpFormat
->nAvgBytesPerSec
/ 100; /* 10 ms chunk */
1603 while ((1 << shift
) <= fsize
)
1606 audio_fragment
= 0x00100000 + shift
; /* 16 fragments of 2^shift */
1609 TRACE("using %d %d byte fragments (%ld ms)\n", audio_fragment
>> 16,
1610 1 << (audio_fragment
& 0xffff),
1611 ((1 << (audio_fragment
& 0xffff)) * 1000) / lpDesc
->lpFormat
->nAvgBytesPerSec
);
1613 if (wwo
->state
!= WINE_WS_CLOSED
) return MMSYSERR_ALLOCATED
;
1614 /* we want to be able to mmap() the device, which means it must be opened readable,
1615 * otherwise mmap() will fail (at least under Linux) */
1616 ret
= OSS_OpenDevice(wwo
->ossdev
,
1617 (dwFlags
& WAVE_DIRECTSOUND
) ? O_RDWR
: O_WRONLY
,
1619 (dwFlags
& WAVE_DIRECTSOUND
) ? 0 : 1,
1620 lpDesc
->lpFormat
->nSamplesPerSec
,
1621 (lpDesc
->lpFormat
->nChannels
> 1) ? 1 : 0,
1622 (lpDesc
->lpFormat
->wBitsPerSample
== 16)
1623 ? AFMT_S16_LE
: AFMT_U8
);
1624 if ((ret
==MMSYSERR_NOERROR
) && (dwFlags
& WAVE_DIRECTSOUND
)) {
1625 lpDesc
->lpFormat
->nSamplesPerSec
=wwo
->ossdev
->sample_rate
;
1626 lpDesc
->lpFormat
->nChannels
=(wwo
->ossdev
->stereo
? 2 : 1);
1627 lpDesc
->lpFormat
->wBitsPerSample
=(wwo
->ossdev
->format
== AFMT_U8
? 8 : 16);
1628 lpDesc
->lpFormat
->nBlockAlign
=lpDesc
->lpFormat
->nChannels
*lpDesc
->lpFormat
->wBitsPerSample
/8;
1629 lpDesc
->lpFormat
->nAvgBytesPerSec
=lpDesc
->lpFormat
->nSamplesPerSec
*lpDesc
->lpFormat
->nBlockAlign
;
1630 TRACE("OSS_OpenDevice returned this format: %ldx%dx%d\n",
1631 lpDesc
->lpFormat
->nSamplesPerSec
,
1632 lpDesc
->lpFormat
->wBitsPerSample
,
1633 lpDesc
->lpFormat
->nChannels
);
1635 if (ret
!= 0) return ret
;
1636 wwo
->state
= WINE_WS_STOPPED
;
1638 wwo
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
1640 memcpy(&wwo
->waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
1641 memcpy(&wwo
->format
, lpDesc
->lpFormat
, sizeof(PCMWAVEFORMAT
));
1643 if (wwo
->format
.wBitsPerSample
== 0) {
1644 WARN("Resetting zeroed wBitsPerSample\n");
1645 wwo
->format
.wBitsPerSample
= 8 *
1646 (wwo
->format
.wf
.nAvgBytesPerSec
/
1647 wwo
->format
.wf
.nSamplesPerSec
) /
1648 wwo
->format
.wf
.nChannels
;
1650 /* Read output space info for future reference */
1651 if (ioctl(wwo
->ossdev
->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
1652 ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n", wwo
->ossdev
->dev_name
, strerror(errno
));
1653 OSS_CloseDevice(wwo
->ossdev
);
1654 wwo
->state
= WINE_WS_CLOSED
;
1655 return MMSYSERR_NOTENABLED
;
1658 /* Check that fragsize is correct per our settings above */
1659 if ((info
.fragsize
> 1024) && (LOWORD(audio_fragment
) <= 10)) {
1660 /* we've tried to set 1K fragments or less, but it didn't work */
1661 ERR("fragment size set failed, size is now %d\n", info
.fragsize
);
1662 MESSAGE("Your Open Sound System driver did not let us configure small enough sound fragments.\n");
1663 MESSAGE("This may cause delays and other problems in audio playback with certain applications.\n");
1666 /* Remember fragsize and total buffer size for future use */
1667 wwo
->dwFragmentSize
= info
.fragsize
;
1668 wwo
->dwBufferSize
= info
.fragstotal
* info
.fragsize
;
1669 wwo
->dwPlayedTotal
= 0;
1670 wwo
->dwWrittenTotal
= 0;
1671 wwo
->bNeedPost
= TRUE
;
1673 OSS_InitRingMessage(&wwo
->msgRing
);
1675 wwo
->hStartUpEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
1676 wwo
->hThread
= CreateThread(NULL
, 0, wodPlayer
, (LPVOID
)(DWORD
)wDevID
, 0, &(wwo
->dwThreadID
));
1677 WaitForSingleObject(wwo
->hStartUpEvent
, INFINITE
);
1678 CloseHandle(wwo
->hStartUpEvent
);
1679 wwo
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
1681 TRACE("fd=%d fragmentSize=%ld\n",
1682 wwo
->ossdev
->fd
, wwo
->dwFragmentSize
);
1683 if (wwo
->dwFragmentSize
% wwo
->format
.wf
.nBlockAlign
)
1684 ERR("Fragment doesn't contain an integral number of data blocks\n");
1686 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
1687 wwo
->format
.wBitsPerSample
, wwo
->format
.wf
.nAvgBytesPerSec
,
1688 wwo
->format
.wf
.nSamplesPerSec
, wwo
->format
.wf
.nChannels
,
1689 wwo
->format
.wf
.nBlockAlign
);
1691 return wodNotifyClient(wwo
, WOM_OPEN
, 0L, 0L);
1694 /**************************************************************************
1695 * wodClose [internal]
1697 static DWORD
wodClose(WORD wDevID
)
1699 DWORD ret
= MMSYSERR_NOERROR
;
1702 TRACE("(%u);\n", wDevID
);
1704 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1705 WARN("bad device ID !\n");
1706 return MMSYSERR_BADDEVICEID
;
1709 wwo
= &WOutDev
[wDevID
];
1710 if (wwo
->lpQueuePtr
) {
1711 WARN("buffers still playing !\n");
1712 ret
= WAVERR_STILLPLAYING
;
1714 if (wwo
->hThread
!= INVALID_HANDLE_VALUE
) {
1715 OSS_AddRingMessage(&wwo
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
1718 OSS_DestroyRingMessage(&wwo
->msgRing
);
1720 OSS_CloseDevice(wwo
->ossdev
);
1721 wwo
->state
= WINE_WS_CLOSED
;
1722 wwo
->dwFragmentSize
= 0;
1723 ret
= wodNotifyClient(wwo
, WOM_CLOSE
, 0L, 0L);
1728 /**************************************************************************
1729 * wodWrite [internal]
1732 static DWORD
wodWrite(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1734 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1736 /* first, do the sanity checks... */
1737 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1738 WARN("bad dev ID !\n");
1739 return MMSYSERR_BADDEVICEID
;
1742 if (lpWaveHdr
->lpData
== NULL
|| !(lpWaveHdr
->dwFlags
& WHDR_PREPARED
))
1743 return WAVERR_UNPREPARED
;
1745 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1746 return WAVERR_STILLPLAYING
;
1748 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1749 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
1750 lpWaveHdr
->lpNext
= 0;
1752 if ((lpWaveHdr
->dwBufferLength
& (WOutDev
[wDevID
].format
.wf
.nBlockAlign
- 1)) != 0)
1754 WARN("WaveHdr length isn't a multiple of the PCM block size: %ld %% %d\n",lpWaveHdr
->dwBufferLength
,WOutDev
[wDevID
].format
.wf
.nBlockAlign
);
1755 lpWaveHdr
->dwBufferLength
&= ~(WOutDev
[wDevID
].format
.wf
.nBlockAlign
- 1);
1758 OSS_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
1760 return MMSYSERR_NOERROR
;
1763 /**************************************************************************
1764 * wodPrepare [internal]
1766 static DWORD
wodPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1768 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1770 if (wDevID
>= numOutDev
) {
1771 WARN("bad device ID !\n");
1772 return MMSYSERR_BADDEVICEID
;
1775 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1776 return WAVERR_STILLPLAYING
;
1778 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
1779 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
1780 return MMSYSERR_NOERROR
;
1783 /**************************************************************************
1784 * wodUnprepare [internal]
1786 static DWORD
wodUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
1788 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
1790 if (wDevID
>= numOutDev
) {
1791 WARN("bad device ID !\n");
1792 return MMSYSERR_BADDEVICEID
;
1795 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
1796 return WAVERR_STILLPLAYING
;
1798 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
1799 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
1801 return MMSYSERR_NOERROR
;
1804 /**************************************************************************
1805 * wodPause [internal]
1807 static DWORD
wodPause(WORD wDevID
)
1809 TRACE("(%u);!\n", wDevID
);
1811 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1812 WARN("bad device ID !\n");
1813 return MMSYSERR_BADDEVICEID
;
1816 OSS_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_PAUSING
, 0, TRUE
);
1818 return MMSYSERR_NOERROR
;
1821 /**************************************************************************
1822 * wodRestart [internal]
1824 static DWORD
wodRestart(WORD wDevID
)
1826 TRACE("(%u);\n", wDevID
);
1828 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1829 WARN("bad device ID !\n");
1830 return MMSYSERR_BADDEVICEID
;
1833 OSS_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_RESTARTING
, 0, TRUE
);
1835 /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
1836 /* FIXME: Myst crashes with this ... hmm -MM
1837 return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
1840 return MMSYSERR_NOERROR
;
1843 /**************************************************************************
1844 * wodReset [internal]
1846 static DWORD
wodReset(WORD wDevID
)
1848 TRACE("(%u);\n", wDevID
);
1850 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1851 WARN("bad device ID !\n");
1852 return MMSYSERR_BADDEVICEID
;
1855 OSS_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_RESETTING
, 0, TRUE
);
1857 return MMSYSERR_NOERROR
;
1860 /**************************************************************************
1861 * wodGetPosition [internal]
1863 static DWORD
wodGetPosition(WORD wDevID
, LPMMTIME lpTime
, DWORD uSize
)
1869 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
1871 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1872 WARN("bad device ID !\n");
1873 return MMSYSERR_BADDEVICEID
;
1876 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
1878 wwo
= &WOutDev
[wDevID
];
1879 #ifdef EXACT_WODPOSITION
1880 OSS_AddRingMessage(&wwo
->msgRing
, WINE_WM_UPDATE
, 0, TRUE
);
1882 val
= wwo
->dwPlayedTotal
;
1884 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
1885 lpTime
->wType
, wwo
->format
.wBitsPerSample
,
1886 wwo
->format
.wf
.nSamplesPerSec
, wwo
->format
.wf
.nChannels
,
1887 wwo
->format
.wf
.nAvgBytesPerSec
);
1888 TRACE("dwPlayedTotal=%lu\n", val
);
1890 switch (lpTime
->wType
) {
1893 TRACE("TIME_BYTES=%lu\n", lpTime
->u
.cb
);
1896 lpTime
->u
.sample
= val
* 8 / wwo
->format
.wBitsPerSample
/wwo
->format
.wf
.nChannels
;
1897 TRACE("TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
1900 time
= val
/ (wwo
->format
.wf
.nAvgBytesPerSec
/ 1000);
1901 lpTime
->u
.smpte
.hour
= time
/ (60 * 60 * 1000);
1902 time
-= lpTime
->u
.smpte
.hour
* (60 * 60 * 1000);
1903 lpTime
->u
.smpte
.min
= time
/ (60 * 1000);
1904 time
-= lpTime
->u
.smpte
.min
* (60 * 1000);
1905 lpTime
->u
.smpte
.sec
= time
/ 1000;
1906 time
-= lpTime
->u
.smpte
.sec
* 1000;
1907 lpTime
->u
.smpte
.frame
= time
* 30 / 1000;
1908 lpTime
->u
.smpte
.fps
= 30;
1909 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
1910 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
1911 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
1914 FIXME("Format %d not supported ! use TIME_MS !\n", lpTime
->wType
);
1915 lpTime
->wType
= TIME_MS
;
1917 lpTime
->u
.ms
= val
/ (wwo
->format
.wf
.nAvgBytesPerSec
/ 1000);
1918 TRACE("TIME_MS=%lu\n", lpTime
->u
.ms
);
1921 return MMSYSERR_NOERROR
;
1924 /**************************************************************************
1925 * wodBreakLoop [internal]
1927 static DWORD
wodBreakLoop(WORD wDevID
)
1929 TRACE("(%u);\n", wDevID
);
1931 if (wDevID
>= numOutDev
|| WOutDev
[wDevID
].state
== WINE_WS_CLOSED
) {
1932 WARN("bad device ID !\n");
1933 return MMSYSERR_BADDEVICEID
;
1935 OSS_AddRingMessage(&WOutDev
[wDevID
].msgRing
, WINE_WM_BREAKLOOP
, 0, TRUE
);
1936 return MMSYSERR_NOERROR
;
1939 /**************************************************************************
1940 * wodGetVolume [internal]
1942 static DWORD
wodGetVolume(WORD wDevID
, LPDWORD lpdwVol
)
1947 DWORD last_left
, last_right
;
1949 TRACE("(%u, %p);\n", wDevID
, lpdwVol
);
1951 if (lpdwVol
== NULL
)
1952 return MMSYSERR_NOTENABLED
;
1953 if (wDevID
>= numOutDev
)
1954 return MMSYSERR_INVALPARAM
;
1956 if ((mixer
= open(WOutDev
[wDevID
].ossdev
->mixer_name
, O_RDONLY
|O_NDELAY
)) < 0) {
1957 WARN("mixer device not available !\n");
1958 return MMSYSERR_NOTENABLED
;
1960 if (ioctl(mixer
, SOUND_MIXER_READ_PCM
, &volume
) == -1) {
1961 WARN("ioctl(%s, SOUND_MIXER_READ_PCM) failed (%s)\n", WOutDev
[wDevID
].ossdev
->mixer_name
, strerror(errno
));
1962 return MMSYSERR_NOTENABLED
;
1965 left
= LOBYTE(volume
);
1966 right
= HIBYTE(volume
);
1967 TRACE("left=%ld right=%ld !\n", left
, right
);
1968 last_left
= (LOWORD(WOutDev
[wDevID
].volume
) * 100) / 0xFFFFl
;
1969 last_right
= (HIWORD(WOutDev
[wDevID
].volume
) * 100) / 0xFFFFl
;
1970 TRACE("last_left=%ld last_right=%ld !\n", last_left
, last_right
);
1971 if (last_left
== left
&& last_right
== right
)
1972 *lpdwVol
= WOutDev
[wDevID
].volume
;
1974 *lpdwVol
= ((left
* 0xFFFFl
) / 100) + (((right
* 0xFFFFl
) / 100) << 16);
1975 return MMSYSERR_NOERROR
;
1978 /**************************************************************************
1979 * wodSetVolume [internal]
1981 static DWORD
wodSetVolume(WORD wDevID
, DWORD dwParam
)
1987 TRACE("(%u, %08lX);\n", wDevID
, dwParam
);
1989 left
= (LOWORD(dwParam
) * 100) / 0xFFFFl
;
1990 right
= (HIWORD(dwParam
) * 100) / 0xFFFFl
;
1991 volume
= left
+ (right
<< 8);
1993 if (wDevID
>= numOutDev
) {
1994 WARN("invalid parameter: wDevID > %d\n", numOutDev
);
1995 return MMSYSERR_INVALPARAM
;
1998 if ((mixer
= open(WOutDev
[wDevID
].ossdev
->mixer_name
, O_WRONLY
|O_NDELAY
)) < 0) {
1999 WARN("mixer device not available !\n");
2000 return MMSYSERR_NOTENABLED
;
2002 if (ioctl(mixer
, SOUND_MIXER_WRITE_PCM
, &volume
) == -1) {
2003 WARN("ioctl(%s, SOUND_MIXER_WRITE_PCM) failed (%s)\n", WOutDev
[wDevID
].ossdev
->mixer_name
, strerror(errno
));
2004 return MMSYSERR_NOTENABLED
;
2006 TRACE("volume=%04x\n", (unsigned)volume
);
2010 /* save requested volume */
2011 WOutDev
[wDevID
].volume
= dwParam
;
2013 return MMSYSERR_NOERROR
;
2016 /**************************************************************************
2017 * wodMessage (WINEOSS.7)
2019 DWORD WINAPI
OSS_wodMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
2020 DWORD dwParam1
, DWORD dwParam2
)
2022 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
2023 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
2030 /* FIXME: Pretend this is supported */
2032 case WODM_OPEN
: return wodOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
2033 case WODM_CLOSE
: return wodClose (wDevID
);
2034 case WODM_WRITE
: return wodWrite (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
2035 case WODM_PAUSE
: return wodPause (wDevID
);
2036 case WODM_GETPOS
: return wodGetPosition (wDevID
, (LPMMTIME
)dwParam1
, dwParam2
);
2037 case WODM_BREAKLOOP
: return wodBreakLoop (wDevID
);
2038 case WODM_PREPARE
: return wodPrepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
2039 case WODM_UNPREPARE
: return wodUnprepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
2040 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (LPWAVEOUTCAPSA
)dwParam1
, dwParam2
);
2041 case WODM_GETNUMDEVS
: return numOutDev
;
2042 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
2043 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
2044 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
2045 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
2046 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (LPDWORD
)dwParam1
);
2047 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, dwParam1
);
2048 case WODM_RESTART
: return wodRestart (wDevID
);
2049 case WODM_RESET
: return wodReset (wDevID
);
2051 case DRV_QUERYDEVICEINTERFACESIZE
: return wdDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
2052 case DRV_QUERYDEVICEINTERFACE
: return wdDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
2053 case DRV_QUERYDSOUNDIFACE
: return wodDsCreate (wDevID
, (PIDSDRIVER
*)dwParam1
);
2054 case DRV_QUERYDSOUNDDESC
: return wodDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
2055 case DRV_QUERYDSOUNDGUID
: return wodDsGuid (wDevID
, (LPGUID
)dwParam1
);
2057 FIXME("unknown message %d!\n", wMsg
);
2059 return MMSYSERR_NOTSUPPORTED
;
2062 /*======================================================================*
2063 * Low level DSOUND definitions *
2064 *======================================================================*/
2066 typedef struct IDsDriverPropertySetImpl IDsDriverPropertySetImpl
;
2067 typedef struct IDsDriverNotifyImpl IDsDriverNotifyImpl
;
2068 typedef struct IDsDriverImpl IDsDriverImpl
;
2069 typedef struct IDsDriverBufferImpl IDsDriverBufferImpl
;
2071 struct IDsDriverPropertySetImpl
2073 /* IUnknown fields */
2074 ICOM_VFIELD(IDsDriverPropertySet
);
2077 IDsDriverBufferImpl
* buffer
;
2080 struct IDsDriverNotifyImpl
2082 /* IUnknown fields */
2083 ICOM_VFIELD(IDsDriverNotify
);
2086 /* IDsDriverNotifyImpl fields */
2087 LPDSBPOSITIONNOTIFY notifies
;
2090 IDsDriverBufferImpl
* buffer
;
2093 struct IDsDriverImpl
2095 /* IUnknown fields */
2096 ICOM_VFIELD(IDsDriver
);
2099 /* IDsDriverImpl fields */
2101 IDsDriverBufferImpl
* primary
;
2104 struct IDsDriverBufferImpl
2106 /* IUnknown fields */
2107 ICOM_VFIELD(IDsDriverBuffer
);
2110 /* IDsDriverBufferImpl fields */
2119 /* IDsDriverNotifyImpl fields */
2120 IDsDriverNotifyImpl
* notify
;
2123 /* IDsDriverPropertySetImpl fields */
2124 IDsDriverPropertySetImpl
* property_set
;
2127 static HRESULT WINAPI
IDsDriverPropertySetImpl_Create(
2128 IDsDriverBufferImpl
* dsdb
,
2129 IDsDriverPropertySetImpl
**pdsdps
);
2131 static HRESULT WINAPI
IDsDriverNotifyImpl_Create(
2132 IDsDriverBufferImpl
* dsdb
,
2133 IDsDriverNotifyImpl
**pdsdn
);
2135 /*======================================================================*
2136 * Low level DSOUND property set implementation *
2137 *======================================================================*/
2139 static HRESULT WINAPI
IDsDriverPropertySetImpl_QueryInterface(
2140 PIDSDRIVERPROPERTYSET iface
,
2144 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2145 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
2147 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
2148 IsEqualGUID(riid
, &IID_IDsDriverPropertySet
) ) {
2149 IDsDriverPropertySet_AddRef(iface
);
2150 *ppobj
= (LPVOID
)This
;
2154 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
2157 return E_NOINTERFACE
;
2160 static ULONG WINAPI
IDsDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface
)
2162 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2164 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
2166 ref
= InterlockedIncrement(&(This
->ref
));
2170 static ULONG WINAPI
IDsDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface
)
2172 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2174 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
2176 ref
= InterlockedDecrement(&(This
->ref
));
2178 IDsDriverBuffer_Release((PIDSDRIVERBUFFER
)This
->buffer
);
2179 HeapFree(GetProcessHeap(),0,This
);
2180 TRACE("(%p) released\n",This
);
2185 static HRESULT WINAPI
IDsDriverPropertySetImpl_Get(
2186 PIDSDRIVERPROPERTYSET iface
,
2187 PDSPROPERTY pDsProperty
,
2188 LPVOID pPropertyParams
,
2189 ULONG cbPropertyParams
,
2190 LPVOID pPropertyData
,
2191 ULONG cbPropertyData
,
2192 PULONG pcbReturnedData
)
2194 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2195 FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This
,pDsProperty
,pPropertyParams
,cbPropertyParams
,pPropertyData
,cbPropertyData
,pcbReturnedData
);
2196 return DSERR_UNSUPPORTED
;
2199 static HRESULT WINAPI
IDsDriverPropertySetImpl_Set(
2200 PIDSDRIVERPROPERTYSET iface
,
2201 PDSPROPERTY pDsProperty
,
2202 LPVOID pPropertyParams
,
2203 ULONG cbPropertyParams
,
2204 LPVOID pPropertyData
,
2205 ULONG cbPropertyData
)
2207 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2208 FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This
,pDsProperty
,pPropertyParams
,cbPropertyParams
,pPropertyData
,cbPropertyData
);
2209 return DSERR_UNSUPPORTED
;
2212 static HRESULT WINAPI
IDsDriverPropertySetImpl_QuerySupport(
2213 PIDSDRIVERPROPERTYSET iface
,
2214 REFGUID PropertySetId
,
2218 ICOM_THIS(IDsDriverPropertySetImpl
,iface
);
2219 FIXME("(%p,%s,%lx,%p)\n",This
,debugstr_guid(PropertySetId
),PropertyId
,pSupport
);
2220 return DSERR_UNSUPPORTED
;
2223 ICOM_VTABLE(IDsDriverPropertySet
) dsdpsvt
=
2225 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2226 IDsDriverPropertySetImpl_QueryInterface
,
2227 IDsDriverPropertySetImpl_AddRef
,
2228 IDsDriverPropertySetImpl_Release
,
2229 IDsDriverPropertySetImpl_Get
,
2230 IDsDriverPropertySetImpl_Set
,
2231 IDsDriverPropertySetImpl_QuerySupport
,
2234 /*======================================================================*
2235 * Low level DSOUND notify implementation *
2236 *======================================================================*/
2238 static HRESULT WINAPI
IDsDriverNotifyImpl_QueryInterface(
2239 PIDSDRIVERNOTIFY iface
,
2243 ICOM_THIS(IDsDriverNotifyImpl
,iface
);
2244 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
2246 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
2247 IsEqualGUID(riid
, &IID_IDsDriverNotify
) ) {
2248 IDsDriverNotify_AddRef(iface
);
2253 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
2256 return E_NOINTERFACE
;
2259 static ULONG WINAPI
IDsDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface
)
2261 ICOM_THIS(IDsDriverNotifyImpl
,iface
);
2263 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
2265 ref
= InterlockedIncrement(&(This
->ref
));
2269 static ULONG WINAPI
IDsDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface
)
2271 ICOM_THIS(IDsDriverNotifyImpl
,iface
);
2273 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
2275 ref
= InterlockedDecrement(&(This
->ref
));
2277 IDsDriverBuffer_Release((PIDSDRIVERBUFFER
)This
->buffer
);
2278 if (This
->notifies
!= NULL
)
2279 HeapFree(GetProcessHeap(), 0, This
->notifies
);
2281 HeapFree(GetProcessHeap(),0,This
);
2282 TRACE("(%p) released\n",This
);
2288 static HRESULT WINAPI
IDsDriverNotifyImpl_SetNotificationPositions(
2289 PIDSDRIVERNOTIFY iface
,
2291 LPCDSBPOSITIONNOTIFY notify
)
2293 ICOM_THIS(IDsDriverNotifyImpl
,iface
);
2294 TRACE("(%p,0x%08lx,%p)\n",This
,howmuch
,notify
);
2297 WARN("invalid parameter\n");
2298 return DSERR_INVALIDPARAM
;
2301 if (TRACE_ON(wave
)) {
2303 for (i
=0;i
<howmuch
;i
++)
2304 TRACE("notify at %ld to 0x%08lx\n",
2305 notify
[i
].dwOffset
,(DWORD
)notify
[i
].hEventNotify
);
2308 /* Make an internal copy of the caller-supplied array.
2309 * Replace the existing copy if one is already present. */
2311 This
->notifies
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2312 This
->notifies
, howmuch
* sizeof(DSBPOSITIONNOTIFY
));
2314 This
->notifies
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
2315 howmuch
* sizeof(DSBPOSITIONNOTIFY
));
2317 memcpy(This
->notifies
, notify
, howmuch
* sizeof(DSBPOSITIONNOTIFY
));
2318 This
->nrofnotifies
= howmuch
;
2323 ICOM_VTABLE(IDsDriverNotify
) dsdnvt
=
2325 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2326 IDsDriverNotifyImpl_QueryInterface
,
2327 IDsDriverNotifyImpl_AddRef
,
2328 IDsDriverNotifyImpl_Release
,
2329 IDsDriverNotifyImpl_SetNotificationPositions
,
2332 /*======================================================================*
2333 * Low level DSOUND implementation *
2334 *======================================================================*/
2336 static HRESULT
DSDB_MapBuffer(IDsDriverBufferImpl
*dsdb
)
2338 TRACE("(%p)\n",dsdb
);
2339 if (!dsdb
->mapping
) {
2340 dsdb
->mapping
= mmap(NULL
, dsdb
->maplen
, PROT_WRITE
, MAP_SHARED
,
2342 if (dsdb
->mapping
== (LPBYTE
)-1) {
2343 TRACE("(%p): Could not map sound device for direct access (%s)\n", dsdb
, strerror(errno
));
2344 return DSERR_GENERIC
;
2346 TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb
, dsdb
->mapping
, dsdb
->maplen
);
2348 /* for some reason, es1371 and sblive! sometimes have junk in here.
2349 * clear it, or we get junk noise */
2350 /* some libc implementations are buggy: their memset reads from the buffer...
2351 * to work around it, we have to zero the block by hand. We don't do the expected:
2352 * memset(dsdb->mapping,0, dsdb->maplen);
2355 unsigned char* p1
= dsdb
->mapping
;
2356 unsigned len
= dsdb
->maplen
;
2357 unsigned char silence
= (dsdb
->wfx
.wBitsPerSample
== 8) ? 128 : 0;
2358 unsigned long ulsilence
= (dsdb
->wfx
.wBitsPerSample
== 8) ? 0x80808080 : 0;
2360 if (len
>= 16) /* so we can have at least a 4 long area to store... */
2362 /* the mmap:ed value is (at least) dword aligned
2363 * so, start filling the complete unsigned long:s
2366 unsigned long* p4
= (unsigned long*)p1
;
2368 while (b
--) *p4
++ = ulsilence
;
2369 /* prepare for filling the rest */
2371 p1
= (unsigned char*)p4
;
2373 /* in all cases, fill the remaining bytes */
2374 while (len
-- != 0) *p1
++ = silence
;
2380 static HRESULT
DSDB_UnmapBuffer(IDsDriverBufferImpl
*dsdb
)
2382 TRACE("(%p)\n",dsdb
);
2383 if (dsdb
->mapping
) {
2384 if (munmap(dsdb
->mapping
, dsdb
->maplen
) < 0) {
2385 ERR("(%p): Could not unmap sound device (%s)\n", dsdb
, strerror(errno
));
2386 return DSERR_GENERIC
;
2388 dsdb
->mapping
= NULL
;
2389 TRACE("(%p): sound device unmapped\n", dsdb
);
2394 static HRESULT WINAPI
IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
2396 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2397 TRACE("(%p,%s,%p)\n",iface
,debugstr_guid(riid
),*ppobj
);
2399 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
2400 IsEqualGUID(riid
, &IID_IDsDriverBuffer
) ) {
2401 IDsDriverBuffer_AddRef(iface
);
2402 *ppobj
= (LPVOID
)This
;
2406 if ( IsEqualGUID( &IID_IDsDriverNotify
, riid
) ) {
2408 IDsDriverNotifyImpl_Create(This
, &(This
->notify
));
2410 IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY
)This
->notify
);
2411 *ppobj
= (LPVOID
)This
->notify
;
2418 if ( IsEqualGUID( &IID_IDsDriverPropertySet
, riid
) ) {
2419 if (!This
->property_set
)
2420 IDsDriverPropertySetImpl_Create(This
, &(This
->property_set
));
2421 if (This
->property_set
) {
2422 IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET
)This
->property_set
);
2423 *ppobj
= (LPVOID
)This
->property_set
;
2430 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
2434 return E_NOINTERFACE
;
2437 static ULONG WINAPI
IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface
)
2439 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2440 TRACE("(%p)\n",This
);
2442 TRACE("ref=%ld\n",This
->ref
);
2446 static ULONG WINAPI
IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface
)
2448 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2449 TRACE("(%p)\n",This
);
2451 TRACE("ref=%ld\n",This
->ref
);
2454 if (This
== This
->drv
->primary
)
2455 This
->drv
->primary
= NULL
;
2456 DSDB_UnmapBuffer(This
);
2457 HeapFree(GetProcessHeap(),0,This
);
2462 static HRESULT WINAPI
IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface
,
2463 LPVOID
*ppvAudio1
,LPDWORD pdwLen1
,
2464 LPVOID
*ppvAudio2
,LPDWORD pdwLen2
,
2465 DWORD dwWritePosition
,DWORD dwWriteLen
,
2468 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2469 /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
2470 * and that we don't support secondary buffers, this method will never be called */
2471 TRACE("(%p): stub\n",iface
);
2472 return DSERR_UNSUPPORTED
;
2475 static HRESULT WINAPI
IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface
,
2476 LPVOID pvAudio1
,DWORD dwLen1
,
2477 LPVOID pvAudio2
,DWORD dwLen2
)
2479 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2480 TRACE("(%p): stub\n",iface
);
2481 return DSERR_UNSUPPORTED
;
2484 static HRESULT WINAPI
IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface
,
2485 LPWAVEFORMATEX pwfx
)
2487 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2489 TRACE("(%p,%p)\n",iface
,pwfx
);
2490 /* On our request (GetDriverDesc flags), DirectSound has by now used
2491 * waveOutClose/waveOutOpen to set the format...
2492 * unfortunately, this means our mmap() is now gone...
2493 * so we need to somehow signal to our DirectSound implementation
2494 * that it should completely recreate this HW buffer...
2495 * this unexpected error code should do the trick... */
2496 return DSERR_BUFFERLOST
;
2499 static HRESULT WINAPI
IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface
, DWORD dwFreq
)
2501 /* ICOM_THIS(IDsDriverBufferImpl,iface); */
2502 TRACE("(%p,%ld): stub\n",iface
,dwFreq
);
2503 return DSERR_UNSUPPORTED
;
2506 static HRESULT WINAPI
IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface
, PDSVOLUMEPAN pVolPan
)
2509 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2510 TRACE("(%p,%p)\n",This
,pVolPan
);
2512 vol
= pVolPan
->dwTotalLeftAmpFactor
| (pVolPan
->dwTotalRightAmpFactor
<< 16);
2514 if (wodSetVolume(This
->drv
->wDevID
, vol
) != MMSYSERR_NOERROR
) {
2515 WARN("wodSetVolume failed\n");
2516 return DSERR_INVALIDPARAM
;
2522 static HRESULT WINAPI
IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface
, DWORD dwNewPos
)
2524 /* ICOM_THIS(IDsDriverImpl,iface); */
2525 TRACE("(%p,%ld): stub\n",iface
,dwNewPos
);
2526 return DSERR_UNSUPPORTED
;
2529 static HRESULT WINAPI
IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface
,
2530 LPDWORD lpdwPlay
, LPDWORD lpdwWrite
)
2532 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2536 TRACE("(%p)\n",iface
);
2537 if (WOutDev
[This
->drv
->wDevID
].state
== WINE_WS_CLOSED
) {
2538 ERR("device not open, but accessing?\n");
2539 return DSERR_UNINITIALIZED
;
2541 if (ioctl(This
->fd
, SNDCTL_DSP_GETOPTR
, &info
) < 0) {
2542 ERR("ioctl(%s, SNDCTL_DSP_GETOPTR) failed (%s)\n",
2543 WOutDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2544 return DSERR_GENERIC
;
2546 ptr
= info
.ptr
& ~3; /* align the pointer, just in case */
2547 if (lpdwPlay
) *lpdwPlay
= ptr
;
2549 /* add some safety margin (not strictly necessary, but...) */
2550 if (WOutDev
[This
->drv
->wDevID
].ossdev
->out_caps
.dwSupport
& WAVECAPS_SAMPLEACCURATE
)
2551 *lpdwWrite
= ptr
+ 32;
2553 *lpdwWrite
= ptr
+ WOutDev
[This
->drv
->wDevID
].dwFragmentSize
;
2554 while (*lpdwWrite
> This
->buflen
)
2555 *lpdwWrite
-= This
->buflen
;
2557 TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay
?*lpdwPlay
:0, lpdwWrite
?*lpdwWrite
:0);
2561 static HRESULT WINAPI
IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface
, DWORD dwRes1
, DWORD dwRes2
, DWORD dwFlags
)
2563 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2565 TRACE("(%p,%lx,%lx,%lx)\n",iface
,dwRes1
,dwRes2
,dwFlags
);
2566 WOutDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
= TRUE
;
2567 enable
= getEnables(WOutDev
[This
->drv
->wDevID
].ossdev
);
2568 if (ioctl(This
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
2569 if (errno
== EINVAL
) {
2570 /* Don't give up yet. OSS trigger support is inconsistent. */
2571 if (WOutDev
[This
->drv
->wDevID
].ossdev
->open_count
== 1) {
2572 /* try the opposite input enable */
2573 if (WOutDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
== FALSE
)
2574 WOutDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
= TRUE
;
2576 WOutDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
= FALSE
;
2578 enable
= getEnables(WOutDev
[This
->drv
->wDevID
].ossdev
);
2579 if (ioctl(This
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) >= 0)
2583 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2584 WOutDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
= FALSE
;
2585 return DSERR_GENERIC
;
2590 static HRESULT WINAPI
IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface
)
2592 ICOM_THIS(IDsDriverBufferImpl
,iface
);
2594 TRACE("(%p)\n",iface
);
2595 /* no more playing */
2596 WOutDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
= FALSE
;
2597 enable
= getEnables(WOutDev
[This
->drv
->wDevID
].ossdev
);
2598 if (ioctl(This
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
2599 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WOutDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2600 return DSERR_GENERIC
;
2603 /* the play position must be reset to the beginning of the buffer */
2604 if (ioctl(This
->fd
, SNDCTL_DSP_RESET
, 0) < 0) {
2605 ERR("ioctl(%s, SNDCTL_DSP_RESET) failed (%s)\n", WOutDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2606 return DSERR_GENERIC
;
2609 /* Most OSS drivers just can't stop the playback without closing the device...
2610 * so we need to somehow signal to our DirectSound implementation
2611 * that it should completely recreate this HW buffer...
2612 * this unexpected error code should do the trick... */
2613 /* FIXME: ...unless we are doing full duplex, then its not nice to close the device */
2614 if (WOutDev
[This
->drv
->wDevID
].ossdev
->open_count
== 1)
2615 return DSERR_BUFFERLOST
;
2620 static ICOM_VTABLE(IDsDriverBuffer
) dsdbvt
=
2622 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2623 IDsDriverBufferImpl_QueryInterface
,
2624 IDsDriverBufferImpl_AddRef
,
2625 IDsDriverBufferImpl_Release
,
2626 IDsDriverBufferImpl_Lock
,
2627 IDsDriverBufferImpl_Unlock
,
2628 IDsDriverBufferImpl_SetFormat
,
2629 IDsDriverBufferImpl_SetFrequency
,
2630 IDsDriverBufferImpl_SetVolumePan
,
2631 IDsDriverBufferImpl_SetPosition
,
2632 IDsDriverBufferImpl_GetPosition
,
2633 IDsDriverBufferImpl_Play
,
2634 IDsDriverBufferImpl_Stop
2637 static HRESULT WINAPI
IDsDriverImpl_QueryInterface(PIDSDRIVER iface
, REFIID riid
, LPVOID
*ppobj
)
2639 ICOM_THIS(IDsDriverImpl
,iface
);
2640 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
2642 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
2643 IsEqualGUID(riid
, &IID_IDsDriver
) ) {
2644 IDsDriver_AddRef(iface
);
2645 *ppobj
= (LPVOID
)This
;
2649 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
2653 return E_NOINTERFACE
;
2656 static ULONG WINAPI
IDsDriverImpl_AddRef(PIDSDRIVER iface
)
2658 ICOM_THIS(IDsDriverImpl
,iface
);
2659 TRACE("(%p)\n",This
);
2661 TRACE("ref=%ld\n",This
->ref
);
2665 static ULONG WINAPI
IDsDriverImpl_Release(PIDSDRIVER iface
)
2667 ICOM_THIS(IDsDriverImpl
,iface
);
2668 TRACE("(%p)\n",This
);
2670 TRACE("ref=%ld\n",This
->ref
);
2673 HeapFree(GetProcessHeap(),0,This
);
2678 static HRESULT WINAPI
IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface
, PDSDRIVERDESC pDesc
)
2680 ICOM_THIS(IDsDriverImpl
,iface
);
2681 TRACE("(%p,%p)\n",iface
,pDesc
);
2683 /* copy version from driver */
2684 memcpy(pDesc
, &(WOutDev
[This
->wDevID
].ossdev
->ds_desc
), sizeof(DSDRIVERDESC
));
2686 pDesc
->dwFlags
|= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
|
2687 DSDDESC_USESYSTEMMEMORY
| DSDDESC_DONTNEEDPRIMARYLOCK
;
2688 pDesc
->dnDevNode
= WOutDev
[This
->wDevID
].waveDesc
.dnDevNode
;
2690 pDesc
->wReserved
= 0;
2691 pDesc
->ulDeviceNum
= This
->wDevID
;
2692 pDesc
->dwHeapType
= DSDHEAP_NOHEAP
;
2693 pDesc
->pvDirectDrawHeap
= NULL
;
2694 pDesc
->dwMemStartAddress
= 0;
2695 pDesc
->dwMemEndAddress
= 0;
2696 pDesc
->dwMemAllocExtra
= 0;
2697 pDesc
->pvReserved1
= NULL
;
2698 pDesc
->pvReserved2
= NULL
;
2702 static HRESULT WINAPI
IDsDriverImpl_Open(PIDSDRIVER iface
)
2704 ICOM_THIS(IDsDriverImpl
,iface
);
2706 TRACE("(%p)\n",iface
);
2708 /* make sure the card doesn't start playing before we want it to */
2709 WOutDev
[This
->wDevID
].ossdev
->bOutputEnabled
= FALSE
;
2710 enable
= getEnables(WOutDev
[This
->wDevID
].ossdev
);
2711 if (ioctl(WOutDev
[This
->wDevID
].ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
2712 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",WOutDev
[This
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2713 return DSERR_GENERIC
;
2718 static HRESULT WINAPI
IDsDriverImpl_Close(PIDSDRIVER iface
)
2720 ICOM_THIS(IDsDriverImpl
,iface
);
2721 TRACE("(%p)\n",iface
);
2722 if (This
->primary
) {
2723 ERR("problem with DirectSound: primary not released\n");
2724 return DSERR_GENERIC
;
2729 static HRESULT WINAPI
IDsDriverImpl_GetCaps(PIDSDRIVER iface
, PDSDRIVERCAPS pCaps
)
2731 ICOM_THIS(IDsDriverImpl
,iface
);
2732 TRACE("(%p,%p)\n",iface
,pCaps
);
2733 memcpy(pCaps
, &(WOutDev
[This
->wDevID
].ossdev
->ds_caps
), sizeof(DSDRIVERCAPS
));
2737 static HRESULT WINAPI
DSD_CreatePrimaryBuffer(PIDSDRIVER iface
,
2738 LPWAVEFORMATEX pwfx
,
2740 DWORD dwCardAddress
,
2741 LPDWORD pdwcbBufferSize
,
2745 ICOM_THIS(IDsDriverImpl
,iface
);
2746 IDsDriverBufferImpl
** ippdsdb
= (IDsDriverBufferImpl
**)ppvObj
;
2748 audio_buf_info info
;
2750 TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
2753 return DSERR_ALLOCATED
;
2754 if (dwFlags
& (DSBCAPS_CTRLFREQUENCY
| DSBCAPS_CTRLPAN
))
2755 return DSERR_CONTROLUNAVAIL
;
2757 *ippdsdb
= (IDsDriverBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsDriverBufferImpl
));
2758 if (*ippdsdb
== NULL
)
2759 return DSERR_OUTOFMEMORY
;
2760 (*ippdsdb
)->lpVtbl
= &dsdbvt
;
2761 (*ippdsdb
)->ref
= 1;
2762 (*ippdsdb
)->drv
= This
;
2763 (*ippdsdb
)->wfx
= *pwfx
;
2764 (*ippdsdb
)->fd
= WOutDev
[This
->wDevID
].ossdev
->fd
;
2765 (*ippdsdb
)->dwFlags
= dwFlags
;
2767 /* check how big the DMA buffer is now */
2768 if (ioctl((*ippdsdb
)->fd
, SNDCTL_DSP_GETOSPACE
, &info
) < 0) {
2769 ERR("ioctl(%s, SNDCTL_DSP_GETOSPACE) failed (%s)\n",
2770 WOutDev
[This
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2771 HeapFree(GetProcessHeap(),0,*ippdsdb
);
2773 return DSERR_GENERIC
;
2775 (*ippdsdb
)->maplen
= (*ippdsdb
)->buflen
= info
.fragstotal
* info
.fragsize
;
2777 /* map the DMA buffer */
2778 err
= DSDB_MapBuffer(*ippdsdb
);
2780 HeapFree(GetProcessHeap(),0,*ippdsdb
);
2785 /* primary buffer is ready to go */
2786 *pdwcbBufferSize
= (*ippdsdb
)->maplen
;
2787 *ppbBuffer
= (*ippdsdb
)->mapping
;
2789 /* some drivers need some extra nudging after mapping */
2790 WOutDev
[This
->wDevID
].ossdev
->bOutputEnabled
= FALSE
;
2791 enable
= getEnables(WOutDev
[This
->wDevID
].ossdev
);
2792 if (ioctl((*ippdsdb
)->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
2793 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n",
2794 WOutDev
[This
->wDevID
].ossdev
->dev_name
, strerror(errno
));
2795 return DSERR_GENERIC
;
2798 This
->primary
= *ippdsdb
;
2803 static HRESULT WINAPI
DSD_CreateSecondaryBuffer(PIDSDRIVER iface
,
2804 LPWAVEFORMATEX pwfx
,
2806 DWORD dwCardAddress
,
2807 LPDWORD pdwcbBufferSize
,
2811 ICOM_THIS(IDsDriverImpl
,iface
);
2812 IDsDriverBufferImpl
** ippdsdb
= (IDsDriverBufferImpl
**)ppvObj
;
2813 FIXME("(%p,%p,%lx,%lx,%p,%p,%p): stub\n",This
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
2816 return DSERR_UNSUPPORTED
;
2819 static HRESULT WINAPI
IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface
,
2820 LPWAVEFORMATEX pwfx
,
2822 DWORD dwCardAddress
,
2823 LPDWORD pdwcbBufferSize
,
2827 TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
2829 if (dwFlags
& DSBCAPS_PRIMARYBUFFER
)
2830 return DSD_CreatePrimaryBuffer(iface
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
2832 return DSD_CreateSecondaryBuffer(iface
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
2835 static HRESULT WINAPI
IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface
,
2836 PIDSDRIVERBUFFER pBuffer
,
2839 /* ICOM_THIS(IDsDriverImpl,iface); */
2840 TRACE("(%p,%p): stub\n",iface
,pBuffer
);
2841 return DSERR_INVALIDCALL
;
2844 static ICOM_VTABLE(IDsDriver
) dsdvt
=
2846 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2847 IDsDriverImpl_QueryInterface
,
2848 IDsDriverImpl_AddRef
,
2849 IDsDriverImpl_Release
,
2850 IDsDriverImpl_GetDriverDesc
,
2852 IDsDriverImpl_Close
,
2853 IDsDriverImpl_GetCaps
,
2854 IDsDriverImpl_CreateSoundBuffer
,
2855 IDsDriverImpl_DuplicateSoundBuffer
2858 static HRESULT WINAPI
IDsDriverPropertySetImpl_Create(
2859 IDsDriverBufferImpl
* dsdb
,
2860 IDsDriverPropertySetImpl
**pdsdps
)
2862 IDsDriverPropertySetImpl
* dsdps
;
2863 TRACE("(%p,%p)\n",dsdb
,pdsdps
);
2865 dsdps
= (IDsDriverPropertySetImpl
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(dsdps
));
2866 if (dsdps
== NULL
) {
2867 WARN("out of memory\n");
2868 return DSERR_OUTOFMEMORY
;
2872 dsdps
->lpVtbl
= &dsdpsvt
;
2873 dsdps
->buffer
= dsdb
;
2874 dsdb
->property_set
= dsdps
;
2875 IDsDriverBuffer_AddRef((PIDSDRIVER
)dsdb
);
2881 static HRESULT WINAPI
IDsDriverNotifyImpl_Create(
2882 IDsDriverBufferImpl
* dsdb
,
2883 IDsDriverNotifyImpl
**pdsdn
)
2885 IDsDriverNotifyImpl
* dsdn
;
2886 TRACE("(%p,%p)\n",dsdb
,pdsdn
);
2888 dsdn
= (IDsDriverNotifyImpl
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(dsdn
));
2891 WARN("out of memory\n");
2892 return DSERR_OUTOFMEMORY
;
2896 dsdn
->lpVtbl
= &dsdnvt
;
2897 dsdn
->buffer
= dsdb
;
2898 dsdb
->notify
= dsdn
;
2899 IDsDriverBuffer_AddRef((PIDSDRIVER
)dsdb
);
2905 static DWORD
wodDsCreate(UINT wDevID
, PIDSDRIVER
* drv
)
2907 IDsDriverImpl
** idrv
= (IDsDriverImpl
**)drv
;
2908 TRACE("(%d,%p)\n",wDevID
,drv
);
2910 /* the HAL isn't much better than the HEL if we can't do mmap() */
2911 if (!(WOutDev
[wDevID
].ossdev
->out_caps
.dwSupport
& WAVECAPS_DIRECTSOUND
)) {
2912 ERR("DirectSound flag not set\n");
2913 MESSAGE("This sound card's driver does not support direct access\n");
2914 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
2915 return MMSYSERR_NOTSUPPORTED
;
2918 *idrv
= (IDsDriverImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsDriverImpl
));
2920 return MMSYSERR_NOMEM
;
2921 (*idrv
)->lpVtbl
= &dsdvt
;
2924 (*idrv
)->wDevID
= wDevID
;
2925 (*idrv
)->primary
= NULL
;
2926 return MMSYSERR_NOERROR
;
2929 static DWORD
wodDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
2931 TRACE("(%d,%p)\n",wDevID
,desc
);
2932 memcpy(desc
, &(WOutDev
[wDevID
].ossdev
->ds_desc
), sizeof(DSDRIVERDESC
));
2933 return MMSYSERR_NOERROR
;
2936 static DWORD
wodDsGuid(UINT wDevID
, LPGUID pGuid
)
2938 TRACE("(%d,%p)\n",wDevID
,pGuid
);
2939 memcpy(pGuid
, &(WOutDev
[wDevID
].ossdev
->ds_guid
), sizeof(GUID
));
2940 return MMSYSERR_NOERROR
;
2943 /*======================================================================*
2944 * Low level WAVE IN implementation *
2945 *======================================================================*/
2947 /**************************************************************************
2948 * widNotifyClient [internal]
2950 static DWORD
widNotifyClient(WINE_WAVEIN
* wwi
, WORD wMsg
, DWORD dwParam1
, DWORD dwParam2
)
2952 TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg
,
2953 wMsg
== WIM_OPEN
? "WIM_OPEN" : wMsg
== WIM_CLOSE
? "WIM_CLOSE" :
2954 wMsg
== WIM_DATA
? "WIM_DATA" : "Unknown", dwParam1
, dwParam2
);
2960 if (wwi
->wFlags
!= DCB_NULL
&&
2961 !DriverCallback(wwi
->waveDesc
.dwCallback
, wwi
->wFlags
,
2962 (HDRVR
)wwi
->waveDesc
.hWave
, wMsg
,
2963 wwi
->waveDesc
.dwInstance
, dwParam1
, dwParam2
)) {
2964 WARN("can't notify client !\n");
2965 return MMSYSERR_ERROR
;
2969 FIXME("Unknown callback message %u\n", wMsg
);
2970 return MMSYSERR_INVALPARAM
;
2972 return MMSYSERR_NOERROR
;
2975 /**************************************************************************
2976 * widGetDevCaps [internal]
2978 static DWORD
widGetDevCaps(WORD wDevID
, LPWAVEINCAPSA lpCaps
, DWORD dwSize
)
2980 TRACE("(%u, %p, %lu);\n", wDevID
, lpCaps
, dwSize
);
2982 if (lpCaps
== NULL
) return MMSYSERR_NOTENABLED
;
2984 if (wDevID
>= numInDev
) {
2985 TRACE("numOutDev reached !\n");
2986 return MMSYSERR_BADDEVICEID
;
2989 memcpy(lpCaps
, &WInDev
[wDevID
].ossdev
->in_caps
, min(dwSize
, sizeof(*lpCaps
)));
2990 return MMSYSERR_NOERROR
;
2993 /**************************************************************************
2994 * widRecorder_ReadHeaders [internal]
2996 static void widRecorder_ReadHeaders(WINE_WAVEIN
* wwi
)
2998 enum win_wm_message tmp_msg
;
3003 while (OSS_RetrieveRingMessage(&wwi
->msgRing
, &tmp_msg
, &tmp_param
, &tmp_ev
)) {
3004 if (tmp_msg
== WINE_WM_HEADER
) {
3006 lpWaveHdr
= (LPWAVEHDR
)tmp_param
;
3007 lpWaveHdr
->lpNext
= 0;
3009 if (wwi
->lpQueuePtr
== 0)
3010 wwi
->lpQueuePtr
= lpWaveHdr
;
3012 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3016 ERR("should only have headers left\n");
3021 /**************************************************************************
3022 * widRecorder [internal]
3024 static DWORD CALLBACK
widRecorder(LPVOID pmt
)
3026 WORD uDevID
= (DWORD
)pmt
;
3027 WINE_WAVEIN
* wwi
= (WINE_WAVEIN
*)&WInDev
[uDevID
];
3031 LPVOID buffer
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, wwi
->dwFragmentSize
);
3032 char *pOffset
= buffer
;
3033 audio_buf_info info
;
3035 enum win_wm_message msg
;
3040 wwi
->state
= WINE_WS_STOPPED
;
3041 wwi
->dwTotalRecorded
= 0;
3042 wwi
->lpQueuePtr
= NULL
;
3044 SetEvent(wwi
->hStartUpEvent
);
3046 /* disable input so capture will begin when triggered */
3047 wwi
->ossdev
->bInputEnabled
= FALSE
;
3048 enable
= getEnables(wwi
->ossdev
);
3049 if (ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0)
3050 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi
->ossdev
->dev_name
, strerror(errno
));
3052 /* the soundblaster live needs a micro wake to get its recording started
3053 * (or GETISPACE will have 0 frags all the time)
3055 read(wwi
->ossdev
->fd
, &xs
, 4);
3057 /* make sleep time to be # of ms to output a fragment */
3058 dwSleepTime
= (wwi
->dwFragmentSize
* 1000) / wwi
->format
.wf
.nAvgBytesPerSec
;
3059 TRACE("sleeptime=%ld ms\n", dwSleepTime
);
3062 /* wait for dwSleepTime or an event in thread's queue */
3063 /* FIXME: could improve wait time depending on queue state,
3064 * ie, number of queued fragments
3067 if (wwi
->lpQueuePtr
!= NULL
&& wwi
->state
== WINE_WS_PLAYING
)
3069 lpWaveHdr
= wwi
->lpQueuePtr
;
3071 ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_GETISPACE
, &info
);
3072 TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info
.fragments
, info
.fragsize
, info
.fragstotal
, info
.bytes
);
3074 /* read all the fragments accumulated so far */
3075 while ((info
.fragments
> 0) && (wwi
->lpQueuePtr
))
3079 if (lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
>= wwi
->dwFragmentSize
)
3081 /* directly read fragment in wavehdr */
3082 bytesRead
= read(wwi
->ossdev
->fd
,
3083 lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
,
3084 wwi
->dwFragmentSize
);
3086 TRACE("bytesRead=%ld (direct)\n", bytesRead
);
3087 if (bytesRead
!= (DWORD
) -1)
3089 /* update number of bytes recorded in current buffer and by this device */
3090 lpWaveHdr
->dwBytesRecorded
+= bytesRead
;
3091 wwi
->dwTotalRecorded
+= bytesRead
;
3093 /* buffer is full. notify client */
3094 if (lpWaveHdr
->dwBytesRecorded
== lpWaveHdr
->dwBufferLength
)
3096 /* must copy the value of next waveHdr, because we have no idea of what
3097 * will be done with the content of lpWaveHdr in callback
3099 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3101 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3102 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3104 wwi
->lpQueuePtr
= lpNext
;
3105 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3112 /* read the fragment in a local buffer */
3113 bytesRead
= read(wwi
->ossdev
->fd
, buffer
, wwi
->dwFragmentSize
);
3116 TRACE("bytesRead=%ld (local)\n", bytesRead
);
3118 /* copy data in client buffers */
3119 while (bytesRead
!= (DWORD
) -1 && bytesRead
> 0)
3121 DWORD dwToCopy
= min (bytesRead
, lpWaveHdr
->dwBufferLength
- lpWaveHdr
->dwBytesRecorded
);
3123 memcpy(lpWaveHdr
->lpData
+ lpWaveHdr
->dwBytesRecorded
,
3127 /* update number of bytes recorded in current buffer and by this device */
3128 lpWaveHdr
->dwBytesRecorded
+= dwToCopy
;
3129 wwi
->dwTotalRecorded
+= dwToCopy
;
3130 bytesRead
-= dwToCopy
;
3131 pOffset
+= dwToCopy
;
3133 /* client buffer is full. notify client */
3134 if (lpWaveHdr
->dwBytesRecorded
== lpWaveHdr
->dwBufferLength
)
3136 /* must copy the value of next waveHdr, because we have no idea of what
3137 * will be done with the content of lpWaveHdr in callback
3139 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3140 TRACE("lpNext=%p\n", lpNext
);
3142 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3143 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3145 wwi
->lpQueuePtr
= lpNext
;
3146 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3149 if (!lpNext
&& bytesRead
) {
3150 /* before we give up, check for more header messages */
3151 while (OSS_PeekRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
))
3153 if (msg
== WINE_WM_HEADER
) {
3155 OSS_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
);
3156 hdr
= ((LPWAVEHDR
)param
);
3157 TRACE("msg = %s, hdr = %p, ev = %p\n", wodPlayerCmdString
[msg
- WM_USER
- 1], hdr
, ev
);
3159 if (lpWaveHdr
== 0) {
3160 /* new head of queue */
3161 wwi
->lpQueuePtr
= lpWaveHdr
= hdr
;
3163 /* insert buffer at the end of queue */
3165 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3172 if (lpWaveHdr
== 0) {
3173 /* no more buffer to copy data to, but we did read more.
3174 * what hasn't been copied will be dropped
3176 WARN("buffer under run! %lu bytes dropped.\n", bytesRead
);
3177 wwi
->lpQueuePtr
= NULL
;
3187 WAIT_OMR(&wwi
->msgRing
, dwSleepTime
);
3189 while (OSS_RetrieveRingMessage(&wwi
->msgRing
, &msg
, ¶m
, &ev
))
3191 TRACE("msg=%s param=0x%lx\n", wodPlayerCmdString
[msg
- WM_USER
- 1], param
);
3193 case WINE_WM_PAUSING
:
3194 wwi
->state
= WINE_WS_PAUSED
;
3195 /*FIXME("Device should stop recording\n");*/
3198 case WINE_WM_STARTING
:
3199 wwi
->state
= WINE_WS_PLAYING
;
3201 if (wwi
->ossdev
->bTriggerSupport
)
3203 /* start the recording */
3204 wwi
->ossdev
->bInputEnabled
= TRUE
;
3205 enable
= getEnables(wwi
->ossdev
);
3206 if (ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
3207 wwi
->ossdev
->bInputEnabled
= FALSE
;
3208 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi
->ossdev
->dev_name
, strerror(errno
));
3213 unsigned char data
[4];
3214 /* read 4 bytes to start the recording */
3215 read(wwi
->ossdev
->fd
, data
, 4);
3220 case WINE_WM_HEADER
:
3221 lpWaveHdr
= (LPWAVEHDR
)param
;
3222 lpWaveHdr
->lpNext
= 0;
3224 /* insert buffer at the end of queue */
3227 for (wh
= &(wwi
->lpQueuePtr
); *wh
; wh
= &((*wh
)->lpNext
));
3231 case WINE_WM_STOPPING
:
3232 if (wwi
->state
!= WINE_WS_STOPPED
)
3234 if (wwi
->ossdev
->bTriggerSupport
)
3236 /* stop the recording */
3237 wwi
->ossdev
->bInputEnabled
= FALSE
;
3238 enable
= getEnables(wwi
->ossdev
);
3239 if (ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
3240 wwi
->ossdev
->bInputEnabled
= FALSE
;
3241 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi
->ossdev
->dev_name
, strerror(errno
));
3245 /* read any headers in queue */
3246 widRecorder_ReadHeaders(wwi
);
3248 /* return current buffer to app */
3249 lpWaveHdr
= wwi
->lpQueuePtr
;
3252 LPWAVEHDR lpNext
= lpWaveHdr
->lpNext
;
3253 TRACE("stop %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
3254 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3255 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3256 wwi
->lpQueuePtr
= lpNext
;
3257 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3260 wwi
->state
= WINE_WS_STOPPED
;
3263 case WINE_WM_RESETTING
:
3264 if (wwi
->state
!= WINE_WS_STOPPED
)
3266 if (wwi
->ossdev
->bTriggerSupport
)
3268 /* stop the recording */
3269 wwi
->ossdev
->bInputEnabled
= FALSE
;
3270 enable
= getEnables(wwi
->ossdev
);
3271 if (ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
3272 wwi
->ossdev
->bInputEnabled
= FALSE
;
3273 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", wwi
->ossdev
->dev_name
, strerror(errno
));
3277 wwi
->state
= WINE_WS_STOPPED
;
3278 wwi
->dwTotalRecorded
= 0;
3280 /* read any headers in queue */
3281 widRecorder_ReadHeaders(wwi
);
3283 /* return all buffers to the app */
3284 for (lpWaveHdr
= wwi
->lpQueuePtr
; lpWaveHdr
; lpWaveHdr
= lpWaveHdr
->lpNext
) {
3285 TRACE("reset %p %p\n", lpWaveHdr
, lpWaveHdr
->lpNext
);
3286 lpWaveHdr
->dwFlags
&= ~WHDR_INQUEUE
;
3287 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3288 wwi
->lpQueuePtr
= lpWaveHdr
->lpNext
;
3289 widNotifyClient(wwi
, WIM_DATA
, (DWORD
)lpWaveHdr
, 0);
3292 wwi
->lpQueuePtr
= NULL
;
3295 case WINE_WM_CLOSING
:
3297 wwi
->state
= WINE_WS_CLOSED
;
3299 HeapFree(GetProcessHeap(), 0, buffer
);
3301 /* shouldn't go here */
3303 FIXME("unknown message %d\n", msg
);
3309 /* just for not generating compilation warnings... should never be executed */
3314 /**************************************************************************
3315 * widOpen [internal]
3317 static DWORD
widOpen(WORD wDevID
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
3324 TRACE("(%u, %p, %08lX);\n", wDevID
, lpDesc
, dwFlags
);
3325 if (lpDesc
== NULL
) {
3326 WARN("Invalid Parameter !\n");
3327 return MMSYSERR_INVALPARAM
;
3329 if (wDevID
>= numInDev
) return MMSYSERR_BADDEVICEID
;
3331 /* only PCM format is supported so far... */
3332 if (lpDesc
->lpFormat
->wFormatTag
!= WAVE_FORMAT_PCM
||
3333 lpDesc
->lpFormat
->nChannels
== 0 ||
3334 lpDesc
->lpFormat
->nSamplesPerSec
== 0) {
3335 WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3336 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
3337 lpDesc
->lpFormat
->nSamplesPerSec
);
3338 return WAVERR_BADFORMAT
;
3341 if (dwFlags
& WAVE_FORMAT_QUERY
) {
3342 TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
3343 lpDesc
->lpFormat
->wFormatTag
, lpDesc
->lpFormat
->nChannels
,
3344 lpDesc
->lpFormat
->nSamplesPerSec
);
3345 return MMSYSERR_NOERROR
;
3348 wwi
= &WInDev
[wDevID
];
3350 if (wwi
->state
!= WINE_WS_CLOSED
) return MMSYSERR_ALLOCATED
;
3352 if ((dwFlags
& WAVE_DIRECTSOUND
) &&
3353 !(wwi
->ossdev
->in_caps_support
& WAVECAPS_DIRECTSOUND
))
3354 /* not supported, ignore it */
3355 dwFlags
&= ~WAVE_DIRECTSOUND
;
3357 if (dwFlags
& WAVE_DIRECTSOUND
) {
3358 TRACE("has DirectSoundCapture driver\n");
3359 if (wwi
->ossdev
->in_caps_support
& WAVECAPS_SAMPLEACCURATE
)
3360 /* we have realtime DirectSound, fragments just waste our time,
3361 * but a large buffer is good, so choose 64KB (32 * 2^11) */
3362 audio_fragment
= 0x0020000B;
3364 /* to approximate realtime, we must use small fragments,
3365 * let's try to fragment the above 64KB (256 * 2^8) */
3366 audio_fragment
= 0x01000008;
3368 TRACE("doesn't have DirectSoundCapture driver\n");
3369 if (wwi
->ossdev
->open_count
> 0) {
3370 TRACE("Using output device audio_fragment\n");
3371 /* FIXME: This may not be optimal for capture but it allows us
3372 * to do hardware playback without hardware capture. */
3373 audio_fragment
= wwi
->ossdev
->audio_fragment
;
3375 /* A wave device must have a worst case latency of 10 ms so calculate
3376 * the largest fragment size less than 10 ms long.
3378 int fsize
= lpDesc
->lpFormat
->nAvgBytesPerSec
/ 100; /* 10 ms chunk */
3380 while ((1 << shift
) <= fsize
)
3383 audio_fragment
= 0x00100000 + shift
; /* 16 fragments of 2^shift */
3387 TRACE("using %d %d byte fragments (%ld ms)\n", audio_fragment
>> 16,
3388 1 << (audio_fragment
& 0xffff),
3389 ((1 << (audio_fragment
& 0xffff)) * 1000) / lpDesc
->lpFormat
->nAvgBytesPerSec
);
3391 ret
= OSS_OpenDevice(wwi
->ossdev
, O_RDONLY
, &audio_fragment
,
3393 lpDesc
->lpFormat
->nSamplesPerSec
,
3394 (lpDesc
->lpFormat
->nChannels
> 1) ? 1 : 0,
3395 (lpDesc
->lpFormat
->wBitsPerSample
== 16)
3396 ? AFMT_S16_LE
: AFMT_U8
);
3397 if (ret
!= 0) return ret
;
3398 wwi
->state
= WINE_WS_STOPPED
;
3400 if (wwi
->lpQueuePtr
) {
3401 WARN("Should have an empty queue (%p)\n", wwi
->lpQueuePtr
);
3402 wwi
->lpQueuePtr
= NULL
;
3404 wwi
->dwTotalRecorded
= 0;
3405 wwi
->wFlags
= HIWORD(dwFlags
& CALLBACK_TYPEMASK
);
3407 memcpy(&wwi
->waveDesc
, lpDesc
, sizeof(WAVEOPENDESC
));
3408 memcpy(&wwi
->format
, lpDesc
->lpFormat
, sizeof(PCMWAVEFORMAT
));
3410 if (wwi
->format
.wBitsPerSample
== 0) {
3411 WARN("Resetting zeroed wBitsPerSample\n");
3412 wwi
->format
.wBitsPerSample
= 8 *
3413 (wwi
->format
.wf
.nAvgBytesPerSec
/
3414 wwi
->format
.wf
.nSamplesPerSec
) /
3415 wwi
->format
.wf
.nChannels
;
3418 ioctl(wwi
->ossdev
->fd
, SNDCTL_DSP_GETBLKSIZE
, &fragment_size
);
3419 if (fragment_size
== -1) {
3420 WARN("ioctl(%s, SNDCTL_DSP_GETBLKSIZE) failed (%s)\n",
3421 wwi
->ossdev
->dev_name
, strerror(errno
));
3422 OSS_CloseDevice(wwi
->ossdev
);
3423 wwi
->state
= WINE_WS_CLOSED
;
3424 return MMSYSERR_NOTENABLED
;
3426 wwi
->dwFragmentSize
= fragment_size
;
3428 TRACE("dwFragmentSize=%lu\n", wwi
->dwFragmentSize
);
3429 TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
3430 wwi
->format
.wBitsPerSample
, wwi
->format
.wf
.nAvgBytesPerSec
,
3431 wwi
->format
.wf
.nSamplesPerSec
, wwi
->format
.wf
.nChannels
,
3432 wwi
->format
.wf
.nBlockAlign
);
3434 OSS_InitRingMessage(&wwi
->msgRing
);
3436 wwi
->hStartUpEvent
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
3437 wwi
->hThread
= CreateThread(NULL
, 0, widRecorder
, (LPVOID
)(DWORD
)wDevID
, 0, &(wwi
->dwThreadID
));
3438 WaitForSingleObject(wwi
->hStartUpEvent
, INFINITE
);
3439 CloseHandle(wwi
->hStartUpEvent
);
3440 wwi
->hStartUpEvent
= INVALID_HANDLE_VALUE
;
3442 return widNotifyClient(wwi
, WIM_OPEN
, 0L, 0L);
3445 /**************************************************************************
3446 * widClose [internal]
3448 static DWORD
widClose(WORD wDevID
)
3452 TRACE("(%u);\n", wDevID
);
3453 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3454 WARN("can't close !\n");
3455 return MMSYSERR_INVALHANDLE
;
3458 wwi
= &WInDev
[wDevID
];
3460 if (wwi
->lpQueuePtr
!= NULL
) {
3461 WARN("still buffers open !\n");
3462 return WAVERR_STILLPLAYING
;
3465 OSS_AddRingMessage(&wwi
->msgRing
, WINE_WM_CLOSING
, 0, TRUE
);
3466 OSS_CloseDevice(wwi
->ossdev
);
3467 wwi
->state
= WINE_WS_CLOSED
;
3468 wwi
->dwFragmentSize
= 0;
3469 OSS_DestroyRingMessage(&wwi
->msgRing
);
3470 return widNotifyClient(wwi
, WIM_CLOSE
, 0L, 0L);
3473 /**************************************************************************
3474 * widAddBuffer [internal]
3476 static DWORD
widAddBuffer(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
3478 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
3480 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3481 WARN("can't do it !\n");
3482 return MMSYSERR_INVALHANDLE
;
3484 if (!(lpWaveHdr
->dwFlags
& WHDR_PREPARED
)) {
3485 TRACE("never been prepared !\n");
3486 return WAVERR_UNPREPARED
;
3488 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
) {
3489 TRACE("header already in use !\n");
3490 return WAVERR_STILLPLAYING
;
3493 lpWaveHdr
->dwFlags
|= WHDR_INQUEUE
;
3494 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
3495 lpWaveHdr
->dwBytesRecorded
= 0;
3496 lpWaveHdr
->lpNext
= NULL
;
3498 OSS_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_HEADER
, (DWORD
)lpWaveHdr
, FALSE
);
3499 return MMSYSERR_NOERROR
;
3502 /**************************************************************************
3503 * widPrepare [internal]
3505 static DWORD
widPrepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
3507 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
3509 if (wDevID
>= numInDev
) return MMSYSERR_INVALHANDLE
;
3511 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
3512 return WAVERR_STILLPLAYING
;
3514 lpWaveHdr
->dwFlags
|= WHDR_PREPARED
;
3515 lpWaveHdr
->dwFlags
&= ~WHDR_DONE
;
3516 lpWaveHdr
->dwBytesRecorded
= 0;
3517 TRACE("header prepared !\n");
3518 return MMSYSERR_NOERROR
;
3521 /**************************************************************************
3522 * widUnprepare [internal]
3524 static DWORD
widUnprepare(WORD wDevID
, LPWAVEHDR lpWaveHdr
, DWORD dwSize
)
3526 TRACE("(%u, %p, %08lX);\n", wDevID
, lpWaveHdr
, dwSize
);
3527 if (wDevID
>= numInDev
) return MMSYSERR_INVALHANDLE
;
3529 if (lpWaveHdr
->dwFlags
& WHDR_INQUEUE
)
3530 return WAVERR_STILLPLAYING
;
3532 lpWaveHdr
->dwFlags
&= ~WHDR_PREPARED
;
3533 lpWaveHdr
->dwFlags
|= WHDR_DONE
;
3535 return MMSYSERR_NOERROR
;
3538 /**************************************************************************
3539 * widStart [internal]
3541 static DWORD
widStart(WORD wDevID
)
3543 TRACE("(%u);\n", wDevID
);
3544 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3545 WARN("can't start recording !\n");
3546 return MMSYSERR_INVALHANDLE
;
3549 OSS_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STARTING
, 0, TRUE
);
3550 return MMSYSERR_NOERROR
;
3553 /**************************************************************************
3554 * widStop [internal]
3556 static DWORD
widStop(WORD wDevID
)
3558 TRACE("(%u);\n", wDevID
);
3559 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3560 WARN("can't stop !\n");
3561 return MMSYSERR_INVALHANDLE
;
3564 OSS_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_STOPPING
, 0, TRUE
);
3566 return MMSYSERR_NOERROR
;
3569 /**************************************************************************
3570 * widReset [internal]
3572 static DWORD
widReset(WORD wDevID
)
3574 TRACE("(%u);\n", wDevID
);
3575 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3576 WARN("can't reset !\n");
3577 return MMSYSERR_INVALHANDLE
;
3579 OSS_AddRingMessage(&WInDev
[wDevID
].msgRing
, WINE_WM_RESETTING
, 0, TRUE
);
3580 return MMSYSERR_NOERROR
;
3583 /**************************************************************************
3584 * widGetPosition [internal]
3586 static DWORD
widGetPosition(WORD wDevID
, LPMMTIME lpTime
, DWORD uSize
)
3591 TRACE("(%u, %p, %lu);\n", wDevID
, lpTime
, uSize
);
3593 if (wDevID
>= numInDev
|| WInDev
[wDevID
].state
== WINE_WS_CLOSED
) {
3594 WARN("can't get pos !\n");
3595 return MMSYSERR_INVALHANDLE
;
3597 if (lpTime
== NULL
) return MMSYSERR_INVALPARAM
;
3599 wwi
= &WInDev
[wDevID
];
3601 TRACE("wType=%04X !\n", lpTime
->wType
);
3602 TRACE("wBitsPerSample=%u\n", wwi
->format
.wBitsPerSample
);
3603 TRACE("nSamplesPerSec=%lu\n", wwi
->format
.wf
.nSamplesPerSec
);
3604 TRACE("nChannels=%u\n", wwi
->format
.wf
.nChannels
);
3605 TRACE("nAvgBytesPerSec=%lu\n", wwi
->format
.wf
.nAvgBytesPerSec
);
3606 TRACE("dwTotalRecorded=%lu\n",wwi
->dwTotalRecorded
);
3607 switch (lpTime
->wType
) {
3609 lpTime
->u
.cb
= wwi
->dwTotalRecorded
;
3610 TRACE("TIME_BYTES=%lu\n", lpTime
->u
.cb
);
3613 lpTime
->u
.sample
= wwi
->dwTotalRecorded
* 8 /
3614 wwi
->format
.wBitsPerSample
/ wwi
->format
.wf
.nChannels
;
3615 TRACE("TIME_SAMPLES=%lu\n", lpTime
->u
.sample
);
3618 time
= wwi
->dwTotalRecorded
/
3619 (wwi
->format
.wf
.nAvgBytesPerSec
/ 1000);
3620 lpTime
->u
.smpte
.hour
= time
/ (60 * 60 * 1000);
3621 time
-= lpTime
->u
.smpte
.hour
* (60 * 60 * 1000);
3622 lpTime
->u
.smpte
.min
= time
/ (60 * 1000);
3623 time
-= lpTime
->u
.smpte
.min
* (60 * 1000);
3624 lpTime
->u
.smpte
.sec
= time
/ 1000;
3625 time
-= lpTime
->u
.smpte
.sec
* 1000;
3626 lpTime
->u
.smpte
.frame
= time
* 30 / 1000;
3627 lpTime
->u
.smpte
.fps
= 30;
3628 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
3629 lpTime
->u
.smpte
.hour
, lpTime
->u
.smpte
.min
,
3630 lpTime
->u
.smpte
.sec
, lpTime
->u
.smpte
.frame
);
3633 FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime
->wType
);
3634 lpTime
->wType
= TIME_MS
;
3636 lpTime
->u
.ms
= wwi
->dwTotalRecorded
/
3637 (wwi
->format
.wf
.nAvgBytesPerSec
/ 1000);
3638 TRACE("TIME_MS=%lu\n", lpTime
->u
.ms
);
3641 return MMSYSERR_NOERROR
;
3644 /**************************************************************************
3645 * widMessage (WINEOSS.6)
3647 DWORD WINAPI
OSS_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
3648 DWORD dwParam1
, DWORD dwParam2
)
3650 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
3651 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
3658 /* FIXME: Pretend this is supported */
3660 case WIDM_OPEN
: return widOpen (wDevID
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
3661 case WIDM_CLOSE
: return widClose (wDevID
);
3662 case WIDM_ADDBUFFER
: return widAddBuffer (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
3663 case WIDM_PREPARE
: return widPrepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
3664 case WIDM_UNPREPARE
: return widUnprepare (wDevID
, (LPWAVEHDR
)dwParam1
, dwParam2
);
3665 case WIDM_GETDEVCAPS
: return widGetDevCaps (wDevID
, (LPWAVEINCAPSA
)dwParam1
, dwParam2
);
3666 case WIDM_GETNUMDEVS
: return numInDev
;
3667 case WIDM_GETPOS
: return widGetPosition(wDevID
, (LPMMTIME
)dwParam1
, dwParam2
);
3668 case WIDM_RESET
: return widReset (wDevID
);
3669 case WIDM_START
: return widStart (wDevID
);
3670 case WIDM_STOP
: return widStop (wDevID
);
3671 case DRV_QUERYDEVICEINTERFACESIZE
: return wdDevInterfaceSize (wDevID
, (LPDWORD
)dwParam1
);
3672 case DRV_QUERYDEVICEINTERFACE
: return wdDevInterface (wDevID
, (PWCHAR
)dwParam1
, dwParam2
);
3673 case DRV_QUERYDSOUNDIFACE
: return widDsCreate (wDevID
, (PIDSCDRIVER
*)dwParam1
);
3674 case DRV_QUERYDSOUNDDESC
: return widDsDesc (wDevID
, (PDSDRIVERDESC
)dwParam1
);
3675 case DRV_QUERYDSOUNDGUID
: return widDsGuid (wDevID
, (LPGUID
)dwParam1
);
3677 FIXME("unknown message %u!\n", wMsg
);
3679 return MMSYSERR_NOTSUPPORTED
;
3682 /*======================================================================*
3683 * Low level DSOUND capture definitions *
3684 *======================================================================*/
3686 typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl
;
3687 typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl
;
3688 typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl
;
3689 typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl
;
3691 struct IDsCaptureDriverPropertySetImpl
3693 /* IUnknown fields */
3694 ICOM_VFIELD(IDsDriverPropertySet
);
3697 IDsCaptureDriverBufferImpl
* capture_buffer
;
3700 struct IDsCaptureDriverNotifyImpl
3702 /* IUnknown fields */
3703 ICOM_VFIELD(IDsDriverNotify
);
3706 /* IDsDriverNotifyImpl fields */
3707 LPDSBPOSITIONNOTIFY notifies
;
3710 IDsCaptureDriverBufferImpl
* capture_buffer
;
3713 struct IDsCaptureDriverImpl
3715 /* IUnknown fields */
3716 ICOM_VFIELD(IDsCaptureDriver
);
3719 /* IDsCaptureDriverImpl fields */
3721 IDsCaptureDriverBufferImpl
* capture_buffer
;
3724 struct IDsCaptureDriverBufferImpl
3726 /* IUnknown fields */
3727 ICOM_VFIELD(IDsCaptureDriverBuffer
);
3730 /* IDsCaptureDriverBufferImpl fields */
3731 IDsCaptureDriverImpl
* drv
;
3738 /* IDsDriverNotifyImpl fields */
3739 IDsCaptureDriverNotifyImpl
* notify
;
3742 /* IDsDriverPropertySetImpl fields */
3743 IDsCaptureDriverPropertySetImpl
* property_set
;
3746 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Create(
3747 IDsCaptureDriverBufferImpl
* dscdb
,
3748 IDsCaptureDriverPropertySetImpl
**pdscdps
);
3750 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_Create(
3751 IDsCaptureDriverBufferImpl
* dsdcb
,
3752 IDsCaptureDriverNotifyImpl
**pdscdn
);
3754 /*======================================================================*
3755 * Low level DSOUND capture property set implementation *
3756 *======================================================================*/
3758 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_QueryInterface(
3759 PIDSDRIVERPROPERTYSET iface
,
3763 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3764 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
3766 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
3767 IsEqualGUID(riid
, &IID_IDsDriverPropertySet
) ) {
3768 IDsDriverPropertySet_AddRef(iface
);
3769 *ppobj
= (LPVOID
)This
;
3773 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
3776 return E_NOINTERFACE
;
3779 static ULONG WINAPI
IDsCaptureDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface
)
3781 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3783 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
3785 ref
= InterlockedIncrement(&(This
->ref
));
3789 static ULONG WINAPI
IDsCaptureDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface
)
3791 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3793 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
3795 ref
= InterlockedDecrement(&(This
->ref
));
3797 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER
)This
->capture_buffer
);
3798 HeapFree(GetProcessHeap(),0,This
);
3799 TRACE("(%p) released\n",This
);
3804 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Get(
3805 PIDSDRIVERPROPERTYSET iface
,
3806 PDSPROPERTY pDsProperty
,
3807 LPVOID pPropertyParams
,
3808 ULONG cbPropertyParams
,
3809 LPVOID pPropertyData
,
3810 ULONG cbPropertyData
,
3811 PULONG pcbReturnedData
)
3813 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3814 FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This
,pDsProperty
,pPropertyParams
,cbPropertyParams
,pPropertyData
,cbPropertyData
,pcbReturnedData
);
3815 return DSERR_UNSUPPORTED
;
3818 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Set(
3819 PIDSDRIVERPROPERTYSET iface
,
3820 PDSPROPERTY pDsProperty
,
3821 LPVOID pPropertyParams
,
3822 ULONG cbPropertyParams
,
3823 LPVOID pPropertyData
,
3824 ULONG cbPropertyData
)
3826 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3827 FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This
,pDsProperty
,pPropertyParams
,cbPropertyParams
,pPropertyData
,cbPropertyData
);
3828 return DSERR_UNSUPPORTED
;
3831 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_QuerySupport(
3832 PIDSDRIVERPROPERTYSET iface
,
3833 REFGUID PropertySetId
,
3837 ICOM_THIS(IDsCaptureDriverPropertySetImpl
,iface
);
3838 FIXME("(%p,%s,%lx,%p)\n",This
,debugstr_guid(PropertySetId
),PropertyId
,pSupport
);
3839 return DSERR_UNSUPPORTED
;
3842 ICOM_VTABLE(IDsDriverPropertySet
) dscdpsvt
=
3844 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3845 IDsCaptureDriverPropertySetImpl_QueryInterface
,
3846 IDsCaptureDriverPropertySetImpl_AddRef
,
3847 IDsCaptureDriverPropertySetImpl_Release
,
3848 IDsCaptureDriverPropertySetImpl_Get
,
3849 IDsCaptureDriverPropertySetImpl_Set
,
3850 IDsCaptureDriverPropertySetImpl_QuerySupport
,
3853 /*======================================================================*
3854 * Low level DSOUND capture notify implementation *
3855 *======================================================================*/
3857 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_QueryInterface(
3858 PIDSDRIVERNOTIFY iface
,
3862 ICOM_THIS(IDsCaptureDriverNotifyImpl
,iface
);
3863 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
3865 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
3866 IsEqualGUID(riid
, &IID_IDsDriverNotify
) ) {
3867 IDsDriverNotify_AddRef(iface
);
3872 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
3875 return E_NOINTERFACE
;
3878 static ULONG WINAPI
IDsCaptureDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface
)
3880 ICOM_THIS(IDsCaptureDriverNotifyImpl
,iface
);
3882 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
3884 ref
= InterlockedIncrement(&(This
->ref
));
3888 static ULONG WINAPI
IDsCaptureDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface
)
3890 ICOM_THIS(IDsCaptureDriverNotifyImpl
,iface
);
3892 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
3894 ref
= InterlockedDecrement(&(This
->ref
));
3896 IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER
)This
->capture_buffer
);
3897 if (This
->notifies
!= NULL
)
3898 HeapFree(GetProcessHeap(), 0, This
->notifies
);
3900 HeapFree(GetProcessHeap(),0,This
);
3901 TRACE("(%p) released\n",This
);
3907 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_SetNotificationPositions(
3908 PIDSDRIVERNOTIFY iface
,
3910 LPCDSBPOSITIONNOTIFY notify
)
3912 ICOM_THIS(IDsCaptureDriverNotifyImpl
,iface
);
3913 TRACE("(%p,0x%08lx,%p)\n",This
,howmuch
,notify
);
3916 WARN("invalid parameter\n");
3917 return DSERR_INVALIDPARAM
;
3920 if (TRACE_ON(wave
)) {
3922 for (i
=0;i
<howmuch
;i
++)
3923 TRACE("notify at %ld to 0x%08lx\n",
3924 notify
[i
].dwOffset
,(DWORD
)notify
[i
].hEventNotify
);
3927 /* Make an internal copy of the caller-supplied array.
3928 * Replace the existing copy if one is already present. */
3930 This
->notifies
= HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
3931 This
->notifies
, howmuch
* sizeof(DSBPOSITIONNOTIFY
));
3933 This
->notifies
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
3934 howmuch
* sizeof(DSBPOSITIONNOTIFY
));
3936 memcpy(This
->notifies
, notify
, howmuch
* sizeof(DSBPOSITIONNOTIFY
));
3937 This
->nrofnotifies
= howmuch
;
3942 ICOM_VTABLE(IDsDriverNotify
) dscdnvt
=
3944 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3945 IDsCaptureDriverNotifyImpl_QueryInterface
,
3946 IDsCaptureDriverNotifyImpl_AddRef
,
3947 IDsCaptureDriverNotifyImpl_Release
,
3948 IDsCaptureDriverNotifyImpl_SetNotificationPositions
,
3951 /*======================================================================*
3952 * Low level DSOUND capture implementation *
3953 *======================================================================*/
3955 static HRESULT
DSCDB_MapBuffer(IDsCaptureDriverBufferImpl
*dscdb
)
3957 if (!dscdb
->mapping
) {
3958 dscdb
->mapping
= mmap(NULL
, dscdb
->maplen
, PROT_READ
, MAP_SHARED
,
3959 WInDev
[dscdb
->drv
->wDevID
].ossdev
->fd
, 0);
3960 if (dscdb
->mapping
== (LPBYTE
)-1) {
3961 TRACE("(%p): Could not map sound device for direct access (%s)\n", dscdb
, strerror(errno
));
3962 return DSERR_GENERIC
;
3964 TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dscdb
, dscdb
->mapping
, dscdb
->maplen
);
3969 static HRESULT
DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl
*dscdb
)
3971 if (dscdb
->mapping
) {
3972 if (munmap(dscdb
->mapping
, dscdb
->maplen
) < 0) {
3973 ERR("(%p): Could not unmap sound device (%s)\n", dscdb
, strerror(errno
));
3974 return DSERR_GENERIC
;
3976 dscdb
->mapping
= NULL
;
3977 TRACE("(%p): sound device unmapped\n", dscdb
);
3982 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_QueryInterface(PIDSCDRIVERBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
3984 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
3985 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
3987 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
3988 IsEqualGUID(riid
, &IID_IDsCaptureDriverBuffer
) ) {
3989 IDsCaptureDriverBuffer_AddRef(iface
);
3990 *ppobj
= (LPVOID
)This
;
3994 if ( IsEqualGUID( &IID_IDsDriverNotify
, riid
) ) {
3996 IDsCaptureDriverNotifyImpl_Create(This
, &(This
->notify
));
3998 IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY
)This
->notify
);
3999 *ppobj
= (LPVOID
)This
->notify
;
4006 if ( IsEqualGUID( &IID_IDsDriverPropertySet
, riid
) ) {
4007 if (!This
->property_set
)
4008 IDsCaptureDriverPropertySetImpl_Create(This
, &(This
->property_set
));
4009 if (This
->property_set
) {
4010 IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET
)This
->property_set
);
4011 *ppobj
= (LPVOID
)This
->property_set
;
4018 FIXME("(%p,%s,%p) unsupported GUID\n", This
, debugstr_guid(riid
), ppobj
);
4022 return DSERR_UNSUPPORTED
;
4025 static ULONG WINAPI
IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface
)
4027 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4032 static ULONG WINAPI
IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface
)
4034 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4037 DSCDB_UnmapBuffer(This
);
4039 IDsDriverNotify_Release((PIDSDRIVERNOTIFY
)This
->notify
);
4040 if (This
->property_set
)
4041 IDsDriverPropertySet_Release((PIDSDRIVERPROPERTYSET
)This
->property_set
);
4042 HeapFree(GetProcessHeap(),0,This
);
4046 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Lock(PIDSCDRIVERBUFFER iface
,
4047 LPVOID
*ppvAudio1
,LPDWORD pdwLen1
,
4048 LPVOID
*ppvAudio2
,LPDWORD pdwLen2
,
4049 DWORD dwWritePosition
,DWORD dwWriteLen
,
4052 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4053 FIXME("(%p,%p,%p,%p,%p,%ld,%ld,0x%08lx): stub!\n",This
,ppvAudio1
,pdwLen1
,ppvAudio2
,pdwLen2
,
4054 dwWritePosition
,dwWriteLen
,dwFlags
);
4058 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Unlock(PIDSCDRIVERBUFFER iface
,
4059 LPVOID pvAudio1
,DWORD dwLen1
,
4060 LPVOID pvAudio2
,DWORD dwLen2
)
4062 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4063 FIXME("(%p,%p,%ld,%p,%ld): stub!\n",This
,pvAudio1
,dwLen1
,pvAudio2
,dwLen2
);
4067 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER iface
,
4068 LPDWORD lpdwCapture
,
4071 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4074 TRACE("(%p,%p,%p)\n",This
,lpdwCapture
,lpdwRead
);
4076 if (WInDev
[This
->drv
->wDevID
].state
== WINE_WS_CLOSED
) {
4077 ERR("device not open, but accessing?\n");
4078 return DSERR_UNINITIALIZED
;
4080 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
->fd
, SNDCTL_DSP_GETIPTR
, &info
) < 0) {
4081 ERR("ioctl(%s, SNDCTL_DSP_GETIPTR) failed (%s)\n", WInDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
4082 return DSERR_GENERIC
;
4084 ptr
= info
.ptr
& ~3; /* align the pointer, just in case */
4085 if (lpdwCapture
) *lpdwCapture
= ptr
;
4087 /* add some safety margin (not strictly necessary, but...) */
4088 if (WInDev
[This
->drv
->wDevID
].ossdev
->in_caps_support
& WAVECAPS_SAMPLEACCURATE
)
4089 *lpdwRead
= ptr
+ 32;
4091 *lpdwRead
= ptr
+ WInDev
[This
->drv
->wDevID
].dwFragmentSize
;
4092 while (*lpdwRead
> This
->buflen
)
4093 *lpdwRead
-= This
->buflen
;
4095 TRACE("capturepos=%ld, readpos=%ld\n", lpdwCapture
?*lpdwCapture
:0, lpdwRead
?*lpdwRead
:0);
4099 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_GetStatus(PIDSCDRIVERBUFFER iface
, LPDWORD lpdwStatus
)
4101 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4102 FIXME("(%p,%p): stub!\n",This
,lpdwStatus
);
4103 return DSERR_UNSUPPORTED
;
4106 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Start(PIDSCDRIVERBUFFER iface
, DWORD dwFlags
)
4108 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4110 TRACE("(%p,%lx)\n",This
,dwFlags
);
4111 WInDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
= TRUE
;
4112 enable
= getEnables(WInDev
[This
->drv
->wDevID
].ossdev
);
4113 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
4114 if (errno
== EINVAL
) {
4115 /* Don't give up yet. OSS trigger support is inconsistent. */
4116 if (WInDev
[This
->drv
->wDevID
].ossdev
->open_count
== 1) {
4117 /* try the opposite output enable */
4118 if (WInDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
== FALSE
)
4119 WInDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
= TRUE
;
4121 WInDev
[This
->drv
->wDevID
].ossdev
->bOutputEnabled
= FALSE
;
4123 enable
= getEnables(WInDev
[This
->drv
->wDevID
].ossdev
);
4124 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) >= 0)
4128 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
4129 WInDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
= FALSE
;
4130 return DSERR_GENERIC
;
4135 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface
)
4137 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4139 TRACE("(%p)\n",This
);
4140 /* no more captureing */
4141 WInDev
[This
->drv
->wDevID
].ossdev
->bInputEnabled
= FALSE
;
4142 enable
= getEnables(WInDev
[This
->drv
->wDevID
].ossdev
);
4143 if (ioctl(WInDev
[This
->drv
->wDevID
].ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
4144 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev
[This
->drv
->wDevID
].ossdev
->dev_name
, strerror(errno
));
4145 return DSERR_GENERIC
;
4148 /* Most OSS drivers just can't stop capturing without closing the device...
4149 * so we need to somehow signal to our DirectSound implementation
4150 * that it should completely recreate this HW buffer...
4151 * this unexpected error code should do the trick... */
4152 return DSERR_BUFFERLOST
;
4155 static HRESULT WINAPI
IDsCaptureDriverBufferImpl_SetFormat(PIDSCDRIVERBUFFER iface
, LPWAVEFORMATEX pwfx
)
4157 ICOM_THIS(IDsCaptureDriverBufferImpl
,iface
);
4158 FIXME("(%p): stub!\n",This
);
4159 return DSERR_UNSUPPORTED
;
4162 static ICOM_VTABLE(IDsCaptureDriverBuffer
) dscdbvt
=
4164 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4165 IDsCaptureDriverBufferImpl_QueryInterface
,
4166 IDsCaptureDriverBufferImpl_AddRef
,
4167 IDsCaptureDriverBufferImpl_Release
,
4168 IDsCaptureDriverBufferImpl_Lock
,
4169 IDsCaptureDriverBufferImpl_Unlock
,
4170 IDsCaptureDriverBufferImpl_SetFormat
,
4171 IDsCaptureDriverBufferImpl_GetPosition
,
4172 IDsCaptureDriverBufferImpl_GetStatus
,
4173 IDsCaptureDriverBufferImpl_Start
,
4174 IDsCaptureDriverBufferImpl_Stop
4177 static HRESULT WINAPI
IDsCaptureDriverImpl_QueryInterface(PIDSCDRIVER iface
, REFIID riid
, LPVOID
*ppobj
)
4179 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4180 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
4182 if ( IsEqualGUID(riid
, &IID_IUnknown
) ||
4183 IsEqualGUID(riid
, &IID_IDsCaptureDriver
) ) {
4184 IDsCaptureDriver_AddRef(iface
);
4185 *ppobj
= (LPVOID
)This
;
4189 FIXME( "Unknown IID %s\n", debugstr_guid( riid
) );
4193 return E_NOINTERFACE
;
4196 static ULONG WINAPI
IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface
)
4198 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4199 TRACE("(%p)\n",This
);
4201 TRACE("ref=%ld\n",This
->ref
);
4205 static ULONG WINAPI
IDsCaptureDriverImpl_Release(PIDSCDRIVER iface
)
4207 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4208 TRACE("(%p)\n",This
);
4210 TRACE("ref=%ld\n",This
->ref
);
4213 HeapFree(GetProcessHeap(),0,This
);
4218 static HRESULT WINAPI
IDsCaptureDriverImpl_GetDriverDesc(PIDSCDRIVER iface
, PDSDRIVERDESC pDesc
)
4220 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4221 TRACE("(%p,%p)\n",This
,pDesc
);
4224 TRACE("invalid parameter\n");
4225 return DSERR_INVALIDPARAM
;
4228 /* copy version from driver */
4229 memcpy(pDesc
, &(WInDev
[This
->wDevID
].ossdev
->ds_desc
), sizeof(DSDRIVERDESC
));
4231 pDesc
->dwFlags
|= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
|
4232 DSDDESC_USESYSTEMMEMORY
| DSDDESC_DONTNEEDPRIMARYLOCK
|
4233 DSDDESC_DONTNEEDSECONDARYLOCK
;
4234 pDesc
->dnDevNode
= WInDev
[This
->wDevID
].waveDesc
.dnDevNode
;
4236 pDesc
->wReserved
= 0;
4237 pDesc
->ulDeviceNum
= This
->wDevID
;
4238 pDesc
->dwHeapType
= DSDHEAP_NOHEAP
;
4239 pDesc
->pvDirectDrawHeap
= NULL
;
4240 pDesc
->dwMemStartAddress
= 0;
4241 pDesc
->dwMemEndAddress
= 0;
4242 pDesc
->dwMemAllocExtra
= 0;
4243 pDesc
->pvReserved1
= NULL
;
4244 pDesc
->pvReserved2
= NULL
;
4248 static HRESULT WINAPI
IDsCaptureDriverImpl_Open(PIDSCDRIVER iface
)
4250 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4251 TRACE("(%p)\n",This
);
4255 static HRESULT WINAPI
IDsCaptureDriverImpl_Close(PIDSCDRIVER iface
)
4257 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4258 TRACE("(%p)\n",This
);
4259 if (This
->capture_buffer
) {
4260 ERR("problem with DirectSound: capture buffer not released\n");
4261 return DSERR_GENERIC
;
4266 static HRESULT WINAPI
IDsCaptureDriverImpl_GetCaps(PIDSCDRIVER iface
, PDSCDRIVERCAPS pCaps
)
4268 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4269 TRACE("(%p,%p)\n",This
,pCaps
);
4270 memcpy(pCaps
, &(WInDev
[This
->wDevID
].ossdev
->dsc_caps
), sizeof(DSCDRIVERCAPS
));
4274 static HRESULT WINAPI
IDsCaptureDriverImpl_CreateCaptureBuffer(PIDSCDRIVER iface
,
4275 LPWAVEFORMATEX pwfx
,
4277 DWORD dwCardAddress
,
4278 LPDWORD pdwcbBufferSize
,
4282 ICOM_THIS(IDsCaptureDriverImpl
,iface
);
4283 IDsCaptureDriverBufferImpl
** ippdscdb
= (IDsCaptureDriverBufferImpl
**)ppvObj
;
4285 audio_buf_info info
;
4287 TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",This
,pwfx
,dwFlags
,dwCardAddress
,pdwcbBufferSize
,ppbBuffer
,ppvObj
);
4289 if (This
->capture_buffer
) {
4290 TRACE("already allocated\n");
4291 return DSERR_ALLOCATED
;
4294 *ippdscdb
= (IDsCaptureDriverBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsCaptureDriverBufferImpl
));
4295 if (*ippdscdb
== NULL
) {
4296 TRACE("out of memory\n");
4297 return DSERR_OUTOFMEMORY
;
4300 (*ippdscdb
)->lpVtbl
= &dscdbvt
;
4301 (*ippdscdb
)->ref
= 1;
4302 (*ippdscdb
)->drv
= This
;
4303 (*ippdscdb
)->notify
= 0;
4304 (*ippdscdb
)->notify_index
= 0;
4305 (*ippdscdb
)->property_set
= 0;
4307 if (WInDev
[This
->wDevID
].state
== WINE_WS_CLOSED
) {
4310 desc
.lpFormat
= pwfx
;
4311 desc
.dwCallback
= 0;
4312 desc
.dwInstance
= 0;
4313 desc
.uMappedDeviceID
= 0;
4315 err
= widOpen(This
->wDevID
, &desc
, dwFlags
| WAVE_DIRECTSOUND
);
4316 if (err
!= MMSYSERR_NOERROR
) {
4317 TRACE("widOpen failed\n");
4322 /* check how big the DMA buffer is now */
4323 if (ioctl(WInDev
[This
->wDevID
].ossdev
->fd
, SNDCTL_DSP_GETISPACE
, &info
) < 0) {
4324 ERR("ioctl(%s, SNDCTL_DSP_GETISPACE) failed (%s)\n", WInDev
[This
->wDevID
].ossdev
->dev_name
, strerror(errno
));
4325 HeapFree(GetProcessHeap(),0,*ippdscdb
);
4327 return DSERR_GENERIC
;
4329 (*ippdscdb
)->maplen
= (*ippdscdb
)->buflen
= info
.fragstotal
* info
.fragsize
;
4331 /* map the DMA buffer */
4332 err
= DSCDB_MapBuffer(*ippdscdb
);
4334 HeapFree(GetProcessHeap(),0,*ippdscdb
);
4339 /* capture buffer is ready to go */
4340 *pdwcbBufferSize
= (*ippdscdb
)->maplen
;
4341 *ppbBuffer
= (*ippdscdb
)->mapping
;
4343 /* some drivers need some extra nudging after mapping */
4344 WInDev
[This
->wDevID
].ossdev
->bInputEnabled
= FALSE
;
4345 enable
= getEnables(WInDev
[This
->wDevID
].ossdev
);
4346 if (ioctl(WInDev
[This
->wDevID
].ossdev
->fd
, SNDCTL_DSP_SETTRIGGER
, &enable
) < 0) {
4347 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER) failed (%s)\n", WInDev
[This
->wDevID
].ossdev
->dev_name
, strerror(errno
));
4348 return DSERR_GENERIC
;
4351 This
->capture_buffer
= *ippdscdb
;
4356 static ICOM_VTABLE(IDsCaptureDriver
) dscdvt
=
4358 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4359 IDsCaptureDriverImpl_QueryInterface
,
4360 IDsCaptureDriverImpl_AddRef
,
4361 IDsCaptureDriverImpl_Release
,
4362 IDsCaptureDriverImpl_GetDriverDesc
,
4363 IDsCaptureDriverImpl_Open
,
4364 IDsCaptureDriverImpl_Close
,
4365 IDsCaptureDriverImpl_GetCaps
,
4366 IDsCaptureDriverImpl_CreateCaptureBuffer
4369 static HRESULT WINAPI
IDsCaptureDriverPropertySetImpl_Create(
4370 IDsCaptureDriverBufferImpl
* dscdb
,
4371 IDsCaptureDriverPropertySetImpl
**pdscdps
)
4373 IDsCaptureDriverPropertySetImpl
* dscdps
;
4374 TRACE("(%p,%p)\n",dscdb
,pdscdps
);
4376 dscdps
= (IDsCaptureDriverPropertySetImpl
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(dscdps
));
4377 if (dscdps
== NULL
) {
4378 WARN("out of memory\n");
4379 return DSERR_OUTOFMEMORY
;
4383 dscdps
->lpVtbl
= &dscdpsvt
;
4384 dscdps
->capture_buffer
= dscdb
;
4385 dscdb
->property_set
= dscdps
;
4386 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER
)dscdb
);
4392 static HRESULT WINAPI
IDsCaptureDriverNotifyImpl_Create(
4393 IDsCaptureDriverBufferImpl
* dscdb
,
4394 IDsCaptureDriverNotifyImpl
**pdscdn
)
4396 IDsCaptureDriverNotifyImpl
* dscdn
;
4397 TRACE("(%p,%p)\n",dscdb
,pdscdn
);
4399 dscdn
= (IDsCaptureDriverNotifyImpl
*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(dscdn
));
4400 if (dscdn
== NULL
) {
4401 WARN("out of memory\n");
4402 return DSERR_OUTOFMEMORY
;
4406 dscdn
->lpVtbl
= &dscdnvt
;
4407 dscdn
->capture_buffer
= dscdb
;
4408 dscdb
->notify
= dscdn
;
4409 IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER
)dscdb
);
4415 static DWORD
widDsCreate(UINT wDevID
, PIDSCDRIVER
* drv
)
4417 IDsCaptureDriverImpl
** idrv
= (IDsCaptureDriverImpl
**)drv
;
4418 TRACE("(%d,%p)\n",wDevID
,drv
);
4420 /* the HAL isn't much better than the HEL if we can't do mmap() */
4421 if (!(WInDev
[wDevID
].ossdev
->in_caps_support
& WAVECAPS_DIRECTSOUND
)) {
4422 ERR("DirectSoundCapture flag not set\n");
4423 MESSAGE("This sound card's driver does not support direct access\n");
4424 MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
4425 return MMSYSERR_NOTSUPPORTED
;
4428 *idrv
= (IDsCaptureDriverImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDsCaptureDriverImpl
));
4430 return MMSYSERR_NOMEM
;
4431 (*idrv
)->lpVtbl
= &dscdvt
;
4434 (*idrv
)->wDevID
= wDevID
;
4435 (*idrv
)->capture_buffer
= NULL
;
4436 return MMSYSERR_NOERROR
;
4439 static DWORD
widDsDesc(UINT wDevID
, PDSDRIVERDESC desc
)
4441 memcpy(desc
, &(WInDev
[wDevID
].ossdev
->ds_desc
), sizeof(DSDRIVERDESC
));
4442 return MMSYSERR_NOERROR
;
4445 static DWORD
widDsGuid(UINT wDevID
, LPGUID pGuid
)
4447 TRACE("(%d,%p)\n",wDevID
,pGuid
);
4449 memcpy(pGuid
, &(WInDev
[wDevID
].ossdev
->dsc_guid
), sizeof(GUID
));
4451 return MMSYSERR_NOERROR
;
4454 #else /* !HAVE_OSS */
4456 /**************************************************************************
4457 * wodMessage (WINEOSS.7)
4459 DWORD WINAPI
OSS_wodMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
4460 DWORD dwParam1
, DWORD dwParam2
)
4462 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
4463 return MMSYSERR_NOTENABLED
;
4466 /**************************************************************************
4467 * widMessage (WINEOSS.6)
4469 DWORD WINAPI
OSS_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
4470 DWORD dwParam1
, DWORD dwParam2
)
4472 FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
4473 return MMSYSERR_NOTENABLED
;
4476 #endif /* HAVE_OSS */