1 /*
2 * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD)
3 *
4 * Copyright 1994 Martin Ayotte
5 * 1999 Eric Pouech (async playing in waveOut/waveIn)
6 * 2000 Eric Pouech (loops in waveOut)
7 * 2002 Eric Pouech (full duplex)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23 /*
24 * FIXME:
25 * pause in waveOut does not work correctly in loop mode
26 * Direct Sound Capture driver does not work (not complete yet)
27 */
28
29 /* an exact wodGetPosition is usually not worth the extra context switches,
30 * as we're going to have near fragment accuracy anyway */
31 #define EXACT_WODPOSITION
32 #define EXACT_WIDPOSITION
33
34 #include "config.h"
35 #include "wine/port.h"
36
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <errno.h>
45 #include <fcntl.h>
46 #ifdef HAVE_SYS_IOCTL_H
47 # include <sys/ioctl.h>
48 #endif
49 #ifdef HAVE_SYS_MMAN_H
50 # include <sys/mman.h>
51 #endif
52 #ifdef HAVE_POLL_H
53 #include <poll.h>
54 #endif
55 #ifdef HAVE_SYS_POLL_H
56 # include <sys/poll.h>
57 #endif
58
59 #include "windef.h"
60 #include "winbase.h"
61 #include "wingdi.h"
62 #include "winuser.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "mmddk.h"
66 #include "mmreg.h"
67 #include "dsound.h"
68 #include "ks.h"
69 #include "ksguid.h"
70 #include "ksmedia.h"
71 #include "initguid.h"
72 #include "dsdriver.h"
73 #include "oss.h"
74 #include "wine/debug.h"
75
76 #include "audio.h"
77
78 WINE_DEFAULT_DEBUG_CHANNEL(wave);
79
80 /* Allow 1% deviation for sample rates (some ES137x cards) */
81 #define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
82
83 #ifdef HAVE_OSS
84
85 WINE_WAVEOUT WOutDev[MAX_WAVEDRV];
86 WINE_WAVEIN WInDev[MAX_WAVEDRV];
87 unsigned numOutDev;
88 unsigned numInDev;
89
90 /* state diagram for waveOut writing:
91 *
92 * +---------+-------------+---------------+---------------------------------+
93 * | state | function | event | new state |
94 * +---------+-------------+---------------+---------------------------------+
95 * | | open() | | STOPPED |
96 * | PAUSED | write() | | PAUSED |
97 * | STOPPED | write() | <thrd create> | PLAYING |
98 * | PLAYING | write() | HEADER | PLAYING |
99 * | (other) | write() | <error> | |
100 * | (any) | pause() | PAUSING | PAUSED |
101 * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
102 * | (any) | reset() | RESETTING | STOPPED |
103 * | (any) | close() | CLOSING | CLOSED |
104 * +---------+-------------+---------------+---------------------------------+
105 */
106
107 /* These strings used only for tracing */
108 static const char * getCmdString(enum win_wm_message msg)
109 {
110 static char unknown[32];
111 #define MSG_TO_STR(x) case x: return #x
112 switch(msg) {
113 MSG_TO_STR(WINE_WM_PAUSING);
114 MSG_TO_STR(WINE_WM_RESTARTING);
115 MSG_TO_STR(WINE_WM_RESETTING);
116 MSG_TO_STR(WINE_WM_HEADER);
117 MSG_TO_STR(WINE_WM_UPDATE);
118 MSG_TO_STR(WINE_WM_BREAKLOOP);
119 MSG_TO_STR(WINE_WM_CLOSING);
120 MSG_TO_STR(WINE_WM_STARTING);
121 MSG_TO_STR(WINE_WM_STOPPING);
122 }
123 #undef MSG_TO_STR
124 sprintf(unknown, "UNKNOWN(0x%08x)", msg);
125 return unknown;
126 }
127
128 int getEnables(OSS_DEVICE *ossdev)
129 {
130 return ( (ossdev->bOutputEnabled ? PCM_ENABLE_OUTPUT : 0) |
131 (ossdev->bInputEnabled ? PCM_ENABLE_INPUT : 0) );
132 }
133
134 static const char * getMessage(UINT msg)
135 {
136 static char unknown[32];
137 #define MSG_TO_STR(x) case x: return #x
138 switch(msg) {
139 MSG_TO_STR(DRVM_INIT);
140 MSG_TO_STR(DRVM_EXIT);
141 MSG_TO_STR(DRVM_ENABLE);
142 MSG_TO_STR(DRVM_DISABLE);
143 MSG_TO_STR(WIDM_OPEN);
144 MSG_TO_STR(WIDM_CLOSE);
145 MSG_TO_STR(WIDM_ADDBUFFER);
146 MSG_TO_STR(WIDM_PREPARE);
147 MSG_TO_STR(WIDM_UNPREPARE);
148 MSG_TO_STR(WIDM_GETDEVCAPS);
149 MSG_TO_STR(WIDM_GETNUMDEVS);
150 MSG_TO_STR(WIDM_GETPOS);
151 MSG_TO_STR(WIDM_RESET);
152 MSG_TO_STR(WIDM_START);
153 MSG_TO_STR(WIDM_STOP);
154 MSG_TO_STR(WODM_OPEN);
155 MSG_TO_STR(WODM_CLOSE);
156 MSG_TO_STR(WODM_WRITE);
157 MSG_TO_STR(WODM_PAUSE);
158 MSG_TO_STR(WODM_GETPOS);
159 MSG_TO_STR(WODM_BREAKLOOP);
160 MSG_TO_STR(WODM_PREPARE);
161 MSG_TO_STR(WODM_UNPREPARE);
162 MSG_TO_STR(WODM_GETDEVCAPS);
163 MSG_TO_STR(WODM_GETNUMDEVS);
164 MSG_TO_STR(WODM_GETPITCH);
165 MSG_TO_STR(WODM_SETPITCH);
166 MSG_TO_STR(WODM_GETPLAYBACKRATE);
167 MSG_TO_STR(WODM_SETPLAYBACKRATE);
168 MSG_TO_STR(WODM_GETVOLUME);
169 MSG_TO_STR(WODM_SETVOLUME);
170 MSG_TO_STR(WODM_RESTART);
171 MSG_TO_STR(WODM_RESET);
172 MSG_TO_STR(DRV_QUERYDEVICEINTERFACESIZE);
173 MSG_TO_STR(DRV_QUERYDEVICEINTERFACE);
174 MSG_TO_STR(DRV_QUERYDSOUNDIFACE);
175 MSG_TO_STR(DRV_QUERYDSOUNDDESC);
176 }
177 #undef MSG_TO_STR
178 sprintf(unknown, "UNKNOWN(0x%04x)", msg);
179 return unknown;
180 }
181
182 static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
183 {
184 TRACE("(%u, %p)\n", wDevID, dwParam1);
185
186 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].ossdev.interface_name, -1,
187 NULL, 0 ) * sizeof(WCHAR);
188 return MMSYSERR_NOERROR;
189 }
190
191 static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
192 {
193 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].ossdev.interface_name, -1,
194 NULL, 0 ) * sizeof(WCHAR))
195 {
196 MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].ossdev.interface_name, -1,
197 dwParam1, dwParam2 / sizeof(WCHAR));
198 return MMSYSERR_NOERROR;
199 }
200
201 return MMSYSERR_INVALPARAM;
202 }
203
204 static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
205 {
206 TRACE("(%u, %p)\n", wDevID, dwParam1);
207
208 *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].ossdev.interface_name, -1,
209 NULL, 0 ) * sizeof(WCHAR);
210 return MMSYSERR_NOERROR;
211 }
212
213 static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
214 {
215 if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].ossdev.interface_name, -1,
216 NULL, 0 ) * sizeof(WCHAR))
217 {
218 MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].ossdev.interface_name, -1,
219 dwParam1, dwParam2 / sizeof(WCHAR));
220 return MMSYSERR_NOERROR;
221 }
222
223 return MMSYSERR_INVALPARAM;
224 }
225
226 static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
227 WAVEFORMATPCMEX* format)
228 {
229 TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%u nChannels=%u nAvgBytesPerSec=%u\n",
230 lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
231 format->Format.nChannels, format->Format.nAvgBytesPerSec);
232 TRACE("Position in bytes=%u\n", position);
233
234 switch (lpTime->wType) {
235 case TIME_SAMPLES:
236 lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
237 TRACE("TIME_SAMPLES=%u\n", lpTime->u.sample);
238 break;
239 case TIME_MS:
240 lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
241 TRACE("TIME_MS=%u\n", lpTime->u.ms);
242 break;
243 case TIME_SMPTE:
244 lpTime->u.smpte.fps = 30;
245 position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
246 position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
247 lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
248 position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
249 lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
250 lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
251 lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
252 lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
253 lpTime->u.smpte.fps = 30;
254 lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
255 TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
256 lpTime->u.smpte.hour, lpTime->u.smpte.min,
257 lpTime->u.smpte.sec, lpTime->u.smpte.frame);
258 break;
259 default:
260 WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
261 lpTime->wType = TIME_BYTES;
262 /* fall through */
263 case TIME_BYTES:
264 lpTime->u.cb = position;
265 TRACE("TIME_BYTES=%u\n", lpTime->u.cb);
266 break;
267 }
268 return MMSYSERR_NOERROR;
269 }
270
271 static BOOL supportedFormat(LPWAVEFORMATEX wf)
272 {
273 TRACE("(%p)\n",wf);
274
275 if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
276 return FALSE;
277
278 if (wf->wFormatTag == WAVE_FORMAT_PCM) {
279 if (wf->nChannels >= 1 && wf->nChannels <= MAX_CHANNELS) {
280 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
281 return TRUE;
282 }
283 } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
284 WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
285
286 if (wf->cbSize == 22 && IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
287 if (wf->nChannels >=1 && wf->nChannels <= MAX_CHANNELS) {
288 if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
289 if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
290 return TRUE;
291 } else
292 WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
293 }
294 } else
295 WARN("only KSDATAFORMAT_SUBTYPE_PCM supported\n");
296 } else
297 WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
298
299 return FALSE;
300 }
301
302 void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
303 {
304 ZeroMemory(wf2, sizeof(wf2));
305 if (wf1->wFormatTag == WAVE_FORMAT_PCM)
306 memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
307 else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
308 memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
309 else
310 memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
311 }
312
313 /*======================================================================*
314 * Low level WAVE implementation *
315 *======================================================================*/
316
317 /******************************************************************
318 * OSS_RawOpenDevice
319 *
320 * Low level device opening (from values stored in ossdev)
321 */
322 static DWORD OSS_RawOpenDevice(OSS_DEVICE* ossdev, int strict_format)
323 {
324 int fd, val, rc;
325 TRACE("(%p,%d)\n",ossdev,strict_format);
326
327 TRACE("open_access=%s\n",
328 ossdev->open_access == O_RDONLY ? "O_RDONLY" :
329 ossdev->open_access == O_WRONLY ? "O_WRONLY" :
330 ossdev->open_access == O_RDWR ? "O_RDWR" : "Unknown");
331
332 if ((fd = open(ossdev->dev_name, ossdev->open_access|O_NDELAY, 0)) == -1)
333 {
334 WARN("Couldn't open %s (%s)\n", ossdev->dev_name, strerror(errno));
335 return (errno == EBUSY) ? MMSYSERR_ALLOCATED : MMSYSERR_ERROR;
336 }
337 fcntl(fd, F_SETFD, 1); /* set close on exec flag */
338 /* turn full duplex on if it has been requested */
339 if (ossdev->open_access == O_RDWR && ossdev->full_duplex) {
340 rc = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
341 /* on *BSD, as full duplex is always enabled by default, this ioctl
342 * will fail with EINVAL
343 * so, we don't consider EINVAL an error here
344 */
345 if (rc != 0 && errno != EINVAL) {
346 WARN("ioctl(%s, SNDCTL_DSP_SETDUPLEX) failed (%s)\n", ossdev->dev_name, strerror(errno));
347 goto error2;
348 }
349 }
350
351 if (ossdev->audio_fragment) {
352 rc = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossdev->audio_fragment);
353 if (rc != 0) {
354 ERR("ioctl(%s, SNDCTL_DSP_SETFRAGMENT) failed (%s)\n", ossdev->dev_name, strerror(errno));
355 goto error2;
356 }
357 }
358
359 /* First size and channels then samplerate */
360 if (ossdev->format>=0)
361 {
362 val = ossdev->format;
363 rc = ioctl(fd, SNDCTL_DSP_SETFMT, &ossdev->format);
364 if (rc != 0 || val != ossdev->format) {
365 TRACE("Can't set format to %d (returned %d)\n", val, ossdev->format);
366 if (strict_format)
367 goto error;
368 }
369 }
370 if (ossdev->channels>=0)
371 {
372 val = ossdev->channels;
373 rc = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossdev->channels);
374 if (rc != 0 || val != ossdev->channels) {
375 TRACE("Can't set channels to %u (returned %d)\n", val, ossdev->channels);
376 if (strict_format)
377 goto error;
378 }
379 }
380 if (ossdev->sample_rate>=0)
381 {
382 val = ossdev->sample_rate;
383 rc = ioctl(fd, SNDCTL_DSP_SPEED, &ossdev->sample_rate);
384 if (rc != 0 || !NEAR_MATCH(val, ossdev->sample_rate)) {
385 TRACE("Can't set sample_rate to %u (returned %d)\n", val, ossdev->sample_rate);
386 if (strict_format)
387 goto error;
388 }
389 }
390 ossdev->fd = fd;
391
392 ossdev->bOutputEnabled = TRUE; /* OSS enables by default */
393 ossdev->bInputEnabled = TRUE; /* OSS enables by default */
394 if (ossdev->open_access == O_RDONLY)
395 ossdev->bOutputEnabled = FALSE;
396 if (ossdev->open_access == O_WRONLY)
397 ossdev->bInputEnabled = FALSE;
398
399 if (ossdev->bTriggerSupport) {
400 int trigger;
401 trigger = getEnables(ossdev);
402 /* If we do not have full duplex, but they opened RDWR
403 ** (as you have to in order for an mmap to succeed)
404 ** then we start out with input off
405 */
406 if (ossdev->open_access == O_RDWR && !ossdev->full_duplex &&
407 ossdev->bInputEnabled && ossdev->bOutputEnabled) {
408 ossdev->bInputEnabled = FALSE;
409 trigger &= ~PCM_ENABLE_INPUT;
410 ioctl(fd, SNDCTL_DSP_SETTRIGGER, &trigger);
411 }
412 }
413
414 return MMSYSERR_NOERROR;
415
416 error:
417 close(fd);
418 return WAVERR_BADFORMAT;
419 error2:
420 close(fd);
421 return MMSYSERR_ERROR;
422 }
423
424 /******************************************************************
425 * OSS_OpenDevice
426 *
427 * since OSS has poor capabilities in full duplex, we try here to let a program
428 * open the device for both waveout and wavein streams...
429 * this is hackish, but it's the way OSS interface is done...
430 */
431 DWORD OSS_OpenDevice(OSS_DEVICE* ossdev, unsigned req_access,
432 int* frag, int strict_format,
433 int sample_rate, int channels, int fmt)
434 {
435 DWORD ret;
436 DWORD open_access;
437 TRACE("(%p,%u,%p,%d,%d,%d,%x)\n",ossdev,req_access,frag,strict_format,sample_rate,channels,fmt);
438
439 if (ossdev->full_duplex && (req_access == O_RDONLY || req_access == O_WRONLY))
440 {
441 TRACE("Opening RDWR because full_duplex=%d and req_access=%d\n",
442 ossdev->full_duplex,req_access);
443 open_access = O_RDWR;
444 }
445 else
446 {
447 open_access=req_access;
448 }
449
450 /* FIXME: this should be protected, and it also contains a race with OSS_CloseDevice */
451 if (ossdev->open_count == 0)
452 {
453 if (access(ossdev->dev_name, 0) != 0) return MMSYSERR_NODRIVER;
454
455 ossdev->audio_fragment = (frag) ? *frag : 0;
456 ossdev->sample_rate = sample_rate;
457 ossdev->channels = channels;
458 ossdev->format = fmt;
459 ossdev->open_access = open_access;
460 ossdev->owner_tid = GetCurrentThreadId();
461
462 if ((ret = OSS_RawOpenDevice(ossdev,strict_format)) != MMSYSERR_NOERROR) return ret;
463 if (ossdev->full_duplex && ossdev->bTriggerSupport &&
464 (req_access == O_RDONLY || req_access == O_WRONLY))
465 {
466 int enable;
467 if (req_access == O_WRONLY)
468 ossdev->bInputEnabled=0;
469 else
470 ossdev->bOutputEnabled=0;
471 enable = getEnables(ossdev);
472 TRACE("Calling SNDCTL_DSP_SETTRIGGER with %x\n",enable);
473 if (ioctl(ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
474 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER, %d) failed (%s)\n",ossdev->dev_name, enable, strerror(errno));
475 }
476 }
477 else
478 {
479 /* check we really open with the same parameters */
480 if (ossdev->open_access != open_access)
481 {
482 ERR("FullDuplex: Mismatch in access. Your sound device is not full duplex capable.\n");
483 return WAVERR_BADFORMAT;
484 }
485
486 /* check if the audio parameters are the same */
487 if (ossdev->sample_rate != sample_rate ||
488 ossdev->channels != channels ||
489 ossdev->format != fmt)
490 {
491 /* This is not a fatal error because MSACM might do the remapping */
492 WARN("FullDuplex: mismatch in PCM parameters for input and output\n"
493 "OSS doesn't allow us different parameters\n"
494 "audio_frag(%x/%x) sample_rate(%d/%d) channels(%d/%d) fmt(%d/%d)\n",
495 ossdev->audio_fragment, frag ? *frag : 0,
496 ossdev->sample_rate, sample_rate,
497 ossdev->channels, channels,
498 ossdev->format, fmt);
499 return WAVERR_BADFORMAT;
500 }
501 /* check if the fragment sizes are the same */
502 if (ossdev->audio_fragment != (frag ? *frag : 0) )
503 {
504 ERR("FullDuplex: Playback and Capture hardware acceleration levels are different.\n"
505 "Please run winecfg, open \"Audio\" page and set\n"
506 "\"Hardware Acceleration\" to \"Emulation\".\n");
507 return WAVERR_BADFORMAT;
508 }
509 if (GetCurrentThreadId() != ossdev->owner_tid)
510 {
511 WARN("Another thread is trying to access audio...\n");
512 return MMSYSERR_ERROR;
513 }
514 if (ossdev->full_duplex && ossdev->bTriggerSupport &&
515 (req_access == O_RDONLY || req_access == O_WRONLY))
516 {
517 int enable;
518 if (req_access == O_WRONLY)
519 ossdev->bOutputEnabled=1;
520 else
521 ossdev->bInputEnabled=1;
522 enable = getEnables(ossdev);
523 TRACE("Calling SNDCTL_DSP_SETTRIGGER with %x\n",enable);
524 if (ioctl(ossdev->fd, SNDCTL_DSP_SETTRIGGER, &enable) < 0)
525 ERR("ioctl(%s, SNDCTL_DSP_SETTRIGGER, %d) failed (%s)\n",ossdev->dev_name, enable, strerror(errno));
526 }
527 }
528
529 ossdev->open_count++;
530
531 return MMSYSERR_NOERROR;
532 }
533
534 /******************************************************************
535 * OSS_CloseDevice
536 *
537 *
538 */
539 void OSS_CloseDevice(OSS_DEVICE* ossdev)
540 {
541 TRACE("(%p)\n",ossdev);
542 if (ossdev->open_count>0) {
543 ossdev->open_count--;
544 } else {
545 WARN("OSS_CloseDevice called too many times\n");
546 }
547 if (ossdev->open_count == 0)
548 {
549 fcntl(ossdev->fd, F_SETFL, fcntl(ossdev->fd, F_GETFL) & ~O_NDELAY);
550 /* reset the device before we close it in case it is in a bad state */
551 ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
552 if (close(ossdev->fd) != 0) FIXME("Cannot close %d: %s\n", ossdev->fd, strerror(errno));
553 }
554 }
555
556 /******************************************************************
557 * OSS_ResetDevice
558 *
559 * Resets the device. OSS Commercial requires the device to be closed
560 * after a SNDCTL_DSP_RESET ioctl call... this function implements
561 * this behavior...
562 * FIXME: This causes problems when doing full duplex so we really
563 * only reset when not doing full duplex. We need to do this better
564 * someday.
565 */
566 static DWORD OSS_ResetDevice(OSS_DEVICE* ossdev)
567 {
568 DWORD ret = MMSYSERR_NOERROR;
569 int old_fd = ossdev->fd;
570 TRACE("(%p)\n", ossdev);
571
572 if (ossdev->open_count == 1) {
573 if (ioctl(ossdev->fd, SNDCTL_DSP_RESET, NULL) == -1)
574 {
575 perror("ioctl SNDCTL_DSP_RESET");
576 return -1;
577 }
578 close(ossdev->fd);
579 ret = OSS_RawOpenDevice(ossdev, 1);
580 TRACE("Changing fd from %d to %d\n", old_fd, ossdev->fd);
581 } else
582 WARN("Not resetting device because it is in full duplex mode!\n");
583
584 return ret;
585 }
586
587 static const int win_std_oss_fmts[2]={AFMT_U8,AFMT_S16_LE};
588 static const int win_std_rates[5]={96000,48000,44100,22050,11025};
589 static const int win_std_formats[2][2][5]=
590 {{{WAVE_FORMAT_96M08, WAVE_FORMAT_48M08, WAVE_FORMAT_4M08,
591 WAVE_FORMAT_2M08, WAVE_FORMAT_1M08},
592 {WAVE_FORMAT_96S08, WAVE_FORMAT_48S08, WAVE_FORMAT_4S08,
593 WAVE_FORMAT_2S08, WAVE_FORMAT_1S08}},
594 {{WAVE_FORMAT_96M16, WAVE_FORMAT_48M16, WAVE_FORMAT_4M16,
595 WAVE_FORMAT_2M16, WAVE_FORMAT_1M16},
596 {WAVE_FORMAT_96S16, WAVE_FORMAT_48S16, WAVE_FORMAT_4S16,
597 WAVE_FORMAT_2S16, WAVE_FORMAT_1S16}},
598 };
599
600 static void OSS_Info(int fd)
601 {
602 /* Note that this only reports the formats supported by the hardware.
603 * The driver may support other formats and do the conversions in
604 * software which is why we don't use this value
605 */
606 int oss_mask, oss_caps;
607 if (ioctl(fd, SNDCTL_DSP_GETFMTS, &oss_mask) >= 0) {
608 TRACE("Formats=%08x ( ", oss_mask);
609 if (oss_mask & AFMT_MU_LAW) TRACE("AFMT_MU_LAW ");
610 if (oss_mask & AFMT_A_LAW) TRACE("AFMT_A_LAW ");
611 if (oss_mask & AFMT_IMA_ADPCM) TRACE("AFMT_IMA_ADPCM ");
612 if (oss_mask & AFMT_U8) TRACE("AFMT_U8 ");
613 if (oss_mask & AFMT_S16_LE) TRACE("AFMT_S16_LE ");
614 if (oss_mask & AFMT_S16_BE) TRACE("AFMT_S16_BE ");
615 if (oss_mask & AFMT_S8) TRACE("AFMT_S8 ");
616 if (oss_mask & AFMT_U16_LE) TRACE("AFMT_U16_LE ");
617 if (oss_mask & AFMT_U16_BE) TRACE("AFMT_U16_BE ");
618 if (oss_mask & AFMT_MPEG) TRACE("AFMT_MPEG ");
619 #ifdef AFMT_AC3
620 if (oss_mask & AFMT_AC3) TRACE("AFMT_AC3 ");
621 #endif
622 #ifdef AFMT_VORBIS
623 if (oss_mask & AFMT_VORBIS) TRACE("AFMT_VORBIS ");
624 #endif
625 #ifdef AFMT_S32_LE
626 if (oss_mask & AFMT_S32_LE) TRACE("AFMT_S32_LE ");
627 #endif
628 #ifdef AFMT_S32_BE
629 if (oss_mask & AFMT_S32_BE) TRACE("AFMT_S32_BE ");
630 #endif
631 #ifdef AFMT_FLOAT
632 if (oss_mask & AFMT_FLOAT) TRACE("AFMT_FLOAT ");
633 #endif
634 #ifdef AFMT_S24_LE
635 if (oss_mask & AFMT_S24_LE) TRACE("AFMT_S24_LE ");
636 #endif
637 #ifdef AFMT_S24_BE
638 if (oss_mask & AFMT_S24_BE) TRACE("AFMT_S24_BE ");
639 #endif
640 #ifdef AFMT_SPDIF_RAW
641 if (oss_mask & AFMT_SPDIF_RAW) TRACE("AFMT_SPDIF_RAW ");
642 #endif
643 TRACE(")\n");
644 }
645 if (ioctl(fd, SNDCTL_DSP_GETCAPS, &oss_caps) >= 0) {
646 TRACE("Caps=%08x\n",oss_caps);
647 TRACE("\tRevision: %d\n", oss_caps&DSP_CAP_REVISION);
648 TRACE("\tDuplex: %s\n", oss_caps & DSP_CAP_DUPLEX ? "true" : "false");
649 TRACE("\tRealtime: %s\n", oss_caps & DSP_CAP_REALTIME ? "true" : "false");
650 TRACE("\tBatch: %s\n", oss_caps & DSP_CAP_BATCH ? "true" : "false");
651 TRACE("\tCoproc: %s\n", oss_caps & DSP_CAP_COPROC ? "true" : "false");
652 TRACE("\tTrigger: %s\n", oss_caps & DSP_CAP_TRIGGER ? "true" : "false");
653 TRACE("\tMmap: %s\n", oss_caps & DSP_CAP_MMAP ? "true" : "false");
654 #ifdef DSP_CAP_MULTI
655 TRACE("\tMulti: %s\n", oss_caps & DSP_CAP_MULTI ? "true" : "false");
656 #endif
657 #ifdef DSP_CAP_BIND
658 TRACE("\tBind: %s\n", oss_caps & DSP_CAP_BIND ? "true" : "false");
659 #endif
660 #ifdef DSP_CAP_INPUT
661 TRACE("\tInput: %s\n", oss_caps & DSP_CAP_INPUT ? "true" : "false");
662 #endif
663 #ifdef DSP_CAP_OUTPUT
664 TRACE("\tOutput: %s\n", oss_caps & DSP_CAP_OUTPUT ? "true" : "false");
665 #endif
666 #ifdef DSP_CAP_VIRTUAL
667 TRACE("\tVirtual: %s\n", oss_caps & DSP_CAP_VIRTUAL ? "true" : "false");
668 #endif
669 #ifdef DSP_CAP_ANALOGOUT
670 TRACE("\tAnalog Out: %s\n", oss_caps & DSP_CAP_ANALOGOUT ? "true" : "false");
671 #endif
672 #ifdef DSP_CAP_ANALOGIN
673 TRACE("\tAnalog In: %s\n", oss_caps & DSP_CAP_ANALOGIN ? "true" : "false");
674 #endif
675 #ifdef DSP_CAP_DIGITALOUT
676 TRACE("\tDigital Out: %s\n", oss_caps & DSP_CAP_DIGITALOUT ? "true" : "false");
677 #endif
678 #ifdef DSP_CAP_DIGITALIN
679 TRACE("\tDigital In: %s\n", oss_caps & DSP_CAP_DIGITALIN ? "true" : "false");
680 #endif
681 #ifdef DSP_CAP_ADMASK
682 TRACE("\tA/D Mask: %s\n", oss_caps & DSP_CAP_ADMASK ? "true" : "false");
683 #endif
684 #ifdef DSP_CAP_SHADOW
685 TRACE("\tShadow: %s\n", oss_caps & DSP_CAP_SHADOW ? "true" : "false");
686 #endif
687 #ifdef DSP_CH_MASK
688 TRACE("\tChannel Mask: %x\n", oss_caps & DSP_CH_MASK);
689 #endif
690 #ifdef DSP_CAP_SLAVE
691 TRACE("\tSlave: %s\n", oss_caps & DSP_CAP_SLAVE ? "true" : "false");
692 #endif
693 }
694 }
695
696 /******************************************************************
697 * OSS_WaveOutInit
698 *
699 *
700 */
701 static BOOL OSS_WaveOutInit(OSS_DEVICE* ossdev)
702 {
703 int rc,arg;
704 int f,c,r;
705 BOOL has_mixer = FALSE;
706 TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
707
708 if (OSS_OpenDevice(ossdev, O_WRONLY, NULL, 0,-1,-1,-1) != 0)
709 return FALSE;
710
711 ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
712
713 #if defined(SNDCTL_MIXERINFO)
714 {
715 int mixer;
716 if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
717 oss_mixerinfo info;
718 info.dev = 0;
719 if (ioctl(mixer, SNDCTL_MIXERINFO, &info) >= 0) {
720 lstrcpynA(ossdev->ds_desc.szDesc, info.name, sizeof(info.name));
721 strcpy(ossdev->ds_desc.szDrvname, "wineoss.drv");
722 MultiByteToWideChar(CP_ACP, 0, info.name, sizeof(info.name),
723 ossdev->out_caps.szPname,
724 sizeof(ossdev->out_caps.szPname) / sizeof(WCHAR));
725 TRACE("%s: %s\n", ossdev->mixer_name, ossdev->ds_desc.szDesc);
726 has_mixer = TRUE;
727 } else {
728 WARN("%s: cannot read SNDCTL_MIXERINFO!\n", ossdev->mixer_name);
729 }
730 close(mixer);
731 } else {
732 WARN("open(%s) failed (%s)\n", ossdev->mixer_name , strerror(errno));
733 }
734 }
735 #elif defined(SOUND_MIXER_INFO)
736 {
737 int mixer;
738 if ((mixer = open(ossdev->mixer_name, O_RDONLY|O_NDELAY)) >= 0) {
739 mixer_info info;
740 if (ioctl(mixer, SOUND_MIXER_INFO, &info) >= 0) {
741 lstrcpynA(ossdev->ds_desc.szDesc, info.name, sizeof(info.name));
742 strcpy(ossdev->ds_desc.szDrvname, "wineoss.drv");
743 MultiByteToWideChar(CP_ACP, 0, info.name, sizeof(info.name),
744 ossdev->out_caps.szPname,
745 sizeof(ossdev->out_caps.szPname) / sizeof(WCHAR));
746 TRACE("%s: %s\n", ossdev->mixer_name, ossdev->ds_desc.szDesc);
747 has_mixer = TRUE;
748 } else {
749 /* FreeBSD up to at least 5.2 provides this ioctl, but does not
750 * implement it properly, and there are probably similar issues
751 * on other platforms, so we warn but try to go ahead.
752 */
753 WARN("%s: cannot read SOUND_MIXER_INFO!\n", ossdev->mixer_name);
754 }
755 close(mixer);
756 } else {
757 WARN("open(%s) failed (%s)\n", ossdev->mixer_name , strerror(errno));
758 }
759 }
760 #endif /* SOUND_MIXER_INFO */
761
762 if (WINE_TRACE_ON(wave))
763 OSS_Info(ossdev->fd);
764
765 ossdev->out_caps.wMid = 0x00FF; /* Manufac ID */
766 ossdev->out_caps.wPid = 0x0001; /* Product ID */
767
768 ossdev->out_caps.vDriverVersion = 0x0100;
769 ossdev->out_caps.wChannels = 1;
770 ossdev->out_caps.dwFormats = 0x00000000;
771 ossdev->out_caps.wReserved1 = 0;
772 ossdev->out_caps.dwSupport = has_mixer ? WAVECAPS_VOLUME : 0;
773
774 /* direct sound caps */
775 ossdev->ds_caps.dwFlags = DSCAPS_CERTIFIED;
776 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY8BIT;
777 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARY16BIT;
778 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYMONO;
779 ossdev->ds_caps.dwFlags |= DSCAPS_SECONDARYSTEREO;
780 ossdev->ds_caps.dwFlags |= DSCAPS_CONTINUOUSRATE;
781
782 ossdev->ds_caps.dwPrimaryBuffers = 1;
783 ossdev->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
784 ossdev->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
785
786 /* We must first set the format and the stereo mode as some sound cards
787 * may support 44kHz mono but not 44kHz stereo. Also we must
788 * systematically check the return value of these ioctls as they will
789 * always succeed (see OSS Linux) but will modify the parameter to match
790 * whatever they support. The OSS specs also say we must first set the
791 * sample size, then the stereo and then the sample rate.
792 */
793 for (f=0;f<2;f++) {
794 arg=win_std_oss_fmts[f];
795 rc=ioctl(ossdev->fd, SNDCTL_DSP_SAMPLESIZE, &arg);
796 if (rc!=0 || arg!=win_std_oss_fmts[f]) {
797 TRACE("DSP_SAMPLESIZE: rc=%d returned %d for %d\n",
798 rc,arg,win_std_oss_fmts[f]);
799 continue;
800 }
801 if (f == 0)
802 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY8BIT;
803 else if (f == 1)
804 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARY16BIT;
805
806 for (c = 1; c <= MAX_CHANNELS; c++) {
807 arg=c;
808 rc=ioctl(ossdev->fd, SNDCTL_DSP_CHANNELS, &arg);
809 if( rc == -1) break;
810 if (rc!=0 || arg!=c) {
811 TRACE("DSP_CHANNELS: rc=%d returned %d for %d\n",rc,arg,c);
812 continue;
813 }
814 if (c == 1) {
815 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
816 } else if (c == 2) {
817 ossdev->out_caps.wChannels = 2;
818 if (has_mixer)
819 ossdev->out_caps.dwSupport|=WAVECAPS_LRVOLUME;
820 ossdev->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
821 } else
822 ossdev->out_caps.wChannels = c;
823
824 for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
825 arg=win_std_rates[r];
826 rc=ioctl(ossdev->fd, SNDCTL_DSP_SPEED, &arg);
827 TRACE("DSP_SPEED: rc=%d returned %d for %dx%dx%d\n",
828 rc,arg,win_std_rates[r],win_std_oss_fmts[f],c);
829 if (rc==0 && arg!=0 && NEAR_MATCH(arg,win_std_rates[r]) && c < 3)
830 ossdev->out_caps.dwFormats|=win_std_formats[f][c-1][r];
831 }
832 }
833 }
834
835 if (ioctl(ossdev->fd, SNDCTL_DSP_GETCAPS, &arg) == 0) {
836 if (arg & DSP_CAP_TRIGGER)
837 ossdev->bTriggerSupport = TRUE;
838 if ((arg & DSP_CAP_REALTIME) && !(arg & DSP_CAP_BATCH)) {
839 ossdev->out_caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
840 }
841 /* well, might as well use the DirectSound cap flag for something */
842 if ((arg & DSP_CAP_TRIGGER) && (arg & DSP_CAP_MMAP) &&
843 !(arg & DSP_CAP_BATCH)) {
844 ossdev->out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
845 } else {
846 ossdev->ds_caps.dwFlags |= DSCAPS_EMULDRIVER;
847 }
848 #ifdef DSP_CAP_MULTI /* not every oss has this */
849 /* check for hardware secondary buffer support (multi open) */
850 if ((arg & DSP_CAP_MULTI) &&
851 (ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
852 TRACE("hardware secondary buffer support available\n");
853
854 ossdev->ds_caps.dwMaxHwMixingAllBuffers = 16;
855 ossdev->ds_caps.dwMaxHwMixingStaticBuffers = 0;
856 ossdev->ds_caps.dwMaxHwMixingStreamingBuffers = 16;
857
858 ossdev->ds_caps.dwFreeHwMixingAllBuffers = 16;
859 ossdev->ds_caps.dwFreeHwMixingStaticBuffers = 0;
860 ossdev->ds_caps.dwFreeHwMixingStreamingBuffers = 16;
861 }
862 #endif
863 }
864 OSS_CloseDevice(ossdev);
865 TRACE("out wChannels = %d, dwFormats = %08X, dwSupport = %08X\n",
866 ossdev->out_caps.wChannels, ossdev->out_caps.dwFormats,
867 ossdev->out_caps.dwSupport);
868 return TRUE;
869 }
870
871 /******************************************************************
872 * OSS_WaveInInit
873 *
874 *
875 */
876 static BOOL OSS_WaveInInit(OSS_DEVICE* ossdev)
877 {
878 int rc,arg;
879 int f,c,r;
880 TRACE("(%p) %s\n", ossdev, ossdev->dev_name);
881
882 if (OSS_OpenDevice(ossdev, O_RDONLY, NULL, 0,-1,-1,-1) != 0)
883 return FALSE;
884
885 ioctl(ossdev->fd, SNDCTL_DSP_RESET, 0);
886