mshtml: Handle protocols with no ports when checking target origin.
[wine.git] / dlls / wineoss.drv / oss.c
blobdf03d3286d9381c7cd7e06c91a40ff16f32944cf
1 /*
2 * OSS driver (unixlib)
4 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * 2022 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include <stdarg.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/ioctl.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <sys/soundcard.h>
34 #include <pthread.h>
36 #include "ntstatus.h"
37 #define WIN32_NO_STATUS
38 #include "winternl.h"
39 #include "initguid.h"
40 #include "audioclient.h"
41 #include "mmddk.h"
43 #include "wine/debug.h"
44 #include "wine/unixlib.h"
46 #include "unixlib.h"
48 struct oss_stream
50 WAVEFORMATEX *fmt;
51 EDataFlow flow;
52 UINT flags;
53 AUDCLNT_SHAREMODE share;
54 HANDLE event;
56 int fd;
58 BOOL playing, mute, please_quit;
59 UINT64 written_frames, last_pos_frames;
60 UINT32 period_frames, bufsize_frames, held_frames, tmp_buffer_frames, in_oss_frames;
61 UINT32 oss_bufsize_bytes, lcl_offs_frames; /* offs into local_buffer where valid data starts */
62 REFERENCE_TIME period;
64 BYTE *local_buffer, *tmp_buffer;
65 INT32 getbuf_last; /* <0 when using tmp_buffer */
67 pthread_mutex_t lock;
70 WINE_DEFAULT_DEBUG_CHANNEL(oss);
72 static const REFERENCE_TIME def_period = 100000;
73 static const REFERENCE_TIME min_period = 50000;
75 static NTSTATUS oss_not_implemented(void *args)
77 return STATUS_SUCCESS;
80 /* copied from kernelbase */
81 static int muldiv( int a, int b, int c )
83 LONGLONG ret;
85 if (!c) return -1;
87 /* We want to deal with a positive divisor to simplify the logic. */
88 if (c < 0)
90 a = -a;
91 c = -c;
94 /* If the result is positive, we "add" to round. else, we subtract to round. */
95 if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
96 ret = (((LONGLONG)a * b) + (c / 2)) / c;
97 else
98 ret = (((LONGLONG)a * b) - (c / 2)) / c;
100 if (ret > 2147483647 || ret < -2147483647) return -1;
101 return ret;
104 static void oss_lock(struct oss_stream *stream)
106 pthread_mutex_lock(&stream->lock);
109 static void oss_unlock(struct oss_stream *stream)
111 pthread_mutex_unlock(&stream->lock);
114 static NTSTATUS oss_unlock_result(struct oss_stream *stream,
115 HRESULT *result, HRESULT value)
117 *result = value;
118 oss_unlock(stream);
119 return STATUS_SUCCESS;
122 static struct oss_stream *handle_get_stream(stream_handle h)
124 return (struct oss_stream *)(UINT_PTR)h;
127 static NTSTATUS oss_test_connect(void *args)
129 struct test_connect_params *params = args;
130 int mixer_fd;
131 oss_sysinfo sysinfo;
133 /* Attempt to determine if we are running on OSS or ALSA's OSS
134 * compatibility layer. There is no official way to do that, so just check
135 * for validity as best as possible, without rejecting valid OSS
136 * implementations. */
138 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
139 if(mixer_fd < 0){
140 TRACE("Priority_Unavailable: open failed\n");
141 params->priority = Priority_Unavailable;
142 return STATUS_SUCCESS;
145 sysinfo.version[0] = 0xFF;
146 sysinfo.versionnum = ~0;
147 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
148 TRACE("Priority_Unavailable: ioctl failed\n");
149 close(mixer_fd);
150 params->priority = Priority_Unavailable;
151 return STATUS_SUCCESS;
154 close(mixer_fd);
156 if(sysinfo.version[0] < '4' || sysinfo.version[0] > '9'){
157 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo.version[0]);
158 params->priority = Priority_Low;
159 return STATUS_SUCCESS;
161 if(sysinfo.versionnum & 0x80000000){
162 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo.versionnum);
163 params->priority = Priority_Low;
164 return STATUS_SUCCESS;
167 TRACE("Priority_Preferred: Seems like valid OSS!\n");
169 params->priority = Priority_Preferred;
170 return STATUS_SUCCESS;
173 /* dst must be large enough to hold devnode */
174 static void oss_clean_devnode(char *dest, const char *devnode)
176 const char *dot, *slash;
177 size_t len;
179 strcpy(dest, devnode);
180 dot = strrchr(dest, '.');
181 if(!dot)
182 return;
184 slash = strrchr(dest, '/');
185 if(slash && dot < slash)
186 return;
188 len = dot - dest;
189 dest[len] = '\0';
192 static int open_device(const char *device, EDataFlow flow)
194 int flags = ((flow == eRender) ? O_WRONLY : O_RDONLY) | O_NONBLOCK;
196 return open(device, flags, 0);
199 static void get_default_device(EDataFlow flow, char device[OSS_DEVNODE_SIZE])
201 int fd, err;
202 oss_audioinfo ai;
204 device[0] = '\0';
205 fd = open_device("/dev/dsp", flow);
206 if(fd < 0){
207 WARN("Couldn't open default device!\n");
208 return;
211 ai.dev = -1;
212 if((err = ioctl(fd, SNDCTL_ENGINEINFO, &ai)) < 0){
213 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err, strerror(errno));
214 close(fd);
215 return;
217 close(fd);
219 TRACE("Default devnode: %s\n", ai.devnode);
220 oss_clean_devnode(device, ai.devnode);
221 return;
224 static NTSTATUS oss_get_endpoint_ids(void *args)
226 struct get_endpoint_ids_params *params = args;
227 oss_sysinfo sysinfo;
228 oss_audioinfo ai;
229 static int print_once = 0;
230 static const WCHAR outW[] = {'O','u','t',':',' ',0};
231 static const WCHAR inW[] = {'I','n',':',' ',0};
232 struct endpoint_info
234 WCHAR name[ARRAY_SIZE(ai.name) + ARRAY_SIZE(outW)];
235 char device[OSS_DEVNODE_SIZE];
236 } *info;
237 unsigned int i, j, num, needed, name_len, device_len, offset, default_idx = 0;
238 char default_device[OSS_DEVNODE_SIZE];
239 struct endpoint *endpoint;
240 int mixer_fd;
242 mixer_fd = open("/dev/mixer", O_RDONLY, 0);
243 if(mixer_fd < 0){
244 ERR("OSS /dev/mixer doesn't seem to exist\n");
245 params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
246 return STATUS_SUCCESS;
249 if(ioctl(mixer_fd, SNDCTL_SYSINFO, &sysinfo) < 0){
250 close(mixer_fd);
251 if(errno == EINVAL){
252 ERR("OSS version too old, need at least OSSv4\n");
253 params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
254 return STATUS_SUCCESS;
257 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno, strerror(errno));
258 params->result = E_FAIL;
259 return STATUS_SUCCESS;
262 if(!print_once){
263 TRACE("OSS sysinfo:\n");
264 TRACE("product: %s\n", sysinfo.product);
265 TRACE("version: %s\n", sysinfo.version);
266 TRACE("versionnum: %x\n", sysinfo.versionnum);
267 TRACE("numaudios: %d\n", sysinfo.numaudios);
268 TRACE("nummixers: %d\n", sysinfo.nummixers);
269 TRACE("numcards: %d\n", sysinfo.numcards);
270 TRACE("numaudioengines: %d\n", sysinfo.numaudioengines);
271 print_once = 1;
274 if(sysinfo.numaudios <= 0){
275 WARN("No audio devices!\n");
276 close(mixer_fd);
277 params->result = AUDCLNT_E_SERVICE_NOT_RUNNING;
278 return STATUS_SUCCESS;
281 info = malloc(sysinfo.numaudios * sizeof(*info));
282 if(!info){
283 close(mixer_fd);
284 params->result = E_OUTOFMEMORY;
285 return STATUS_SUCCESS;
288 get_default_device(params->flow, default_device);
290 num = 0;
291 for(i = 0; i < sysinfo.numaudios; ++i){
292 char devnode[OSS_DEVNODE_SIZE];
293 int fd, prefix_len;
294 const WCHAR *prefix;
296 memset(&ai, 0, sizeof(ai));
297 ai.dev = i;
298 if(ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai) < 0){
299 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i, errno,
300 strerror(errno));
301 continue;
304 oss_clean_devnode(devnode, ai.devnode);
306 /* check for duplicates */
307 for(j = 0; j < num; j++)
308 if(!strcmp(devnode, info[j].device))
309 break;
310 if(j < num)
311 continue;
313 fd = open_device(devnode, params->flow);
314 if(fd < 0){
315 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
316 devnode, errno, strerror(errno));
317 continue;
319 close(fd);
321 if((params->flow == eCapture && !(ai.caps & PCM_CAP_INPUT)) ||
322 (params->flow == eRender && !(ai.caps & PCM_CAP_OUTPUT)))
323 continue;
325 strcpy(info[num].device, devnode);
327 if(params->flow == eRender){
328 prefix = outW;
329 prefix_len = ARRAY_SIZE(outW) - 1;
330 }else{
331 prefix = inW;
332 prefix_len = ARRAY_SIZE(inW) - 1;
334 memcpy(info[num].name, prefix, prefix_len * sizeof(WCHAR));
335 ntdll_umbstowcs(ai.name, strlen(ai.name) + 1, info[num].name + prefix_len,
336 ARRAY_SIZE(info[num].name) - prefix_len);
337 if(!strcmp(default_device, info[num].device))
338 default_idx = num;
339 num++;
341 close(mixer_fd);
343 offset = needed = num * sizeof(*params->endpoints);
344 endpoint = params->endpoints;
346 for(i = 0; i < num; i++){
347 name_len = wcslen(info[i].name) + 1;
348 device_len = strlen(info[i].device) + 1;
349 needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
351 if(needed <= params->size){
352 endpoint->name = offset;
353 memcpy((char *)params->endpoints + offset, info[i].name, name_len * sizeof(WCHAR));
354 offset += name_len * sizeof(WCHAR);
355 endpoint->device = offset;
356 memcpy((char *)params->endpoints + offset, info[i].device, device_len);
357 offset += (device_len + 1) & ~1;
358 endpoint++;
361 free(info);
363 params->num = num;
364 params->default_idx = default_idx;
366 if(needed > params->size){
367 params->size = needed;
368 params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
369 } else
370 params->result = S_OK;
372 return STATUS_SUCCESS;
375 static UINT get_channel_mask(unsigned int channels)
377 switch(channels){
378 case 0:
379 return 0;
380 case 1:
381 return KSAUDIO_SPEAKER_MONO;
382 case 2:
383 return KSAUDIO_SPEAKER_STEREO;
384 case 3:
385 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
386 case 4:
387 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
388 case 5:
389 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
390 case 6:
391 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
392 case 7:
393 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
394 case 8:
395 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
397 FIXME("Unknown speaker configuration: %u\n", channels);
398 return 0;
401 static int get_oss_format(const WAVEFORMATEX *fmt)
403 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
405 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
406 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
407 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
408 switch(fmt->wBitsPerSample){
409 case 8:
410 return AFMT_U8;
411 case 16:
412 return AFMT_S16_LE;
413 case 24:
414 return AFMT_S24_LE;
415 case 32:
416 return AFMT_S32_LE;
418 return -1;
421 #ifdef AFMT_FLOAT
422 if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
423 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
424 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
425 if(fmt->wBitsPerSample != 32)
426 return -1;
428 return AFMT_FLOAT;
430 #endif
432 return -1;
435 static WAVEFORMATEXTENSIBLE *clone_format(const WAVEFORMATEX *fmt)
437 WAVEFORMATEXTENSIBLE *ret;
438 size_t size;
440 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
441 size = sizeof(WAVEFORMATEXTENSIBLE);
442 else
443 size = sizeof(WAVEFORMATEX);
445 ret = malloc(size);
446 if(!ret)
447 return NULL;
449 memcpy(ret, fmt, size);
451 ret->Format.cbSize = size - sizeof(WAVEFORMATEX);
453 return ret;
456 static HRESULT setup_oss_device(AUDCLNT_SHAREMODE share, int fd,
457 const WAVEFORMATEX *fmt, WAVEFORMATEXTENSIBLE *out)
459 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
460 int tmp, oss_format;
461 double tenth;
462 HRESULT ret = S_OK;
463 WAVEFORMATEXTENSIBLE *closest;
465 tmp = oss_format = get_oss_format(fmt);
466 if(oss_format < 0)
467 return AUDCLNT_E_UNSUPPORTED_FORMAT;
468 if(ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0){
469 WARN("SETFMT failed: %d (%s)\n", errno, strerror(errno));
470 return E_FAIL;
472 if(tmp != oss_format){
473 TRACE("Format unsupported by this OSS version: %x\n", oss_format);
474 return AUDCLNT_E_UNSUPPORTED_FORMAT;
477 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
478 (fmtex->Format.nAvgBytesPerSec == 0 ||
479 fmtex->Format.nBlockAlign == 0 ||
480 fmtex->Samples.wValidBitsPerSample > fmtex->Format.wBitsPerSample))
481 return E_INVALIDARG;
483 if(fmt->nChannels == 0)
484 return AUDCLNT_E_UNSUPPORTED_FORMAT;
486 closest = clone_format(fmt);
487 if(!closest)
488 return E_OUTOFMEMORY;
490 tmp = fmt->nSamplesPerSec;
491 if(ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0){
492 WARN("SPEED failed: %d (%s)\n", errno, strerror(errno));
493 free(closest);
494 return E_FAIL;
496 tenth = fmt->nSamplesPerSec * 0.1;
497 if(tmp > fmt->nSamplesPerSec + tenth || tmp < fmt->nSamplesPerSec - tenth){
498 ret = S_FALSE;
499 closest->Format.nSamplesPerSec = tmp;
502 tmp = fmt->nChannels;
503 if(ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0){
504 WARN("CHANNELS failed: %d (%s)\n", errno, strerror(errno));
505 free(closest);
506 return E_FAIL;
508 if(tmp != fmt->nChannels){
509 ret = S_FALSE;
510 closest->Format.nChannels = tmp;
513 if(closest->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
514 closest->dwChannelMask = get_channel_mask(closest->Format.nChannels);
516 if(fmt->nBlockAlign != fmt->nChannels * fmt->wBitsPerSample / 8 ||
517 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec ||
518 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
519 fmtex->Samples.wValidBitsPerSample < fmtex->Format.wBitsPerSample))
520 ret = S_FALSE;
522 if(share == AUDCLNT_SHAREMODE_EXCLUSIVE &&
523 fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
524 if(fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED)
525 ret = S_FALSE;
528 if(ret == S_FALSE && !out)
529 ret = AUDCLNT_E_UNSUPPORTED_FORMAT;
531 if(ret == S_FALSE && out){
532 closest->Format.nBlockAlign =
533 closest->Format.nChannels * closest->Format.wBitsPerSample / 8;
534 closest->Format.nAvgBytesPerSec =
535 closest->Format.nBlockAlign * closest->Format.nSamplesPerSec;
536 if(closest->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
537 closest->Samples.wValidBitsPerSample = closest->Format.wBitsPerSample;
538 memcpy(out, closest, closest->Format.cbSize + sizeof(WAVEFORMATEX));
540 free(closest);
542 TRACE("returning: %08x\n", (unsigned)ret);
543 return ret;
546 static ULONG_PTR zero_bits(void)
548 #ifdef _WIN64
549 return !NtCurrentTeb()->WowTebOffset ? 0 : 0x7fffffff;
550 #else
551 return 0;
552 #endif
555 static NTSTATUS oss_create_stream(void *args)
557 struct create_stream_params *params = args;
558 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE *)params->fmt;
559 struct oss_stream *stream;
560 oss_audioinfo ai;
561 SIZE_T size;
563 params->result = S_OK;
565 if (params->share == AUDCLNT_SHAREMODE_SHARED) {
566 params->period = def_period;
567 if (params->duration < 3 * params->period)
568 params->duration = 3 * params->period;
569 } else {
570 if (fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
571 (fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED))
572 params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
573 else {
574 if (!params->period)
575 params->period = def_period;
576 if (params->period < min_period || params->period > 5000000)
577 params->result = AUDCLNT_E_INVALID_DEVICE_PERIOD;
578 else if (params->duration > 20000000) /* The smaller the period, the lower this limit. */
579 params->result = AUDCLNT_E_BUFFER_SIZE_ERROR;
580 else if (params->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) {
581 if (params->duration != params->period)
582 params->result = AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL;
584 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
586 params->result = AUDCLNT_E_DEVICE_IN_USE;
587 } else if (params->duration < 8 * params->period)
588 params->duration = 8 * params->period; /* May grow above 2s. */
592 if (FAILED(params->result))
593 return STATUS_SUCCESS;
595 stream = calloc(1, sizeof(*stream));
596 if(!stream){
597 params->result = E_OUTOFMEMORY;
598 return STATUS_SUCCESS;
601 stream->flow = params->flow;
602 pthread_mutex_init(&stream->lock, NULL);
604 stream->fd = open_device(params->device, params->flow);
605 if(stream->fd < 0){
606 WARN("Unable to open device %s: %d (%s)\n", params->device, errno, strerror(errno));
607 params->result = AUDCLNT_E_DEVICE_INVALIDATED;
608 goto exit;
611 ai.dev = -1;
612 if(ioctl(stream->fd, SNDCTL_ENGINEINFO, &ai) < 0){
613 WARN("Unable to get audio info for device %s: %d (%s)\n", params->device, errno, strerror(errno));
614 params->result = E_FAIL;
615 goto exit;
618 TRACE("OSS audioinfo:\n");
619 TRACE("devnode: %s\n", ai.devnode);
620 TRACE("name: %s\n", ai.name);
621 TRACE("busy: %x\n", ai.busy);
622 TRACE("caps: %x\n", ai.caps);
623 TRACE("iformats: %x\n", ai.iformats);
624 TRACE("oformats: %x\n", ai.oformats);
625 TRACE("enabled: %d\n", ai.enabled);
626 TRACE("min_rate: %d\n", ai.min_rate);
627 TRACE("max_rate: %d\n", ai.max_rate);
628 TRACE("min_channels: %d\n", ai.min_channels);
629 TRACE("max_channels: %d\n", ai.max_channels);
631 params->result = setup_oss_device(params->share, stream->fd, params->fmt, NULL);
632 if(FAILED(params->result))
633 goto exit;
635 fmtex = clone_format(params->fmt);
636 if(!fmtex){
637 params->result = E_OUTOFMEMORY;
638 goto exit;
640 stream->fmt = &fmtex->Format;
642 stream->period = params->period;
643 stream->period_frames = muldiv(params->fmt->nSamplesPerSec, params->period, 10000000);
645 stream->bufsize_frames = muldiv(params->duration, params->fmt->nSamplesPerSec, 10000000);
646 if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
647 stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames;
648 size = stream->bufsize_frames * params->fmt->nBlockAlign;
649 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, zero_bits(),
650 &size, MEM_COMMIT, PAGE_READWRITE)){
651 params->result = E_OUTOFMEMORY;
652 goto exit;
655 stream->share = params->share;
656 stream->flags = params->flags;
657 stream->oss_bufsize_bytes = 0;
659 exit:
660 if(FAILED(params->result)){
661 if(stream->fd >= 0) close(stream->fd);
662 if(stream->local_buffer){
663 size = 0;
664 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE);
666 pthread_mutex_destroy(&stream->lock);
667 free(stream->fmt);
668 free(stream);
669 }else{
670 *params->stream = (stream_handle)(UINT_PTR)stream;
673 return STATUS_SUCCESS;
676 static NTSTATUS oss_release_stream(void *args)
678 struct release_stream_params *params = args;
679 struct oss_stream *stream = handle_get_stream(params->stream);
680 SIZE_T size;
682 if(params->timer_thread){
683 stream->please_quit = TRUE;
684 NtWaitForSingleObject(params->timer_thread, FALSE, NULL);
685 NtClose(params->timer_thread);
688 close(stream->fd);
689 if(stream->local_buffer){
690 size = 0;
691 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, &size, MEM_RELEASE);
693 if(stream->tmp_buffer){
694 size = 0;
695 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE);
697 free(stream->fmt);
698 pthread_mutex_destroy(&stream->lock);
699 free(stream);
701 params->result = S_OK;
702 return STATUS_SUCCESS;
705 static NTSTATUS oss_start(void *args)
707 struct start_params *params = args;
708 struct oss_stream *stream = handle_get_stream(params->stream);
710 oss_lock(stream);
712 if((stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) && !stream->event)
713 return oss_unlock_result(stream, &params->result, AUDCLNT_E_EVENTHANDLE_NOT_SET);
715 if(stream->playing)
716 return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
718 stream->playing = TRUE;
720 return oss_unlock_result(stream, &params->result, S_OK);
723 static NTSTATUS oss_stop(void *args)
725 struct stop_params *params = args;
726 struct oss_stream *stream = handle_get_stream(params->stream);
728 oss_lock(stream);
730 if(!stream->playing)
731 return oss_unlock_result(stream, &params->result, S_FALSE);
733 stream->playing = FALSE;
734 stream->in_oss_frames = 0;
736 return oss_unlock_result(stream, &params->result, S_OK);
739 static NTSTATUS oss_reset(void *args)
741 struct reset_params *params = args;
742 struct oss_stream *stream = handle_get_stream(params->stream);
744 oss_lock(stream);
746 if(stream->playing)
747 return oss_unlock_result(stream, &params->result, AUDCLNT_E_NOT_STOPPED);
749 if(stream->getbuf_last)
750 return oss_unlock_result(stream, &params->result, AUDCLNT_E_BUFFER_OPERATION_PENDING);
752 if(stream->flow == eRender){
753 stream->written_frames = 0;
754 stream->last_pos_frames = 0;
755 }else{
756 stream->written_frames += stream->held_frames;
758 stream->held_frames = 0;
759 stream->lcl_offs_frames = 0;
760 stream->in_oss_frames = 0;
762 return oss_unlock_result(stream, &params->result, S_OK);
765 static void silence_buffer(struct oss_stream *stream, BYTE *buffer, UINT32 frames)
767 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
768 if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
769 (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
770 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
771 stream->fmt->wBitsPerSample == 8)
772 memset(buffer, 128, frames * stream->fmt->nBlockAlign);
773 else
774 memset(buffer, 0, frames * stream->fmt->nBlockAlign);
777 static void oss_write_data(struct oss_stream *stream)
779 ssize_t written_bytes;
780 UINT32 written_frames, in_oss_frames, write_limit, max_period, write_offs_frames, new_frames;
781 SIZE_T to_write_frames, to_write_bytes, advanced;
782 audio_buf_info bi;
783 BYTE *buf;
785 if(ioctl(stream->fd, SNDCTL_DSP_GETOSPACE, &bi) < 0){
786 WARN("GETOSPACE failed: %d (%s)\n", errno, strerror(errno));
787 return;
790 max_period = max(bi.fragsize / stream->fmt->nBlockAlign, stream->period_frames);
792 if(bi.bytes > stream->oss_bufsize_bytes){
793 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
794 bi.bytes, stream->oss_bufsize_bytes);
795 stream->oss_bufsize_bytes = bi.bytes;
796 in_oss_frames = 0;
797 }else
798 in_oss_frames = (stream->oss_bufsize_bytes - bi.bytes) / stream->fmt->nBlockAlign;
800 if(in_oss_frames > stream->in_oss_frames){
801 TRACE("Capping reported frames from %u to %u\n",
802 in_oss_frames, stream->in_oss_frames);
803 in_oss_frames = stream->in_oss_frames;
806 write_limit = 0;
807 while(write_limit + in_oss_frames < max_period * 3)
808 write_limit += max_period;
809 if(write_limit == 0)
810 return;
812 /* vvvvvv - in_oss_frames
813 * [--xxxxxxxxxx]
814 * [xxxxxxxxxx--]
815 * ^^^^^^^^^^ - held_frames
816 * ^ - lcl_offs_frames
818 advanced = stream->in_oss_frames - in_oss_frames;
819 if(advanced > stream->held_frames)
820 advanced = stream->held_frames;
821 stream->lcl_offs_frames += advanced;
822 stream->lcl_offs_frames %= stream->bufsize_frames;
823 stream->held_frames -= advanced;
824 stream->in_oss_frames = in_oss_frames;
825 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
826 advanced, stream->lcl_offs_frames, stream->held_frames, stream->in_oss_frames);
829 if(stream->held_frames == stream->in_oss_frames)
830 return;
832 write_offs_frames = (stream->lcl_offs_frames + stream->in_oss_frames) % stream->bufsize_frames;
833 new_frames = stream->held_frames - stream->in_oss_frames;
835 if(write_offs_frames + new_frames > stream->bufsize_frames)
836 to_write_frames = stream->bufsize_frames - write_offs_frames;
837 else
838 to_write_frames = new_frames;
840 to_write_frames = min(to_write_frames, write_limit);
841 to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
842 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames,
843 write_offs_frames, to_write_frames + write_offs_frames,
844 stream->bufsize_frames);
846 buf = stream->local_buffer + write_offs_frames * stream->fmt->nBlockAlign;
848 if(stream->mute)
849 silence_buffer(stream, buf, to_write_frames);
851 written_bytes = write(stream->fd, buf, to_write_bytes);
852 if(written_bytes < 0){
853 /* EAGAIN is OSS buffer full, log that too */
854 WARN("write failed: %d (%s)\n", errno, strerror(errno));
855 return;
857 written_frames = written_bytes / stream->fmt->nBlockAlign;
859 stream->in_oss_frames += written_frames;
861 if(written_frames < to_write_frames){
862 /* OSS buffer probably full */
863 return;
866 if(new_frames > written_frames && written_frames < write_limit){
867 /* wrapped and have some data back at the start to write */
869 to_write_frames = min(write_limit - written_frames, new_frames - written_frames);
870 to_write_bytes = to_write_frames * stream->fmt->nBlockAlign;
872 if(stream->mute)
873 silence_buffer(stream, stream->local_buffer, to_write_frames);
875 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames);
877 written_bytes = write(stream->fd, stream->local_buffer, to_write_bytes);
878 if(written_bytes < 0){
879 WARN("write failed: %d (%s)\n", errno, strerror(errno));
880 return;
882 written_frames = written_bytes / stream->fmt->nBlockAlign;
883 stream->in_oss_frames += written_frames;
887 static void oss_read_data(struct oss_stream *stream)
889 UINT64 pos, readable;
890 ssize_t nread;
892 pos = (stream->held_frames + stream->lcl_offs_frames) % stream->bufsize_frames;
893 readable = (stream->bufsize_frames - pos) * stream->fmt->nBlockAlign;
895 nread = read(stream->fd, stream->local_buffer + pos * stream->fmt->nBlockAlign,
896 readable);
897 if(nread < 0){
898 WARN("read failed: %d (%s)\n", errno, strerror(errno));
899 return;
902 stream->held_frames += nread / stream->fmt->nBlockAlign;
904 if(stream->held_frames > stream->bufsize_frames){
905 WARN("Overflow of unread data\n");
906 stream->lcl_offs_frames += stream->held_frames;
907 stream->lcl_offs_frames %= stream->bufsize_frames;
908 stream->held_frames = stream->bufsize_frames;
912 static NTSTATUS oss_timer_loop(void *args)
914 struct timer_loop_params *params = args;
915 struct oss_stream *stream = handle_get_stream(params->stream);
916 LARGE_INTEGER delay, now, next;
917 int adjust;
919 oss_lock(stream);
921 delay.QuadPart = -stream->period;
922 NtQueryPerformanceCounter(&now, NULL);
923 next.QuadPart = now.QuadPart + stream->period;
925 while(!stream->please_quit){
926 if(stream->playing){
927 if(stream->flow == eRender && stream->held_frames)
928 oss_write_data(stream);
929 else if(stream->flow == eCapture)
930 oss_read_data(stream);
932 if(stream->event)
933 NtSetEvent(stream->event, NULL);
934 oss_unlock(stream);
936 NtDelayExecution(FALSE, &delay);
938 oss_lock(stream);
939 NtQueryPerformanceCounter(&now, NULL);
940 adjust = next.QuadPart - now.QuadPart;
941 if(adjust > stream->period / 2)
942 adjust = stream->period / 2;
943 else if(adjust < -stream->period / 2)
944 adjust = -stream->period / 2;
945 delay.QuadPart = -(stream->period + adjust);
946 next.QuadPart += stream->period;
949 oss_unlock(stream);
951 return STATUS_SUCCESS;
954 static NTSTATUS oss_get_render_buffer(void *args)
956 struct get_render_buffer_params *params = args;
957 struct oss_stream *stream = handle_get_stream(params->stream);
958 UINT32 write_pos, frames = params->frames;
959 BYTE **data = params->data;
960 SIZE_T size;
962 oss_lock(stream);
964 if(stream->getbuf_last)
965 return oss_unlock_result(stream, &params->result, AUDCLNT_E_OUT_OF_ORDER);
967 if(!frames)
968 return oss_unlock_result(stream, &params->result, S_OK);
970 if(stream->held_frames + frames > stream->bufsize_frames)
971 return oss_unlock_result(stream, &params->result, AUDCLNT_E_BUFFER_TOO_LARGE);
973 write_pos =
974 (stream->lcl_offs_frames + stream->held_frames) % stream->bufsize_frames;
975 if(write_pos + frames > stream->bufsize_frames){
976 if(stream->tmp_buffer_frames < frames){
977 if(stream->tmp_buffer){
978 size = 0;
979 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE);
980 stream->tmp_buffer = NULL;
982 size = frames * stream->fmt->nBlockAlign;
983 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
984 &size, MEM_COMMIT, PAGE_READWRITE)){
985 stream->tmp_buffer_frames = 0;
986 return oss_unlock_result(stream, &params->result, E_OUTOFMEMORY);
988 stream->tmp_buffer_frames = frames;
990 *data = stream->tmp_buffer;
991 stream->getbuf_last = -frames;
992 }else{
993 *data = stream->local_buffer + write_pos * stream->fmt->nBlockAlign;
994 stream->getbuf_last = frames;
997 silence_buffer(stream, *data, frames);
999 return oss_unlock_result(stream, &params->result, S_OK);
1002 static void oss_wrap_buffer(struct oss_stream *stream, BYTE *buffer, UINT32 written_frames)
1004 UINT32 write_offs_frames =
1005 (stream->lcl_offs_frames + stream->held_frames) % stream->bufsize_frames;
1006 UINT32 write_offs_bytes = write_offs_frames * stream->fmt->nBlockAlign;
1007 UINT32 chunk_frames = stream->bufsize_frames - write_offs_frames;
1008 UINT32 chunk_bytes = chunk_frames * stream->fmt->nBlockAlign;
1009 UINT32 written_bytes = written_frames * stream->fmt->nBlockAlign;
1011 if(written_bytes <= chunk_bytes){
1012 memcpy(stream->local_buffer + write_offs_bytes, buffer, written_bytes);
1013 }else{
1014 memcpy(stream->local_buffer + write_offs_bytes, buffer, chunk_bytes);
1015 memcpy(stream->local_buffer, buffer + chunk_bytes,
1016 written_bytes - chunk_bytes);
1020 static NTSTATUS oss_release_render_buffer(void *args)
1022 struct release_render_buffer_params *params = args;
1023 struct oss_stream *stream = handle_get_stream(params->stream);
1024 UINT32 written_frames = params->written_frames;
1025 UINT flags = params->flags;
1026 BYTE *buffer;
1028 oss_lock(stream);
1030 if(!written_frames){
1031 stream->getbuf_last = 0;
1032 return oss_unlock_result(stream, &params->result, S_OK);
1035 if(!stream->getbuf_last)
1036 return oss_unlock_result(stream, &params->result, AUDCLNT_E_OUT_OF_ORDER);
1038 if(written_frames > (stream->getbuf_last >= 0 ? stream->getbuf_last : -stream->getbuf_last))
1039 return oss_unlock_result(stream, &params->result, AUDCLNT_E_INVALID_SIZE);
1041 if(stream->getbuf_last >= 0)
1042 buffer = stream->local_buffer + stream->fmt->nBlockAlign *
1043 ((stream->lcl_offs_frames + stream->held_frames) % stream->bufsize_frames);
1044 else
1045 buffer = stream->tmp_buffer;
1047 if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
1048 silence_buffer(stream, buffer, written_frames);
1050 if(stream->getbuf_last < 0)
1051 oss_wrap_buffer(stream, buffer, written_frames);
1053 stream->held_frames += written_frames;
1054 stream->written_frames += written_frames;
1055 stream->getbuf_last = 0;
1057 return oss_unlock_result(stream, &params->result, S_OK);
1060 static NTSTATUS oss_get_capture_buffer(void *args)
1062 struct get_capture_buffer_params *params = args;
1063 struct oss_stream *stream = handle_get_stream(params->stream);
1064 UINT64 *devpos = params->devpos, *qpcpos = params->qpcpos;
1065 UINT32 *frames = params->frames;
1066 UINT *flags = params->flags;
1067 BYTE **data = params->data;
1068 SIZE_T size;
1070 oss_lock(stream);
1072 if(stream->getbuf_last)
1073 return oss_unlock_result(stream, &params->result, AUDCLNT_E_OUT_OF_ORDER);
1075 if(stream->held_frames < stream->period_frames){
1076 *frames = 0;
1077 return oss_unlock_result(stream, &params->result, AUDCLNT_S_BUFFER_EMPTY);
1080 *flags = 0;
1082 *frames = stream->period_frames;
1084 if(stream->lcl_offs_frames + *frames > stream->bufsize_frames){
1085 UINT32 chunk_bytes, offs_bytes, frames_bytes;
1086 if(stream->tmp_buffer_frames < *frames){
1087 if(stream->tmp_buffer){
1088 size = 0;
1089 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, &size, MEM_RELEASE);
1090 stream->tmp_buffer = NULL;
1092 size = *frames * stream->fmt->nBlockAlign;
1093 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
1094 &size, MEM_COMMIT, PAGE_READWRITE)){
1095 stream->tmp_buffer_frames = 0;
1096 return oss_unlock_result(stream, &params->result, E_OUTOFMEMORY);
1098 stream->tmp_buffer_frames = *frames;
1101 *data = stream->tmp_buffer;
1102 chunk_bytes = (stream->bufsize_frames - stream->lcl_offs_frames) *
1103 stream->fmt->nBlockAlign;
1104 offs_bytes = stream->lcl_offs_frames * stream->fmt->nBlockAlign;
1105 frames_bytes = *frames * stream->fmt->nBlockAlign;
1106 memcpy(stream->tmp_buffer, stream->local_buffer + offs_bytes, chunk_bytes);
1107 memcpy(stream->tmp_buffer + chunk_bytes, stream->local_buffer,
1108 frames_bytes - chunk_bytes);
1109 }else
1110 *data = stream->local_buffer +
1111 stream->lcl_offs_frames * stream->fmt->nBlockAlign;
1113 stream->getbuf_last = *frames;
1115 if(devpos)
1116 *devpos = stream->written_frames;
1117 if(qpcpos){
1118 LARGE_INTEGER stamp, freq;
1119 NtQueryPerformanceCounter(&stamp, &freq);
1120 *qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1123 return oss_unlock_result(stream, &params->result, *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY);
1126 static NTSTATUS oss_release_capture_buffer(void *args)
1128 struct release_capture_buffer_params *params = args;
1129 struct oss_stream *stream = handle_get_stream(params->stream);
1130 UINT32 done = params->done;
1132 oss_lock(stream);
1134 if(!done){
1135 stream->getbuf_last = 0;
1136 return oss_unlock_result(stream, &params->result, S_OK);
1139 if(!stream->getbuf_last)
1140 return oss_unlock_result(stream, &params->result, AUDCLNT_E_OUT_OF_ORDER);
1142 if(stream->getbuf_last != done)
1143 return oss_unlock_result(stream, &params->result, AUDCLNT_E_INVALID_SIZE);
1145 stream->written_frames += done;
1146 stream->held_frames -= done;
1147 stream->lcl_offs_frames += done;
1148 stream->lcl_offs_frames %= stream->bufsize_frames;
1149 stream->getbuf_last = 0;
1151 return oss_unlock_result(stream, &params->result, S_OK);
1154 static NTSTATUS oss_is_format_supported(void *args)
1156 struct is_format_supported_params *params = args;
1157 int fd;
1159 params->result = S_OK;
1161 if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out))
1162 params->result = E_POINTER;
1163 else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE)
1164 params->result = E_INVALIDARG;
1165 else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
1166 params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1167 params->result = E_INVALIDARG;
1168 if(FAILED(params->result))
1169 return STATUS_SUCCESS;
1171 fd = open_device(params->device, params->flow);
1172 if(fd < 0){
1173 WARN("Unable to open device %s: %d (%s)\n", params->device, errno, strerror(errno));
1174 params->result = AUDCLNT_E_DEVICE_INVALIDATED;
1175 return STATUS_SUCCESS;
1177 params->result = setup_oss_device(params->share, fd, params->fmt_in, params->fmt_out);
1178 close(fd);
1180 return STATUS_SUCCESS;
1183 static NTSTATUS oss_get_mix_format(void *args)
1185 struct get_mix_format_params *params = args;
1186 WAVEFORMATEXTENSIBLE *fmt = params->fmt;
1187 oss_audioinfo ai;
1188 int formats, fd;
1190 if(params->flow != eRender && params->flow != eCapture){
1191 params->result = E_UNEXPECTED;
1192 return STATUS_SUCCESS;
1195 fd = open_device(params->device, params->flow);
1196 if(fd < 0){
1197 WARN("Unable to open device %s: %d (%s)\n", params->device, errno, strerror(errno));
1198 params->result = AUDCLNT_E_DEVICE_INVALIDATED;
1199 return STATUS_SUCCESS;
1202 ai.dev = -1;
1203 if(ioctl(fd, SNDCTL_ENGINEINFO, &ai) < 0){
1204 WARN("Unable to get audio info for device %s: %d (%s)\n", params->device, errno, strerror(errno));
1205 close(fd);
1206 params->result = E_FAIL;
1207 return STATUS_SUCCESS;
1209 close(fd);
1211 if(params->flow == eRender)
1212 formats = ai.oformats;
1213 else
1214 formats = ai.iformats;
1216 fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1217 if(formats & AFMT_S16_LE){
1218 fmt->Format.wBitsPerSample = 16;
1219 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1220 #ifdef AFMT_FLOAT
1221 }else if(formats & AFMT_FLOAT){
1222 fmt->Format.wBitsPerSample = 32;
1223 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1224 #endif
1225 }else if(formats & AFMT_U8){
1226 fmt->Format.wBitsPerSample = 8;
1227 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1228 }else if(formats & AFMT_S32_LE){
1229 fmt->Format.wBitsPerSample = 32;
1230 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1231 }else if(formats & AFMT_S24_LE){
1232 fmt->Format.wBitsPerSample = 24;
1233 fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
1234 }else{
1235 WARN("Didn't recognize any available OSS formats: %x\n", formats);
1236 params->result = E_FAIL;
1237 return STATUS_SUCCESS;
1240 /* some OSS drivers are buggy, so set reasonable defaults if
1241 * the reported values seem wacky */
1242 fmt->Format.nChannels = max(ai.max_channels, ai.min_channels);
1243 if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
1244 fmt->Format.nChannels = 2;
1246 /* For most hardware on Windows, users must choose a configuration with an even
1247 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1248 * channels, but those channels are still reported to applications from
1249 * GetMixFormat! Some applications behave badly if given an odd number of
1250 * channels (e.g. 2.1). */
1251 if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1))
1253 if(fmt->Format.nChannels < ai.max_channels)
1254 fmt->Format.nChannels += 1;
1255 else
1256 /* We could "fake" more channels and downmix the emulated channels,
1257 * but at that point you really ought to tweak your OSS setup or
1258 * just use PulseAudio. */
1259 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
1262 if(ai.max_rate == 0)
1263 fmt->Format.nSamplesPerSec = 44100;
1264 else
1265 fmt->Format.nSamplesPerSec = min(ai.max_rate, 44100);
1266 if(fmt->Format.nSamplesPerSec < ai.min_rate)
1267 fmt->Format.nSamplesPerSec = ai.min_rate;
1269 fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
1271 fmt->Format.nBlockAlign = (fmt->Format.wBitsPerSample *
1272 fmt->Format.nChannels) / 8;
1273 fmt->Format.nAvgBytesPerSec = fmt->Format.nSamplesPerSec *
1274 fmt->Format.nBlockAlign;
1276 fmt->Samples.wValidBitsPerSample = fmt->Format.wBitsPerSample;
1277 fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1279 params->result = S_OK;
1280 return STATUS_SUCCESS;
1283 static NTSTATUS oss_get_device_period(void *args)
1285 struct get_device_period_params *params = args;
1287 if (params->def_period)
1288 *params->def_period = def_period;
1289 if (params->min_period)
1290 *params->min_period = min_period;
1292 params->result = S_OK;
1294 return STATUS_SUCCESS;
1297 static NTSTATUS oss_get_buffer_size(void *args)
1299 struct get_buffer_size_params *params = args;
1300 struct oss_stream *stream = handle_get_stream(params->stream);
1302 oss_lock(stream);
1304 *params->frames = stream->bufsize_frames;
1306 return oss_unlock_result(stream, &params->result, S_OK);
1309 static NTSTATUS oss_get_latency(void *args)
1311 struct get_latency_params *params = args;
1312 struct oss_stream *stream = handle_get_stream(params->stream);
1314 oss_lock(stream);
1316 /* pretend we process audio in Period chunks, so max latency includes
1317 * the period time. Some native machines add .6666ms in shared mode. */
1318 *params->latency = stream->period + 6666;
1320 return oss_unlock_result(stream, &params->result, S_OK);
1323 static NTSTATUS oss_get_current_padding(void *args)
1325 struct get_current_padding_params *params = args;
1326 struct oss_stream *stream = handle_get_stream(params->stream);
1328 oss_lock(stream);
1330 *params->padding = stream->held_frames;
1332 return oss_unlock_result(stream, &params->result, S_OK);
1335 static NTSTATUS oss_get_next_packet_size(void *args)
1337 struct get_next_packet_size_params *params = args;
1338 struct oss_stream *stream = handle_get_stream(params->stream);
1339 UINT32 *frames = params->frames;
1341 oss_lock(stream);
1343 *frames = stream->held_frames < stream->period_frames ? 0 : stream->period_frames;
1345 return oss_unlock_result(stream, &params->result, S_OK);
1348 static NTSTATUS oss_get_frequency(void *args)
1350 struct get_frequency_params *params = args;
1351 struct oss_stream *stream = handle_get_stream(params->stream);
1352 UINT64 *freq = params->freq;
1354 oss_lock(stream);
1356 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1357 *freq = (UINT64)stream->fmt->nSamplesPerSec * stream->fmt->nBlockAlign;
1358 else
1359 *freq = stream->fmt->nSamplesPerSec;
1361 return oss_unlock_result(stream, &params->result, S_OK);
1364 static NTSTATUS oss_get_position(void *args)
1366 struct get_position_params *params = args;
1367 struct oss_stream *stream = handle_get_stream(params->stream);
1368 UINT64 *pos = params->pos, *qpctime = params->qpctime;
1370 if (params->device) {
1371 FIXME("Device position reporting not implemented\n");
1372 params->result = E_NOTIMPL;
1373 return STATUS_SUCCESS;
1376 oss_lock(stream);
1378 if(stream->flow == eRender){
1379 *pos = stream->written_frames - stream->held_frames;
1380 if(*pos < stream->last_pos_frames)
1381 *pos = stream->last_pos_frames;
1382 }else if(stream->flow == eCapture){
1383 audio_buf_info bi;
1384 UINT32 held;
1386 if(ioctl(stream->fd, SNDCTL_DSP_GETISPACE, &bi) < 0){
1387 TRACE("GETISPACE failed: %d (%s)\n", errno, strerror(errno));
1388 held = 0;
1389 }else{
1390 if(bi.bytes <= bi.fragsize)
1391 held = 0;
1392 else
1393 held = bi.bytes / stream->fmt->nBlockAlign;
1396 *pos = stream->written_frames + held;
1399 stream->last_pos_frames = *pos;
1401 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos));
1402 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1403 *pos *= stream->fmt->nBlockAlign;
1405 if(qpctime){
1406 LARGE_INTEGER stamp, freq;
1407 NtQueryPerformanceCounter(&stamp, &freq);
1408 *qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1411 return oss_unlock_result(stream, &params->result, S_OK);
1414 static NTSTATUS oss_set_volumes(void *args)
1416 struct set_volumes_params *params = args;
1417 struct oss_stream *stream = handle_get_stream(params->stream);
1418 UINT16 i;
1420 if (params->master_volume) {
1421 for (i = 0; i < stream->fmt->nChannels; ++i) {
1422 if (params->master_volume * params->volumes[i] * params->session_volumes[i] != 1.0f) {
1423 FIXME("Volume control is not implemented\n");
1424 break;
1429 oss_lock(stream);
1430 stream->mute = !params->master_volume;
1431 oss_unlock(stream);
1433 return STATUS_SUCCESS;
1436 static NTSTATUS oss_set_event_handle(void *args)
1438 struct set_event_handle_params *params = args;
1439 struct oss_stream *stream = handle_get_stream(params->stream);
1441 oss_lock(stream);
1443 if(!(stream->flags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))
1444 return oss_unlock_result(stream, &params->result, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED);
1446 if (stream->event){
1447 FIXME("called twice\n");
1448 return oss_unlock_result(stream, &params->result, HRESULT_FROM_WIN32(ERROR_INVALID_NAME));
1451 stream->event = params->event;
1453 return oss_unlock_result(stream, &params->result, S_OK);
1456 static NTSTATUS oss_is_started(void *args)
1458 struct is_started_params *params = args;
1459 struct oss_stream *stream = handle_get_stream(params->stream);
1461 oss_lock(stream);
1463 return oss_unlock_result(stream, &params->result, stream->playing ? S_OK : S_FALSE);
1466 /* Aux driver */
1468 static unsigned int num_aux;
1470 #define MIXER_DEV "/dev/mixer"
1472 static UINT aux_init(void)
1474 int mixer;
1476 TRACE("()\n");
1478 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1480 WARN("mixer device not available !\n");
1481 num_aux = 0;
1483 else
1485 close(mixer);
1486 num_aux = 6;
1488 return 0;
1491 static UINT aux_exit(void)
1493 TRACE("()\n");
1494 return 0;
1497 static UINT aux_get_devcaps(WORD dev_id, AUXCAPSW *caps, UINT size)
1499 int mixer, volume;
1500 static const WCHAR ini[] = {'O','S','S',' ','A','u','x',' ','#','0',0};
1502 TRACE("(%04X, %p, %u);\n", dev_id, caps, size);
1503 if (caps == NULL) return MMSYSERR_NOTENABLED;
1504 if (dev_id >= num_aux) return MMSYSERR_BADDEVICEID;
1505 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1507 WARN("mixer device not available !\n");
1508 return MMSYSERR_NOTENABLED;
1510 if (ioctl(mixer, SOUND_MIXER_READ_LINE, &volume) == -1)
1512 close(mixer);
1513 WARN("unable to read mixer !\n");
1514 return MMSYSERR_NOTENABLED;
1516 close(mixer);
1517 caps->wMid = 0xAA;
1518 caps->wPid = 0x55 + dev_id;
1519 caps->vDriverVersion = 0x0100;
1520 memcpy(caps->szPname, ini, sizeof(ini));
1521 caps->szPname[9] = '0' + dev_id; /* 6 at max */
1522 caps->wTechnology = (dev_id == 2) ? AUXCAPS_CDAUDIO : AUXCAPS_AUXIN;
1523 caps->wReserved1 = 0;
1524 caps->dwSupport = AUXCAPS_VOLUME | AUXCAPS_LRVOLUME;
1526 return MMSYSERR_NOERROR;
1529 static UINT aux_get_volume(WORD dev_id, UINT *vol)
1531 int mixer, volume, left, right, cmd;
1533 TRACE("(%04X, %p);\n", dev_id, vol);
1534 if (vol == NULL) return MMSYSERR_NOTENABLED;
1535 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1537 WARN("mixer device not available !\n");
1538 return MMSYSERR_NOTENABLED;
1540 switch(dev_id)
1542 case 0:
1543 TRACE("SOUND_MIXER_READ_PCM !\n");
1544 cmd = SOUND_MIXER_READ_PCM;
1545 break;
1546 case 1:
1547 TRACE("SOUND_MIXER_READ_SYNTH !\n");
1548 cmd = SOUND_MIXER_READ_SYNTH;
1549 break;
1550 case 2:
1551 TRACE("SOUND_MIXER_READ_CD !\n");
1552 cmd = SOUND_MIXER_READ_CD;
1553 break;
1554 case 3:
1555 TRACE("SOUND_MIXER_READ_LINE !\n");
1556 cmd = SOUND_MIXER_READ_LINE;
1557 break;
1558 case 4:
1559 TRACE("SOUND_MIXER_READ_MIC !\n");
1560 cmd = SOUND_MIXER_READ_MIC;
1561 break;
1562 case 5:
1563 TRACE("SOUND_MIXER_READ_VOLUME !\n");
1564 cmd = SOUND_MIXER_READ_VOLUME;
1565 break;
1566 default:
1567 WARN("invalid device id=%04X !\n", dev_id);
1568 close(mixer);
1569 return MMSYSERR_NOTENABLED;
1571 if (ioctl(mixer, cmd, &volume) == -1)
1573 WARN("unable to read mixer !\n");
1574 close(mixer);
1575 return MMSYSERR_NOTENABLED;
1577 close(mixer);
1578 left = LOBYTE(LOWORD(volume));
1579 right = HIBYTE(LOWORD(volume));
1580 TRACE("left=%d right=%d !\n", left, right);
1581 *vol = MAKELONG((left * 0xFFFFL) / 100, (right * 0xFFFFL) / 100);
1582 return MMSYSERR_NOERROR;
1585 static UINT aux_set_volume(WORD dev_id, UINT vol)
1587 int mixer;
1588 int volume, left, right;
1589 int cmd;
1591 TRACE("(%04X, %08X);\n", dev_id, vol);
1593 left = (LOWORD(vol) * 100) >> 16;
1594 right = (HIWORD(vol) * 100) >> 16;
1595 volume = (right << 8) | left;
1597 if ((mixer = open(MIXER_DEV, O_RDWR)) < 0)
1599 WARN("mixer device not available !\n");
1600 return MMSYSERR_NOTENABLED;
1603 switch(dev_id)
1605 case 0:
1606 TRACE("SOUND_MIXER_WRITE_PCM !\n");
1607 cmd = SOUND_MIXER_WRITE_PCM;
1608 break;
1609 case 1:
1610 TRACE("SOUND_MIXER_WRITE_SYNTH !\n");
1611 cmd = SOUND_MIXER_WRITE_SYNTH;
1612 break;
1613 case 2:
1614 TRACE("SOUND_MIXER_WRITE_CD !\n");
1615 cmd = SOUND_MIXER_WRITE_CD;
1616 break;
1617 case 3:
1618 TRACE("SOUND_MIXER_WRITE_LINE !\n");
1619 cmd = SOUND_MIXER_WRITE_LINE;
1620 break;
1621 case 4:
1622 TRACE("SOUND_MIXER_WRITE_MIC !\n");
1623 cmd = SOUND_MIXER_WRITE_MIC;
1624 break;
1625 case 5:
1626 TRACE("SOUND_MIXER_WRITE_VOLUME !\n");
1627 cmd = SOUND_MIXER_WRITE_VOLUME;
1628 break;
1629 default:
1630 WARN("invalid device id=%04X !\n", dev_id);
1631 close(mixer);
1632 return MMSYSERR_NOTENABLED;
1634 if (ioctl(mixer, cmd, &volume) == -1)
1636 WARN("unable to set mixer !\n");
1637 close(mixer);
1638 return MMSYSERR_NOTENABLED;
1640 close(mixer);
1641 return MMSYSERR_NOERROR;
1644 static NTSTATUS oss_aux_message(void *args)
1646 struct aux_message_params *params = args;
1648 switch (params->msg)
1650 case DRVM_INIT:
1651 *params->err = aux_init();
1652 break;
1653 case DRVM_EXIT:
1654 *params->err = aux_exit();
1655 break;
1656 case DRVM_ENABLE:
1657 case DRVM_DISABLE:
1658 /* FIXME: Pretend this is supported */
1659 *params->err = 0;
1660 break;
1661 case AUXDM_GETDEVCAPS:
1662 *params->err = aux_get_devcaps(params->dev_id, (AUXCAPSW *)params->param_1, params->param_2);
1663 break;
1664 case AUXDM_GETNUMDEVS:
1665 TRACE("return %d;\n", num_aux);
1666 *params->err = num_aux;
1667 break;
1668 case AUXDM_GETVOLUME:
1669 *params->err = aux_get_volume(params->dev_id, (UINT *)params->param_1);
1670 break;
1671 case AUXDM_SETVOLUME:
1672 *params->err = aux_set_volume(params->dev_id, params->param_1);
1673 break;
1674 default:
1675 WARN("unknown message !\n");
1676 *params->err = MMSYSERR_NOTSUPPORTED;
1677 break;
1680 return STATUS_SUCCESS;
1683 unixlib_entry_t __wine_unix_call_funcs[] =
1685 oss_not_implemented,
1686 oss_not_implemented,
1687 oss_not_implemented,
1688 oss_get_endpoint_ids,
1689 oss_create_stream,
1690 oss_release_stream,
1691 oss_start,
1692 oss_stop,
1693 oss_reset,
1694 oss_timer_loop,
1695 oss_get_render_buffer,
1696 oss_release_render_buffer,
1697 oss_get_capture_buffer,
1698 oss_release_capture_buffer,
1699 oss_is_format_supported,
1700 oss_get_mix_format,
1701 oss_get_device_period,
1702 oss_get_buffer_size,
1703 oss_get_latency,
1704 oss_get_current_padding,
1705 oss_get_next_packet_size,
1706 oss_get_frequency,
1707 oss_get_position,
1708 oss_set_volumes,
1709 oss_set_event_handle,
1710 oss_test_connect,
1711 oss_is_started,
1712 oss_not_implemented,
1713 oss_not_implemented,
1714 oss_midi_release,
1715 oss_midi_out_message,
1716 oss_midi_in_message,
1717 oss_midi_notify_wait,
1718 oss_aux_message,
1721 #ifdef _WIN64
1723 typedef UINT PTR32;
1725 static NTSTATUS oss_wow64_test_connect(void *args)
1727 struct
1729 PTR32 name;
1730 enum driver_priority priority;
1731 } *params32 = args;
1732 struct test_connect_params params =
1734 .name = ULongToPtr(params32->name),
1736 oss_test_connect(&params);
1737 params32->priority = params.priority;
1738 return STATUS_SUCCESS;
1741 static NTSTATUS oss_wow64_get_endpoint_ids(void *args)
1743 struct
1745 EDataFlow flow;
1746 PTR32 endpoints;
1747 unsigned int size;
1748 HRESULT result;
1749 unsigned int num;
1750 unsigned int default_idx;
1751 } *params32 = args;
1752 struct get_endpoint_ids_params params =
1754 .flow = params32->flow,
1755 .endpoints = ULongToPtr(params32->endpoints),
1756 .size = params32->size
1758 oss_get_endpoint_ids(&params);
1759 params32->size = params.size;
1760 params32->result = params.result;
1761 params32->num = params.num;
1762 params32->default_idx = params.default_idx;
1763 return STATUS_SUCCESS;
1766 static NTSTATUS oss_wow64_create_stream(void *args)
1768 struct
1770 PTR32 name;
1771 PTR32 device;
1772 EDataFlow flow;
1773 AUDCLNT_SHAREMODE share;
1774 UINT flags;
1775 REFERENCE_TIME duration;
1776 REFERENCE_TIME period;
1777 PTR32 fmt;
1778 HRESULT result;
1779 PTR32 channel_count;
1780 PTR32 stream;
1781 } *params32 = args;
1782 struct create_stream_params params =
1784 .name = ULongToPtr(params32->name),
1785 .device = ULongToPtr(params32->device),
1786 .flow = params32->flow,
1787 .share = params32->share,
1788 .flags = params32->flags,
1789 .duration = params32->duration,
1790 .period = params32->period,
1791 .fmt = ULongToPtr(params32->fmt),
1792 .channel_count = ULongToPtr(params32->channel_count),
1793 .stream = ULongToPtr(params32->stream)
1795 oss_create_stream(&params);
1796 params32->result = params.result;
1797 return STATUS_SUCCESS;
1800 static NTSTATUS oss_wow64_release_stream(void *args)
1802 struct
1804 stream_handle stream;
1805 PTR32 timer_thread;
1806 HRESULT result;
1807 } *params32 = args;
1808 struct release_stream_params params =
1810 .stream = params32->stream,
1811 .timer_thread = ULongToHandle(params32->timer_thread)
1813 oss_release_stream(&params);
1814 params32->result = params.result;
1815 return STATUS_SUCCESS;
1818 static NTSTATUS oss_wow64_get_render_buffer(void *args)
1820 struct
1822 stream_handle stream;
1823 UINT32 frames;
1824 HRESULT result;
1825 PTR32 data;
1826 } *params32 = args;
1827 BYTE *data = NULL;
1828 struct get_render_buffer_params params =
1830 .stream = params32->stream,
1831 .frames = params32->frames,
1832 .data = &data
1834 oss_get_render_buffer(&params);
1835 params32->result = params.result;
1836 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1837 return STATUS_SUCCESS;
1840 static NTSTATUS oss_wow64_get_capture_buffer(void *args)
1842 struct
1844 stream_handle stream;
1845 HRESULT result;
1846 PTR32 data;
1847 PTR32 frames;
1848 PTR32 flags;
1849 PTR32 devpos;
1850 PTR32 qpcpos;
1851 } *params32 = args;
1852 BYTE *data = NULL;
1853 struct get_capture_buffer_params params =
1855 .stream = params32->stream,
1856 .data = &data,
1857 .frames = ULongToPtr(params32->frames),
1858 .flags = ULongToPtr(params32->flags),
1859 .devpos = ULongToPtr(params32->devpos),
1860 .qpcpos = ULongToPtr(params32->qpcpos)
1862 oss_get_capture_buffer(&params);
1863 params32->result = params.result;
1864 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1865 return STATUS_SUCCESS;
1868 static NTSTATUS oss_wow64_is_format_supported(void *args)
1870 struct
1872 PTR32 device;
1873 EDataFlow flow;
1874 AUDCLNT_SHAREMODE share;
1875 PTR32 fmt_in;
1876 PTR32 fmt_out;
1877 HRESULT result;
1878 } *params32 = args;
1879 struct is_format_supported_params params =
1881 .device = ULongToPtr(params32->device),
1882 .flow = params32->flow,
1883 .share = params32->share,
1884 .fmt_in = ULongToPtr(params32->fmt_in),
1885 .fmt_out = ULongToPtr(params32->fmt_out)
1887 oss_is_format_supported(&params);
1888 params32->result = params.result;
1889 return STATUS_SUCCESS;
1892 static NTSTATUS oss_wow64_get_mix_format(void *args)
1894 struct
1896 PTR32 device;
1897 EDataFlow flow;
1898 PTR32 fmt;
1899 HRESULT result;
1900 } *params32 = args;
1901 struct get_mix_format_params params =
1903 .device = ULongToPtr(params32->device),
1904 .flow = params32->flow,
1905 .fmt = ULongToPtr(params32->fmt)
1907 oss_get_mix_format(&params);
1908 params32->result = params.result;
1909 return STATUS_SUCCESS;
1912 static NTSTATUS oss_wow64_get_device_period(void *args)
1914 struct
1916 PTR32 device;
1917 EDataFlow flow;
1918 HRESULT result;
1919 PTR32 def_period;
1920 PTR32 min_period;
1921 } *params32 = args;
1922 struct get_device_period_params params =
1924 .device = ULongToPtr(params32->device),
1925 .flow = params32->flow,
1926 .def_period = ULongToPtr(params32->def_period),
1927 .min_period = ULongToPtr(params32->min_period),
1929 oss_get_device_period(&params);
1930 params32->result = params.result;
1931 return STATUS_SUCCESS;
1934 static NTSTATUS oss_wow64_get_buffer_size(void *args)
1936 struct
1938 stream_handle stream;
1939 HRESULT result;
1940 PTR32 frames;
1941 } *params32 = args;
1942 struct get_buffer_size_params params =
1944 .stream = params32->stream,
1945 .frames = ULongToPtr(params32->frames)
1947 oss_get_buffer_size(&params);
1948 params32->result = params.result;
1949 return STATUS_SUCCESS;
1952 static NTSTATUS oss_wow64_get_latency(void *args)
1954 struct
1956 stream_handle stream;
1957 HRESULT result;
1958 PTR32 latency;
1959 } *params32 = args;
1960 struct get_latency_params params =
1962 .stream = params32->stream,
1963 .latency = ULongToPtr(params32->latency)
1965 oss_get_latency(&params);
1966 params32->result = params.result;
1967 return STATUS_SUCCESS;
1970 static NTSTATUS oss_wow64_get_current_padding(void *args)
1972 struct
1974 stream_handle stream;
1975 HRESULT result;
1976 PTR32 padding;
1977 } *params32 = args;
1978 struct get_current_padding_params params =
1980 .stream = params32->stream,
1981 .padding = ULongToPtr(params32->padding)
1983 oss_get_current_padding(&params);
1984 params32->result = params.result;
1985 return STATUS_SUCCESS;
1988 static NTSTATUS oss_wow64_get_next_packet_size(void *args)
1990 struct
1992 stream_handle stream;
1993 HRESULT result;
1994 PTR32 frames;
1995 } *params32 = args;
1996 struct get_next_packet_size_params params =
1998 .stream = params32->stream,
1999 .frames = ULongToPtr(params32->frames)
2001 oss_get_next_packet_size(&params);
2002 params32->result = params.result;
2003 return STATUS_SUCCESS;
2006 static NTSTATUS oss_wow64_get_frequency(void *args)
2008 struct
2010 stream_handle stream;
2011 HRESULT result;
2012 PTR32 freq;
2013 } *params32 = args;
2014 struct get_frequency_params params =
2016 .stream = params32->stream,
2017 .freq = ULongToPtr(params32->freq)
2019 oss_get_frequency(&params);
2020 params32->result = params.result;
2021 return STATUS_SUCCESS;
2024 static NTSTATUS oss_wow64_get_position(void *args)
2026 struct
2028 stream_handle stream;
2029 BOOL device;
2030 HRESULT result;
2031 PTR32 pos;
2032 PTR32 qpctime;
2033 } *params32 = args;
2034 struct get_position_params params =
2036 .stream = params32->stream,
2037 .device = params32->device,
2038 .pos = ULongToPtr(params32->pos),
2039 .qpctime = ULongToPtr(params32->qpctime)
2041 oss_get_position(&params);
2042 params32->result = params.result;
2043 return STATUS_SUCCESS;
2046 static NTSTATUS oss_wow64_set_volumes(void *args)
2048 struct
2050 stream_handle stream;
2051 float master_volume;
2052 PTR32 volumes;
2053 PTR32 session_volumes;
2054 } *params32 = args;
2055 struct set_volumes_params params =
2057 .stream = params32->stream,
2058 .master_volume = params32->master_volume,
2059 .volumes = ULongToPtr(params32->volumes),
2060 .session_volumes = ULongToPtr(params32->session_volumes),
2062 return oss_set_volumes(&params);
2065 static NTSTATUS oss_wow64_set_event_handle(void *args)
2067 struct
2069 stream_handle stream;
2070 PTR32 event;
2071 HRESULT result;
2072 } *params32 = args;
2073 struct set_event_handle_params params =
2075 .stream = params32->stream,
2076 .event = ULongToHandle(params32->event)
2079 oss_set_event_handle(&params);
2080 params32->result = params.result;
2081 return STATUS_SUCCESS;
2084 static NTSTATUS oss_wow64_aux_message(void *args)
2086 struct
2088 UINT dev_id;
2089 UINT msg;
2090 UINT user;
2091 UINT param_1;
2092 UINT param_2;
2093 PTR32 err;
2094 } *params32 = args;
2095 struct aux_message_params params =
2097 .dev_id = params32->dev_id,
2098 .msg = params32->msg,
2099 .user = params32->user,
2100 .param_1 = params32->param_1,
2101 .param_2 = params32->param_2,
2102 .err = ULongToPtr(params32->err),
2104 return oss_aux_message(&params);
2107 unixlib_entry_t __wine_unix_call_wow64_funcs[] =
2109 oss_not_implemented,
2110 oss_not_implemented,
2111 oss_not_implemented,
2112 oss_wow64_get_endpoint_ids,
2113 oss_wow64_create_stream,
2114 oss_wow64_release_stream,
2115 oss_start,
2116 oss_stop,
2117 oss_reset,
2118 oss_timer_loop,
2119 oss_wow64_get_render_buffer,
2120 oss_release_render_buffer,
2121 oss_wow64_get_capture_buffer,
2122 oss_release_capture_buffer,
2123 oss_wow64_is_format_supported,
2124 oss_wow64_get_mix_format,
2125 oss_wow64_get_device_period,
2126 oss_wow64_get_buffer_size,
2127 oss_wow64_get_latency,
2128 oss_wow64_get_current_padding,
2129 oss_wow64_get_next_packet_size,
2130 oss_wow64_get_frequency,
2131 oss_wow64_get_position,
2132 oss_wow64_set_volumes,
2133 oss_wow64_set_event_handle,
2134 oss_wow64_test_connect,
2135 oss_is_started,
2136 oss_not_implemented,
2137 oss_not_implemented,
2138 oss_midi_release,
2139 oss_wow64_midi_out_message,
2140 oss_wow64_midi_in_message,
2141 oss_wow64_midi_notify_wait,
2142 oss_wow64_aux_message,
2145 #endif /* _WIN64 */