ntdll: Validate blocks in the heap pending free request list.
[wine.git] / dlls / winecoreaudio.drv / coreaudio.c
blobb48a0c751fde46986b70731d445bc0e99186dbb9
1 /*
2 * Unixlib for winecoreaudio driver.
4 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * Copyright 2021 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #define LoadResource __carbon_LoadResource
28 #define CompareString __carbon_CompareString
29 #define GetCurrentThread __carbon_GetCurrentThread
30 #define GetCurrentProcess __carbon_GetCurrentProcess
32 #include <stdarg.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 #include <fcntl.h>
43 #include <fenv.h>
44 #include <unistd.h>
46 #include <libkern/OSAtomic.h>
47 #include <CoreAudio/CoreAudio.h>
48 #include <AudioToolbox/AudioFormat.h>
49 #include <AudioToolbox/AudioConverter.h>
50 #include <AudioUnit/AudioUnit.h>
52 #undef LoadResource
53 #undef CompareString
54 #undef GetCurrentThread
55 #undef GetCurrentProcess
56 #undef _CDECL
58 #include "ntstatus.h"
59 #define WIN32_NO_STATUS
60 #include "windef.h"
61 #include "winbase.h"
62 #include "winnls.h"
63 #include "winreg.h"
64 #include "winternl.h"
65 #include "mmdeviceapi.h"
66 #include "initguid.h"
67 #include "audioclient.h"
68 #include "wine/debug.h"
69 #include "wine/unixlib.h"
71 #include "unixlib.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
75 #define MAX_DEV_NAME_LEN 10 /* Max 32 bit digits */
77 struct coreaudio_stream
79 OSSpinLock lock;
80 AudioComponentInstance unit;
81 AudioConverterRef converter;
82 AudioStreamBasicDescription dev_desc; /* audio unit format, not necessarily the same as fmt */
83 AudioDeviceID dev_id;
84 EDataFlow flow;
85 AUDCLNT_SHAREMODE share;
87 BOOL playing;
88 UINT32 period_ms, period_frames;
89 UINT32 bufsize_frames, resamp_bufsize_frames;
90 UINT32 lcl_offs_frames, held_frames, wri_offs_frames, tmp_buffer_frames;
91 UINT32 cap_bufsize_frames, cap_offs_frames, cap_held_frames;
92 UINT32 wrap_bufsize_frames;
93 UINT64 written_frames;
94 INT32 getbuf_last;
95 WAVEFORMATEX *fmt;
96 BYTE *local_buffer, *cap_buffer, *wrap_buffer, *resamp_buffer, *tmp_buffer;
99 static HRESULT osstatus_to_hresult(OSStatus sc)
101 switch(sc){
102 case kAudioFormatUnsupportedDataFormatError:
103 case kAudioFormatUnknownFormatError:
104 case kAudioDeviceUnsupportedFormatError:
105 return AUDCLNT_E_UNSUPPORTED_FORMAT;
106 case kAudioHardwareBadDeviceError:
107 return AUDCLNT_E_DEVICE_INVALIDATED;
109 return E_FAIL;
112 static struct coreaudio_stream *handle_get_stream(stream_handle h)
114 return (struct coreaudio_stream *)(UINT_PTR)h;
117 /* copied from kernelbase */
118 static int muldiv( int a, int b, int c )
120 LONGLONG ret;
122 if (!c) return -1;
124 /* We want to deal with a positive divisor to simplify the logic. */
125 if (c < 0)
127 a = -a;
128 c = -c;
131 /* If the result is positive, we "add" to round. else, we subtract to round. */
132 if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
133 ret = (((LONGLONG)a * b) + (c / 2)) / c;
134 else
135 ret = (((LONGLONG)a * b) - (c / 2)) / c;
137 if (ret > 2147483647 || ret < -2147483647) return -1;
138 return ret;
141 static AudioObjectPropertyScope get_scope(EDataFlow flow)
143 return (flow == eRender) ? kAudioDevicePropertyScopeOutput : kAudioDevicePropertyScopeInput;
146 static BOOL device_has_channels(AudioDeviceID device, EDataFlow flow)
148 AudioObjectPropertyAddress addr;
149 AudioBufferList *buffers;
150 BOOL ret = FALSE;
151 OSStatus sc;
152 UInt32 size;
153 int i;
155 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
156 addr.mScope = get_scope(flow);
157 addr.mElement = 0;
159 sc = AudioObjectGetPropertyDataSize(device, &addr, 0, NULL, &size);
160 if(sc != noErr){
161 WARN("Unable to get _StreamConfiguration property size for device %u: %x\n",
162 (unsigned int)device, (int)sc);
163 return FALSE;
166 buffers = malloc(size);
167 if(!buffers) return FALSE;
169 sc = AudioObjectGetPropertyData(device, &addr, 0, NULL, &size, buffers);
170 if(sc != noErr){
171 WARN("Unable to get _StreamConfiguration property for device %u: %x\n",
172 (unsigned int)device, (int)sc);
173 free(buffers);
174 return FALSE;
177 for(i = 0; i < buffers->mNumberBuffers; i++){
178 if(buffers->mBuffers[i].mNumberChannels > 0){
179 ret = TRUE;
180 break;
183 free(buffers);
184 return ret;
187 static NTSTATUS unix_get_endpoint_ids(void *args)
189 struct get_endpoint_ids_params *params = args;
190 unsigned int num_devices, i, needed, offset;
191 AudioDeviceID *devices, default_id;
192 AudioObjectPropertyAddress addr;
193 struct endpoint *endpoint;
194 UInt32 devsize, size;
195 struct endpoint_info
197 CFStringRef name;
198 AudioDeviceID id;
199 } *info;
200 OSStatus sc;
201 UniChar *ptr;
203 params->num = 0;
204 params->default_idx = 0;
206 addr.mScope = kAudioObjectPropertyScopeGlobal;
207 addr.mElement = kAudioObjectPropertyElementMaster;
208 if(params->flow == eRender) addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
209 else if(params->flow == eCapture) addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
210 else{
211 params->result = E_INVALIDARG;
212 return STATUS_SUCCESS;
215 size = sizeof(default_id);
216 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &default_id);
217 if(sc != noErr){
218 WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc);
219 default_id = -1;
222 addr.mSelector = kAudioHardwarePropertyDevices;
223 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &devsize);
224 if(sc != noErr){
225 WARN("Getting _Devices property size failed: %x\n", (int)sc);
226 params->result = osstatus_to_hresult(sc);
227 return STATUS_SUCCESS;
230 num_devices = devsize / sizeof(AudioDeviceID);
231 devices = malloc(devsize);
232 info = malloc(num_devices * sizeof(*info));
233 if(!devices || !info){
234 free(info);
235 free(devices);
236 params->result = E_OUTOFMEMORY;
237 return STATUS_SUCCESS;
240 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &devsize, devices);
241 if(sc != noErr){
242 WARN("Getting _Devices property failed: %x\n", (int)sc);
243 free(info);
244 free(devices);
245 params->result = osstatus_to_hresult(sc);
246 return STATUS_SUCCESS;
249 addr.mSelector = kAudioObjectPropertyName;
250 addr.mScope = get_scope(params->flow);
251 addr.mElement = 0;
253 for(i = 0; i < num_devices; i++){
254 if(!device_has_channels(devices[i], params->flow)) continue;
256 size = sizeof(CFStringRef);
257 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL, &size, &info[params->num].name);
258 if(sc != noErr){
259 WARN("Unable to get _Name property for device %u: %x\n",
260 (unsigned int)devices[i], (int)sc);
261 continue;
263 info[params->num++].id = devices[i];
265 free(devices);
267 offset = needed = sizeof(*endpoint) * params->num;
268 endpoint = params->endpoints;
270 for(i = 0; i < params->num; i++){
271 const SIZE_T name_len = CFStringGetLength(info[i].name) + 1;
272 const SIZE_T device_len = MAX_DEV_NAME_LEN + 1;
273 needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
275 if(needed <= params->size){
276 endpoint->name = offset;
277 ptr = (UniChar *)((char *)params->endpoints + offset);
278 CFStringGetCharacters(info[i].name, CFRangeMake(0, name_len - 1), ptr);
279 ptr[name_len - 1] = 0;
280 offset += name_len * sizeof(WCHAR);
281 endpoint->device = offset;
282 sprintf((char *)params->endpoints + offset, "%u", (unsigned int)info[i].id);
283 offset += (device_len + 1) & ~1;
284 endpoint++;
286 CFRelease(info[i].name);
287 if(info[i].id == default_id) params->default_idx = i;
289 free(info);
291 if(needed > params->size){
292 params->size = needed;
293 params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
295 else params->result = S_OK;
297 return STATUS_SUCCESS;
300 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
302 WAVEFORMATEX *ret;
303 size_t size;
305 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
306 size = sizeof(WAVEFORMATEXTENSIBLE);
307 else
308 size = sizeof(WAVEFORMATEX);
310 ret = malloc(size);
311 if(!ret)
312 return NULL;
314 memcpy(ret, fmt, size);
316 ret->cbSize = size - sizeof(WAVEFORMATEX);
318 return ret;
321 static void silence_buffer(struct coreaudio_stream *stream, BYTE *buffer, UINT32 frames)
323 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
324 if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
325 (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
326 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
327 stream->fmt->wBitsPerSample == 8)
328 memset(buffer, 128, frames * stream->fmt->nBlockAlign);
329 else
330 memset(buffer, 0, frames * stream->fmt->nBlockAlign);
333 /* CA is pulling data from us */
334 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
335 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
336 AudioBufferList *data)
338 struct coreaudio_stream *stream = user;
339 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
341 OSSpinLockLock(&stream->lock);
343 if(stream->playing){
344 lcl_offs_bytes = stream->lcl_offs_frames * stream->fmt->nBlockAlign;
345 to_copy_frames = min(nframes, stream->held_frames);
346 to_copy_bytes = to_copy_frames * stream->fmt->nBlockAlign;
348 chunk_bytes = (stream->bufsize_frames - stream->lcl_offs_frames) * stream->fmt->nBlockAlign;
350 if(to_copy_bytes > chunk_bytes){
351 memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, chunk_bytes);
352 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, stream->local_buffer, to_copy_bytes - chunk_bytes);
353 }else
354 memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, to_copy_bytes);
356 stream->lcl_offs_frames += to_copy_frames;
357 stream->lcl_offs_frames %= stream->bufsize_frames;
358 stream->held_frames -= to_copy_frames;
359 }else
360 to_copy_bytes = to_copy_frames = 0;
362 if(nframes > to_copy_frames)
363 silence_buffer(stream, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
365 OSSpinLockUnlock(&stream->lock);
367 return noErr;
370 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
371 BYTE *src, UINT32 src_bytes)
373 UINT32 chunk_bytes = dst_bytes - dst_offs;
375 if(chunk_bytes < src_bytes){
376 memcpy(dst + dst_offs, src, chunk_bytes);
377 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
378 }else
379 memcpy(dst + dst_offs, src, src_bytes);
382 /* we need to trigger CA to pull data from the device and give it to us
384 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
386 * raw data is resampled from cap_buffer into resamp_buffer in period-size
387 * chunks and copied to local_buffer
389 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
390 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
391 AudioBufferList *data)
393 struct coreaudio_stream *stream = user;
394 AudioBufferList list;
395 OSStatus sc;
396 UINT32 cap_wri_offs_frames;
398 OSSpinLockLock(&stream->lock);
400 cap_wri_offs_frames = (stream->cap_offs_frames + stream->cap_held_frames) % stream->cap_bufsize_frames;
402 list.mNumberBuffers = 1;
403 list.mBuffers[0].mNumberChannels = stream->fmt->nChannels;
404 list.mBuffers[0].mDataByteSize = nframes * stream->fmt->nBlockAlign;
406 if(!stream->playing || cap_wri_offs_frames + nframes > stream->cap_bufsize_frames){
407 if(stream->wrap_bufsize_frames < nframes){
408 free(stream->wrap_buffer);
409 stream->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
410 stream->wrap_bufsize_frames = nframes;
413 list.mBuffers[0].mData = stream->wrap_buffer;
414 }else
415 list.mBuffers[0].mData = stream->cap_buffer + cap_wri_offs_frames * stream->fmt->nBlockAlign;
417 sc = AudioUnitRender(stream->unit, flags, ts, bus, nframes, &list);
418 if(sc != noErr){
419 OSSpinLockUnlock(&stream->lock);
420 return sc;
423 if(stream->playing){
424 if(list.mBuffers[0].mData == stream->wrap_buffer){
425 ca_wrap_buffer(stream->cap_buffer,
426 cap_wri_offs_frames * stream->fmt->nBlockAlign,
427 stream->cap_bufsize_frames * stream->fmt->nBlockAlign,
428 stream->wrap_buffer, list.mBuffers[0].mDataByteSize);
431 stream->cap_held_frames += list.mBuffers[0].mDataByteSize / stream->fmt->nBlockAlign;
432 if(stream->cap_held_frames > stream->cap_bufsize_frames){
433 stream->cap_offs_frames += stream->cap_held_frames % stream->cap_bufsize_frames;
434 stream->cap_offs_frames %= stream->cap_bufsize_frames;
435 stream->cap_held_frames = stream->cap_bufsize_frames;
439 OSSpinLockUnlock(&stream->lock);
440 return noErr;
443 static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
445 AudioComponentInstance unit;
446 AudioComponent comp;
447 AudioComponentDescription desc;
448 OSStatus sc;
450 memset(&desc, 0, sizeof(desc));
451 desc.componentType = kAudioUnitType_Output;
452 desc.componentSubType = kAudioUnitSubType_HALOutput;
453 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
455 if(!(comp = AudioComponentFindNext(NULL, &desc))){
456 WARN("AudioComponentFindNext failed\n");
457 return NULL;
460 sc = AudioComponentInstanceNew(comp, &unit);
461 if(sc != noErr){
462 WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
463 return NULL;
466 if(dataflow == eCapture){
467 UInt32 enableio;
469 enableio = 1;
470 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
471 kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
472 if(sc != noErr){
473 WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
474 AudioComponentInstanceDispose(unit);
475 return NULL;
478 enableio = 0;
479 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
480 kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
481 if(sc != noErr){
482 WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
483 AudioComponentInstanceDispose(unit);
484 return NULL;
488 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
489 kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
490 if(sc != noErr){
491 WARN("Couldn't set audio unit device\n");
492 AudioComponentInstanceDispose(unit);
493 return NULL;
496 return unit;
499 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
501 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
502 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
503 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
504 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
505 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
506 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
509 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
510 const WAVEFORMATEX *fmt)
512 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
514 desc->mFormatFlags = 0;
516 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
517 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
518 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
519 desc->mFormatID = kAudioFormatLinearPCM;
520 if(fmt->wBitsPerSample > 8)
521 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
522 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
523 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
524 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
525 desc->mFormatID = kAudioFormatLinearPCM;
526 desc->mFormatFlags = kAudioFormatFlagIsFloat;
527 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
528 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
529 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
530 desc->mFormatID = kAudioFormatULaw;
531 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
532 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
533 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
534 desc->mFormatID = kAudioFormatALaw;
535 }else
536 return AUDCLNT_E_UNSUPPORTED_FORMAT;
538 desc->mSampleRate = fmt->nSamplesPerSec;
539 desc->mBytesPerPacket = fmt->nBlockAlign;
540 desc->mFramesPerPacket = 1;
541 desc->mBytesPerFrame = fmt->nBlockAlign;
542 desc->mChannelsPerFrame = fmt->nChannels;
543 desc->mBitsPerChannel = fmt->wBitsPerSample;
544 desc->mReserved = 0;
546 return S_OK;
549 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
550 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
551 AudioConverterRef *converter)
553 OSStatus sc;
554 HRESULT hr;
556 if(dataflow == eCapture){
557 AudioStreamBasicDescription desc;
558 UInt32 size;
559 Float64 rate;
560 fenv_t fenv;
561 BOOL fenv_stored = TRUE;
563 hr = ca_get_audiodesc(&desc, fmt);
564 if(FAILED(hr))
565 return hr;
566 dump_adesc("requested", &desc);
568 /* input-only units can't perform sample rate conversion, so we have to
569 * set up our own AudioConverter to support arbitrary sample rates. */
570 size = sizeof(*dev_desc);
571 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
572 kAudioUnitScope_Input, 1, dev_desc, &size);
573 if(sc != noErr){
574 WARN("Couldn't get unit format: %x\n", (int)sc);
575 return osstatus_to_hresult(sc);
577 dump_adesc("hardware", dev_desc);
579 rate = dev_desc->mSampleRate;
580 *dev_desc = desc;
581 dev_desc->mSampleRate = rate;
583 dump_adesc("final", dev_desc);
584 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
585 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
586 if(sc != noErr){
587 WARN("Couldn't set unit format: %x\n", (int)sc);
588 return osstatus_to_hresult(sc);
591 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
592 if(feholdexcept(&fenv)){
593 WARN("Failed to store fenv state\n");
594 fenv_stored = FALSE;
597 sc = AudioConverterNew(dev_desc, &desc, converter);
599 if(fenv_stored && fesetenv(&fenv))
600 WARN("Failed to restore fenv state\n");
602 if(sc != noErr){
603 WARN("Couldn't create audio converter: %x\n", (int)sc);
604 return osstatus_to_hresult(sc);
606 }else{
607 hr = ca_get_audiodesc(dev_desc, fmt);
608 if(FAILED(hr))
609 return hr;
611 dump_adesc("final", dev_desc);
612 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
613 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
614 if(sc != noErr){
615 WARN("Couldn't set format: %x\n", (int)sc);
616 return osstatus_to_hresult(sc);
620 return S_OK;
623 static ULONG_PTR zero_bits(void)
625 #ifdef _WIN64
626 return !NtCurrentTeb()->WowTebOffset ? 0 : 0x7fffffff;
627 #else
628 return 0;
629 #endif
632 static AudioDeviceID dev_id_from_device(const char *device)
634 return strtoul(device, NULL, 10);
637 static NTSTATUS unix_create_stream(void *args)
639 struct create_stream_params *params = args;
640 struct coreaudio_stream *stream = calloc(1, sizeof(*stream));
641 AURenderCallbackStruct input;
642 OSStatus sc;
643 SIZE_T size;
645 if(!stream){
646 params->result = E_OUTOFMEMORY;
647 return STATUS_SUCCESS;
650 stream->fmt = clone_format(params->fmt);
651 if(!stream->fmt){
652 params->result = E_OUTOFMEMORY;
653 goto end;
656 stream->period_ms = params->period / 10000;
657 stream->period_frames = muldiv(params->period, stream->fmt->nSamplesPerSec, 10000000);
658 stream->dev_id = dev_id_from_device(params->device);
659 stream->flow = params->flow;
660 stream->share = params->share;
662 stream->bufsize_frames = muldiv(params->duration, stream->fmt->nSamplesPerSec, 10000000);
663 if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
664 stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames;
666 if(!(stream->unit = get_audiounit(stream->flow, stream->dev_id))){
667 params->result = AUDCLNT_E_DEVICE_INVALIDATED;
668 goto end;
671 params->result = ca_setup_audiounit(stream->flow, stream->unit, stream->fmt, &stream->dev_desc, &stream->converter);
672 if(FAILED(params->result)) goto end;
674 input.inputProcRefCon = stream;
675 if(stream->flow == eCapture){
676 input.inputProc = ca_capture_cb;
677 sc = AudioUnitSetProperty(stream->unit, kAudioOutputUnitProperty_SetInputCallback,
678 kAudioUnitScope_Output, 1, &input, sizeof(input));
679 }else{
680 input.inputProc = ca_render_cb;
681 sc = AudioUnitSetProperty(stream->unit, kAudioUnitProperty_SetRenderCallback,
682 kAudioUnitScope_Input, 0, &input, sizeof(input));
684 if(sc != noErr){
685 WARN("Couldn't set callback: %x\n", (int)sc);
686 params->result = osstatus_to_hresult(sc);
687 goto end;
690 sc = AudioUnitInitialize(stream->unit);
691 if(sc != noErr){
692 WARN("Couldn't initialize: %x\n", (int)sc);
693 params->result = osstatus_to_hresult(sc);
694 goto end;
697 /* we play audio continuously because AudioOutputUnitStart sometimes takes
698 * a while to return */
699 sc = AudioOutputUnitStart(stream->unit);
700 if(sc != noErr){
701 WARN("Unit failed to start: %x\n", (int)sc);
702 params->result = osstatus_to_hresult(sc);
703 goto end;
706 size = stream->bufsize_frames * stream->fmt->nBlockAlign;
707 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, zero_bits(),
708 &size, MEM_COMMIT, PAGE_READWRITE)){
709 params->result = E_OUTOFMEMORY;
710 goto end;
712 silence_buffer(stream, stream->local_buffer, stream->bufsize_frames);
714 if(stream->flow == eCapture){
715 stream->cap_bufsize_frames = muldiv(params->duration, stream->dev_desc.mSampleRate, 10000000);
716 stream->cap_buffer = malloc(stream->cap_bufsize_frames * stream->fmt->nBlockAlign);
718 params->result = S_OK;
720 end:
721 if(FAILED(params->result)){
722 if(stream->converter) AudioConverterDispose(stream->converter);
723 if(stream->unit) AudioComponentInstanceDispose(stream->unit);
724 free(stream->fmt);
725 free(stream);
726 } else
727 *params->stream = (stream_handle)(UINT_PTR)stream;
729 return STATUS_SUCCESS;
732 static NTSTATUS unix_release_stream( void *args )
734 struct release_stream_params *params = args;
735 struct coreaudio_stream *stream = handle_get_stream(params->stream);
736 SIZE_T size;
738 if(stream->unit){
739 AudioOutputUnitStop(stream->unit);
740 AudioComponentInstanceDispose(stream->unit);
743 if(stream->converter) AudioConverterDispose(stream->converter);
744 free(stream->resamp_buffer);
745 free(stream->wrap_buffer);
746 free(stream->cap_buffer);
747 if(stream->local_buffer){
748 size = 0;
749 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer,
750 &size, MEM_RELEASE);
752 if(stream->tmp_buffer){
753 size = 0;
754 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer,
755 &size, MEM_RELEASE);
757 free(stream->fmt);
758 free(stream);
759 params->result = S_OK;
760 return STATUS_SUCCESS;
763 static UINT ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
765 int i;
766 UINT mask = 0;
768 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
769 switch (layout->mChannelDescriptions[i].mChannelLabel) {
770 default: FIXME("Unhandled channel 0x%x\n",
771 (unsigned int)layout->mChannelDescriptions[i].mChannelLabel); break;
772 case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
773 case kAudioChannelLabel_Mono:
774 case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
775 case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
776 case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
777 case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
778 case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
779 case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
780 case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
781 case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
782 case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
783 case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
784 case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
785 case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
786 case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
787 case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
788 case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
789 case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
790 case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
794 return mask;
797 /* For most hardware on Windows, users must choose a configuration with an even
798 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
799 * channels, but those channels are still reported to applications from
800 * GetMixFormat! Some applications behave badly if given an odd number of
801 * channels (e.g. 2.1). Here, we find the nearest configuration that Windows
802 * would report for a given channel layout. */
803 static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
805 UINT ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
807 TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
809 if (ca_layout->mNumberChannelDescriptions == 1)
811 fmt->Format.nChannels = 1;
812 fmt->dwChannelMask = ca_mask;
813 return;
816 /* compare against known configurations and find smallest configuration
817 * which is a superset of the given speakers */
819 if (ca_layout->mNumberChannelDescriptions <= 2 &&
820 (ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
822 fmt->Format.nChannels = 2;
823 fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
824 return;
827 if (ca_layout->mNumberChannelDescriptions <= 4 &&
828 (ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
830 fmt->Format.nChannels = 4;
831 fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
832 return;
835 if (ca_layout->mNumberChannelDescriptions <= 4 &&
836 (ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
838 fmt->Format.nChannels = 4;
839 fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
840 return;
843 if (ca_layout->mNumberChannelDescriptions <= 6 &&
844 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
846 fmt->Format.nChannels = 6;
847 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
848 return;
851 if (ca_layout->mNumberChannelDescriptions <= 6 &&
852 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
854 fmt->Format.nChannels = 6;
855 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
856 return;
859 if (ca_layout->mNumberChannelDescriptions <= 8 &&
860 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
862 fmt->Format.nChannels = 8;
863 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
864 return;
867 if (ca_layout->mNumberChannelDescriptions <= 8 &&
868 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
870 fmt->Format.nChannels = 8;
871 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
872 return;
875 /* oddball format, report truthfully */
876 fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
877 fmt->dwChannelMask = ca_mask;
880 static DWORD get_channel_mask(unsigned int channels)
882 switch(channels){
883 case 0:
884 return 0;
885 case 1:
886 return KSAUDIO_SPEAKER_MONO;
887 case 2:
888 return KSAUDIO_SPEAKER_STEREO;
889 case 3:
890 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
891 case 4:
892 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
893 case 5:
894 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
895 case 6:
896 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
897 case 7:
898 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
899 case 8:
900 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
902 FIXME("Unknown speaker configuration: %u\n", channels);
903 return 0;
906 static NTSTATUS unix_get_mix_format(void *args)
908 struct get_mix_format_params *params = args;
909 AudioObjectPropertyAddress addr;
910 AudioChannelLayout *layout;
911 AudioBufferList *buffers;
912 Float64 rate;
913 UInt32 size;
914 OSStatus sc;
915 int i;
916 const AudioDeviceID dev_id = dev_id_from_device(params->device);
918 params->fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
920 addr.mScope = get_scope(params->flow);
921 addr.mElement = 0;
922 addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
924 sc = AudioObjectGetPropertyDataSize(dev_id, &addr, 0, NULL, &size);
925 if(sc == noErr){
926 layout = malloc(size);
927 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, layout);
928 if(sc == noErr){
929 TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
930 (unsigned int)layout->mChannelLayoutTag, (unsigned int)layout->mChannelBitmap,
931 (unsigned int)layout->mNumberChannelDescriptions);
933 if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
934 convert_channel_layout(layout, params->fmt);
935 }else{
936 WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n",
937 (unsigned int)layout->mChannelLayoutTag);
938 params->fmt->Format.nChannels = 0;
940 }else{
941 TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
942 params->fmt->Format.nChannels = 0;
945 free(layout);
946 }else{
947 TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
948 params->fmt->Format.nChannels = 0;
951 if(params->fmt->Format.nChannels == 0){
952 addr.mScope = get_scope(params->flow);
953 addr.mElement = 0;
954 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
956 sc = AudioObjectGetPropertyDataSize(dev_id, &addr, 0, NULL, &size);
957 if(sc != noErr){
958 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
959 params->result = osstatus_to_hresult(sc);
960 return STATUS_SUCCESS;
963 buffers = malloc(size);
964 if(!buffers){
965 params->result = E_OUTOFMEMORY;
966 return STATUS_SUCCESS;
969 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, buffers);
970 if(sc != noErr){
971 free(buffers);
972 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
973 params->result = osstatus_to_hresult(sc);
974 return STATUS_SUCCESS;
977 for(i = 0; i < buffers->mNumberBuffers; ++i)
978 params->fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
980 free(buffers);
982 params->fmt->dwChannelMask = get_channel_mask(params->fmt->Format.nChannels);
985 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
986 size = sizeof(Float64);
987 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, &rate);
988 if(sc != noErr){
989 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
990 params->result = osstatus_to_hresult(sc);
991 return STATUS_SUCCESS;
993 params->fmt->Format.nSamplesPerSec = rate;
995 params->fmt->Format.wBitsPerSample = 32;
996 params->fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
998 params->fmt->Format.nBlockAlign = (params->fmt->Format.wBitsPerSample *
999 params->fmt->Format.nChannels) / 8;
1000 params->fmt->Format.nAvgBytesPerSec = params->fmt->Format.nSamplesPerSec *
1001 params->fmt->Format.nBlockAlign;
1003 params->fmt->Samples.wValidBitsPerSample = params->fmt->Format.wBitsPerSample;
1004 params->fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1005 params->result = S_OK;
1006 return STATUS_SUCCESS;
1009 static NTSTATUS unix_is_format_supported(void *args)
1011 struct is_format_supported_params *params = args;
1012 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)params->fmt_in;
1013 AudioStreamBasicDescription dev_desc;
1014 AudioConverterRef converter;
1015 AudioComponentInstance unit;
1016 const AudioDeviceID dev_id = dev_id_from_device(params->device);
1018 params->result = S_OK;
1020 if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out))
1021 params->result = E_POINTER;
1022 else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE)
1023 params->result = E_INVALIDARG;
1024 else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1025 if(params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1026 params->result = E_INVALIDARG;
1027 else if(params->fmt_in->nAvgBytesPerSec == 0 || params->fmt_in->nBlockAlign == 0 ||
1028 fmtex->Samples.wValidBitsPerSample > params->fmt_in->wBitsPerSample)
1029 params->result = E_INVALIDARG;
1030 else if(fmtex->Samples.wValidBitsPerSample < params->fmt_in->wBitsPerSample)
1031 goto unsupported;
1032 else if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1033 (fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED))
1034 goto unsupported;
1036 if(FAILED(params->result)) return STATUS_SUCCESS;
1038 if(params->fmt_in->nBlockAlign != params->fmt_in->nChannels * params->fmt_in->wBitsPerSample / 8 ||
1039 params->fmt_in->nAvgBytesPerSec != params->fmt_in->nBlockAlign * params->fmt_in->nSamplesPerSec)
1040 goto unsupported;
1042 if(params->fmt_in->nChannels == 0){
1043 params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
1044 return STATUS_SUCCESS;
1046 unit = get_audiounit(params->flow, dev_id);
1048 converter = NULL;
1049 params->result = ca_setup_audiounit(params->flow, unit, params->fmt_in, &dev_desc, &converter);
1050 AudioComponentInstanceDispose(unit);
1051 if(FAILED(params->result)) goto unsupported;
1052 if(converter) AudioConverterDispose(converter);
1054 params->result = S_OK;
1055 return STATUS_SUCCESS;
1057 unsupported:
1058 if(params->fmt_out){
1059 struct get_mix_format_params get_mix_params =
1061 .device = params->device,
1062 .flow = params->flow,
1063 .fmt = params->fmt_out,
1066 unix_get_mix_format(&get_mix_params);
1067 params->result = get_mix_params.result;
1068 if(SUCCEEDED(params->result)) params->result = S_FALSE;
1070 else params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
1071 return STATUS_SUCCESS;
1074 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1076 if(left <= right)
1077 return right - left;
1078 return bufsize - (left - right);
1081 /* place data from cap_buffer into provided AudioBufferList */
1082 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1083 AudioStreamPacketDescription **packets, void *user)
1085 struct coreaudio_stream *stream = user;
1087 *nframes = min(*nframes, stream->cap_held_frames);
1088 if(!*nframes){
1089 data->mBuffers[0].mData = NULL;
1090 data->mBuffers[0].mDataByteSize = 0;
1091 data->mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1092 return noErr;
1095 data->mBuffers[0].mDataByteSize = *nframes * stream->fmt->nBlockAlign;
1096 data->mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1098 if(stream->cap_offs_frames + *nframes > stream->cap_bufsize_frames){
1099 UINT32 chunk_frames = stream->cap_bufsize_frames - stream->cap_offs_frames;
1101 if(stream->wrap_bufsize_frames < *nframes){
1102 free(stream->wrap_buffer);
1103 stream->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1104 stream->wrap_bufsize_frames = *nframes;
1107 memcpy(stream->wrap_buffer, stream->cap_buffer + stream->cap_offs_frames * stream->fmt->nBlockAlign,
1108 chunk_frames * stream->fmt->nBlockAlign);
1109 memcpy(stream->wrap_buffer + chunk_frames * stream->fmt->nBlockAlign, stream->cap_buffer,
1110 (*nframes - chunk_frames) * stream->fmt->nBlockAlign);
1112 data->mBuffers[0].mData = stream->wrap_buffer;
1113 }else
1114 data->mBuffers[0].mData = stream->cap_buffer + stream->cap_offs_frames * stream->fmt->nBlockAlign;
1116 stream->cap_offs_frames += *nframes;
1117 stream->cap_offs_frames %= stream->cap_bufsize_frames;
1118 stream->cap_held_frames -= *nframes;
1120 if(packets)
1121 *packets = NULL;
1123 return noErr;
1126 static void capture_resample(struct coreaudio_stream *stream)
1128 UINT32 resamp_period_frames = muldiv(stream->period_frames, stream->dev_desc.mSampleRate,
1129 stream->fmt->nSamplesPerSec);
1130 OSStatus sc;
1132 /* the resampling process often needs more source frames than we'd
1133 * guess from a straight conversion using the sample rate ratio. so
1134 * only convert if we have extra source data. */
1135 while(stream->cap_held_frames > resamp_period_frames * 2){
1136 AudioBufferList converted_list;
1137 UInt32 wanted_frames = stream->period_frames;
1139 converted_list.mNumberBuffers = 1;
1140 converted_list.mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1141 converted_list.mBuffers[0].mDataByteSize = wanted_frames * stream->fmt->nBlockAlign;
1143 if(stream->resamp_bufsize_frames < wanted_frames){
1144 free(stream->resamp_buffer);
1145 stream->resamp_buffer = malloc(converted_list.mBuffers[0].mDataByteSize);
1146 stream->resamp_bufsize_frames = wanted_frames;
1149 converted_list.mBuffers[0].mData = stream->resamp_buffer;
1151 sc = AudioConverterFillComplexBuffer(stream->converter, feed_cb,
1152 stream, &wanted_frames, &converted_list, NULL);
1153 if(sc != noErr){
1154 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1155 break;
1158 ca_wrap_buffer(stream->local_buffer,
1159 stream->wri_offs_frames * stream->fmt->nBlockAlign,
1160 stream->bufsize_frames * stream->fmt->nBlockAlign,
1161 stream->resamp_buffer, wanted_frames * stream->fmt->nBlockAlign);
1163 stream->wri_offs_frames += wanted_frames;
1164 stream->wri_offs_frames %= stream->bufsize_frames;
1165 if(stream->held_frames + wanted_frames > stream->bufsize_frames){
1166 stream->lcl_offs_frames += buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames,
1167 stream->bufsize_frames);
1168 stream->held_frames = stream->bufsize_frames;
1169 }else
1170 stream->held_frames += wanted_frames;
1174 static NTSTATUS unix_get_buffer_size(void *args)
1176 struct get_buffer_size_params *params = args;
1177 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1179 OSSpinLockLock(&stream->lock);
1180 *params->frames = stream->bufsize_frames;
1181 OSSpinLockUnlock(&stream->lock);
1182 params->result = S_OK;
1183 return STATUS_SUCCESS;
1186 static HRESULT ca_get_max_stream_latency(struct coreaudio_stream *stream, UInt32 *max)
1188 AudioObjectPropertyAddress addr;
1189 AudioStreamID *ids;
1190 UInt32 size;
1191 OSStatus sc;
1192 int nstreams, i;
1194 addr.mScope = get_scope(stream->flow);
1195 addr.mElement = 0;
1196 addr.mSelector = kAudioDevicePropertyStreams;
1198 sc = AudioObjectGetPropertyDataSize(stream->dev_id, &addr, 0, NULL, &size);
1199 if(sc != noErr){
1200 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1201 return osstatus_to_hresult(sc);
1204 ids = malloc(size);
1205 if(!ids)
1206 return E_OUTOFMEMORY;
1208 sc = AudioObjectGetPropertyData(stream->dev_id, &addr, 0, NULL, &size, ids);
1209 if(sc != noErr){
1210 WARN("Unable to get _Streams property: %x\n", (int)sc);
1211 free(ids);
1212 return osstatus_to_hresult(sc);
1215 nstreams = size / sizeof(AudioStreamID);
1216 *max = 0;
1218 addr.mSelector = kAudioStreamPropertyLatency;
1219 for(i = 0; i < nstreams; ++i){
1220 UInt32 latency;
1222 size = sizeof(latency);
1223 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL, &size, &latency);
1224 if(sc != noErr){
1225 WARN("Unable to get _Latency property: %x\n", (int)sc);
1226 continue;
1229 if(latency > *max)
1230 *max = latency;
1233 free(ids);
1235 return S_OK;
1238 static NTSTATUS unix_get_latency(void *args)
1240 struct get_latency_params *params = args;
1241 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1242 UInt32 latency, stream_latency, size;
1243 AudioObjectPropertyAddress addr;
1244 OSStatus sc;
1246 OSSpinLockLock(&stream->lock);
1248 addr.mScope = get_scope(stream->flow);
1249 addr.mSelector = kAudioDevicePropertyLatency;
1250 addr.mElement = 0;
1252 size = sizeof(latency);
1253 sc = AudioObjectGetPropertyData(stream->dev_id, &addr, 0, NULL, &size, &latency);
1254 if(sc != noErr){
1255 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1256 OSSpinLockUnlock(&stream->lock);
1257 params->result = osstatus_to_hresult(sc);
1258 return STATUS_SUCCESS;
1261 params->result = ca_get_max_stream_latency(stream, &stream_latency);
1262 if(FAILED(params->result)){
1263 OSSpinLockUnlock(&stream->lock);
1264 return STATUS_SUCCESS;
1267 latency += stream_latency;
1268 /* pretend we process audio in Period chunks, so max latency includes
1269 * the period time */
1270 *params->latency = muldiv(latency, 10000000, stream->fmt->nSamplesPerSec)
1271 + stream->period_ms * 10000;
1273 OSSpinLockUnlock(&stream->lock);
1274 params->result = S_OK;
1275 return STATUS_SUCCESS;
1278 static UINT32 get_current_padding_nolock(struct coreaudio_stream *stream)
1280 if(stream->flow == eCapture) capture_resample(stream);
1281 return stream->held_frames;
1284 static NTSTATUS unix_get_current_padding(void *args)
1286 struct get_current_padding_params *params = args;
1287 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1289 OSSpinLockLock(&stream->lock);
1290 *params->padding = get_current_padding_nolock(stream);
1291 OSSpinLockUnlock(&stream->lock);
1292 params->result = S_OK;
1293 return STATUS_SUCCESS;
1296 static NTSTATUS unix_start(void *args)
1298 struct start_params *params = args;
1299 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1301 OSSpinLockLock(&stream->lock);
1303 if(stream->playing)
1304 params->result = AUDCLNT_E_NOT_STOPPED;
1305 else{
1306 stream->playing = TRUE;
1307 params->result = S_OK;
1310 OSSpinLockUnlock(&stream->lock);
1312 return STATUS_SUCCESS;
1315 static NTSTATUS unix_stop(void *args)
1317 struct stop_params *params = args;
1318 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1320 OSSpinLockLock(&stream->lock);
1322 if(!stream->playing)
1323 params->result = S_FALSE;
1324 else{
1325 stream->playing = FALSE;
1326 params->result = S_OK;
1329 OSSpinLockUnlock(&stream->lock);
1331 return STATUS_SUCCESS;
1334 static NTSTATUS unix_reset(void *args)
1336 struct reset_params *params = args;
1337 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1339 OSSpinLockLock(&stream->lock);
1341 if(stream->playing)
1342 params->result = AUDCLNT_E_NOT_STOPPED;
1343 else if(stream->getbuf_last)
1344 params->result = AUDCLNT_E_BUFFER_OPERATION_PENDING;
1345 else{
1346 if(stream->flow == eRender)
1347 stream->written_frames = 0;
1348 else
1349 stream->written_frames += stream->held_frames;
1350 stream->held_frames = 0;
1351 stream->lcl_offs_frames = 0;
1352 stream->wri_offs_frames = 0;
1353 stream->cap_offs_frames = 0;
1354 stream->cap_held_frames = 0;
1355 params->result = S_OK;
1358 OSSpinLockUnlock(&stream->lock);
1359 return STATUS_SUCCESS;
1362 static NTSTATUS unix_get_render_buffer(void *args)
1364 struct get_render_buffer_params *params = args;
1365 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1366 SIZE_T size;
1367 UINT32 pad;
1369 OSSpinLockLock(&stream->lock);
1371 pad = get_current_padding_nolock(stream);
1373 if(stream->getbuf_last){
1374 params->result = AUDCLNT_E_OUT_OF_ORDER;
1375 goto end;
1377 if(!params->frames){
1378 params->result = S_OK;
1379 goto end;
1381 if(pad + params->frames > stream->bufsize_frames){
1382 params->result = AUDCLNT_E_BUFFER_TOO_LARGE;
1383 goto end;
1386 if(stream->wri_offs_frames + params->frames > stream->bufsize_frames){
1387 if(stream->tmp_buffer_frames < params->frames){
1388 if(stream->tmp_buffer){
1389 size = 0;
1390 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer,
1391 &size, MEM_RELEASE);
1392 stream->tmp_buffer = NULL;
1394 size = params->frames * stream->fmt->nBlockAlign;
1395 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
1396 &size, MEM_COMMIT, PAGE_READWRITE)){
1397 stream->tmp_buffer_frames = 0;
1398 params->result = E_OUTOFMEMORY;
1399 goto end;
1401 stream->tmp_buffer_frames = params->frames;
1403 *params->data = stream->tmp_buffer;
1404 stream->getbuf_last = -params->frames;
1405 }else{
1406 *params->data = stream->local_buffer + stream->wri_offs_frames * stream->fmt->nBlockAlign;
1407 stream->getbuf_last = params->frames;
1410 silence_buffer(stream, *params->data, params->frames);
1411 params->result = S_OK;
1413 end:
1414 OSSpinLockUnlock(&stream->lock);
1416 return STATUS_SUCCESS;
1419 static NTSTATUS unix_release_render_buffer(void *args)
1421 struct release_render_buffer_params *params = args;
1422 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1423 BYTE *buffer;
1425 OSSpinLockLock(&stream->lock);
1427 if(!params->written_frames){
1428 stream->getbuf_last = 0;
1429 params->result = S_OK;
1430 }else if(!stream->getbuf_last)
1431 params->result = AUDCLNT_E_OUT_OF_ORDER;
1432 else if(params->written_frames > (stream->getbuf_last >= 0 ? stream->getbuf_last : -stream->getbuf_last))
1433 params->result = AUDCLNT_E_INVALID_SIZE;
1434 else{
1435 if(stream->getbuf_last >= 0)
1436 buffer = stream->local_buffer + stream->wri_offs_frames * stream->fmt->nBlockAlign;
1437 else
1438 buffer = stream->tmp_buffer;
1440 if(params->flags & AUDCLNT_BUFFERFLAGS_SILENT)
1441 silence_buffer(stream, buffer, params->written_frames);
1443 if(stream->getbuf_last < 0)
1444 ca_wrap_buffer(stream->local_buffer,
1445 stream->wri_offs_frames * stream->fmt->nBlockAlign,
1446 stream->bufsize_frames * stream->fmt->nBlockAlign,
1447 buffer, params->written_frames * stream->fmt->nBlockAlign);
1449 stream->wri_offs_frames += params->written_frames;
1450 stream->wri_offs_frames %= stream->bufsize_frames;
1451 stream->held_frames += params->written_frames;
1452 stream->written_frames += params->written_frames;
1453 stream->getbuf_last = 0;
1455 params->result = S_OK;
1458 OSSpinLockUnlock(&stream->lock);
1460 return STATUS_SUCCESS;
1463 static NTSTATUS unix_get_capture_buffer(void *args)
1465 struct get_capture_buffer_params *params = args;
1466 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1467 UINT32 chunk_bytes, chunk_frames;
1468 LARGE_INTEGER stamp, freq;
1469 SIZE_T size;
1471 OSSpinLockLock(&stream->lock);
1473 if(stream->getbuf_last){
1474 params->result = AUDCLNT_E_OUT_OF_ORDER;
1475 goto end;
1478 capture_resample(stream);
1480 *params->frames = 0;
1482 if(stream->held_frames < stream->period_frames){
1483 params->result = AUDCLNT_S_BUFFER_EMPTY;
1484 goto end;
1487 *params->flags = 0;
1488 chunk_frames = stream->bufsize_frames - stream->lcl_offs_frames;
1489 if(chunk_frames < stream->period_frames){
1490 chunk_bytes = chunk_frames * stream->fmt->nBlockAlign;
1491 if(!stream->tmp_buffer){
1492 size = stream->period_frames * stream->fmt->nBlockAlign;
1493 NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
1494 &size, MEM_COMMIT, PAGE_READWRITE);
1496 *params->data = stream->tmp_buffer;
1497 memcpy(*params->data, stream->local_buffer + stream->lcl_offs_frames * stream->fmt->nBlockAlign,
1498 chunk_bytes);
1499 memcpy(*params->data + chunk_bytes, stream->local_buffer,
1500 stream->period_frames * stream->fmt->nBlockAlign - chunk_bytes);
1501 }else
1502 *params->data = stream->local_buffer + stream->lcl_offs_frames * stream->fmt->nBlockAlign;
1504 stream->getbuf_last = *params->frames = stream->period_frames;
1506 if(params->devpos)
1507 *params->devpos = stream->written_frames;
1508 if(params->qpcpos){ /* fixme: qpc of recording time */
1509 NtQueryPerformanceCounter(&stamp, &freq);
1510 *params->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1512 params->result = S_OK;
1514 end:
1515 OSSpinLockUnlock(&stream->lock);
1516 return STATUS_SUCCESS;
1519 static NTSTATUS unix_release_capture_buffer(void *args)
1521 struct release_capture_buffer_params *params = args;
1522 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1524 OSSpinLockLock(&stream->lock);
1526 if(!params->done){
1527 stream->getbuf_last = 0;
1528 params->result = S_OK;
1529 }else if(!stream->getbuf_last)
1530 params->result = AUDCLNT_E_OUT_OF_ORDER;
1531 else if(stream->getbuf_last != params->done)
1532 params->result = AUDCLNT_E_INVALID_SIZE;
1533 else{
1534 stream->written_frames += params->done;
1535 stream->held_frames -= params->done;
1536 stream->lcl_offs_frames += params->done;
1537 stream->lcl_offs_frames %= stream->bufsize_frames;
1538 stream->getbuf_last = 0;
1539 params->result = S_OK;
1542 OSSpinLockUnlock(&stream->lock);
1544 return STATUS_SUCCESS;
1547 static NTSTATUS unix_get_next_packet_size(void *args)
1549 struct get_next_packet_size_params *params = args;
1550 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1552 OSSpinLockLock(&stream->lock);
1554 capture_resample(stream);
1556 if(stream->held_frames >= stream->period_frames)
1557 *params->frames = stream->period_frames;
1558 else
1559 *params->frames = 0;
1561 OSSpinLockUnlock(&stream->lock);
1563 params->result = S_OK;
1564 return STATUS_SUCCESS;
1567 static NTSTATUS unix_get_position(void *args)
1569 struct get_position_params *params = args;
1570 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1571 LARGE_INTEGER stamp, freq;
1573 OSSpinLockLock(&stream->lock);
1575 *params->pos = stream->written_frames - stream->held_frames;
1577 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1578 *params->pos *= stream->fmt->nBlockAlign;
1580 if(params->qpctime){
1581 NtQueryPerformanceCounter(&stamp, &freq);
1582 *params->qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1585 OSSpinLockUnlock(&stream->lock);
1587 params->result = S_OK;
1588 return STATUS_SUCCESS;
1591 static NTSTATUS unix_get_frequency(void *args)
1593 struct get_frequency_params *params = args;
1594 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1596 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1597 *params->freq = (UINT64)stream->fmt->nSamplesPerSec * stream->fmt->nBlockAlign;
1598 else
1599 *params->freq = stream->fmt->nSamplesPerSec;
1601 params->result = S_OK;
1602 return STATUS_SUCCESS;
1605 static NTSTATUS unix_is_started(void *args)
1607 struct is_started_params *params = args;
1608 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1610 if(stream->playing)
1611 params->result = S_OK;
1612 else
1613 params->result = S_FALSE;
1615 return STATUS_SUCCESS;
1618 static NTSTATUS unix_set_volumes(void *args)
1620 struct set_volumes_params *params = args;
1621 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1622 Float32 level = 1.0, tmp;
1623 OSStatus sc;
1624 UINT32 i;
1626 if(params->channel >= stream->fmt->nChannels || params->channel < -1){
1627 ERR("Incorrect channel %d\n", params->channel);
1628 return STATUS_SUCCESS;
1631 if(params->channel == -1){
1632 for(i = 0; i < stream->fmt->nChannels; ++i){
1633 tmp = params->master_volume * params->volumes[i] * params->session_volumes[i];
1634 level = tmp < level ? tmp : level;
1636 }else
1637 level = params->master_volume * params->volumes[params->channel] *
1638 params->session_volumes[params->channel];
1640 sc = AudioUnitSetParameter(stream->unit, kHALOutputParam_Volume,
1641 kAudioUnitScope_Global, 0, level, 0);
1642 if(sc != noErr)
1643 WARN("Couldn't set volume: %x\n", (int)sc);
1645 return STATUS_SUCCESS;
1648 unixlib_entry_t __wine_unix_call_funcs[] =
1650 NULL,
1651 NULL,
1652 NULL,
1653 unix_get_endpoint_ids,
1654 unix_create_stream,
1655 unix_release_stream,
1656 unix_start,
1657 unix_stop,
1658 unix_reset,
1659 NULL,
1660 unix_get_render_buffer,
1661 unix_release_render_buffer,
1662 unix_get_capture_buffer,
1663 unix_release_capture_buffer,
1664 unix_is_format_supported,
1665 unix_get_mix_format,
1666 NULL,
1667 unix_get_buffer_size,
1668 unix_get_latency,
1669 unix_get_current_padding,
1670 unix_get_next_packet_size,
1671 unix_get_frequency,
1672 unix_get_position,
1673 unix_set_volumes,
1674 NULL,
1675 NULL,
1676 unix_is_started,
1677 NULL,
1678 unix_midi_init,
1679 unix_midi_release,
1680 unix_midi_out_message,
1681 unix_midi_in_message,
1682 unix_midi_notify_wait,
1683 NULL,
1686 #ifdef _WIN64
1688 typedef UINT PTR32;
1690 static NTSTATUS unix_wow64_get_endpoint_ids(void *args)
1692 struct
1694 EDataFlow flow;
1695 PTR32 endpoints;
1696 unsigned int size;
1697 HRESULT result;
1698 unsigned int num;
1699 unsigned int default_idx;
1700 } *params32 = args;
1701 struct get_endpoint_ids_params params =
1703 .flow = params32->flow,
1704 .endpoints = ULongToPtr(params32->endpoints),
1705 .size = params32->size
1707 unix_get_endpoint_ids(&params);
1708 params32->size = params.size;
1709 params32->result = params.result;
1710 params32->num = params.num;
1711 params32->default_idx = params.default_idx;
1712 return STATUS_SUCCESS;
1715 static NTSTATUS unix_wow64_create_stream(void *args)
1717 struct
1719 PTR32 name;
1720 PTR32 device;
1721 EDataFlow flow;
1722 AUDCLNT_SHAREMODE share;
1723 DWORD flags;
1724 REFERENCE_TIME duration;
1725 REFERENCE_TIME period;
1726 PTR32 fmt;
1727 HRESULT result;
1728 PTR32 channel_count;
1729 PTR32 stream;
1730 } *params32 = args;
1731 struct create_stream_params params =
1733 .name = ULongToPtr(params32->name),
1734 .device = ULongToPtr(params32->device),
1735 .flow = params32->flow,
1736 .share = params32->share,
1737 .flags = params32->flags,
1738 .duration = params32->duration,
1739 .period = params32->period,
1740 .fmt = ULongToPtr(params32->fmt),
1741 .channel_count = ULongToPtr(params32->channel_count),
1742 .stream = ULongToPtr(params32->stream)
1744 unix_create_stream(&params);
1745 params32->result = params.result;
1746 return STATUS_SUCCESS;
1749 static NTSTATUS unix_wow64_release_stream(void *args)
1751 struct
1753 stream_handle stream;
1754 PTR32 timer_thread;
1755 HRESULT result;
1756 } *params32 = args;
1757 struct release_stream_params params =
1759 .stream = params32->stream,
1760 .timer_thread = ULongToHandle(params32->timer_thread)
1762 unix_release_stream(&params);
1763 params32->result = params.result;
1764 return STATUS_SUCCESS;
1767 static NTSTATUS unix_wow64_get_render_buffer(void *args)
1769 struct
1771 stream_handle stream;
1772 UINT32 frames;
1773 HRESULT result;
1774 PTR32 data;
1775 } *params32 = args;
1776 BYTE *data = NULL;
1777 struct get_render_buffer_params params =
1779 .stream = params32->stream,
1780 .frames = params32->frames,
1781 .data = &data
1783 unix_get_render_buffer(&params);
1784 params32->result = params.result;
1785 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1786 return STATUS_SUCCESS;
1789 static NTSTATUS unix_wow64_get_capture_buffer(void *args)
1791 struct
1793 stream_handle stream;
1794 HRESULT result;
1795 PTR32 data;
1796 PTR32 frames;
1797 PTR32 flags;
1798 PTR32 devpos;
1799 PTR32 qpcpos;
1800 } *params32 = args;
1801 BYTE *data = NULL;
1802 struct get_capture_buffer_params params =
1804 .stream = params32->stream,
1805 .data = &data,
1806 .frames = ULongToPtr(params32->frames),
1807 .flags = ULongToPtr(params32->flags),
1808 .devpos = ULongToPtr(params32->devpos),
1809 .qpcpos = ULongToPtr(params32->qpcpos)
1811 unix_get_capture_buffer(&params);
1812 params32->result = params.result;
1813 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1814 return STATUS_SUCCESS;
1817 static NTSTATUS unix_wow64_is_format_supported(void *args)
1819 struct
1821 PTR32 device;
1822 EDataFlow flow;
1823 AUDCLNT_SHAREMODE share;
1824 PTR32 fmt_in;
1825 PTR32 fmt_out;
1826 HRESULT result;
1827 } *params32 = args;
1828 struct is_format_supported_params params =
1830 .device = ULongToPtr(params32->device),
1831 .flow = params32->flow,
1832 .share = params32->share,
1833 .fmt_in = ULongToPtr(params32->fmt_in),
1834 .fmt_out = ULongToPtr(params32->fmt_out)
1836 unix_is_format_supported(&params);
1837 params32->result = params.result;
1838 return STATUS_SUCCESS;
1841 static NTSTATUS unix_wow64_get_mix_format(void *args)
1843 struct
1845 PTR32 device;
1846 EDataFlow flow;
1847 PTR32 fmt;
1848 HRESULT result;
1849 } *params32 = args;
1850 struct get_mix_format_params params =
1852 .device = ULongToPtr(params32->device),
1853 .flow = params32->flow,
1854 .fmt = ULongToPtr(params32->fmt)
1856 unix_get_mix_format(&params);
1857 params32->result = params.result;
1858 return STATUS_SUCCESS;
1861 static NTSTATUS unix_wow64_get_buffer_size(void *args)
1863 struct
1865 stream_handle stream;
1866 HRESULT result;
1867 PTR32 frames;
1868 } *params32 = args;
1869 struct get_buffer_size_params params =
1871 .stream = params32->stream,
1872 .frames = ULongToPtr(params32->frames)
1874 unix_get_buffer_size(&params);
1875 params32->result = params.result;
1876 return STATUS_SUCCESS;
1879 static NTSTATUS unix_wow64_get_latency(void *args)
1881 struct
1883 stream_handle stream;
1884 HRESULT result;
1885 PTR32 latency;
1886 } *params32 = args;
1887 struct get_latency_params params =
1889 .stream = params32->stream,
1890 .latency = ULongToPtr(params32->latency)
1892 unix_get_latency(&params);
1893 params32->result = params.result;
1894 return STATUS_SUCCESS;
1897 static NTSTATUS unix_wow64_get_current_padding(void *args)
1899 struct
1901 stream_handle stream;
1902 HRESULT result;
1903 PTR32 padding;
1904 } *params32 = args;
1905 struct get_current_padding_params params =
1907 .stream = params32->stream,
1908 .padding = ULongToPtr(params32->padding)
1910 unix_get_current_padding(&params);
1911 params32->result = params.result;
1912 return STATUS_SUCCESS;
1915 static NTSTATUS unix_wow64_get_next_packet_size(void *args)
1917 struct
1919 stream_handle stream;
1920 HRESULT result;
1921 PTR32 frames;
1922 } *params32 = args;
1923 struct get_next_packet_size_params params =
1925 .stream = params32->stream,
1926 .frames = ULongToPtr(params32->frames)
1928 unix_get_next_packet_size(&params);
1929 params32->result = params.result;
1930 return STATUS_SUCCESS;
1933 static NTSTATUS unix_wow64_get_position(void *args)
1935 struct
1937 stream_handle stream;
1938 HRESULT result;
1939 PTR32 pos;
1940 PTR32 qpctime;
1941 } *params32 = args;
1942 struct get_position_params params =
1944 .stream = params32->stream,
1945 .pos = ULongToPtr(params32->pos),
1946 .qpctime = ULongToPtr(params32->qpctime)
1948 unix_get_position(&params);
1949 params32->result = params.result;
1950 return STATUS_SUCCESS;
1953 static NTSTATUS unix_wow64_get_frequency(void *args)
1955 struct
1957 stream_handle stream;
1958 HRESULT result;
1959 PTR32 freq;
1960 } *params32 = args;
1961 struct get_frequency_params params =
1963 .stream = params32->stream,
1964 .freq = ULongToPtr(params32->freq)
1966 unix_get_frequency(&params);
1967 params32->result = params.result;
1968 return STATUS_SUCCESS;
1971 static NTSTATUS unix_wow64_set_volumes(void *args)
1973 struct
1975 stream_handle stream;
1976 float master_volume;
1977 PTR32 volumes;
1978 PTR32 session_volumes;
1979 int channel;
1980 } *params32 = args;
1981 struct set_volumes_params params =
1983 .stream = params32->stream,
1984 .master_volume = params32->master_volume,
1985 .volumes = ULongToPtr(params32->volumes),
1986 .session_volumes = ULongToPtr(params32->session_volumes),
1987 .channel = params32->channel
1989 return unix_set_volumes(&params);
1992 unixlib_entry_t __wine_unix_call_wow64_funcs[] =
1994 NULL,
1995 NULL,
1996 NULL,
1997 unix_wow64_get_endpoint_ids,
1998 unix_wow64_create_stream,
1999 unix_wow64_release_stream,
2000 unix_start,
2001 unix_stop,
2002 unix_reset,
2003 NULL,
2004 unix_wow64_get_render_buffer,
2005 unix_release_render_buffer,
2006 unix_wow64_get_capture_buffer,
2007 unix_release_capture_buffer,
2008 unix_wow64_is_format_supported,
2009 unix_wow64_get_mix_format,
2010 NULL,
2011 unix_wow64_get_buffer_size,
2012 unix_wow64_get_latency,
2013 unix_wow64_get_current_padding,
2014 unix_wow64_get_next_packet_size,
2015 unix_wow64_get_frequency,
2016 unix_wow64_get_position,
2017 unix_wow64_set_volumes,
2018 NULL,
2019 NULL,
2020 unix_is_started,
2021 NULL,
2022 unix_wow64_midi_init,
2023 unix_midi_release,
2024 unix_wow64_midi_out_message,
2025 unix_wow64_midi_in_message,
2026 unix_wow64_midi_notify_wait,
2027 NULL,
2030 #endif /* _WIN64 */