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 ULONG_PTR zero_bits
= 0;
77 static NTSTATUS
oss_not_implemented(void *args
)
79 return STATUS_SUCCESS
;
82 /* copied from kernelbase */
83 static int muldiv( int a
, int b
, int c
)
89 /* We want to deal with a positive divisor to simplify the logic. */
96 /* If the result is positive, we "add" to round. else, we subtract to round. */
97 if ((a
< 0 && b
< 0) || (a
>= 0 && b
>= 0))
98 ret
= (((LONGLONG
)a
* b
) + (c
/ 2)) / c
;
100 ret
= (((LONGLONG
)a
* b
) - (c
/ 2)) / c
;
102 if (ret
> 2147483647 || ret
< -2147483647) return -1;
106 static void oss_lock(struct oss_stream
*stream
)
108 pthread_mutex_lock(&stream
->lock
);
111 static void oss_unlock(struct oss_stream
*stream
)
113 pthread_mutex_unlock(&stream
->lock
);
116 static NTSTATUS
oss_unlock_result(struct oss_stream
*stream
,
117 HRESULT
*result
, HRESULT value
)
121 return STATUS_SUCCESS
;
124 static struct oss_stream
*handle_get_stream(stream_handle h
)
126 return (struct oss_stream
*)(UINT_PTR
)h
;
129 static NTSTATUS
oss_test_connect(void *args
)
131 struct test_connect_params
*params
= args
;
135 /* Attempt to determine if we are running on OSS or ALSA's OSS
136 * compatibility layer. There is no official way to do that, so just check
137 * for validity as best as possible, without rejecting valid OSS
138 * implementations. */
140 mixer_fd
= open("/dev/mixer", O_RDONLY
, 0);
142 TRACE("Priority_Unavailable: open failed\n");
143 params
->priority
= Priority_Unavailable
;
144 return STATUS_SUCCESS
;
147 sysinfo
.version
[0] = 0xFF;
148 sysinfo
.versionnum
= ~0;
149 if(ioctl(mixer_fd
, SNDCTL_SYSINFO
, &sysinfo
) < 0){
150 TRACE("Priority_Unavailable: ioctl failed\n");
152 params
->priority
= Priority_Unavailable
;
153 return STATUS_SUCCESS
;
158 if(sysinfo
.version
[0] < '4' || sysinfo
.version
[0] > '9'){
159 TRACE("Priority_Low: sysinfo.version[0]: %x\n", sysinfo
.version
[0]);
160 params
->priority
= Priority_Low
;
161 return STATUS_SUCCESS
;
163 if(sysinfo
.versionnum
& 0x80000000){
164 TRACE("Priority_Low: sysinfo.versionnum: %x\n", sysinfo
.versionnum
);
165 params
->priority
= Priority_Low
;
166 return STATUS_SUCCESS
;
169 TRACE("Priority_Preferred: Seems like valid OSS!\n");
171 params
->priority
= Priority_Preferred
;
172 return STATUS_SUCCESS
;
175 /* dst must be large enough to hold devnode */
176 static void oss_clean_devnode(char *dest
, const char *devnode
)
178 const char *dot
, *slash
;
181 strcpy(dest
, devnode
);
182 dot
= strrchr(dest
, '.');
186 slash
= strrchr(dest
, '/');
187 if(slash
&& dot
< slash
)
194 static int open_device(const char *device
, EDataFlow flow
)
196 int flags
= ((flow
== eRender
) ? O_WRONLY
: O_RDONLY
) | O_NONBLOCK
;
198 return open(device
, flags
, 0);
201 static void get_default_device(EDataFlow flow
, char device
[OSS_DEVNODE_SIZE
])
207 fd
= open_device("/dev/dsp", flow
);
209 WARN("Couldn't open default device!\n");
214 if((err
= ioctl(fd
, SNDCTL_ENGINEINFO
, &ai
)) < 0){
215 WARN("SNDCTL_ENGINEINFO failed: %d (%s)\n", err
, strerror(errno
));
221 TRACE("Default devnode: %s\n", ai
.devnode
);
222 oss_clean_devnode(device
, ai
.devnode
);
226 static NTSTATUS
oss_process_attach(void *args
)
229 if (NtCurrentTeb()->WowTebOffset
)
231 SYSTEM_BASIC_INFORMATION info
;
233 NtQuerySystemInformation(SystemEmulationBasicInformation
, &info
, sizeof(info
), NULL
);
234 zero_bits
= (ULONG_PTR
)info
.HighestUserAddress
| 0x7fffffff;
237 return STATUS_SUCCESS
;
240 static NTSTATUS
oss_main_loop(void *args
)
242 struct main_loop_params
*params
= args
;
243 NtSetEvent(params
->event
, NULL
);
244 return STATUS_SUCCESS
;
247 static NTSTATUS
oss_get_endpoint_ids(void *args
)
249 struct get_endpoint_ids_params
*params
= args
;
252 static int print_once
= 0;
253 static const WCHAR outW
[] = {'O','u','t',':',' ',0};
254 static const WCHAR inW
[] = {'I','n',':',' ',0};
257 WCHAR name
[ARRAY_SIZE(ai
.name
) + ARRAY_SIZE(outW
)];
258 char device
[OSS_DEVNODE_SIZE
];
260 unsigned int i
, j
, num
, needed
, name_len
, device_len
, offset
, default_idx
= 0;
261 char default_device
[OSS_DEVNODE_SIZE
];
262 struct endpoint
*endpoint
;
265 mixer_fd
= open("/dev/mixer", O_RDONLY
, 0);
267 ERR("OSS /dev/mixer doesn't seem to exist\n");
268 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
269 return STATUS_SUCCESS
;
272 if(ioctl(mixer_fd
, SNDCTL_SYSINFO
, &sysinfo
) < 0){
275 ERR("OSS version too old, need at least OSSv4\n");
276 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
277 return STATUS_SUCCESS
;
280 ERR("Error getting SNDCTL_SYSINFO: %d (%s)\n", errno
, strerror(errno
));
281 params
->result
= E_FAIL
;
282 return STATUS_SUCCESS
;
286 TRACE("OSS sysinfo:\n");
287 TRACE("product: %s\n", sysinfo
.product
);
288 TRACE("version: %s\n", sysinfo
.version
);
289 TRACE("versionnum: %x\n", sysinfo
.versionnum
);
290 TRACE("numaudios: %d\n", sysinfo
.numaudios
);
291 TRACE("nummixers: %d\n", sysinfo
.nummixers
);
292 TRACE("numcards: %d\n", sysinfo
.numcards
);
293 TRACE("numaudioengines: %d\n", sysinfo
.numaudioengines
);
297 if(sysinfo
.numaudios
<= 0){
298 WARN("No audio devices!\n");
300 params
->result
= AUDCLNT_E_SERVICE_NOT_RUNNING
;
301 return STATUS_SUCCESS
;
304 info
= malloc(sysinfo
.numaudios
* sizeof(*info
));
307 params
->result
= E_OUTOFMEMORY
;
308 return STATUS_SUCCESS
;
311 get_default_device(params
->flow
, default_device
);
314 for(i
= 0; i
< sysinfo
.numaudios
; ++i
){
315 char devnode
[OSS_DEVNODE_SIZE
];
319 memset(&ai
, 0, sizeof(ai
));
321 if(ioctl(mixer_fd
, SNDCTL_AUDIOINFO
, &ai
) < 0){
322 WARN("Error getting AUDIOINFO for dev %d: %d (%s)\n", i
, errno
,
327 oss_clean_devnode(devnode
, ai
.devnode
);
329 /* check for duplicates */
330 for(j
= 0; j
< num
; j
++)
331 if(!strcmp(devnode
, info
[j
].device
))
336 fd
= open_device(devnode
, params
->flow
);
338 WARN("Opening device \"%s\" failed, pretending it doesn't exist: %d (%s)\n",
339 devnode
, errno
, strerror(errno
));
344 if((params
->flow
== eCapture
&& !(ai
.caps
& PCM_CAP_INPUT
)) ||
345 (params
->flow
== eRender
&& !(ai
.caps
& PCM_CAP_OUTPUT
)))
348 strcpy(info
[num
].device
, devnode
);
350 if(params
->flow
== eRender
){
352 prefix_len
= ARRAY_SIZE(outW
) - 1;
355 prefix_len
= ARRAY_SIZE(inW
) - 1;
357 memcpy(info
[num
].name
, prefix
, prefix_len
* sizeof(WCHAR
));
358 ntdll_umbstowcs(ai
.name
, strlen(ai
.name
) + 1, info
[num
].name
+ prefix_len
,
359 ARRAY_SIZE(info
[num
].name
) - prefix_len
);
360 if(!strcmp(default_device
, info
[num
].device
))
366 offset
= needed
= num
* sizeof(*params
->endpoints
);
367 endpoint
= params
->endpoints
;
369 for(i
= 0; i
< num
; i
++){
370 name_len
= wcslen(info
[i
].name
) + 1;
371 device_len
= strlen(info
[i
].device
) + 1;
372 needed
+= name_len
* sizeof(WCHAR
) + ((device_len
+ 1) & ~1);
374 if(needed
<= params
->size
){
375 endpoint
->name
= offset
;
376 memcpy((char *)params
->endpoints
+ offset
, info
[i
].name
, name_len
* sizeof(WCHAR
));
377 offset
+= name_len
* sizeof(WCHAR
);
378 endpoint
->device
= offset
;
379 memcpy((char *)params
->endpoints
+ offset
, info
[i
].device
, device_len
);
380 offset
+= (device_len
+ 1) & ~1;
387 params
->default_idx
= default_idx
;
389 if(needed
> params
->size
){
390 params
->size
= needed
;
391 params
->result
= HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
);
393 params
->result
= S_OK
;
395 return STATUS_SUCCESS
;
398 static UINT
get_channel_mask(unsigned int channels
)
404 return KSAUDIO_SPEAKER_MONO
;
406 return KSAUDIO_SPEAKER_STEREO
;
408 return KSAUDIO_SPEAKER_STEREO
| SPEAKER_LOW_FREQUENCY
;
410 return KSAUDIO_SPEAKER_QUAD
; /* not _SURROUND */
412 return KSAUDIO_SPEAKER_QUAD
| SPEAKER_LOW_FREQUENCY
;
414 return KSAUDIO_SPEAKER_5POINT1
; /* not 5POINT1_SURROUND */
416 return KSAUDIO_SPEAKER_5POINT1
| SPEAKER_BACK_CENTER
;
418 return KSAUDIO_SPEAKER_7POINT1_SURROUND
; /* Vista deprecates 7POINT1 */
420 FIXME("Unknown speaker configuration: %u\n", channels
);
424 static int get_oss_format(const WAVEFORMATEX
*fmt
)
426 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)fmt
;
428 if(fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
429 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
430 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
431 switch(fmt
->wBitsPerSample
){
445 if(fmt
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
446 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
447 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))){
448 if(fmt
->wBitsPerSample
!= 32)
458 static WAVEFORMATEXTENSIBLE
*clone_format(const WAVEFORMATEX
*fmt
)
460 WAVEFORMATEXTENSIBLE
*ret
;
463 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
464 size
= sizeof(WAVEFORMATEXTENSIBLE
);
466 size
= sizeof(WAVEFORMATEX
);
472 memcpy(ret
, fmt
, size
);
474 ret
->Format
.cbSize
= size
- sizeof(WAVEFORMATEX
);
479 static HRESULT
setup_oss_device(AUDCLNT_SHAREMODE share
, int fd
,
480 const WAVEFORMATEX
*fmt
, WAVEFORMATEXTENSIBLE
*out
)
482 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
486 WAVEFORMATEXTENSIBLE
*closest
;
488 tmp
= oss_format
= get_oss_format(fmt
);
490 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
491 if(ioctl(fd
, SNDCTL_DSP_SETFMT
, &tmp
) < 0){
492 WARN("SETFMT failed: %d (%s)\n", errno
, strerror(errno
));
495 if(tmp
!= oss_format
){
496 TRACE("Format unsupported by this OSS version: %x\n", oss_format
);
497 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
500 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
501 (fmtex
->Format
.nAvgBytesPerSec
== 0 ||
502 fmtex
->Format
.nBlockAlign
== 0 ||
503 fmtex
->Samples
.wValidBitsPerSample
> fmtex
->Format
.wBitsPerSample
))
506 if(fmt
->nChannels
== 0)
507 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
509 closest
= clone_format(fmt
);
511 return E_OUTOFMEMORY
;
513 tmp
= fmt
->nSamplesPerSec
;
514 if(ioctl(fd
, SNDCTL_DSP_SPEED
, &tmp
) < 0){
515 WARN("SPEED failed: %d (%s)\n", errno
, strerror(errno
));
519 tenth
= fmt
->nSamplesPerSec
* 0.1;
520 if(tmp
> fmt
->nSamplesPerSec
+ tenth
|| tmp
< fmt
->nSamplesPerSec
- tenth
){
522 closest
->Format
.nSamplesPerSec
= tmp
;
525 tmp
= fmt
->nChannels
;
526 if(ioctl(fd
, SNDCTL_DSP_CHANNELS
, &tmp
) < 0){
527 WARN("CHANNELS failed: %d (%s)\n", errno
, strerror(errno
));
531 if(tmp
!= fmt
->nChannels
){
533 closest
->Format
.nChannels
= tmp
;
536 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
537 closest
->dwChannelMask
= get_channel_mask(closest
->Format
.nChannels
);
539 if(fmt
->nBlockAlign
!= fmt
->nChannels
* fmt
->wBitsPerSample
/ 8 ||
540 fmt
->nAvgBytesPerSec
!= fmt
->nBlockAlign
* fmt
->nSamplesPerSec
||
541 (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
542 fmtex
->Samples
.wValidBitsPerSample
< fmtex
->Format
.wBitsPerSample
))
545 if(share
== AUDCLNT_SHAREMODE_EXCLUSIVE
&&
546 fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
547 if(fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
)
551 if(ret
== S_FALSE
&& !out
)
552 ret
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
554 if(ret
== S_FALSE
&& out
){
555 closest
->Format
.nBlockAlign
=
556 closest
->Format
.nChannels
* closest
->Format
.wBitsPerSample
/ 8;
557 closest
->Format
.nAvgBytesPerSec
=
558 closest
->Format
.nBlockAlign
* closest
->Format
.nSamplesPerSec
;
559 if(closest
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
560 closest
->Samples
.wValidBitsPerSample
= closest
->Format
.wBitsPerSample
;
561 memcpy(out
, closest
, closest
->Format
.cbSize
+ sizeof(WAVEFORMATEX
));
565 TRACE("returning: %08x\n", (unsigned)ret
);
569 static NTSTATUS
oss_create_stream(void *args
)
571 struct create_stream_params
*params
= args
;
572 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)params
->fmt
;
573 struct oss_stream
*stream
;
577 params
->result
= S_OK
;
579 if (params
->share
== AUDCLNT_SHAREMODE_SHARED
) {
580 params
->period
= def_period
;
581 if (params
->duration
< 3 * params
->period
)
582 params
->duration
= 3 * params
->period
;
584 if (fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
585 (fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
))
586 params
->result
= AUDCLNT_E_UNSUPPORTED_FORMAT
;
589 params
->period
= def_period
;
590 if (params
->period
< min_period
|| params
->period
> 5000000)
591 params
->result
= AUDCLNT_E_INVALID_DEVICE_PERIOD
;
592 else if (params
->duration
> 20000000) /* The smaller the period, the lower this limit. */
593 params
->result
= AUDCLNT_E_BUFFER_SIZE_ERROR
;
594 else if (params
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) {
595 if (params
->duration
!= params
->period
)
596 params
->result
= AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
598 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
600 params
->result
= AUDCLNT_E_DEVICE_IN_USE
;
601 } else if (params
->duration
< 8 * params
->period
)
602 params
->duration
= 8 * params
->period
; /* May grow above 2s. */
606 if (FAILED(params
->result
))
607 return STATUS_SUCCESS
;
609 stream
= calloc(1, sizeof(*stream
));
611 params
->result
= E_OUTOFMEMORY
;
612 return STATUS_SUCCESS
;
615 stream
->flow
= params
->flow
;
616 pthread_mutex_init(&stream
->lock
, NULL
);
618 stream
->fd
= open_device(params
->device
, params
->flow
);
620 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
621 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
626 if(ioctl(stream
->fd
, SNDCTL_ENGINEINFO
, &ai
) < 0){
627 WARN("Unable to get audio info for device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
628 params
->result
= E_FAIL
;
632 TRACE("OSS audioinfo:\n");
633 TRACE("devnode: %s\n", ai
.devnode
);
634 TRACE("name: %s\n", ai
.name
);
635 TRACE("busy: %x\n", ai
.busy
);
636 TRACE("caps: %x\n", ai
.caps
);
637 TRACE("iformats: %x\n", ai
.iformats
);
638 TRACE("oformats: %x\n", ai
.oformats
);
639 TRACE("enabled: %d\n", ai
.enabled
);
640 TRACE("min_rate: %d\n", ai
.min_rate
);
641 TRACE("max_rate: %d\n", ai
.max_rate
);
642 TRACE("min_channels: %d\n", ai
.min_channels
);
643 TRACE("max_channels: %d\n", ai
.max_channels
);
645 params
->result
= setup_oss_device(params
->share
, stream
->fd
, params
->fmt
, NULL
);
646 if(FAILED(params
->result
))
649 fmtex
= clone_format(params
->fmt
);
651 params
->result
= E_OUTOFMEMORY
;
654 stream
->fmt
= &fmtex
->Format
;
656 stream
->period
= params
->period
;
657 stream
->period_frames
= muldiv(params
->fmt
->nSamplesPerSec
, params
->period
, 10000000);
659 stream
->bufsize_frames
= muldiv(params
->duration
, params
->fmt
->nSamplesPerSec
, 10000000);
660 if(params
->share
== AUDCLNT_SHAREMODE_EXCLUSIVE
)
661 stream
->bufsize_frames
-= stream
->bufsize_frames
% stream
->period_frames
;
662 size
= stream
->bufsize_frames
* params
->fmt
->nBlockAlign
;
663 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, zero_bits
,
664 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
665 params
->result
= E_OUTOFMEMORY
;
669 stream
->share
= params
->share
;
670 stream
->flags
= params
->flags
;
671 stream
->oss_bufsize_bytes
= 0;
674 if(FAILED(params
->result
)){
675 if(stream
->fd
>= 0) close(stream
->fd
);
676 if(stream
->local_buffer
){
678 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
680 pthread_mutex_destroy(&stream
->lock
);
684 *params
->channel_count
= params
->fmt
->nChannels
;
685 *params
->stream
= (stream_handle
)(UINT_PTR
)stream
;
688 return STATUS_SUCCESS
;
691 static NTSTATUS
oss_release_stream(void *args
)
693 struct release_stream_params
*params
= args
;
694 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
697 if(params
->timer_thread
){
698 stream
->please_quit
= TRUE
;
699 NtWaitForSingleObject(params
->timer_thread
, FALSE
, NULL
);
700 NtClose(params
->timer_thread
);
704 if(stream
->local_buffer
){
706 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->local_buffer
, &size
, MEM_RELEASE
);
708 if(stream
->tmp_buffer
){
710 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
713 pthread_mutex_destroy(&stream
->lock
);
716 params
->result
= S_OK
;
717 return STATUS_SUCCESS
;
720 static NTSTATUS
oss_start(void *args
)
722 struct start_params
*params
= args
;
723 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
727 if((stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) && !stream
->event
)
728 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_SET
);
731 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
733 stream
->playing
= TRUE
;
735 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
738 static NTSTATUS
oss_stop(void *args
)
740 struct stop_params
*params
= args
;
741 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
746 return oss_unlock_result(stream
, ¶ms
->result
, S_FALSE
);
748 stream
->playing
= FALSE
;
749 stream
->in_oss_frames
= 0;
751 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
754 static NTSTATUS
oss_reset(void *args
)
756 struct reset_params
*params
= args
;
757 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
762 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_NOT_STOPPED
);
764 if(stream
->getbuf_last
)
765 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_OPERATION_PENDING
);
767 if(stream
->flow
== eRender
){
768 stream
->written_frames
= 0;
769 stream
->last_pos_frames
= 0;
771 stream
->written_frames
+= stream
->held_frames
;
773 stream
->held_frames
= 0;
774 stream
->lcl_offs_frames
= 0;
775 stream
->in_oss_frames
= 0;
777 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
780 static void silence_buffer(struct oss_stream
*stream
, BYTE
*buffer
, UINT32 frames
)
782 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->fmt
;
783 if((stream
->fmt
->wFormatTag
== WAVE_FORMAT_PCM
||
784 (stream
->fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
785 IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))) &&
786 stream
->fmt
->wBitsPerSample
== 8)
787 memset(buffer
, 128, frames
* stream
->fmt
->nBlockAlign
);
789 memset(buffer
, 0, frames
* stream
->fmt
->nBlockAlign
);
792 static void oss_write_data(struct oss_stream
*stream
)
794 ssize_t written_bytes
;
795 UINT32 written_frames
, in_oss_frames
, write_limit
, max_period
, write_offs_frames
, new_frames
;
796 SIZE_T to_write_frames
, to_write_bytes
, advanced
;
800 if(ioctl(stream
->fd
, SNDCTL_DSP_GETOSPACE
, &bi
) < 0){
801 WARN("GETOSPACE failed: %d (%s)\n", errno
, strerror(errno
));
805 max_period
= max(bi
.fragsize
/ stream
->fmt
->nBlockAlign
, stream
->period_frames
);
807 if(bi
.bytes
> stream
->oss_bufsize_bytes
){
808 TRACE("New buffer size (%u) is larger than old buffer size (%u)\n",
809 bi
.bytes
, stream
->oss_bufsize_bytes
);
810 stream
->oss_bufsize_bytes
= bi
.bytes
;
813 in_oss_frames
= (stream
->oss_bufsize_bytes
- bi
.bytes
) / stream
->fmt
->nBlockAlign
;
815 if(in_oss_frames
> stream
->in_oss_frames
){
816 TRACE("Capping reported frames from %u to %u\n",
817 in_oss_frames
, stream
->in_oss_frames
);
818 in_oss_frames
= stream
->in_oss_frames
;
822 while(write_limit
+ in_oss_frames
< max_period
* 3)
823 write_limit
+= max_period
;
827 /* vvvvvv - in_oss_frames
830 * ^^^^^^^^^^ - held_frames
831 * ^ - lcl_offs_frames
833 advanced
= stream
->in_oss_frames
- in_oss_frames
;
834 if(advanced
> stream
->held_frames
)
835 advanced
= stream
->held_frames
;
836 stream
->lcl_offs_frames
+= advanced
;
837 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
838 stream
->held_frames
-= advanced
;
839 stream
->in_oss_frames
= in_oss_frames
;
840 TRACE("advanced by %lu, lcl_offs: %u, held: %u, in_oss: %u\n",
841 advanced
, stream
->lcl_offs_frames
, stream
->held_frames
, stream
->in_oss_frames
);
844 if(stream
->held_frames
== stream
->in_oss_frames
)
847 write_offs_frames
= (stream
->lcl_offs_frames
+ stream
->in_oss_frames
) % stream
->bufsize_frames
;
848 new_frames
= stream
->held_frames
- stream
->in_oss_frames
;
850 if(write_offs_frames
+ new_frames
> stream
->bufsize_frames
)
851 to_write_frames
= stream
->bufsize_frames
- write_offs_frames
;
853 to_write_frames
= new_frames
;
855 to_write_frames
= min(to_write_frames
, write_limit
);
856 to_write_bytes
= to_write_frames
* stream
->fmt
->nBlockAlign
;
857 TRACE("going to write %lu frames from %u (%lu of %u)\n", to_write_frames
,
858 write_offs_frames
, to_write_frames
+ write_offs_frames
,
859 stream
->bufsize_frames
);
861 buf
= stream
->local_buffer
+ write_offs_frames
* stream
->fmt
->nBlockAlign
;
864 silence_buffer(stream
, buf
, to_write_frames
);
866 written_bytes
= write(stream
->fd
, buf
, to_write_bytes
);
867 if(written_bytes
< 0){
868 /* EAGAIN is OSS buffer full, log that too */
869 WARN("write failed: %d (%s)\n", errno
, strerror(errno
));
872 written_frames
= written_bytes
/ stream
->fmt
->nBlockAlign
;
874 stream
->in_oss_frames
+= written_frames
;
876 if(written_frames
< to_write_frames
){
877 /* OSS buffer probably full */
881 if(new_frames
> written_frames
&& written_frames
< write_limit
){
882 /* wrapped and have some data back at the start to write */
884 to_write_frames
= min(write_limit
- written_frames
, new_frames
- written_frames
);
885 to_write_bytes
= to_write_frames
* stream
->fmt
->nBlockAlign
;
888 silence_buffer(stream
, stream
->local_buffer
, to_write_frames
);
890 TRACE("wrapping to write %lu frames from beginning\n", to_write_frames
);
892 written_bytes
= write(stream
->fd
, stream
->local_buffer
, to_write_bytes
);
893 if(written_bytes
< 0){
894 WARN("write failed: %d (%s)\n", errno
, strerror(errno
));
897 written_frames
= written_bytes
/ stream
->fmt
->nBlockAlign
;
898 stream
->in_oss_frames
+= written_frames
;
902 static void oss_read_data(struct oss_stream
*stream
)
904 UINT64 pos
, readable
;
907 pos
= (stream
->held_frames
+ stream
->lcl_offs_frames
) % stream
->bufsize_frames
;
908 readable
= (stream
->bufsize_frames
- pos
) * stream
->fmt
->nBlockAlign
;
910 nread
= read(stream
->fd
, stream
->local_buffer
+ pos
* stream
->fmt
->nBlockAlign
,
913 WARN("read failed: %d (%s)\n", errno
, strerror(errno
));
917 stream
->held_frames
+= nread
/ stream
->fmt
->nBlockAlign
;
919 if(stream
->held_frames
> stream
->bufsize_frames
){
920 WARN("Overflow of unread data\n");
921 stream
->lcl_offs_frames
+= stream
->held_frames
;
922 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
923 stream
->held_frames
= stream
->bufsize_frames
;
927 static NTSTATUS
oss_timer_loop(void *args
)
929 struct timer_loop_params
*params
= args
;
930 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
931 LARGE_INTEGER delay
, now
, next
;
936 delay
.QuadPart
= -stream
->period
;
937 NtQueryPerformanceCounter(&now
, NULL
);
938 next
.QuadPart
= now
.QuadPart
+ stream
->period
;
940 while(!stream
->please_quit
){
942 if(stream
->flow
== eRender
&& stream
->held_frames
)
943 oss_write_data(stream
);
944 else if(stream
->flow
== eCapture
)
945 oss_read_data(stream
);
948 NtSetEvent(stream
->event
, NULL
);
951 NtDelayExecution(FALSE
, &delay
);
954 NtQueryPerformanceCounter(&now
, NULL
);
955 adjust
= next
.QuadPart
- now
.QuadPart
;
956 if(adjust
> stream
->period
/ 2)
957 adjust
= stream
->period
/ 2;
958 else if(adjust
< -stream
->period
/ 2)
959 adjust
= -stream
->period
/ 2;
960 delay
.QuadPart
= -(stream
->period
+ adjust
);
961 next
.QuadPart
+= stream
->period
;
966 return STATUS_SUCCESS
;
969 static NTSTATUS
oss_get_render_buffer(void *args
)
971 struct get_render_buffer_params
*params
= args
;
972 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
973 UINT32 write_pos
, frames
= params
->frames
;
974 BYTE
**data
= params
->data
;
979 if(stream
->getbuf_last
)
980 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
983 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
985 if(stream
->held_frames
+ frames
> stream
->bufsize_frames
)
986 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_BUFFER_TOO_LARGE
);
989 (stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
;
990 if(write_pos
+ frames
> stream
->bufsize_frames
){
991 if(stream
->tmp_buffer_frames
< frames
){
992 if(stream
->tmp_buffer
){
994 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
995 stream
->tmp_buffer
= NULL
;
997 size
= frames
* stream
->fmt
->nBlockAlign
;
998 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits
,
999 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
1000 stream
->tmp_buffer_frames
= 0;
1001 return oss_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1003 stream
->tmp_buffer_frames
= frames
;
1005 *data
= stream
->tmp_buffer
;
1006 stream
->getbuf_last
= -frames
;
1008 *data
= stream
->local_buffer
+ write_pos
* stream
->fmt
->nBlockAlign
;
1009 stream
->getbuf_last
= frames
;
1012 silence_buffer(stream
, *data
, frames
);
1014 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1017 static void oss_wrap_buffer(struct oss_stream
*stream
, BYTE
*buffer
, UINT32 written_frames
)
1019 UINT32 write_offs_frames
=
1020 (stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
;
1021 UINT32 write_offs_bytes
= write_offs_frames
* stream
->fmt
->nBlockAlign
;
1022 UINT32 chunk_frames
= stream
->bufsize_frames
- write_offs_frames
;
1023 UINT32 chunk_bytes
= chunk_frames
* stream
->fmt
->nBlockAlign
;
1024 UINT32 written_bytes
= written_frames
* stream
->fmt
->nBlockAlign
;
1026 if(written_bytes
<= chunk_bytes
){
1027 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, written_bytes
);
1029 memcpy(stream
->local_buffer
+ write_offs_bytes
, buffer
, chunk_bytes
);
1030 memcpy(stream
->local_buffer
, buffer
+ chunk_bytes
,
1031 written_bytes
- chunk_bytes
);
1035 static NTSTATUS
oss_release_render_buffer(void *args
)
1037 struct release_render_buffer_params
*params
= args
;
1038 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1039 UINT32 written_frames
= params
->written_frames
;
1040 UINT flags
= params
->flags
;
1045 if(!written_frames
){
1046 stream
->getbuf_last
= 0;
1047 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1050 if(!stream
->getbuf_last
)
1051 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1053 if(written_frames
> (stream
->getbuf_last
>= 0 ? stream
->getbuf_last
: -stream
->getbuf_last
))
1054 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1056 if(stream
->getbuf_last
>= 0)
1057 buffer
= stream
->local_buffer
+ stream
->fmt
->nBlockAlign
*
1058 ((stream
->lcl_offs_frames
+ stream
->held_frames
) % stream
->bufsize_frames
);
1060 buffer
= stream
->tmp_buffer
;
1062 if(flags
& AUDCLNT_BUFFERFLAGS_SILENT
)
1063 silence_buffer(stream
, buffer
, written_frames
);
1065 if(stream
->getbuf_last
< 0)
1066 oss_wrap_buffer(stream
, buffer
, written_frames
);
1068 stream
->held_frames
+= written_frames
;
1069 stream
->written_frames
+= written_frames
;
1070 stream
->getbuf_last
= 0;
1072 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1075 static NTSTATUS
oss_get_capture_buffer(void *args
)
1077 struct get_capture_buffer_params
*params
= args
;
1078 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1079 UINT64
*devpos
= params
->devpos
, *qpcpos
= params
->qpcpos
;
1080 UINT32
*frames
= params
->frames
;
1081 UINT
*flags
= params
->flags
;
1082 BYTE
**data
= params
->data
;
1087 if(stream
->getbuf_last
)
1088 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1090 if(stream
->held_frames
< stream
->period_frames
){
1092 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_S_BUFFER_EMPTY
);
1097 *frames
= stream
->period_frames
;
1099 if(stream
->lcl_offs_frames
+ *frames
> stream
->bufsize_frames
){
1100 UINT32 chunk_bytes
, offs_bytes
, frames_bytes
;
1101 if(stream
->tmp_buffer_frames
< *frames
){
1102 if(stream
->tmp_buffer
){
1104 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, &size
, MEM_RELEASE
);
1105 stream
->tmp_buffer
= NULL
;
1107 size
= *frames
* stream
->fmt
->nBlockAlign
;
1108 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream
->tmp_buffer
, zero_bits
,
1109 &size
, MEM_COMMIT
, PAGE_READWRITE
)){
1110 stream
->tmp_buffer_frames
= 0;
1111 return oss_unlock_result(stream
, ¶ms
->result
, E_OUTOFMEMORY
);
1113 stream
->tmp_buffer_frames
= *frames
;
1116 *data
= stream
->tmp_buffer
;
1117 chunk_bytes
= (stream
->bufsize_frames
- stream
->lcl_offs_frames
) *
1118 stream
->fmt
->nBlockAlign
;
1119 offs_bytes
= stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1120 frames_bytes
= *frames
* stream
->fmt
->nBlockAlign
;
1121 memcpy(stream
->tmp_buffer
, stream
->local_buffer
+ offs_bytes
, chunk_bytes
);
1122 memcpy(stream
->tmp_buffer
+ chunk_bytes
, stream
->local_buffer
,
1123 frames_bytes
- chunk_bytes
);
1125 *data
= stream
->local_buffer
+
1126 stream
->lcl_offs_frames
* stream
->fmt
->nBlockAlign
;
1128 stream
->getbuf_last
= *frames
;
1131 *devpos
= stream
->written_frames
;
1133 LARGE_INTEGER stamp
, freq
;
1134 NtQueryPerformanceCounter(&stamp
, &freq
);
1135 *qpcpos
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1138 return oss_unlock_result(stream
, ¶ms
->result
, *frames
? S_OK
: AUDCLNT_S_BUFFER_EMPTY
);
1141 static NTSTATUS
oss_release_capture_buffer(void *args
)
1143 struct release_capture_buffer_params
*params
= args
;
1144 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1145 UINT32 done
= params
->done
;
1150 stream
->getbuf_last
= 0;
1151 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1154 if(!stream
->getbuf_last
)
1155 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_OUT_OF_ORDER
);
1157 if(stream
->getbuf_last
!= done
)
1158 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_INVALID_SIZE
);
1160 stream
->written_frames
+= done
;
1161 stream
->held_frames
-= done
;
1162 stream
->lcl_offs_frames
+= done
;
1163 stream
->lcl_offs_frames
%= stream
->bufsize_frames
;
1164 stream
->getbuf_last
= 0;
1166 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1169 static NTSTATUS
oss_is_format_supported(void *args
)
1171 struct is_format_supported_params
*params
= args
;
1174 params
->result
= S_OK
;
1176 if(!params
->fmt_in
|| (params
->share
== AUDCLNT_SHAREMODE_SHARED
&& !params
->fmt_out
))
1177 params
->result
= E_POINTER
;
1178 else if(params
->share
!= AUDCLNT_SHAREMODE_SHARED
&& params
->share
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
1179 params
->result
= E_INVALIDARG
;
1180 else if(params
->fmt_in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
1181 params
->fmt_in
->cbSize
< sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
))
1182 params
->result
= E_INVALIDARG
;
1183 if(FAILED(params
->result
))
1184 return STATUS_SUCCESS
;
1186 fd
= open_device(params
->device
, params
->flow
);
1188 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1189 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1190 return STATUS_SUCCESS
;
1192 params
->result
= setup_oss_device(params
->share
, fd
, params
->fmt_in
, params
->fmt_out
);
1195 return STATUS_SUCCESS
;
1198 static NTSTATUS
oss_get_mix_format(void *args
)
1200 struct get_mix_format_params
*params
= args
;
1201 WAVEFORMATEXTENSIBLE
*fmt
= params
->fmt
;
1205 if(params
->flow
!= eRender
&& params
->flow
!= eCapture
){
1206 params
->result
= E_UNEXPECTED
;
1207 return STATUS_SUCCESS
;
1210 fd
= open_device(params
->device
, params
->flow
);
1212 WARN("Unable to open device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1213 params
->result
= AUDCLNT_E_DEVICE_INVALIDATED
;
1214 return STATUS_SUCCESS
;
1218 if(ioctl(fd
, SNDCTL_ENGINEINFO
, &ai
) < 0){
1219 WARN("Unable to get audio info for device %s: %d (%s)\n", params
->device
, errno
, strerror(errno
));
1221 params
->result
= E_FAIL
;
1222 return STATUS_SUCCESS
;
1226 if(params
->flow
== eRender
)
1227 formats
= ai
.oformats
;
1229 formats
= ai
.iformats
;
1231 fmt
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1232 if(formats
& AFMT_S16_LE
){
1233 fmt
->Format
.wBitsPerSample
= 16;
1234 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1236 }else if(formats
& AFMT_FLOAT
){
1237 fmt
->Format
.wBitsPerSample
= 32;
1238 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1240 }else if(formats
& AFMT_U8
){
1241 fmt
->Format
.wBitsPerSample
= 8;
1242 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1243 }else if(formats
& AFMT_S32_LE
){
1244 fmt
->Format
.wBitsPerSample
= 32;
1245 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1246 }else if(formats
& AFMT_S24_LE
){
1247 fmt
->Format
.wBitsPerSample
= 24;
1248 fmt
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1250 WARN("Didn't recognize any available OSS formats: %x\n", formats
);
1251 params
->result
= E_FAIL
;
1252 return STATUS_SUCCESS
;
1255 /* some OSS drivers are buggy, so set reasonable defaults if
1256 * the reported values seem wacky */
1257 fmt
->Format
.nChannels
= max(ai
.max_channels
, ai
.min_channels
);
1258 if(fmt
->Format
.nChannels
== 0 || fmt
->Format
.nChannels
> 8)
1259 fmt
->Format
.nChannels
= 2;
1261 /* For most hardware on Windows, users must choose a configuration with an even
1262 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
1263 * channels, but those channels are still reported to applications from
1264 * GetMixFormat! Some applications behave badly if given an odd number of
1265 * channels (e.g. 2.1). */
1266 if(fmt
->Format
.nChannels
> 1 && (fmt
->Format
.nChannels
& 0x1))
1268 if(fmt
->Format
.nChannels
< ai
.max_channels
)
1269 fmt
->Format
.nChannels
+= 1;
1271 /* We could "fake" more channels and downmix the emulated channels,
1272 * but at that point you really ought to tweak your OSS setup or
1273 * just use PulseAudio. */
1274 WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt
->Format
.nChannels
);
1277 if(ai
.max_rate
== 0)
1278 fmt
->Format
.nSamplesPerSec
= 44100;
1280 fmt
->Format
.nSamplesPerSec
= min(ai
.max_rate
, 44100);
1281 if(fmt
->Format
.nSamplesPerSec
< ai
.min_rate
)
1282 fmt
->Format
.nSamplesPerSec
= ai
.min_rate
;
1284 fmt
->dwChannelMask
= get_channel_mask(fmt
->Format
.nChannels
);
1286 fmt
->Format
.nBlockAlign
= (fmt
->Format
.wBitsPerSample
*
1287 fmt
->Format
.nChannels
) / 8;
1288 fmt
->Format
.nAvgBytesPerSec
= fmt
->Format
.nSamplesPerSec
*
1289 fmt
->Format
.nBlockAlign
;
1291 fmt
->Samples
.wValidBitsPerSample
= fmt
->Format
.wBitsPerSample
;
1292 fmt
->Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
1294 params
->result
= S_OK
;
1295 return STATUS_SUCCESS
;
1298 static NTSTATUS
oss_get_device_period(void *args
)
1300 struct get_device_period_params
*params
= args
;
1302 if (params
->def_period
)
1303 *params
->def_period
= def_period
;
1304 if (params
->min_period
)
1305 *params
->min_period
= min_period
;
1307 params
->result
= S_OK
;
1309 return STATUS_SUCCESS
;
1312 static NTSTATUS
oss_get_buffer_size(void *args
)
1314 struct get_buffer_size_params
*params
= args
;
1315 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1319 *params
->frames
= stream
->bufsize_frames
;
1321 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1324 static NTSTATUS
oss_get_latency(void *args
)
1326 struct get_latency_params
*params
= args
;
1327 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1331 /* pretend we process audio in Period chunks, so max latency includes
1332 * the period time. Some native machines add .6666ms in shared mode. */
1333 *params
->latency
= stream
->period
+ 6666;
1335 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1338 static NTSTATUS
oss_get_current_padding(void *args
)
1340 struct get_current_padding_params
*params
= args
;
1341 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1345 *params
->padding
= stream
->held_frames
;
1347 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1350 static NTSTATUS
oss_get_next_packet_size(void *args
)
1352 struct get_next_packet_size_params
*params
= args
;
1353 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1354 UINT32
*frames
= params
->frames
;
1358 *frames
= stream
->held_frames
< stream
->period_frames
? 0 : stream
->period_frames
;
1360 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1363 static NTSTATUS
oss_get_frequency(void *args
)
1365 struct get_frequency_params
*params
= args
;
1366 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1367 UINT64
*freq
= params
->freq
;
1371 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
1372 *freq
= (UINT64
)stream
->fmt
->nSamplesPerSec
* stream
->fmt
->nBlockAlign
;
1374 *freq
= stream
->fmt
->nSamplesPerSec
;
1376 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1379 static NTSTATUS
oss_get_position(void *args
)
1381 struct get_position_params
*params
= args
;
1382 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1383 UINT64
*pos
= params
->pos
, *qpctime
= params
->qpctime
;
1385 if (params
->device
) {
1386 FIXME("Device position reporting not implemented\n");
1387 params
->result
= E_NOTIMPL
;
1388 return STATUS_SUCCESS
;
1393 if(stream
->flow
== eRender
){
1394 *pos
= stream
->written_frames
- stream
->held_frames
;
1395 if(*pos
< stream
->last_pos_frames
)
1396 *pos
= stream
->last_pos_frames
;
1397 }else if(stream
->flow
== eCapture
){
1401 if(ioctl(stream
->fd
, SNDCTL_DSP_GETISPACE
, &bi
) < 0){
1402 TRACE("GETISPACE failed: %d (%s)\n", errno
, strerror(errno
));
1405 if(bi
.bytes
<= bi
.fragsize
)
1408 held
= bi
.bytes
/ stream
->fmt
->nBlockAlign
;
1411 *pos
= stream
->written_frames
+ held
;
1414 stream
->last_pos_frames
= *pos
;
1416 TRACE("returning: %s\n", wine_dbgstr_longlong(*pos
));
1417 if(stream
->share
== AUDCLNT_SHAREMODE_SHARED
)
1418 *pos
*= stream
->fmt
->nBlockAlign
;
1421 LARGE_INTEGER stamp
, freq
;
1422 NtQueryPerformanceCounter(&stamp
, &freq
);
1423 *qpctime
= (stamp
.QuadPart
* (INT64
)10000000) / freq
.QuadPart
;
1426 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1429 static NTSTATUS
oss_set_volumes(void *args
)
1431 struct set_volumes_params
*params
= args
;
1432 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1435 if (params
->master_volume
) {
1436 for (i
= 0; i
< stream
->fmt
->nChannels
; ++i
) {
1437 if (params
->master_volume
* params
->volumes
[i
] * params
->session_volumes
[i
] != 1.0f
) {
1438 FIXME("Volume control is not implemented\n");
1445 stream
->mute
= !params
->master_volume
;
1448 return STATUS_SUCCESS
;
1451 static NTSTATUS
oss_set_event_handle(void *args
)
1453 struct set_event_handle_params
*params
= args
;
1454 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1458 if(!(stream
->flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
))
1459 return oss_unlock_result(stream
, ¶ms
->result
, AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED
);
1462 FIXME("called twice\n");
1463 return oss_unlock_result(stream
, ¶ms
->result
, HRESULT_FROM_WIN32(ERROR_INVALID_NAME
));
1466 stream
->event
= params
->event
;
1468 return oss_unlock_result(stream
, ¶ms
->result
, S_OK
);
1471 static NTSTATUS
oss_is_started(void *args
)
1473 struct is_started_params
*params
= args
;
1474 struct oss_stream
*stream
= handle_get_stream(params
->stream
);
1478 return oss_unlock_result(stream
, ¶ms
->result
, stream
->playing
? S_OK
: S_FALSE
);
1483 static unsigned int num_aux
;
1485 #define MIXER_DEV "/dev/mixer"
1487 static UINT
aux_init(void)
1493 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1495 WARN("mixer device not available !\n");
1506 static UINT
aux_exit(void)
1512 static UINT
aux_get_devcaps(WORD dev_id
, AUXCAPSW
*caps
, UINT size
)
1515 static const WCHAR ini
[] = {'O','S','S',' ','A','u','x',' ','#','0',0};
1517 TRACE("(%04X, %p, %u);\n", dev_id
, caps
, size
);
1518 if (caps
== NULL
) return MMSYSERR_NOTENABLED
;
1519 if (dev_id
>= num_aux
) return MMSYSERR_BADDEVICEID
;
1520 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1522 WARN("mixer device not available !\n");
1523 return MMSYSERR_NOTENABLED
;
1525 if (ioctl(mixer
, SOUND_MIXER_READ_LINE
, &volume
) == -1)
1528 WARN("unable to read mixer !\n");
1529 return MMSYSERR_NOTENABLED
;
1533 caps
->wPid
= 0x55 + dev_id
;
1534 caps
->vDriverVersion
= 0x0100;
1535 memcpy(caps
->szPname
, ini
, sizeof(ini
));
1536 caps
->szPname
[9] = '0' + dev_id
; /* 6 at max */
1537 caps
->wTechnology
= (dev_id
== 2) ? AUXCAPS_CDAUDIO
: AUXCAPS_AUXIN
;
1538 caps
->wReserved1
= 0;
1539 caps
->dwSupport
= AUXCAPS_VOLUME
| AUXCAPS_LRVOLUME
;
1541 return MMSYSERR_NOERROR
;
1544 static UINT
aux_get_volume(WORD dev_id
, UINT
*vol
)
1546 int mixer
, volume
, left
, right
, cmd
;
1548 TRACE("(%04X, %p);\n", dev_id
, vol
);
1549 if (vol
== NULL
) return MMSYSERR_NOTENABLED
;
1550 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1552 WARN("mixer device not available !\n");
1553 return MMSYSERR_NOTENABLED
;
1558 TRACE("SOUND_MIXER_READ_PCM !\n");
1559 cmd
= SOUND_MIXER_READ_PCM
;
1562 TRACE("SOUND_MIXER_READ_SYNTH !\n");
1563 cmd
= SOUND_MIXER_READ_SYNTH
;
1566 TRACE("SOUND_MIXER_READ_CD !\n");
1567 cmd
= SOUND_MIXER_READ_CD
;
1570 TRACE("SOUND_MIXER_READ_LINE !\n");
1571 cmd
= SOUND_MIXER_READ_LINE
;
1574 TRACE("SOUND_MIXER_READ_MIC !\n");
1575 cmd
= SOUND_MIXER_READ_MIC
;
1578 TRACE("SOUND_MIXER_READ_VOLUME !\n");
1579 cmd
= SOUND_MIXER_READ_VOLUME
;
1582 WARN("invalid device id=%04X !\n", dev_id
);
1584 return MMSYSERR_NOTENABLED
;
1586 if (ioctl(mixer
, cmd
, &volume
) == -1)
1588 WARN("unable to read mixer !\n");
1590 return MMSYSERR_NOTENABLED
;
1593 left
= LOBYTE(LOWORD(volume
));
1594 right
= HIBYTE(LOWORD(volume
));
1595 TRACE("left=%d right=%d !\n", left
, right
);
1596 *vol
= MAKELONG((left
* 0xFFFFL
) / 100, (right
* 0xFFFFL
) / 100);
1597 return MMSYSERR_NOERROR
;
1600 static UINT
aux_set_volume(WORD dev_id
, UINT vol
)
1603 int volume
, left
, right
;
1606 TRACE("(%04X, %08X);\n", dev_id
, vol
);
1608 left
= (LOWORD(vol
) * 100) >> 16;
1609 right
= (HIWORD(vol
) * 100) >> 16;
1610 volume
= (right
<< 8) | left
;
1612 if ((mixer
= open(MIXER_DEV
, O_RDWR
)) < 0)
1614 WARN("mixer device not available !\n");
1615 return MMSYSERR_NOTENABLED
;
1621 TRACE("SOUND_MIXER_WRITE_PCM !\n");
1622 cmd
= SOUND_MIXER_WRITE_PCM
;
1625 TRACE("SOUND_MIXER_WRITE_SYNTH !\n");
1626 cmd
= SOUND_MIXER_WRITE_SYNTH
;
1629 TRACE("SOUND_MIXER_WRITE_CD !\n");
1630 cmd
= SOUND_MIXER_WRITE_CD
;
1633 TRACE("SOUND_MIXER_WRITE_LINE !\n");
1634 cmd
= SOUND_MIXER_WRITE_LINE
;
1637 TRACE("SOUND_MIXER_WRITE_MIC !\n");
1638 cmd
= SOUND_MIXER_WRITE_MIC
;
1641 TRACE("SOUND_MIXER_WRITE_VOLUME !\n");
1642 cmd
= SOUND_MIXER_WRITE_VOLUME
;
1645 WARN("invalid device id=%04X !\n", dev_id
);
1647 return MMSYSERR_NOTENABLED
;
1649 if (ioctl(mixer
, cmd
, &volume
) == -1)
1651 WARN("unable to set mixer !\n");
1653 return MMSYSERR_NOTENABLED
;
1656 return MMSYSERR_NOERROR
;
1659 static NTSTATUS
oss_aux_message(void *args
)
1661 struct aux_message_params
*params
= args
;
1663 switch (params
->msg
)
1666 *params
->err
= aux_init();
1669 *params
->err
= aux_exit();
1673 /* FIXME: Pretend this is supported */
1676 case AUXDM_GETDEVCAPS
:
1677 *params
->err
= aux_get_devcaps(params
->dev_id
, (AUXCAPSW
*)params
->param_1
, params
->param_2
);
1679 case AUXDM_GETNUMDEVS
:
1680 TRACE("return %d;\n", num_aux
);
1681 *params
->err
= num_aux
;
1683 case AUXDM_GETVOLUME
:
1684 *params
->err
= aux_get_volume(params
->dev_id
, (UINT
*)params
->param_1
);
1686 case AUXDM_SETVOLUME
:
1687 *params
->err
= aux_set_volume(params
->dev_id
, params
->param_1
);
1690 WARN("unknown message !\n");
1691 *params
->err
= MMSYSERR_NOTSUPPORTED
;
1695 return STATUS_SUCCESS
;
1698 unixlib_entry_t __wine_unix_call_funcs
[] =
1701 oss_not_implemented
,
1703 oss_get_endpoint_ids
,
1710 oss_get_render_buffer
,
1711 oss_release_render_buffer
,
1712 oss_get_capture_buffer
,
1713 oss_release_capture_buffer
,
1714 oss_is_format_supported
,
1716 oss_get_device_period
,
1717 oss_get_buffer_size
,
1719 oss_get_current_padding
,
1720 oss_get_next_packet_size
,
1724 oss_set_event_handle
,
1727 oss_not_implemented
,
1728 oss_not_implemented
,
1730 oss_midi_out_message
,
1731 oss_midi_in_message
,
1732 oss_midi_notify_wait
,
1740 static NTSTATUS
oss_wow64_test_connect(void *args
)
1745 enum driver_priority priority
;
1747 struct test_connect_params params
=
1749 .name
= ULongToPtr(params32
->name
),
1751 oss_test_connect(¶ms
);
1752 params32
->priority
= params
.priority
;
1753 return STATUS_SUCCESS
;
1756 static NTSTATUS
oss_wow64_main_loop(void *args
)
1762 struct main_loop_params params
=
1764 .event
= ULongToHandle(params32
->event
)
1766 return oss_main_loop(¶ms
);
1769 static NTSTATUS
oss_wow64_get_endpoint_ids(void *args
)
1778 unsigned int default_idx
;
1780 struct get_endpoint_ids_params params
=
1782 .flow
= params32
->flow
,
1783 .endpoints
= ULongToPtr(params32
->endpoints
),
1784 .size
= params32
->size
1786 oss_get_endpoint_ids(¶ms
);
1787 params32
->size
= params
.size
;
1788 params32
->result
= params
.result
;
1789 params32
->num
= params
.num
;
1790 params32
->default_idx
= params
.default_idx
;
1791 return STATUS_SUCCESS
;
1794 static NTSTATUS
oss_wow64_create_stream(void *args
)
1801 AUDCLNT_SHAREMODE share
;
1803 REFERENCE_TIME duration
;
1804 REFERENCE_TIME period
;
1807 PTR32 channel_count
;
1810 struct create_stream_params params
=
1812 .name
= ULongToPtr(params32
->name
),
1813 .device
= ULongToPtr(params32
->device
),
1814 .flow
= params32
->flow
,
1815 .share
= params32
->share
,
1816 .flags
= params32
->flags
,
1817 .duration
= params32
->duration
,
1818 .period
= params32
->period
,
1819 .fmt
= ULongToPtr(params32
->fmt
),
1820 .channel_count
= ULongToPtr(params32
->channel_count
),
1821 .stream
= ULongToPtr(params32
->stream
)
1823 oss_create_stream(¶ms
);
1824 params32
->result
= params
.result
;
1825 return STATUS_SUCCESS
;
1828 static NTSTATUS
oss_wow64_release_stream(void *args
)
1832 stream_handle stream
;
1836 struct release_stream_params params
=
1838 .stream
= params32
->stream
,
1839 .timer_thread
= ULongToHandle(params32
->timer_thread
)
1841 oss_release_stream(¶ms
);
1842 params32
->result
= params
.result
;
1843 return STATUS_SUCCESS
;
1846 static NTSTATUS
oss_wow64_get_render_buffer(void *args
)
1850 stream_handle stream
;
1856 struct get_render_buffer_params params
=
1858 .stream
= params32
->stream
,
1859 .frames
= params32
->frames
,
1862 oss_get_render_buffer(¶ms
);
1863 params32
->result
= params
.result
;
1864 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
1865 return STATUS_SUCCESS
;
1868 static NTSTATUS
oss_wow64_get_capture_buffer(void *args
)
1872 stream_handle stream
;
1881 struct get_capture_buffer_params params
=
1883 .stream
= params32
->stream
,
1885 .frames
= ULongToPtr(params32
->frames
),
1886 .flags
= ULongToPtr(params32
->flags
),
1887 .devpos
= ULongToPtr(params32
->devpos
),
1888 .qpcpos
= ULongToPtr(params32
->qpcpos
)
1890 oss_get_capture_buffer(¶ms
);
1891 params32
->result
= params
.result
;
1892 *(unsigned int *)ULongToPtr(params32
->data
) = PtrToUlong(data
);
1893 return STATUS_SUCCESS
;
1896 static NTSTATUS
oss_wow64_is_format_supported(void *args
)
1902 AUDCLNT_SHAREMODE share
;
1907 struct is_format_supported_params params
=
1909 .device
= ULongToPtr(params32
->device
),
1910 .flow
= params32
->flow
,
1911 .share
= params32
->share
,
1912 .fmt_in
= ULongToPtr(params32
->fmt_in
),
1913 .fmt_out
= ULongToPtr(params32
->fmt_out
)
1915 oss_is_format_supported(¶ms
);
1916 params32
->result
= params
.result
;
1917 return STATUS_SUCCESS
;
1920 static NTSTATUS
oss_wow64_get_mix_format(void *args
)
1929 struct get_mix_format_params params
=
1931 .device
= ULongToPtr(params32
->device
),
1932 .flow
= params32
->flow
,
1933 .fmt
= ULongToPtr(params32
->fmt
)
1935 oss_get_mix_format(¶ms
);
1936 params32
->result
= params
.result
;
1937 return STATUS_SUCCESS
;
1940 static NTSTATUS
oss_wow64_get_device_period(void *args
)
1950 struct get_device_period_params params
=
1952 .device
= ULongToPtr(params32
->device
),
1953 .flow
= params32
->flow
,
1954 .def_period
= ULongToPtr(params32
->def_period
),
1955 .min_period
= ULongToPtr(params32
->min_period
),
1957 oss_get_device_period(¶ms
);
1958 params32
->result
= params
.result
;
1959 return STATUS_SUCCESS
;
1962 static NTSTATUS
oss_wow64_get_buffer_size(void *args
)
1966 stream_handle stream
;
1970 struct get_buffer_size_params params
=
1972 .stream
= params32
->stream
,
1973 .frames
= ULongToPtr(params32
->frames
)
1975 oss_get_buffer_size(¶ms
);
1976 params32
->result
= params
.result
;
1977 return STATUS_SUCCESS
;
1980 static NTSTATUS
oss_wow64_get_latency(void *args
)
1984 stream_handle stream
;
1988 struct get_latency_params params
=
1990 .stream
= params32
->stream
,
1991 .latency
= ULongToPtr(params32
->latency
)
1993 oss_get_latency(¶ms
);
1994 params32
->result
= params
.result
;
1995 return STATUS_SUCCESS
;
1998 static NTSTATUS
oss_wow64_get_current_padding(void *args
)
2002 stream_handle stream
;
2006 struct get_current_padding_params params
=
2008 .stream
= params32
->stream
,
2009 .padding
= ULongToPtr(params32
->padding
)
2011 oss_get_current_padding(¶ms
);
2012 params32
->result
= params
.result
;
2013 return STATUS_SUCCESS
;
2016 static NTSTATUS
oss_wow64_get_next_packet_size(void *args
)
2020 stream_handle stream
;
2024 struct get_next_packet_size_params params
=
2026 .stream
= params32
->stream
,
2027 .frames
= ULongToPtr(params32
->frames
)
2029 oss_get_next_packet_size(¶ms
);
2030 params32
->result
= params
.result
;
2031 return STATUS_SUCCESS
;
2034 static NTSTATUS
oss_wow64_get_frequency(void *args
)
2038 stream_handle stream
;
2042 struct get_frequency_params params
=
2044 .stream
= params32
->stream
,
2045 .freq
= ULongToPtr(params32
->freq
)
2047 oss_get_frequency(¶ms
);
2048 params32
->result
= params
.result
;
2049 return STATUS_SUCCESS
;
2052 static NTSTATUS
oss_wow64_get_position(void *args
)
2056 stream_handle stream
;
2062 struct get_position_params params
=
2064 .stream
= params32
->stream
,
2065 .device
= params32
->device
,
2066 .pos
= ULongToPtr(params32
->pos
),
2067 .qpctime
= ULongToPtr(params32
->qpctime
)
2069 oss_get_position(¶ms
);
2070 params32
->result
= params
.result
;
2071 return STATUS_SUCCESS
;
2074 static NTSTATUS
oss_wow64_set_volumes(void *args
)
2078 stream_handle stream
;
2079 float master_volume
;
2081 PTR32 session_volumes
;
2083 struct set_volumes_params params
=
2085 .stream
= params32
->stream
,
2086 .master_volume
= params32
->master_volume
,
2087 .volumes
= ULongToPtr(params32
->volumes
),
2088 .session_volumes
= ULongToPtr(params32
->session_volumes
),
2090 return oss_set_volumes(¶ms
);
2093 static NTSTATUS
oss_wow64_set_event_handle(void *args
)
2097 stream_handle stream
;
2101 struct set_event_handle_params params
=
2103 .stream
= params32
->stream
,
2104 .event
= ULongToHandle(params32
->event
)
2107 oss_set_event_handle(¶ms
);
2108 params32
->result
= params
.result
;
2109 return STATUS_SUCCESS
;
2112 static NTSTATUS
oss_wow64_aux_message(void *args
)
2123 struct aux_message_params params
=
2125 .dev_id
= params32
->dev_id
,
2126 .msg
= params32
->msg
,
2127 .user
= params32
->user
,
2128 .param_1
= params32
->param_1
,
2129 .param_2
= params32
->param_2
,
2130 .err
= ULongToPtr(params32
->err
),
2132 return oss_aux_message(¶ms
);
2135 unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
2138 oss_not_implemented
,
2139 oss_wow64_main_loop
,
2140 oss_wow64_get_endpoint_ids
,
2141 oss_wow64_create_stream
,
2142 oss_wow64_release_stream
,
2147 oss_wow64_get_render_buffer
,
2148 oss_release_render_buffer
,
2149 oss_wow64_get_capture_buffer
,
2150 oss_release_capture_buffer
,
2151 oss_wow64_is_format_supported
,
2152 oss_wow64_get_mix_format
,
2153 oss_wow64_get_device_period
,
2154 oss_wow64_get_buffer_size
,
2155 oss_wow64_get_latency
,
2156 oss_wow64_get_current_padding
,
2157 oss_wow64_get_next_packet_size
,
2158 oss_wow64_get_frequency
,
2159 oss_wow64_get_position
,
2160 oss_wow64_set_volumes
,
2161 oss_wow64_set_event_handle
,
2162 oss_wow64_test_connect
,
2164 oss_not_implemented
,
2165 oss_not_implemented
,
2167 oss_wow64_midi_out_message
,
2168 oss_wow64_midi_in_message
,
2169 oss_wow64_midi_notify_wait
,
2170 oss_wow64_aux_message
,