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 /* copied from kernelbase */
73 static int muldiv( int a
, int b
, int c
)
79 /* We want to deal with a positive divisor to simplify the logic. */
86 /* If the result is positive, we "add" to round. else, we subtract to round. */
87 if ((a
< 0 && b
< 0) || (a
>= 0 && b
>= 0))
88 ret
= (((LONGLONG
)a
* b
) + (c
/ 2)) / c
;
90 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
92 if (ret
> 2147483647 || ret
< -2147483647) return -1;
96 static void oss_lock(struct oss_stream
*stream
)
98 pthread_mutex_lock(&stream
->lock
);
101 static void oss_unlock(struct oss_stream
*stream
)
103 pthread_mutex_unlock(&stream
->lock
);
106 static NTSTATUS
oss_unlock_result(struct oss_stream
*stream
,
107 HRESULT
*result
, HRESULT value
)
111 return STATUS_SUCCESS
;
114 static struct oss_stream
*handle_get_stream(stream_handle h
)
116 return (struct oss_stream
*)(UINT_PTR
)h
;
119 static NTSTATUS
test_connect(void *args
)
121 struct test_connect_params
*params
= args
;
125 /* Attempt to determine if we are running on OSS or ALSA's OSS
126 * compatibility layer. There is no official way to do that, so just check
127 * for validity as best as possible, without rejecting valid OSS
128 * implementations. */
130 mixer_fd
= open("/dev/mixer", O_RDONLY
, 0);
132 TRACE("Priority_Unavailable: open failed\n");
133 params
->priority
= Priority_Unavailable
;
134 return STATUS_SUCCESS
;
137 sysinfo
.version
[0] = 0xFF;
138 sysinfo
.versionnum
= ~0;
139 if(ioctl(mixer_fd
, SNDCTL_SYSINFO
, &sysinfo
) < 0){
140 TRACE("Priority_Unavailable: ioctl failed\n");
142 params
->priority
= Priority_Unavailable
;
143 return STATUS_SUCCESS
;
148 if(sysinfo
.version
[0] < '4' || sysinfo
.version
[0] > '9'){
149 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo
.version
[0]);
150 params
->priority
= Priority_Low
;
151 return STATUS_SUCCESS
;
153 if(sysinfo
.versionnum
& 0x80000000){
154 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo
.versionnum
);
155 params
->priority
= Priority_Low
;
156 return STATUS_SUCCESS
;
159 TRACE("Priority_Preferred: Seems like valid OSS!\n");
161 params
->priority
= Priority_Preferred
;
162 return STATUS_SUCCESS
;
165 /* dst must be large enough to hold devnode */
166 static void oss_clean_devnode(char *dest
, const char *devnode
)
168 const char *dot
, *slash
;
171 strcpy(dest
, devnode
);
172 dot
= strrchr(dest
, '.');
176 slash
= strrchr(dest
, '/');
177 if(slash
&& dot
< slash
)
184 static int open_device(const char *device
, EDataFlow flow
)
186 int flags
= ((flow
== eRender
) ? O_WRONLY
: O_RDONLY
) | O_NONBLOCK
;
188 return open(device
, flags
, 0);
191 static void get_default_device(EDataFlow flow
, char device
[OSS_DEVNODE_SIZE
])
197 fd
= open_device("/dev/dsp", flow
);
199 WARN("Couldn't open default device!\n");
204 if((err
= ioctl(fd
, SNDCTL_ENGINEINFO
, &ai
)) < 0){
205 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err
, strerror(errno
));
211 TRACE("Default devnode: %s\n", ai
.devnode
);
212 oss_clean_devnode(device
, ai
.devnode
);
216 static NTSTATUS
get_endpoint_ids(void *args
)
218 struct get_endpoint_ids_params
*params
= args
;
221 static int print_once
= 0;
222 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
223 static const WCHAR inW
[] = {'I','n',':',' ',0};
226 WCHAR name
[ARRAY_SIZE(ai
.name
) + ARRAY_SIZE(outW
)];
227 char device
[OSS_DEVNODE_SIZE
];
229 unsigned int i
, j
, num
, needed
, name_len
, device_len
, offset
, default_idx
= 0;
230 char default_device
[OSS_DEVNODE_SIZE
];
231 struct endpoint
*endpoint
;
234 mixer_fd
= open("/dev/mixer", O_RDONLY
, 0);
236 ERR("OSS /dev/mixer doesn't seem to exist\n");
237 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
238 return STATUS_SUCCESS
;
241 if(ioctl(mixer_fd
, SNDCTL_SYSINFO
, &sysinfo
) < 0){
244 ERR("OSS version too old, need at least OSSv4\n");
245 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
246 return STATUS_SUCCESS
;
249 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno
, strerror(errno
));
250 params
->result
= E_FAIL
;
251 return STATUS_SUCCESS
;
255 TRACE("OSS sysinfo:\n");
256 TRACE("product: %s\n", sysinfo
.product
);
257 TRACE("version: %s\n", sysinfo
.version
);
258 TRACE("versionnum: %x\n", sysinfo
.versionnum
);
259 TRACE("numaudios: %d\n", sysinfo
.numaudios
);
260 TRACE("nummixers: %d\n", sysinfo
.nummixers
);
261 TRACE("numcards: %d\n", sysinfo
.numcards
);
262 TRACE("numaudioengines: %d\n", sysinfo
.numaudioengines
);
266 if(sysinfo
.numaudios
<= 0){
267 WARN("No audio devices!\n");
269 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
270 return STATUS_SUCCESS
;
273 info
= malloc(sysinfo
.numaudios
* sizeof(*info
));
276 params
->result
= E_OUTOFMEMORY
;
277 return STATUS_SUCCESS
;
280 get_default_device(params
->flow
, default_device
);
283 for(i
= 0; i
< sysinfo
.numaudios
; ++i
){
284 char devnode
[OSS_DEVNODE_SIZE
];
288 memset(&ai
, 0, sizeof(ai
));
290 if(ioctl(mixer_fd
, SNDCTL_AUDIOINFO
, &ai
) < 0){
291 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i
, errno
,
296 oss_clean_devnode(devnode
, ai
.devnode
);
298 /* check for duplicates */
299 for(j
= 0; j
< num
; j
++)
300 if(!strcmp(devnode
, info
[j
].device
))
305 fd
= open_device(devnode
, params
->flow
);
307 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
308 devnode
, errno
, strerror(errno
));
313 if((params
->flow
== eCapture
&& !(ai
.caps
& PCM_CAP_INPUT
)) ||
314 (params
->flow
== eRender
&& !(ai
.caps
& PCM_CAP_OUTPUT
)))
317 strcpy(info
[num
].device
, devnode
);
319 if(params
->flow
== eRender
){
321 prefix_len
= ARRAY_SIZE(outW
) - 1;
324 prefix_len
= ARRAY_SIZE(inW
) - 1;
326 memcpy(info
[num
].name
, prefix
, prefix_len
* sizeof(WCHAR
));
327 ntdll_umbstowcs(ai
.name
, strlen(ai
.name
) + 1, info
[num
].name
+ prefix_len
,
328 ARRAY_SIZE(info
[num
].name
) - prefix_len
);
329 if(!strcmp(default_device
, info
[num
].device
))
335 offset
= needed
= num
* sizeof(*params
->endpoints
);
336 endpoint
= params
->endpoints
;
338 for(i
= 0; i
< num
; i
++){
339 name_len
= wcslen(info
[i
].name
) + 1;
340 device_len
= strlen(info
[i
].device
) + 1;
341 needed
+= name_len
* sizeof(WCHAR
) + ((device_len
+ 1) & ~1);
343 if(needed
<= params
->size
){
344 endpoint
->name
= offset
;
345 memcpy((char *)params
->endpoints
+ offset
, info
[i
].name
, name_len
* sizeof(WCHAR
));
346 offset
+= name_len
* sizeof(WCHAR
);
347 endpoint
->device
= offset
;
348 memcpy((char *)params
->endpoints
+ offset
, info
[i
].device
, device_len
);
349 offset
+= (device_len
+ 1) & ~1;
356 params
->default_idx
= default_idx
;
358 if(needed
> params
->size
){
359 params
->size
= needed
;
360 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
362 params
->result
= S_OK
;
364 return STATUS_SUCCESS
;
367 static UINT
get_channel_mask(unsigned int channels
)
373 return KSAUDIO_SPEAKER_MONO
;
375 return KSAUDIO_SPEAKER_STEREO
;
377 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
379 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
381 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
383 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
385 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
387 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
389 FIXME("Unknown speaker configuration: %u\n", channels
);
393 static int get_oss_format(const WAVEFORMATEX
*fmt
)
395 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)fmt
;
397 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
398 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
399 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
400 switch(fmt
->wBitsPerSample
){
414 if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
415 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
416 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
417 if(fmt
->wBitsPerSample
!= 32)
427 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
429 WAVEFORMATEXTENSIBLE
*ret
;
432 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
433 size
= sizeof(WAVEFORMATEXTENSIBLE
);
435 size
= sizeof(WAVEFORMATEX
);
441 memcpy(ret
, fmt
, size
);
443 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
448 static HRESULT
setup_oss_device(AUDCLNT_SHAREMODE share
, int fd
,
449 const WAVEFORMATEX
*fmt
, WAVEFORMATEXTENSIBLE
*out
)
451 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
455 WAVEFORMATEXTENSIBLE
*closest
;
457 tmp
= oss_format
= get_oss_format(fmt
);
459 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
460 if(ioctl(fd
, SNDCTL_DSP_SETFMT
, &tmp
) < 0){
461 WARN("SETFMT failed: %d (%s)\n", errno
, strerror(errno
));
464 if(tmp
!= oss_format
){
465 TRACE("Format unsupported by this OSS version: %x\n", oss_format
);
466 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
469 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
470 (fmtex
->Format
.nAvgBytesPerSec
== 0 ||
471 fmtex
->Format
.nBlockAlign
== 0 ||
472 fmtex
->Samples
.wValidBitsPerSample
> fmtex
->Format
.wBitsPerSample
))
475 if(fmt
->nChannels
== 0)
476 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
478 closest
= clone_format(fmt
);
480 return E_OUTOFMEMORY
;
482 tmp
= fmt
->nSamplesPerSec
;
483 if(ioctl(fd
, SNDCTL_DSP_SPEED
, &tmp
) < 0){
484 WARN("SPEED failed: %d (%s)\n", errno
, strerror(errno
));
488 tenth
= fmt
->nSamplesPerSec
* 0.1;
489 if(tmp
> fmt
->nSamplesPerSec
+ tenth
|| tmp
< fmt
->nSamplesPerSec
- tenth
){
491 closest
->Format
.nSamplesPerSec
= tmp
;
494 tmp
= fmt
->nChannels
;
495 if(ioctl(fd
, SNDCTL_DSP_CHANNELS
, &tmp
) < 0){
496 WARN("CHANNELS failed: %d (%s)\n", errno
, strerror(errno
));
500 if(tmp
!= fmt
->nChannels
){
502 closest
->Format
.nChannels
= tmp
;
505 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
506 closest
->dwChannelMask
= get_channel_mask(closest
->Format
.nChannels
);
508 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
509 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
510 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
511 fmtex
->Samples
.wValidBitsPerSample
< fmtex
->Format
.wBitsPerSample
))
514 if(share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
515 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
516 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
520 if(ret
== S_FALSE
&& !out
)
521 ret
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
523 if(ret
== S_FALSE
&& out
){
524 closest
->Format
.nBlockAlign
=
525 closest
->Format
.nChannels
* closest
->Format
.wBitsPerSample
/ 8;
526 closest
->Format
.nAvgBytesPerSec
=
527 closest
->Format
.nBlockAlign
* closest
->Format
.nSamplesPerSec
;
528 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
529 closest
->Samples
.wValidBitsPerSample
= closest
->Format
.wBitsPerSample
;
530 memcpy(out
, closest
, closest
->Format
.cbSize
+ sizeof(WAVEFORMATEX
));
534 TRACE("returning: %08x\n", (unsigned)ret
);
538 static ULONG_PTR
zero_bits(void)
541 return !NtCurrentTeb()->WowTebOffset
? 0 : 0x7fffffff;
547 static NTSTATUS
create_stream(void *args
)
549 struct create_stream_params
*params
= args
;
550 WAVEFORMATEXTENSIBLE
*fmtex
;
551 struct oss_stream
*stream
;
555 stream
= calloc(1, sizeof(*stream
));
557 params
->result
= E_OUTOFMEMORY
;
558 return STATUS_SUCCESS
;
561 stream
->flow
= params
->flow
;
562 pthread_mutex_init(&stream
->lock
, NULL
);
564 stream
->fd
= open_device(params
->device
, params
->flow
);
566 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
567 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
572 if(ioctl(stream
->fd
, SNDCTL_ENGINEINFO
, &ai
) < 0){
573 WARN("Unable to get audio info for device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
574 params
->result
= E_FAIL
;
578 TRACE("OSS audioinfo:\n");
579 TRACE("devnode: %s\n", ai
.devnode
);
580 TRACE("name: %s\n", ai
.name
);
581 TRACE("busy: %x\n", ai
.busy
);
582 TRACE("caps: %x\n", ai
.caps
);
583 TRACE("iformats: %x\n", ai
.iformats
);
584 TRACE("oformats: %x\n", ai
.oformats
);
585 TRACE("enabled: %d\n", ai
.enabled
);
586 TRACE("min_rate: %d\n", ai
.min_rate
);
587 TRACE("max_rate: %d\n", ai
.max_rate
);
588 TRACE("min_channels: %d\n", ai
.min_channels
);
589 TRACE("max_channels: %d\n", ai
.max_channels
);
591 params
->result
= setup_oss_device(params
->share
, stream
->fd
, params
->fmt
, NULL
);
592 if(FAILED(params
->result
))
595 fmtex
= clone_format(params
->fmt
);
597 params
->result
= E_OUTOFMEMORY
;
600 stream
->fmt
= &fmtex
->Format
;
602 stream
->period
= params
->period
;
603 stream
->period_frames
= muldiv(params
->fmt
->nSamplesPerSec
, params
->period
, 10000000);
605 stream
->bufsize_frames
= muldiv(params
->duration
, params
->fmt
->nSamplesPerSec
, 10000000);
606 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
607 stream
->bufsize_frames
-= stream
->bufsize_frames
% stream
->period_frames
;
608 size
= stream
->bufsize_frames
* params
->fmt
->nBlockAlign
;
609 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, zero_bits(),
610 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
611 params
->result
= E_OUTOFMEMORY
;
615 stream
->share
= params
->share
;
616 stream
->flags
= params
->flags
;
617 stream
->oss_bufsize_bytes
= 0;
620 if(FAILED(params
->result
)){
621 if(stream
->fd
>= 0) close(stream
->fd
);
622 if(stream
->local_buffer
){
624 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
626 pthread_mutex_destroy(&stream
->lock
);
630 *params
->stream
= (stream_handle
)(UINT_PTR
)stream
;
633 return STATUS_SUCCESS
;
636 static NTSTATUS
release_stream(void *args
)
638 struct release_stream_params
*params
= args
;
639 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
642 if(params
->timer_thread
){
643 stream
->please_quit
= TRUE
;
644 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
645 NtClose(params
->timer_thread
);
649 if(stream
->local_buffer
){
651 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
653 if(stream
->tmp_buffer
){
655 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
658 pthread_mutex_destroy(&stream
->lock
);
661 params
->result
= S_OK
;
662 return STATUS_SUCCESS
;
665 static NTSTATUS
start(void *args
)
667 struct start_params
*params
= args
;
668 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
672 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
673 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
676 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
678 stream
->playing
= TRUE
;
680 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
683 static NTSTATUS
stop(void *args
)
685 struct stop_params
*params
= args
;
686 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
691 return oss_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
693 stream
->playing
= FALSE
;
694 stream
->in_oss_frames
= 0;
696 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
699 static NTSTATUS
reset(void *args
)
701 struct reset_params
*params
= args
;
702 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
707 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
709 if(stream
->getbuf_last
)
710 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
712 if(stream
->flow
== eRender
){
713 stream
->written_frames
= 0;
714 stream
->last_pos_frames
= 0;
716 stream
->written_frames
+= stream
->held_frames
;
718 stream
->held_frames
= 0;
719 stream
->lcl_offs_frames
= 0;
720 stream
->in_oss_frames
= 0;
722 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
725 static void silence_buffer(struct oss_stream
*stream
, BYTE
*buffer
, UINT32 frames
)
727 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->fmt
;
728 if((stream
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
729 (stream
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
730 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
731 stream
->fmt
->wBitsPerSample
== 8)
732 memset(buffer
, 128, frames
* stream
->fmt
->nBlockAlign
);
734 memset(buffer
, 0, frames
* stream
->fmt
->nBlockAlign
);
737 static void oss_write_data(struct oss_stream
*stream
)
739 ssize_t written_bytes
;
740 UINT32 written_frames
, in_oss_frames
, write_limit
, max_period
, write_offs_frames
, new_frames
;
741 SIZE_T to_write_frames
, to_write_bytes
, advanced
;
745 if(ioctl(stream
->fd
, SNDCTL_DSP_GETOSPACE
, &bi
) < 0){
746 WARN("GETOSPACE failed: %d (%s)\n", errno
, strerror(errno
));
750 max_period
= max(bi
.fragsize
/ stream
->fmt
->nBlockAlign
, stream
->period_frames
);
752 if(bi
.bytes
> stream
->oss_bufsize_bytes
){
753 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
754 bi
.bytes
, stream
->oss_bufsize_bytes
);
755 stream
->oss_bufsize_bytes
= bi
.bytes
;
758 in_oss_frames
= (stream
->oss_bufsize_bytes
- bi
.bytes
) / stream
->fmt
->nBlockAlign
;
760 if(in_oss_frames
> stream
->in_oss_frames
){
761 TRACE("Capping reported frames from %u to %u\n",
762 in_oss_frames
, stream
->in_oss_frames
);
763 in_oss_frames
= stream
->in_oss_frames
;
767 while(write_limit
+ in_oss_frames
< max_period
* 3)
768 write_limit
+= max_period
;
772 /* vvvvvv - in_oss_frames
775 * ^^^^^^^^^^ - held_frames
776 * ^ - lcl_offs_frames
778 advanced
= stream
->in_oss_frames
- in_oss_frames
;
779 if(advanced
> stream
->held_frames
)
780 advanced
= stream
->held_frames
;
781 stream
->lcl_offs_frames
+= advanced
;
782 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
783 stream
->held_frames
-= advanced
;
784 stream
->in_oss_frames
= in_oss_frames
;
785 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
786 advanced
, stream
->lcl_offs_frames
, stream
->held_frames
, stream
->in_oss_frames
);
789 if(stream
->held_frames
== stream
->in_oss_frames
)
792 write_offs_frames
= (stream
->lcl_offs_frames
+ stream
->in_oss_frames
) % stream
->bufsize_frames
;
793 new_frames
= stream
->held_frames
- stream
->in_oss_frames
;
795 if(write_offs_frames
+ new_frames
> stream
->bufsize_frames
)
796 to_write_frames
= stream
->bufsize_frames
- write_offs_frames
;
798 to_write_frames
= new_frames
;
800 to_write_frames
= min(to_write_frames
, write_limit
);
801 to_write_bytes
= to_write_frames
* stream
->fmt
->nBlockAlign
;
802 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames
,
803 write_offs_frames
, to_write_frames
+ write_offs_frames
,
804 stream
->bufsize_frames
);
806 buf
= stream
->local_buffer
+ write_offs_frames
* stream
->fmt
->nBlockAlign
;
809 silence_buffer(stream
, buf
, to_write_frames
);
811 written_bytes
= write(stream
->fd
, buf
, to_write_bytes
);
812 if(written_bytes
< 0){
813 /* EAGAIN is OSS buffer full, log that too */
814 WARN("write failed: %d (%s)\n", errno
, strerror(errno
));
817 written_frames
= written_bytes
/ stream
->fmt
->nBlockAlign
;
819 stream
->in_oss_frames
+= written_frames
;
821 if(written_frames
< to_write_frames
){
822 /* OSS buffer probably full */
826 if(new_frames
> written_frames
&& written_frames
< write_limit
){
827 /* wrapped and have some data back at the start to write */
829 to_write_frames
= min(write_limit
- written_frames
, new_frames
- written_frames
);
830 to_write_bytes
= to_write_frames
* stream
->fmt
->nBlockAlign
;
833 silence_buffer(stream
, stream
->local_buffer
, to_write_frames
);
835 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames
);
837 written_bytes
= write(stream
->fd
, stream
->local_buffer
, to_write_bytes
);
838 if(written_bytes
< 0){
839 WARN("write failed: %d (%s)\n", errno
, strerror(errno
));
842 written_frames
= written_bytes
/ stream
->fmt
->nBlockAlign
;
843 stream
->in_oss_frames
+= written_frames
;
847 static void oss_read_data(struct oss_stream
*stream
)
849 UINT64 pos
, readable
;
852 pos
= (stream
->held_frames
+ stream
->lcl_offs_frames
) % stream
->bufsize_frames
;
853 readable
= (stream
->bufsize_frames
- pos
) * stream
->fmt
->nBlockAlign
;
855 nread
= read(stream
->fd
, stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
,
858 WARN("read failed: %d (%s)\n", errno
, strerror(errno
));
862 stream
->held_frames
+= nread
/ stream
->fmt
->nBlockAlign
;
864 if(stream
->held_frames
> stream
->bufsize_frames
){
865 WARN("Overflow of unread data\n");
866 stream
->lcl_offs_frames
+= stream
->held_frames
;
867 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
868 stream
->held_frames
= stream
->bufsize_frames
;
872 static NTSTATUS
timer_loop(void *args
)
874 struct timer_loop_params
*params
= args
;
875 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
876 LARGE_INTEGER delay
, now
, next
;
881 delay
.QuadPart
= -stream
->period
;
882 NtQueryPerformanceCounter(&now
, NULL
);
883 next
.QuadPart
= now
.QuadPart
+ stream
->period
;
885 while(!stream
->please_quit
){
887 if(stream
->flow
== eRender
&& stream
->held_frames
)
888 oss_write_data(stream
);
889 else if(stream
->flow
== eCapture
)
890 oss_read_data(stream
);
893 NtSetEvent(stream
->event
, NULL
);
896 NtDelayExecution(FALSE
, &delay
);
899 NtQueryPerformanceCounter(&now
, NULL
);
900 adjust
= next
.QuadPart
- now
.QuadPart
;
901 if(adjust
> stream
->period
/ 2)
902 adjust
= stream
->period
/ 2;
903 else if(adjust
< -stream
->period
/ 2)
904 adjust
= -stream
->period
/ 2;
905 delay
.QuadPart
= -(stream
->period
+ adjust
);
906 next
.QuadPart
+= stream
->period
;
911 return STATUS_SUCCESS
;
914 static NTSTATUS
get_render_buffer(void *args
)
916 struct get_render_buffer_params
*params
= args
;
917 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
918 UINT32 write_pos
, frames
= params
->frames
;
919 BYTE
**data
= params
->data
;
924 if(stream
->getbuf_last
)
925 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
928 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
930 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
931 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
934 (stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
;
935 if(write_pos
+ frames
> stream
->bufsize_frames
){
936 if(stream
->tmp_buffer_frames
< frames
){
937 if(stream
->tmp_buffer
){
939 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
940 stream
->tmp_buffer
= NULL
;
942 size
= frames
* stream
->fmt
->nBlockAlign
;
943 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(),
944 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
945 stream
->tmp_buffer_frames
= 0;
946 return oss_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
948 stream
->tmp_buffer_frames
= frames
;
950 *data
= stream
->tmp_buffer
;
951 stream
->getbuf_last
= -frames
;
953 *data
= stream
->local_buffer
+ write_pos
* stream
->fmt
->nBlockAlign
;
954 stream
->getbuf_last
= frames
;
957 silence_buffer(stream
, *data
, frames
);
959 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
962 static void oss_wrap_buffer(struct oss_stream
*stream
, BYTE
*buffer
, UINT32 written_frames
)
964 UINT32 write_offs_frames
=
965 (stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
;
966 UINT32 write_offs_bytes
= write_offs_frames
* stream
->fmt
->nBlockAlign
;
967 UINT32 chunk_frames
= stream
->bufsize_frames
- write_offs_frames
;
968 UINT32 chunk_bytes
= chunk_frames
* stream
->fmt
->nBlockAlign
;
969 UINT32 written_bytes
= written_frames
* stream
->fmt
->nBlockAlign
;
971 if(written_bytes
<= chunk_bytes
){
972 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
974 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
975 memcpy(stream
->local_buffer
, buffer
+ chunk_bytes
,
976 written_bytes
- chunk_bytes
);
980 static NTSTATUS
release_render_buffer(void *args
)
982 struct release_render_buffer_params
*params
= args
;
983 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
984 UINT32 written_frames
= params
->written_frames
;
985 UINT flags
= params
->flags
;
991 stream
->getbuf_last
= 0;
992 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
995 if(!stream
->getbuf_last
)
996 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
998 if(written_frames
> (stream
->getbuf_last
>= 0 ? stream
->getbuf_last
: -stream
->getbuf_last
))
999 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1001 if(stream
->getbuf_last
>= 0)
1002 buffer
= stream
->local_buffer
+ stream
->fmt
->nBlockAlign
*
1003 ((stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
);
1005 buffer
= stream
->tmp_buffer
;
1007 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1008 silence_buffer(stream
, buffer
, written_frames
);
1010 if(stream
->getbuf_last
< 0)
1011 oss_wrap_buffer(stream
, buffer
, written_frames
);
1013 stream
->held_frames
+= written_frames
;
1014 stream
->written_frames
+= written_frames
;
1015 stream
->getbuf_last
= 0;
1017 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1020 static NTSTATUS
get_capture_buffer(void *args
)
1022 struct get_capture_buffer_params
*params
= args
;
1023 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1024 UINT64
*devpos
= params
->devpos
, *qpcpos
= params
->qpcpos
;
1025 UINT32
*frames
= params
->frames
;
1026 UINT
*flags
= params
->flags
;
1027 BYTE
**data
= params
->data
;
1032 if(stream
->getbuf_last
)
1033 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1035 if(stream
->held_frames
< stream
->period_frames
){
1037 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
1042 *frames
= stream
->period_frames
;
1044 if(stream
->lcl_offs_frames
+ *frames
> stream
->bufsize_frames
){
1045 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1046 if(stream
->tmp_buffer_frames
< *frames
){
1047 if(stream
->tmp_buffer
){
1049 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1050 stream
->tmp_buffer
= NULL
;
1052 size
= *frames
* stream
->fmt
->nBlockAlign
;
1053 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits(),
1054 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
1055 stream
->tmp_buffer_frames
= 0;
1056 return oss_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1058 stream
->tmp_buffer_frames
= *frames
;
1061 *data
= stream
->tmp_buffer
;
1062 chunk_bytes
= (stream
->bufsize_frames
- stream
->lcl_offs_frames
) *
1063 stream
->fmt
->nBlockAlign
;
1064 offs_bytes
= stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1065 frames_bytes
= *frames
* stream
->fmt
->nBlockAlign
;
1066 memcpy(stream
->tmp_buffer
, stream
->local_buffer
+ offs_bytes
, chunk_bytes
);
1067 memcpy(stream
->tmp_buffer
+ chunk_bytes
, stream
->local_buffer
,
1068 frames_bytes
- chunk_bytes
);
1070 *data
= stream
->local_buffer
+
1071 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1073 stream
->getbuf_last
= *frames
;
1076 *devpos
= stream
->written_frames
;
1078 LARGE_INTEGER stamp
, freq
;
1079 NtQueryPerformanceCounter(&stamp
, &freq
);
1080 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1083 return oss_unlock_result(stream
, ¶ms
->result
, *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
);
1086 static NTSTATUS
release_capture_buffer(void *args
)
1088 struct release_capture_buffer_params
*params
= args
;
1089 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1090 UINT32 done
= params
->done
;
1095 stream
->getbuf_last
= 0;
1096 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1099 if(!stream
->getbuf_last
)
1100 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1102 if(stream
->getbuf_last
!= done
)
1103 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1105 stream
->written_frames
+= done
;
1106 stream
->held_frames
-= done
;
1107 stream
->lcl_offs_frames
+= done
;
1108 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1109 stream
->getbuf_last
= 0;
1111 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1114 static NTSTATUS
is_format_supported(void *args
)
1116 struct is_format_supported_params
*params
= args
;
1119 params
->result
= S_OK
;
1121 if(!params
->fmt_in
|| (params
->share
== AUDCLNT_SHAREMODE_SHARED
&& !params
->fmt_out
))
1122 params
->result
= E_POINTER
;
1123 else if(params
->share
!= AUDCLNT_SHAREMODE_SHARED
&& params
->share
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1124 params
->result
= E_INVALIDARG
;
1125 else if(params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1126 params
->fmt_in
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1127 params
->result
= E_INVALIDARG
;
1128 if(FAILED(params
->result
))
1129 return STATUS_SUCCESS
;
1131 fd
= open_device(params
->device
, params
->flow
);
1133 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1134 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1135 return STATUS_SUCCESS
;
1137 params
->result
= setup_oss_device(params
->share
, fd
, params
->fmt_in
, params
->fmt_out
);
1140 return STATUS_SUCCESS
;
1143 static NTSTATUS
get_mix_format(void *args
)
1145 struct get_mix_format_params
*params
= args
;
1146 WAVEFORMATEXTENSIBLE
*fmt
= params
->fmt
;
1150 if(params
->flow
!= eRender
&& params
->flow
!= eCapture
){
1151 params
->result
= E_UNEXPECTED
;
1152 return STATUS_SUCCESS
;
1155 fd
= open_device(params
->device
, params
->flow
);
1157 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1158 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1159 return STATUS_SUCCESS
;
1163 if(ioctl(fd
, SNDCTL_ENGINEINFO
, &ai
) < 0){
1164 WARN("Unable to get audio info for device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1166 params
->result
= E_FAIL
;
1167 return STATUS_SUCCESS
;
1171 if(params
->flow
== eRender
)
1172 formats
= ai
.oformats
;
1174 formats
= ai
.iformats
;
1176 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1177 if(formats
& AFMT_S16_LE
){
1178 fmt
->Format
.wBitsPerSample
= 16;
1179 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1181 }else if(formats
& AFMT_FLOAT
){
1182 fmt
->Format
.wBitsPerSample
= 32;
1183 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1185 }else if(formats
& AFMT_U8
){
1186 fmt
->Format
.wBitsPerSample
= 8;
1187 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1188 }else if(formats
& AFMT_S32_LE
){
1189 fmt
->Format
.wBitsPerSample
= 32;
1190 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1191 }else if(formats
& AFMT_S24_LE
){
1192 fmt
->Format
.wBitsPerSample
= 24;
1193 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1195 WARN("Didn't recognize any available OSS formats: %x\n", formats
);
1196 params
->result
= E_FAIL
;
1197 return STATUS_SUCCESS
;
1200 /* some OSS drivers are buggy, so set reasonable defaults if
1201 * the reported values seem wacky */
1202 fmt
->Format
.nChannels
= max(ai
.max_channels
, ai
.min_channels
);
1203 if(fmt
->Format
.nChannels
== 0 || fmt
->Format
.nChannels
> 8)
1204 fmt
->Format
.nChannels
= 2;
1206 /* For most hardware on Windows, users must choose a configuration with an even
1207 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1208 * channels, but those channels are still reported to applications from
1209 * GetMixFormat! Some applications behave badly if given an odd number of
1210 * channels (e.g. 2.1). */
1211 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1))
1213 if(fmt
->Format
.nChannels
< ai
.max_channels
)
1214 fmt
->Format
.nChannels
+= 1;
1216 /* We could "fake" more channels and downmix the emulated channels,
1217 * but at that point you really ought to tweak your OSS setup or
1218 * just use PulseAudio. */
1219 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
1222 if(ai
.max_rate
== 0)
1223 fmt
->Format
.nSamplesPerSec
= 44100;
1225 fmt
->Format
.nSamplesPerSec
= min(ai
.max_rate
, 44100);
1226 if(fmt
->Format
.nSamplesPerSec
< ai
.min_rate
)
1227 fmt
->Format
.nSamplesPerSec
= ai
.min_rate
;
1229 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1231 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1232 fmt
->Format
.nChannels
) / 8;
1233 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1234 fmt
->Format
.nBlockAlign
;
1236 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1237 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1239 params
->result
= S_OK
;
1240 return STATUS_SUCCESS
;
1243 static NTSTATUS
get_buffer_size(void *args
)
1245 struct get_buffer_size_params
*params
= args
;
1246 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1250 *params
->size
= stream
->bufsize_frames
;
1252 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1255 static NTSTATUS
get_latency(void *args
)
1257 struct get_latency_params
*params
= args
;
1258 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1262 /* pretend we process audio in Period chunks, so max latency includes
1263 * the period time. Some native machines add .6666ms in shared mode. */
1264 *params
->latency
= stream
->period
+ 6666;
1266 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1269 static NTSTATUS
get_current_padding(void *args
)
1271 struct get_current_padding_params
*params
= args
;
1272 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1276 *params
->padding
= stream
->held_frames
;
1278 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1281 static NTSTATUS
get_next_packet_size(void *args
)
1283 struct get_next_packet_size_params
*params
= args
;
1284 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1285 UINT32
*frames
= params
->frames
;
1289 *frames
= stream
->held_frames
< stream
->period_frames
? 0 : stream
->period_frames
;
1291 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1294 static NTSTATUS
get_frequency(void *args
)
1296 struct get_frequency_params
*params
= args
;
1297 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1298 UINT64
*freq
= params
->frequency
;
1302 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
1303 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
1305 *freq
= stream
->fmt
->nSamplesPerSec
;
1307 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1310 static NTSTATUS
get_position(void *args
)
1312 struct get_position_params
*params
= args
;
1313 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1314 UINT64
*pos
= params
->position
, *qpctime
= params
->qpctime
;
1318 if(stream
->flow
== eRender
){
1319 *pos
= stream
->written_frames
- stream
->held_frames
;
1320 if(*pos
< stream
->last_pos_frames
)
1321 *pos
= stream
->last_pos_frames
;
1322 }else if(stream
->flow
== eCapture
){
1326 if(ioctl(stream
->fd
, SNDCTL_DSP_GETISPACE
, &bi
) < 0){
1327 TRACE("GETISPACE failed: %d (%s)\n", errno
, strerror(errno
));
1330 if(bi
.bytes
<= bi
.fragsize
)
1333 held
= bi
.bytes
/ stream
->fmt
->nBlockAlign
;
1336 *pos
= stream
->written_frames
+ held
;
1339 stream
->last_pos_frames
= *pos
;
1341 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos
));
1342 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
1343 *pos
*= stream
->fmt
->nBlockAlign
;
1346 LARGE_INTEGER stamp
, freq
;
1347 NtQueryPerformanceCounter(&stamp
, &freq
);
1348 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1351 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1354 static NTSTATUS
set_volumes(void *args
)
1356 struct set_volumes_params
*params
= args
;
1357 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1360 stream
->mute
= !params
->master_volume
;
1363 return STATUS_SUCCESS
;
1366 static NTSTATUS
set_event_handle(void *args
)
1368 struct set_event_handle_params
*params
= args
;
1369 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1373 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1374 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
1377 FIXME("called twice\n");
1378 return oss_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
1381 stream
->event
= params
->event
;
1383 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1386 static NTSTATUS
is_started(void *args
)
1388 struct is_started_params
*params
= args
;
1389 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1393 return oss_unlock_result(stream
, ¶ms
->result
, stream
->playing
? S_OK
: S_FALSE
);
1398 static unsigned int num_aux
;
1400 #define MIXER_DEV "/dev/mixer"
1402 static UINT
aux_init(void)
1408 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1410 WARN("mixer device not available !\n");
1421 static UINT
aux_exit(void)
1427 static UINT
aux_get_devcaps(WORD dev_id
, AUXCAPSW
*caps
, UINT size
)
1430 static const WCHAR ini
[] = {'O','S','S',' ','A','u','x',' ','#','0',0};
1432 TRACE("(%04X, %p, %u);\n", dev_id
, caps
, size
);
1433 if (caps
== NULL
) return MMSYSERR_NOTENABLED
;
1434 if (dev_id
>= num_aux
) return MMSYSERR_BADDEVICEID
;
1435 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1437 WARN("mixer device not available !\n");
1438 return MMSYSERR_NOTENABLED
;
1440 if (ioctl(mixer
, SOUND_MIXER_READ_LINE
, &volume
) == -1)
1443 WARN("unable to read mixer !\n");
1444 return MMSYSERR_NOTENABLED
;
1448 caps
->wPid
= 0x55 + dev_id
;
1449 caps
->vDriverVersion
= 0x0100;
1450 memcpy(caps
->szPname
, ini
, sizeof(ini
));
1451 caps
->szPname
[9] = '0' + dev_id
; /* 6 at max */
1452 caps
->wTechnology
= (dev_id
== 2) ? AUXCAPS_CDAUDIO
: AUXCAPS_AUXIN
;
1453 caps
->wReserved1
= 0;
1454 caps
->dwSupport
= AUXCAPS_VOLUME
| AUXCAPS_LRVOLUME
;
1456 return MMSYSERR_NOERROR
;
1459 static UINT
aux_get_volume(WORD dev_id
, UINT
*vol
)
1461 int mixer
, volume
, left
, right
, cmd
;
1463 TRACE("(%04X, %p);\n", dev_id
, vol
);
1464 if (vol
== NULL
) return MMSYSERR_NOTENABLED
;
1465 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1467 WARN("mixer device not available !\n");
1468 return MMSYSERR_NOTENABLED
;
1473 TRACE("SOUND_MIXER_READ_PCM !\n");
1474 cmd
= SOUND_MIXER_READ_PCM
;
1477 TRACE("SOUND_MIXER_READ_SYNTH !\n");
1478 cmd
= SOUND_MIXER_READ_SYNTH
;
1481 TRACE("SOUND_MIXER_READ_CD !\n");
1482 cmd
= SOUND_MIXER_READ_CD
;
1485 TRACE("SOUND_MIXER_READ_LINE !\n");
1486 cmd
= SOUND_MIXER_READ_LINE
;
1489 TRACE("SOUND_MIXER_READ_MIC !\n");
1490 cmd
= SOUND_MIXER_READ_MIC
;
1493 TRACE("SOUND_MIXER_READ_VOLUME !\n");
1494 cmd
= SOUND_MIXER_READ_VOLUME
;
1497 WARN("invalid device id=%04X !\n", dev_id
);
1499 return MMSYSERR_NOTENABLED
;
1501 if (ioctl(mixer
, cmd
, &volume
) == -1)
1503 WARN("unable to read mixer !\n");
1505 return MMSYSERR_NOTENABLED
;
1508 left
= LOBYTE(LOWORD(volume
));
1509 right
= HIBYTE(LOWORD(volume
));
1510 TRACE("left=%d right=%d !\n", left
, right
);
1511 *vol
= MAKELONG((left
* 0xFFFFL
) / 100, (right
* 0xFFFFL
) / 100);
1512 return MMSYSERR_NOERROR
;
1515 static UINT
aux_set_volume(WORD dev_id
, UINT vol
)
1518 int volume
, left
, right
;
1521 TRACE("(%04X, %08X);\n", dev_id
, vol
);
1523 left
= (LOWORD(vol
) * 100) >> 16;
1524 right
= (HIWORD(vol
) * 100) >> 16;
1525 volume
= (right
<< 8) | left
;
1527 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1529 WARN("mixer device not available !\n");
1530 return MMSYSERR_NOTENABLED
;
1536 TRACE("SOUND_MIXER_WRITE_PCM !\n");
1537 cmd
= SOUND_MIXER_WRITE_PCM
;
1540 TRACE("SOUND_MIXER_WRITE_SYNTH !\n");
1541 cmd
= SOUND_MIXER_WRITE_SYNTH
;
1544 TRACE("SOUND_MIXER_WRITE_CD !\n");
1545 cmd
= SOUND_MIXER_WRITE_CD
;
1548 TRACE("SOUND_MIXER_WRITE_LINE !\n");
1549 cmd
= SOUND_MIXER_WRITE_LINE
;
1552 TRACE("SOUND_MIXER_WRITE_MIC !\n");
1553 cmd
= SOUND_MIXER_WRITE_MIC
;
1556 TRACE("SOUND_MIXER_WRITE_VOLUME !\n");
1557 cmd
= SOUND_MIXER_WRITE_VOLUME
;
1560 WARN("invalid device id=%04X !\n", dev_id
);
1562 return MMSYSERR_NOTENABLED
;
1564 if (ioctl(mixer
, cmd
, &volume
) == -1)
1566 WARN("unable to set mixer !\n");
1568 return MMSYSERR_NOTENABLED
;
1571 return MMSYSERR_NOERROR
;
1574 static NTSTATUS
aux_message(void *args
)
1576 struct aux_message_params
*params
= args
;
1578 switch (params
->msg
)
1581 *params
->err
= aux_init();
1584 *params
->err
= aux_exit();
1588 /* FIXME: Pretend this is supported */
1591 case AUXDM_GETDEVCAPS
:
1592 *params
->err
= aux_get_devcaps(params
->dev_id
, (AUXCAPSW
*)params
->param_1
, params
->param_2
);
1594 case AUXDM_GETNUMDEVS
:
1595 TRACE("return %d;\n", num_aux
);
1596 *params
->err
= num_aux
;
1598 case AUXDM_GETVOLUME
:
1599 *params
->err
= aux_get_volume(params
->dev_id
, (UINT
*)params
->param_1
);
1601 case AUXDM_SETVOLUME
:
1602 *params
->err
= aux_set_volume(params
->dev_id
, params
->param_1
);
1605 WARN("unknown message !\n");
1606 *params
->err
= MMSYSERR_NOTSUPPORTED
;
1610 return STATUS_SUCCESS
;
1613 unixlib_entry_t __wine_unix_call_funcs
[] =
1624 release_render_buffer
,
1626 release_capture_buffer
,
1627 is_format_supported
,
1631 get_current_padding
,
1632 get_next_packet_size
,
1649 static NTSTATUS
wow64_get_endpoint_ids(void *args
)
1658 unsigned int default_idx
;
1660 struct get_endpoint_ids_params params
=
1662 .flow
= params32
->flow
,
1663 .endpoints
= ULongToPtr(params32
->endpoints
),
1664 .size
= params32
->size
1666 get_endpoint_ids(¶ms
);
1667 params32
->size
= params
.size
;
1668 params32
->result
= params
.result
;
1669 params32
->num
= params
.num
;
1670 params32
->default_idx
= params
.default_idx
;
1671 return STATUS_SUCCESS
;
1674 static NTSTATUS
wow64_create_stream(void *args
)
1680 AUDCLNT_SHAREMODE share
;
1682 REFERENCE_TIME duration
;
1683 REFERENCE_TIME period
;
1688 struct create_stream_params params
=
1690 .device
= ULongToPtr(params32
->device
),
1691 .flow
= params32
->flow
,
1692 .share
= params32
->share
,
1693 .flags
= params32
->flags
,
1694 .duration
= params32
->duration
,
1695 .period
= params32
->period
,
1696 .fmt
= ULongToPtr(params32
->fmt
),
1697 .stream
= ULongToPtr(params32
->stream
)
1699 create_stream(¶ms
);
1700 params32
->result
= params
.result
;
1701 return STATUS_SUCCESS
;
1704 static NTSTATUS
wow64_release_stream(void *args
)
1708 stream_handle stream
;
1712 struct release_stream_params params
=
1714 .stream
= params32
->stream
,
1715 .timer_thread
= ULongToHandle(params32
->timer_thread
)
1717 release_stream(¶ms
);
1718 params32
->result
= params
.result
;
1719 return STATUS_SUCCESS
;
1722 static NTSTATUS
wow64_get_render_buffer(void *args
)
1726 stream_handle stream
;
1732 struct get_render_buffer_params params
=
1734 .stream
= params32
->stream
,
1735 .frames
= params32
->frames
,
1738 get_render_buffer(¶ms
);
1739 params32
->result
= params
.result
;
1740 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
1741 return STATUS_SUCCESS
;
1744 static NTSTATUS
wow64_get_capture_buffer(void *args
)
1748 stream_handle stream
;
1757 struct get_capture_buffer_params params
=
1759 .stream
= params32
->stream
,
1761 .frames
= ULongToPtr(params32
->frames
),
1762 .flags
= ULongToPtr(params32
->flags
),
1763 .devpos
= ULongToPtr(params32
->devpos
),
1764 .qpcpos
= ULongToPtr(params32
->qpcpos
)
1766 get_capture_buffer(¶ms
);
1767 params32
->result
= params
.result
;
1768 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
1769 return STATUS_SUCCESS
;
1772 static NTSTATUS
wow64_is_format_supported(void *args
)
1778 AUDCLNT_SHAREMODE share
;
1783 struct is_format_supported_params params
=
1785 .device
= ULongToPtr(params32
->device
),
1786 .flow
= params32
->flow
,
1787 .share
= params32
->share
,
1788 .fmt_in
= ULongToPtr(params32
->fmt_in
),
1789 .fmt_out
= ULongToPtr(params32
->fmt_out
)
1791 is_format_supported(¶ms
);
1792 params32
->result
= params
.result
;
1793 return STATUS_SUCCESS
;
1796 static NTSTATUS
wow64_get_mix_format(void *args
)
1805 struct get_mix_format_params params
=
1807 .device
= ULongToPtr(params32
->device
),
1808 .flow
= params32
->flow
,
1809 .fmt
= ULongToPtr(params32
->fmt
)
1811 get_mix_format(¶ms
);
1812 params32
->result
= params
.result
;
1813 return STATUS_SUCCESS
;
1816 static NTSTATUS
wow64_get_buffer_size(void *args
)
1820 stream_handle stream
;
1824 struct get_buffer_size_params params
=
1826 .stream
= params32
->stream
,
1827 .size
= ULongToPtr(params32
->size
)
1829 get_buffer_size(¶ms
);
1830 params32
->result
= params
.result
;
1831 return STATUS_SUCCESS
;
1834 static NTSTATUS
wow64_get_latency(void *args
)
1838 stream_handle stream
;
1842 struct get_latency_params params
=
1844 .stream
= params32
->stream
,
1845 .latency
= ULongToPtr(params32
->latency
)
1847 get_latency(¶ms
);
1848 params32
->result
= params
.result
;
1849 return STATUS_SUCCESS
;
1852 static NTSTATUS
wow64_get_current_padding(void *args
)
1856 stream_handle stream
;
1860 struct get_current_padding_params params
=
1862 .stream
= params32
->stream
,
1863 .padding
= ULongToPtr(params32
->padding
)
1865 get_current_padding(¶ms
);
1866 params32
->result
= params
.result
;
1867 return STATUS_SUCCESS
;
1870 static NTSTATUS
wow64_get_next_packet_size(void *args
)
1874 stream_handle stream
;
1878 struct get_next_packet_size_params params
=
1880 .stream
= params32
->stream
,
1881 .frames
= ULongToPtr(params32
->frames
)
1883 get_next_packet_size(¶ms
);
1884 params32
->result
= params
.result
;
1885 return STATUS_SUCCESS
;
1888 static NTSTATUS
wow64_get_frequency(void *args
)
1892 stream_handle stream
;
1896 struct get_frequency_params params
=
1898 .stream
= params32
->stream
,
1899 .frequency
= ULongToPtr(params32
->frequency
)
1901 get_frequency(¶ms
);
1902 params32
->result
= params
.result
;
1903 return STATUS_SUCCESS
;
1906 static NTSTATUS
wow64_get_position(void *args
)
1910 stream_handle stream
;
1915 struct get_position_params params
=
1917 .stream
= params32
->stream
,
1918 .position
= ULongToPtr(params32
->position
),
1919 .qpctime
= ULongToPtr(params32
->qpctime
)
1921 get_position(¶ms
);
1922 params32
->result
= params
.result
;
1923 return STATUS_SUCCESS
;
1926 static NTSTATUS
wow64_set_volumes(void *args
)
1930 stream_handle stream
;
1931 float master_volume
;
1933 PTR32 session_volumes
;
1935 struct set_volumes_params params
=
1937 .stream
= params32
->stream
,
1938 .master_volume
= params32
->master_volume
,
1939 .volumes
= ULongToPtr(params32
->volumes
),
1940 .session_volumes
= ULongToPtr(params32
->session_volumes
)
1942 return set_volumes(¶ms
);
1945 static NTSTATUS
wow64_set_event_handle(void *args
)
1949 stream_handle stream
;
1953 struct set_event_handle_params params
=
1955 .stream
= params32
->stream
,
1956 .event
= ULongToHandle(params32
->event
)
1959 set_event_handle(¶ms
);
1960 params32
->result
= params
.result
;
1961 return STATUS_SUCCESS
;
1964 static NTSTATUS
wow64_aux_message(void *args
)
1975 struct aux_message_params params
=
1977 .dev_id
= params32
->dev_id
,
1978 .msg
= params32
->msg
,
1979 .user
= params32
->user
,
1980 .param_1
= params32
->param_1
,
1981 .param_2
= params32
->param_2
,
1982 .err
= ULongToPtr(params32
->err
),
1984 return aux_message(¶ms
);
1987 unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
1990 wow64_get_endpoint_ids
,
1991 wow64_create_stream
,
1992 wow64_release_stream
,
1997 wow64_get_render_buffer
,
1998 release_render_buffer
,
1999 wow64_get_capture_buffer
,
2000 release_capture_buffer
,
2001 wow64_is_format_supported
,
2002 wow64_get_mix_format
,
2003 wow64_get_buffer_size
,
2005 wow64_get_current_padding
,
2006 wow64_get_next_packet_size
,
2007 wow64_get_frequency
,
2010 wow64_set_event_handle
,
2013 wow64_midi_out_message
,
2014 wow64_midi_in_message
,
2015 wow64_midi_notify_wait
,