4 * Copyright 2011 Andrew Eikum for CodeWeavers
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
27 #include <sys/types.h>
29 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
37 #define WIN32_NO_STATUS
40 #include "audioclient.h"
43 #include "wine/debug.h"
44 #include "wine/unixlib.h"
53 AUDCLNT_SHAREMODE share
;
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 */
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
)
87 /* We want to deal with a positive divisor to simplify the logic. */
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
;
98 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
100 if (ret
> 2147483647 || ret
< -2147483647) return -1;
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
)
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
;
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);
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");
150 params
->priority
= Priority_Unavailable
;
151 return STATUS_SUCCESS
;
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
;
179 strcpy(dest
, devnode
);
180 dot
= strrchr(dest
, '.');
184 slash
= strrchr(dest
, '/');
185 if(slash
&& dot
< slash
)
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
])
205 fd
= open_device("/dev/dsp", flow
);
207 WARN("Couldn't open default device!\n");
212 if((err
= ioctl(fd
, SNDCTL_ENGINEINFO
, &ai
)) < 0){
213 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err
, strerror(errno
));
219 TRACE("Default devnode: %s\n", ai
.devnode
);
220 oss_clean_devnode(device
, ai
.devnode
);
224 static NTSTATUS
oss_get_endpoint_ids(void *args
)
226 struct get_endpoint_ids_params
*params
= args
;
229 static int print_once
= 0;
230 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
231 static const WCHAR inW
[] = {'I','n',':',' ',0};
234 WCHAR name
[ARRAY_SIZE(ai
.name
) + ARRAY_SIZE(outW
)];
235 char device
[OSS_DEVNODE_SIZE
];
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
;
242 mixer_fd
= open("/dev/mixer", O_RDONLY
, 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){
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
;
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
);
274 if(sysinfo
.numaudios
<= 0){
275 WARN("No audio devices!\n");
277 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
278 return STATUS_SUCCESS
;
281 info
= malloc(sysinfo
.numaudios
* sizeof(*info
));
284 params
->result
= E_OUTOFMEMORY
;
285 return STATUS_SUCCESS
;
288 get_default_device(params
->flow
, default_device
);
291 for(i
= 0; i
< sysinfo
.numaudios
; ++i
){
292 char devnode
[OSS_DEVNODE_SIZE
];
296 memset(&ai
, 0, sizeof(ai
));
298 if(ioctl(mixer_fd
, SNDCTL_AUDIOINFO
, &ai
) < 0){
299 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i
, errno
,
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
))
313 fd
= open_device(devnode
, params
->flow
);
315 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
316 devnode
, errno
, strerror(errno
));
321 if((params
->flow
== eCapture
&& !(ai
.caps
& PCM_CAP_INPUT
)) ||
322 (params
->flow
== eRender
&& !(ai
.caps
& PCM_CAP_OUTPUT
)))
325 strcpy(info
[num
].device
, devnode
);
327 if(params
->flow
== eRender
){
329 prefix_len
= ARRAY_SIZE(outW
) - 1;
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
))
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;
364 params
->default_idx
= default_idx
;
366 if(needed
> params
->size
){
367 params
->size
= needed
;
368 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
370 params
->result
= S_OK
;
372 return STATUS_SUCCESS
;
375 static UINT
get_channel_mask(unsigned int channels
)
381 return KSAUDIO_SPEAKER_MONO
;
383 return KSAUDIO_SPEAKER_STEREO
;
385 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
387 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
389 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
391 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
393 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
395 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
397 FIXME("Unknown speaker configuration: %u\n", channels
);
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
){
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)
435 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
437 WAVEFORMATEXTENSIBLE
*ret
;
440 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
441 size
= sizeof(WAVEFORMATEXTENSIBLE
);
443 size
= sizeof(WAVEFORMATEX
);
449 memcpy(ret
, fmt
, size
);
451 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
456 static HRESULT
setup_oss_device(AUDCLNT_SHAREMODE share
, int fd
,
457 const WAVEFORMATEX
*fmt
, WAVEFORMATEXTENSIBLE
*out
)
459 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
463 WAVEFORMATEXTENSIBLE
*closest
;
465 tmp
= oss_format
= get_oss_format(fmt
);
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
));
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
))
483 if(fmt
->nChannels
== 0)
484 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
486 closest
= clone_format(fmt
);
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
));
496 tenth
= fmt
->nSamplesPerSec
* 0.1;
497 if(tmp
> fmt
->nSamplesPerSec
+ tenth
|| tmp
< fmt
->nSamplesPerSec
- tenth
){
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
));
508 if(tmp
!= fmt
->nChannels
){
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
))
522 if(share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
523 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
524 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
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
));
542 TRACE("returning: %08x\n", (unsigned)ret
);
546 static ULONG_PTR
zero_bits(void)
549 return !NtCurrentTeb()->WowTebOffset
? 0 : 0x7fffffff;
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
;
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
;
570 if (fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
571 (fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
))
572 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
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
));
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
);
606 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
607 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
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
;
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
))
635 fmtex
= clone_format(params
->fmt
);
637 params
->result
= E_OUTOFMEMORY
;
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
;
655 stream
->share
= params
->share
;
656 stream
->flags
= params
->flags
;
657 stream
->oss_bufsize_bytes
= 0;
660 if(FAILED(params
->result
)){
661 if(stream
->fd
>= 0) close(stream
->fd
);
662 if(stream
->local_buffer
){
664 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
666 pthread_mutex_destroy(&stream
->lock
);
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
);
682 if(params
->timer_thread
){
683 stream
->please_quit
= TRUE
;
684 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
685 NtClose(params
->timer_thread
);
689 if(stream
->local_buffer
){
691 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
693 if(stream
->tmp_buffer
){
695 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
698 pthread_mutex_destroy(&stream
->lock
);
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
);
712 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
713 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
716 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
718 stream
->playing
= TRUE
;
720 return oss_unlock_result(stream
, ¶ms
->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
);
731 return oss_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
733 stream
->playing
= FALSE
;
734 stream
->in_oss_frames
= 0;
736 return oss_unlock_result(stream
, ¶ms
->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
);
747 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
749 if(stream
->getbuf_last
)
750 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
752 if(stream
->flow
== eRender
){
753 stream
->written_frames
= 0;
754 stream
->last_pos_frames
= 0;
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
, ¶ms
->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
);
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
;
785 if(ioctl(stream
->fd
, SNDCTL_DSP_GETOSPACE
, &bi
) < 0){
786 WARN("GETOSPACE failed: %d (%s)\n", errno
, strerror(errno
));
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
;
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
;
807 while(write_limit
+ in_oss_frames
< max_period
* 3)
808 write_limit
+= max_period
;
812 /* vvvvvv - in_oss_frames
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
)
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
;
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
;
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
));
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 */
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
;
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
));
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
;
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
,
898 WARN("read failed: %d (%s)\n", errno
, strerror(errno
));
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
;
921 delay
.QuadPart
= -stream
->period
;
922 NtQueryPerformanceCounter(&now
, NULL
);
923 next
.QuadPart
= now
.QuadPart
+ stream
->period
;
925 while(!stream
->please_quit
){
927 if(stream
->flow
== eRender
&& stream
->held_frames
)
928 oss_write_data(stream
);
929 else if(stream
->flow
== eCapture
)
930 oss_read_data(stream
);
933 NtSetEvent(stream
->event
, NULL
);
936 NtDelayExecution(FALSE
, &delay
);
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
;
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
;
964 if(stream
->getbuf_last
)
965 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
968 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
970 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
971 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
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
){
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
, ¶ms
->result
, E_OUTOFMEMORY
);
988 stream
->tmp_buffer_frames
= frames
;
990 *data
= stream
->tmp_buffer
;
991 stream
->getbuf_last
= -frames
;
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
, ¶ms
->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
);
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
;
1030 if(!written_frames
){
1031 stream
->getbuf_last
= 0;
1032 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1035 if(!stream
->getbuf_last
)
1036 return oss_unlock_result(stream
, ¶ms
->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
, ¶ms
->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
);
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
, ¶ms
->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
;
1072 if(stream
->getbuf_last
)
1073 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1075 if(stream
->held_frames
< stream
->period_frames
){
1077 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
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
){
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
, ¶ms
->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
);
1110 *data
= stream
->local_buffer
+
1111 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1113 stream
->getbuf_last
= *frames
;
1116 *devpos
= stream
->written_frames
;
1118 LARGE_INTEGER stamp
, freq
;
1119 NtQueryPerformanceCounter(&stamp
, &freq
);
1120 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1123 return oss_unlock_result(stream
, ¶ms
->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
;
1135 stream
->getbuf_last
= 0;
1136 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1139 if(!stream
->getbuf_last
)
1140 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1142 if(stream
->getbuf_last
!= done
)
1143 return oss_unlock_result(stream
, ¶ms
->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
, ¶ms
->result
, S_OK
);
1154 static NTSTATUS
oss_is_format_supported(void *args
)
1156 struct is_format_supported_params
*params
= args
;
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
);
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
);
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
;
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
);
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
;
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
));
1206 params
->result
= E_FAIL
;
1207 return STATUS_SUCCESS
;
1211 if(params
->flow
== eRender
)
1212 formats
= ai
.oformats
;
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
;
1221 }else if(formats
& AFMT_FLOAT
){
1222 fmt
->Format
.wBitsPerSample
= 32;
1223 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
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
;
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;
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;
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
);
1304 *params
->frames
= stream
->bufsize_frames
;
1306 return oss_unlock_result(stream
, ¶ms
->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
);
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
, ¶ms
->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
);
1330 *params
->padding
= stream
->held_frames
;
1332 return oss_unlock_result(stream
, ¶ms
->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
;
1343 *frames
= stream
->held_frames
< stream
->period_frames
? 0 : stream
->period_frames
;
1345 return oss_unlock_result(stream
, ¶ms
->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
;
1356 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
1357 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
1359 *freq
= stream
->fmt
->nSamplesPerSec
;
1361 return oss_unlock_result(stream
, ¶ms
->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
;
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
){
1386 if(ioctl(stream
->fd
, SNDCTL_DSP_GETISPACE
, &bi
) < 0){
1387 TRACE("GETISPACE failed: %d (%s)\n", errno
, strerror(errno
));
1390 if(bi
.bytes
<= bi
.fragsize
)
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
;
1406 LARGE_INTEGER stamp
, freq
;
1407 NtQueryPerformanceCounter(&stamp
, &freq
);
1408 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1411 return oss_unlock_result(stream
, ¶ms
->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
);
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");
1430 stream
->mute
= !params
->master_volume
;
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
);
1443 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1444 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
1447 FIXME("called twice\n");
1448 return oss_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
1451 stream
->event
= params
->event
;
1453 return oss_unlock_result(stream
, ¶ms
->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
);
1463 return oss_unlock_result(stream
, ¶ms
->result
, stream
->playing
? S_OK
: S_FALSE
);
1468 static unsigned int num_aux
;
1470 #define MIXER_DEV "/dev/mixer"
1472 static UINT
aux_init(void)
1478 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1480 WARN("mixer device not available !\n");
1491 static UINT
aux_exit(void)
1497 static UINT
aux_get_devcaps(WORD dev_id
, AUXCAPSW
*caps
, UINT size
)
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)
1513 WARN("unable to read mixer !\n");
1514 return MMSYSERR_NOTENABLED
;
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
;
1543 TRACE("SOUND_MIXER_READ_PCM !\n");
1544 cmd
= SOUND_MIXER_READ_PCM
;
1547 TRACE("SOUND_MIXER_READ_SYNTH !\n");
1548 cmd
= SOUND_MIXER_READ_SYNTH
;
1551 TRACE("SOUND_MIXER_READ_CD !\n");
1552 cmd
= SOUND_MIXER_READ_CD
;
1555 TRACE("SOUND_MIXER_READ_LINE !\n");
1556 cmd
= SOUND_MIXER_READ_LINE
;
1559 TRACE("SOUND_MIXER_READ_MIC !\n");
1560 cmd
= SOUND_MIXER_READ_MIC
;
1563 TRACE("SOUND_MIXER_READ_VOLUME !\n");
1564 cmd
= SOUND_MIXER_READ_VOLUME
;
1567 WARN("invalid device id=%04X !\n", dev_id
);
1569 return MMSYSERR_NOTENABLED
;
1571 if (ioctl(mixer
, cmd
, &volume
) == -1)
1573 WARN("unable to read mixer !\n");
1575 return MMSYSERR_NOTENABLED
;
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
)
1588 int volume
, left
, right
;
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
;
1606 TRACE("SOUND_MIXER_WRITE_PCM !\n");
1607 cmd
= SOUND_MIXER_WRITE_PCM
;
1610 TRACE("SOUND_MIXER_WRITE_SYNTH !\n");
1611 cmd
= SOUND_MIXER_WRITE_SYNTH
;
1614 TRACE("SOUND_MIXER_WRITE_CD !\n");
1615 cmd
= SOUND_MIXER_WRITE_CD
;
1618 TRACE("SOUND_MIXER_WRITE_LINE !\n");
1619 cmd
= SOUND_MIXER_WRITE_LINE
;
1622 TRACE("SOUND_MIXER_WRITE_MIC !\n");
1623 cmd
= SOUND_MIXER_WRITE_MIC
;
1626 TRACE("SOUND_MIXER_WRITE_VOLUME !\n");
1627 cmd
= SOUND_MIXER_WRITE_VOLUME
;
1630 WARN("invalid device id=%04X !\n", dev_id
);
1632 return MMSYSERR_NOTENABLED
;
1634 if (ioctl(mixer
, cmd
, &volume
) == -1)
1636 WARN("unable to set mixer !\n");
1638 return MMSYSERR_NOTENABLED
;
1641 return MMSYSERR_NOERROR
;
1644 static NTSTATUS
oss_aux_message(void *args
)
1646 struct aux_message_params
*params
= args
;
1648 switch (params
->msg
)
1651 *params
->err
= aux_init();
1654 *params
->err
= aux_exit();
1658 /* FIXME: Pretend this is supported */
1661 case AUXDM_GETDEVCAPS
:
1662 *params
->err
= aux_get_devcaps(params
->dev_id
, (AUXCAPSW
*)params
->param_1
, params
->param_2
);
1664 case AUXDM_GETNUMDEVS
:
1665 TRACE("return %d;\n", num_aux
);
1666 *params
->err
= num_aux
;
1668 case AUXDM_GETVOLUME
:
1669 *params
->err
= aux_get_volume(params
->dev_id
, (UINT
*)params
->param_1
);
1671 case AUXDM_SETVOLUME
:
1672 *params
->err
= aux_set_volume(params
->dev_id
, params
->param_1
);
1675 WARN("unknown message !\n");
1676 *params
->err
= MMSYSERR_NOTSUPPORTED
;
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
,
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
,
1701 oss_get_device_period
,
1702 oss_get_buffer_size
,
1704 oss_get_current_padding
,
1705 oss_get_next_packet_size
,
1709 oss_set_event_handle
,
1712 oss_not_implemented
,
1713 oss_not_implemented
,
1715 oss_midi_out_message
,
1716 oss_midi_in_message
,
1717 oss_midi_notify_wait
,
1725 static NTSTATUS
oss_wow64_test_connect(void *args
)
1730 enum driver_priority priority
;
1732 struct test_connect_params params
=
1734 .name
= ULongToPtr(params32
->name
),
1736 oss_test_connect(¶ms
);
1737 params32
->priority
= params
.priority
;
1738 return STATUS_SUCCESS
;
1741 static NTSTATUS
oss_wow64_get_endpoint_ids(void *args
)
1750 unsigned int default_idx
;
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(¶ms
);
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
)
1773 AUDCLNT_SHAREMODE share
;
1775 REFERENCE_TIME duration
;
1776 REFERENCE_TIME period
;
1779 PTR32 channel_count
;
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(¶ms
);
1796 params32
->result
= params
.result
;
1797 return STATUS_SUCCESS
;
1800 static NTSTATUS
oss_wow64_release_stream(void *args
)
1804 stream_handle stream
;
1808 struct release_stream_params params
=
1810 .stream
= params32
->stream
,
1811 .timer_thread
= ULongToHandle(params32
->timer_thread
)
1813 oss_release_stream(¶ms
);
1814 params32
->result
= params
.result
;
1815 return STATUS_SUCCESS
;
1818 static NTSTATUS
oss_wow64_get_render_buffer(void *args
)
1822 stream_handle stream
;
1828 struct get_render_buffer_params params
=
1830 .stream
= params32
->stream
,
1831 .frames
= params32
->frames
,
1834 oss_get_render_buffer(¶ms
);
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
)
1844 stream_handle stream
;
1853 struct get_capture_buffer_params params
=
1855 .stream
= params32
->stream
,
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(¶ms
);
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
)
1874 AUDCLNT_SHAREMODE share
;
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(¶ms
);
1888 params32
->result
= params
.result
;
1889 return STATUS_SUCCESS
;
1892 static NTSTATUS
oss_wow64_get_mix_format(void *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(¶ms
);
1908 params32
->result
= params
.result
;
1909 return STATUS_SUCCESS
;
1912 static NTSTATUS
oss_wow64_get_device_period(void *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(¶ms
);
1930 params32
->result
= params
.result
;
1931 return STATUS_SUCCESS
;
1934 static NTSTATUS
oss_wow64_get_buffer_size(void *args
)
1938 stream_handle stream
;
1942 struct get_buffer_size_params params
=
1944 .stream
= params32
->stream
,
1945 .frames
= ULongToPtr(params32
->frames
)
1947 oss_get_buffer_size(¶ms
);
1948 params32
->result
= params
.result
;
1949 return STATUS_SUCCESS
;
1952 static NTSTATUS
oss_wow64_get_latency(void *args
)
1956 stream_handle stream
;
1960 struct get_latency_params params
=
1962 .stream
= params32
->stream
,
1963 .latency
= ULongToPtr(params32
->latency
)
1965 oss_get_latency(¶ms
);
1966 params32
->result
= params
.result
;
1967 return STATUS_SUCCESS
;
1970 static NTSTATUS
oss_wow64_get_current_padding(void *args
)
1974 stream_handle stream
;
1978 struct get_current_padding_params params
=
1980 .stream
= params32
->stream
,
1981 .padding
= ULongToPtr(params32
->padding
)
1983 oss_get_current_padding(¶ms
);
1984 params32
->result
= params
.result
;
1985 return STATUS_SUCCESS
;
1988 static NTSTATUS
oss_wow64_get_next_packet_size(void *args
)
1992 stream_handle stream
;
1996 struct get_next_packet_size_params params
=
1998 .stream
= params32
->stream
,
1999 .frames
= ULongToPtr(params32
->frames
)
2001 oss_get_next_packet_size(¶ms
);
2002 params32
->result
= params
.result
;
2003 return STATUS_SUCCESS
;
2006 static NTSTATUS
oss_wow64_get_frequency(void *args
)
2010 stream_handle stream
;
2014 struct get_frequency_params params
=
2016 .stream
= params32
->stream
,
2017 .freq
= ULongToPtr(params32
->freq
)
2019 oss_get_frequency(¶ms
);
2020 params32
->result
= params
.result
;
2021 return STATUS_SUCCESS
;
2024 static NTSTATUS
oss_wow64_get_position(void *args
)
2028 stream_handle stream
;
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(¶ms
);
2042 params32
->result
= params
.result
;
2043 return STATUS_SUCCESS
;
2046 static NTSTATUS
oss_wow64_set_volumes(void *args
)
2050 stream_handle stream
;
2051 float master_volume
;
2053 PTR32 session_volumes
;
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(¶ms
);
2065 static NTSTATUS
oss_wow64_set_event_handle(void *args
)
2069 stream_handle stream
;
2073 struct set_event_handle_params params
=
2075 .stream
= params32
->stream
,
2076 .event
= ULongToHandle(params32
->event
)
2079 oss_set_event_handle(¶ms
);
2080 params32
->result
= params
.result
;
2081 return STATUS_SUCCESS
;
2084 static NTSTATUS
oss_wow64_aux_message(void *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(¶ms
);
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
,
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
,
2136 oss_not_implemented
,
2137 oss_not_implemented
,
2139 oss_wow64_midi_out_message
,
2140 oss_wow64_midi_in_message
,
2141 oss_wow64_midi_notify_wait
,
2142 oss_wow64_aux_message
,