winecoreaudio: Store flags in unixlib.
[wine.git] / dlls / winecoreaudio.drv / coreaudio.c
blobee660b0887eca2de7aff193a1ba8f999e739a227
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;
85 EDataFlow flow;
86 DWORD flags;
87 AUDCLNT_SHAREMODE share;
89 BOOL playing;
90 REFERENCE_TIME period;
91 UINT32 period_frames;
92 UINT32 bufsize_frames, resamp_bufsize_frames;
93 UINT32 lcl_offs_frames, held_frames, wri_offs_frames, tmp_buffer_frames;
94 UINT32 cap_bufsize_frames, cap_offs_frames, cap_held_frames;
95 UINT32 wrap_bufsize_frames;
96 UINT64 written_frames;
97 INT32 getbuf_last;
98 WAVEFORMATEX *fmt;
99 BYTE *local_buffer, *cap_buffer, *wrap_buffer, *resamp_buffer, *tmp_buffer;
102 static NTSTATUS unix_not_implemented(void *args)
104 return STATUS_SUCCESS;
107 static HRESULT osstatus_to_hresult(OSStatus sc)
109 switch(sc){
110 case kAudioFormatUnsupportedDataFormatError:
111 case kAudioFormatUnknownFormatError:
112 case kAudioDeviceUnsupportedFormatError:
113 return AUDCLNT_E_UNSUPPORTED_FORMAT;
114 case kAudioHardwareBadDeviceError:
115 return AUDCLNT_E_DEVICE_INVALIDATED;
117 return E_FAIL;
120 static struct coreaudio_stream *handle_get_stream(stream_handle h)
122 return (struct coreaudio_stream *)(UINT_PTR)h;
125 /* copied from kernelbase */
126 static int muldiv( int a, int b, int c )
128 LONGLONG ret;
130 if (!c) return -1;
132 /* We want to deal with a positive divisor to simplify the logic. */
133 if (c < 0)
135 a = -a;
136 c = -c;
139 /* If the result is positive, we "add" to round. else, we subtract to round. */
140 if ((a < 0 && b < 0) || (a >= 0 && b >= 0))
141 ret = (((LONGLONG)a * b) + (c / 2)) / c;
142 else
143 ret = (((LONGLONG)a * b) - (c / 2)) / c;
145 if (ret > 2147483647 || ret < -2147483647) return -1;
146 return ret;
149 static AudioObjectPropertyScope get_scope(EDataFlow flow)
151 return (flow == eRender) ? kAudioDevicePropertyScopeOutput : kAudioDevicePropertyScopeInput;
154 static BOOL device_has_channels(AudioDeviceID device, EDataFlow flow)
156 AudioObjectPropertyAddress addr;
157 AudioBufferList *buffers;
158 BOOL ret = FALSE;
159 OSStatus sc;
160 UInt32 size;
161 int i;
163 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
164 addr.mScope = get_scope(flow);
165 addr.mElement = 0;
167 sc = AudioObjectGetPropertyDataSize(device, &addr, 0, NULL, &size);
168 if(sc != noErr){
169 WARN("Unable to get _StreamConfiguration property size for device %u: %x\n",
170 (unsigned int)device, (int)sc);
171 return FALSE;
174 buffers = malloc(size);
175 if(!buffers) return FALSE;
177 sc = AudioObjectGetPropertyData(device, &addr, 0, NULL, &size, buffers);
178 if(sc != noErr){
179 WARN("Unable to get _StreamConfiguration property for device %u: %x\n",
180 (unsigned int)device, (int)sc);
181 free(buffers);
182 return FALSE;
185 for(i = 0; i < buffers->mNumberBuffers; i++){
186 if(buffers->mBuffers[i].mNumberChannels > 0){
187 ret = TRUE;
188 break;
191 free(buffers);
192 return ret;
195 static NTSTATUS unix_get_endpoint_ids(void *args)
197 struct get_endpoint_ids_params *params = args;
198 unsigned int num_devices, i, needed, offset;
199 AudioDeviceID *devices, default_id;
200 AudioObjectPropertyAddress addr;
201 struct endpoint *endpoint;
202 UInt32 devsize, size;
203 struct endpoint_info
205 CFStringRef name;
206 AudioDeviceID id;
207 } *info;
208 OSStatus sc;
209 UniChar *ptr;
211 params->num = 0;
212 params->default_idx = 0;
214 addr.mScope = kAudioObjectPropertyScopeGlobal;
215 addr.mElement = kAudioObjectPropertyElementMaster;
216 if(params->flow == eRender) addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
217 else if(params->flow == eCapture) addr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
218 else{
219 params->result = E_INVALIDARG;
220 return STATUS_SUCCESS;
223 size = sizeof(default_id);
224 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &default_id);
225 if(sc != noErr){
226 WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc);
227 default_id = -1;
230 addr.mSelector = kAudioHardwarePropertyDevices;
231 sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &devsize);
232 if(sc != noErr){
233 WARN("Getting _Devices property size failed: %x\n", (int)sc);
234 params->result = osstatus_to_hresult(sc);
235 return STATUS_SUCCESS;
238 num_devices = devsize / sizeof(AudioDeviceID);
239 devices = malloc(devsize);
240 info = malloc(num_devices * sizeof(*info));
241 if(!devices || !info){
242 free(info);
243 free(devices);
244 params->result = E_OUTOFMEMORY;
245 return STATUS_SUCCESS;
248 sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &devsize, devices);
249 if(sc != noErr){
250 WARN("Getting _Devices property failed: %x\n", (int)sc);
251 free(info);
252 free(devices);
253 params->result = osstatus_to_hresult(sc);
254 return STATUS_SUCCESS;
257 addr.mSelector = kAudioObjectPropertyName;
258 addr.mScope = get_scope(params->flow);
259 addr.mElement = 0;
261 for(i = 0; i < num_devices; i++){
262 if(!device_has_channels(devices[i], params->flow)) continue;
264 size = sizeof(CFStringRef);
265 sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL, &size, &info[params->num].name);
266 if(sc != noErr){
267 WARN("Unable to get _Name property for device %u: %x\n",
268 (unsigned int)devices[i], (int)sc);
269 continue;
271 info[params->num++].id = devices[i];
273 free(devices);
275 offset = needed = sizeof(*endpoint) * params->num;
276 endpoint = params->endpoints;
278 for(i = 0; i < params->num; i++){
279 const SIZE_T name_len = CFStringGetLength(info[i].name) + 1;
280 const SIZE_T device_len = MAX_DEV_NAME_LEN + 1;
281 needed += name_len * sizeof(WCHAR) + ((device_len + 1) & ~1);
283 if(needed <= params->size){
284 endpoint->name = offset;
285 ptr = (UniChar *)((char *)params->endpoints + offset);
286 CFStringGetCharacters(info[i].name, CFRangeMake(0, name_len - 1), ptr);
287 ptr[name_len - 1] = 0;
288 offset += name_len * sizeof(WCHAR);
289 endpoint->device = offset;
290 sprintf((char *)params->endpoints + offset, "%u", (unsigned int)info[i].id);
291 offset += (device_len + 1) & ~1;
292 endpoint++;
294 CFRelease(info[i].name);
295 if(info[i].id == default_id) params->default_idx = i;
297 free(info);
299 if(needed > params->size){
300 params->size = needed;
301 params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
303 else params->result = S_OK;
305 return STATUS_SUCCESS;
308 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
310 WAVEFORMATEX *ret;
311 size_t size;
313 if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
314 size = sizeof(WAVEFORMATEXTENSIBLE);
315 else
316 size = sizeof(WAVEFORMATEX);
318 ret = malloc(size);
319 if(!ret)
320 return NULL;
322 memcpy(ret, fmt, size);
324 ret->cbSize = size - sizeof(WAVEFORMATEX);
326 return ret;
329 static void silence_buffer(struct coreaudio_stream *stream, BYTE *buffer, UINT32 frames)
331 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)stream->fmt;
332 if((stream->fmt->wFormatTag == WAVE_FORMAT_PCM ||
333 (stream->fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
334 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))) &&
335 stream->fmt->wBitsPerSample == 8)
336 memset(buffer, 128, frames * stream->fmt->nBlockAlign);
337 else
338 memset(buffer, 0, frames * stream->fmt->nBlockAlign);
341 /* CA is pulling data from us */
342 static OSStatus ca_render_cb(void *user, AudioUnitRenderActionFlags *flags,
343 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
344 AudioBufferList *data)
346 struct coreaudio_stream *stream = user;
347 UINT32 to_copy_bytes, to_copy_frames, chunk_bytes, lcl_offs_bytes;
349 OSSpinLockLock(&stream->lock);
351 if(stream->playing){
352 lcl_offs_bytes = stream->lcl_offs_frames * stream->fmt->nBlockAlign;
353 to_copy_frames = min(nframes, stream->held_frames);
354 to_copy_bytes = to_copy_frames * stream->fmt->nBlockAlign;
356 chunk_bytes = (stream->bufsize_frames - stream->lcl_offs_frames) * stream->fmt->nBlockAlign;
358 if(to_copy_bytes > chunk_bytes){
359 memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, chunk_bytes);
360 memcpy(((BYTE *)data->mBuffers[0].mData) + chunk_bytes, stream->local_buffer, to_copy_bytes - chunk_bytes);
361 }else
362 memcpy(data->mBuffers[0].mData, stream->local_buffer + lcl_offs_bytes, to_copy_bytes);
364 stream->lcl_offs_frames += to_copy_frames;
365 stream->lcl_offs_frames %= stream->bufsize_frames;
366 stream->held_frames -= to_copy_frames;
367 }else
368 to_copy_bytes = to_copy_frames = 0;
370 if(nframes > to_copy_frames)
371 silence_buffer(stream, ((BYTE *)data->mBuffers[0].mData) + to_copy_bytes, nframes - to_copy_frames);
373 OSSpinLockUnlock(&stream->lock);
375 return noErr;
378 static void ca_wrap_buffer(BYTE *dst, UINT32 dst_offs, UINT32 dst_bytes,
379 BYTE *src, UINT32 src_bytes)
381 UINT32 chunk_bytes = dst_bytes - dst_offs;
383 if(chunk_bytes < src_bytes){
384 memcpy(dst + dst_offs, src, chunk_bytes);
385 memcpy(dst, src + chunk_bytes, src_bytes - chunk_bytes);
386 }else
387 memcpy(dst + dst_offs, src, src_bytes);
390 /* we need to trigger CA to pull data from the device and give it to us
392 * raw data from CA is stored in cap_buffer, possibly via wrap_buffer
394 * raw data is resampled from cap_buffer into resamp_buffer in period-size
395 * chunks and copied to local_buffer
397 static OSStatus ca_capture_cb(void *user, AudioUnitRenderActionFlags *flags,
398 const AudioTimeStamp *ts, UInt32 bus, UInt32 nframes,
399 AudioBufferList *data)
401 struct coreaudio_stream *stream = user;
402 AudioBufferList list;
403 OSStatus sc;
404 UINT32 cap_wri_offs_frames;
406 OSSpinLockLock(&stream->lock);
408 cap_wri_offs_frames = (stream->cap_offs_frames + stream->cap_held_frames) % stream->cap_bufsize_frames;
410 list.mNumberBuffers = 1;
411 list.mBuffers[0].mNumberChannels = stream->fmt->nChannels;
412 list.mBuffers[0].mDataByteSize = nframes * stream->fmt->nBlockAlign;
414 if(!stream->playing || cap_wri_offs_frames + nframes > stream->cap_bufsize_frames){
415 if(stream->wrap_bufsize_frames < nframes){
416 free(stream->wrap_buffer);
417 stream->wrap_buffer = malloc(list.mBuffers[0].mDataByteSize);
418 stream->wrap_bufsize_frames = nframes;
421 list.mBuffers[0].mData = stream->wrap_buffer;
422 }else
423 list.mBuffers[0].mData = stream->cap_buffer + cap_wri_offs_frames * stream->fmt->nBlockAlign;
425 sc = AudioUnitRender(stream->unit, flags, ts, bus, nframes, &list);
426 if(sc != noErr){
427 OSSpinLockUnlock(&stream->lock);
428 return sc;
431 if(stream->playing){
432 if(list.mBuffers[0].mData == stream->wrap_buffer){
433 ca_wrap_buffer(stream->cap_buffer,
434 cap_wri_offs_frames * stream->fmt->nBlockAlign,
435 stream->cap_bufsize_frames * stream->fmt->nBlockAlign,
436 stream->wrap_buffer, list.mBuffers[0].mDataByteSize);
439 stream->cap_held_frames += list.mBuffers[0].mDataByteSize / stream->fmt->nBlockAlign;
440 if(stream->cap_held_frames > stream->cap_bufsize_frames){
441 stream->cap_offs_frames += stream->cap_held_frames % stream->cap_bufsize_frames;
442 stream->cap_offs_frames %= stream->cap_bufsize_frames;
443 stream->cap_held_frames = stream->cap_bufsize_frames;
447 OSSpinLockUnlock(&stream->lock);
448 return noErr;
451 static AudioComponentInstance get_audiounit(EDataFlow dataflow, AudioDeviceID adevid)
453 AudioComponentInstance unit;
454 AudioComponent comp;
455 AudioComponentDescription desc;
456 OSStatus sc;
458 memset(&desc, 0, sizeof(desc));
459 desc.componentType = kAudioUnitType_Output;
460 desc.componentSubType = kAudioUnitSubType_HALOutput;
461 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
463 if(!(comp = AudioComponentFindNext(NULL, &desc))){
464 WARN("AudioComponentFindNext failed\n");
465 return NULL;
468 sc = AudioComponentInstanceNew(comp, &unit);
469 if(sc != noErr){
470 WARN("AudioComponentInstanceNew failed: %x\n", (int)sc);
471 return NULL;
474 if(dataflow == eCapture){
475 UInt32 enableio;
477 enableio = 1;
478 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
479 kAudioUnitScope_Input, 1, &enableio, sizeof(enableio));
480 if(sc != noErr){
481 WARN("Couldn't enable I/O on input element: %x\n", (int)sc);
482 AudioComponentInstanceDispose(unit);
483 return NULL;
486 enableio = 0;
487 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO,
488 kAudioUnitScope_Output, 0, &enableio, sizeof(enableio));
489 if(sc != noErr){
490 WARN("Couldn't disable I/O on output element: %x\n", (int)sc);
491 AudioComponentInstanceDispose(unit);
492 return NULL;
496 sc = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
497 kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));
498 if(sc != noErr){
499 WARN("Couldn't set audio unit device\n");
500 AudioComponentInstanceDispose(unit);
501 return NULL;
504 return unit;
507 static void dump_adesc(const char *aux, AudioStreamBasicDescription *desc)
509 TRACE("%s: mSampleRate: %f\n", aux, desc->mSampleRate);
510 TRACE("%s: mBytesPerPacket: %u\n", aux, (unsigned int)desc->mBytesPerPacket);
511 TRACE("%s: mFramesPerPacket: %u\n", aux, (unsigned int)desc->mFramesPerPacket);
512 TRACE("%s: mBytesPerFrame: %u\n", aux, (unsigned int)desc->mBytesPerFrame);
513 TRACE("%s: mChannelsPerFrame: %u\n", aux, (unsigned int)desc->mChannelsPerFrame);
514 TRACE("%s: mBitsPerChannel: %u\n", aux, (unsigned int)desc->mBitsPerChannel);
517 static HRESULT ca_get_audiodesc(AudioStreamBasicDescription *desc,
518 const WAVEFORMATEX *fmt)
520 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
522 desc->mFormatFlags = 0;
524 if(fmt->wFormatTag == WAVE_FORMAT_PCM ||
525 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
526 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
527 desc->mFormatID = kAudioFormatLinearPCM;
528 if(fmt->wBitsPerSample > 8)
529 desc->mFormatFlags = kAudioFormatFlagIsSignedInteger;
530 }else if(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
531 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
532 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))){
533 desc->mFormatID = kAudioFormatLinearPCM;
534 desc->mFormatFlags = kAudioFormatFlagIsFloat;
535 }else if(fmt->wFormatTag == WAVE_FORMAT_MULAW ||
536 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
537 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_MULAW))){
538 desc->mFormatID = kAudioFormatULaw;
539 }else if(fmt->wFormatTag == WAVE_FORMAT_ALAW ||
540 (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
541 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_ALAW))){
542 desc->mFormatID = kAudioFormatALaw;
543 }else
544 return AUDCLNT_E_UNSUPPORTED_FORMAT;
546 desc->mSampleRate = fmt->nSamplesPerSec;
547 desc->mBytesPerPacket = fmt->nBlockAlign;
548 desc->mFramesPerPacket = 1;
549 desc->mBytesPerFrame = fmt->nBlockAlign;
550 desc->mChannelsPerFrame = fmt->nChannels;
551 desc->mBitsPerChannel = fmt->wBitsPerSample;
552 desc->mReserved = 0;
554 return S_OK;
557 static HRESULT ca_setup_audiounit(EDataFlow dataflow, AudioComponentInstance unit,
558 const WAVEFORMATEX *fmt, AudioStreamBasicDescription *dev_desc,
559 AudioConverterRef *converter)
561 OSStatus sc;
562 HRESULT hr;
564 if(dataflow == eCapture){
565 AudioStreamBasicDescription desc;
566 UInt32 size;
567 Float64 rate;
568 fenv_t fenv;
569 BOOL fenv_stored = TRUE;
571 hr = ca_get_audiodesc(&desc, fmt);
572 if(FAILED(hr))
573 return hr;
574 dump_adesc("requested", &desc);
576 /* input-only units can't perform sample rate conversion, so we have to
577 * set up our own AudioConverter to support arbitrary sample rates. */
578 size = sizeof(*dev_desc);
579 sc = AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat,
580 kAudioUnitScope_Input, 1, dev_desc, &size);
581 if(sc != noErr){
582 WARN("Couldn't get unit format: %x\n", (int)sc);
583 return osstatus_to_hresult(sc);
585 dump_adesc("hardware", dev_desc);
587 rate = dev_desc->mSampleRate;
588 *dev_desc = desc;
589 dev_desc->mSampleRate = rate;
591 dump_adesc("final", dev_desc);
592 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
593 kAudioUnitScope_Output, 1, dev_desc, sizeof(*dev_desc));
594 if(sc != noErr){
595 WARN("Couldn't set unit format: %x\n", (int)sc);
596 return osstatus_to_hresult(sc);
599 /* AudioConverterNew requires divide-by-zero SSE exceptions to be masked */
600 if(feholdexcept(&fenv)){
601 WARN("Failed to store fenv state\n");
602 fenv_stored = FALSE;
605 sc = AudioConverterNew(dev_desc, &desc, converter);
607 if(fenv_stored && fesetenv(&fenv))
608 WARN("Failed to restore fenv state\n");
610 if(sc != noErr){
611 WARN("Couldn't create audio converter: %x\n", (int)sc);
612 return osstatus_to_hresult(sc);
614 }else{
615 hr = ca_get_audiodesc(dev_desc, fmt);
616 if(FAILED(hr))
617 return hr;
619 dump_adesc("final", dev_desc);
620 sc = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
621 kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));
622 if(sc != noErr){
623 WARN("Couldn't set format: %x\n", (int)sc);
624 return osstatus_to_hresult(sc);
628 return S_OK;
631 static ULONG_PTR zero_bits(void)
633 #ifdef _WIN64
634 return !NtCurrentTeb()->WowTebOffset ? 0 : 0x7fffffff;
635 #else
636 return 0;
637 #endif
640 static AudioDeviceID dev_id_from_device(const char *device)
642 return strtoul(device, NULL, 10);
645 static NTSTATUS unix_create_stream(void *args)
647 struct create_stream_params *params = args;
648 struct coreaudio_stream *stream = calloc(1, sizeof(*stream));
649 AURenderCallbackStruct input;
650 OSStatus sc;
651 SIZE_T size;
653 if(!stream){
654 params->result = E_OUTOFMEMORY;
655 return STATUS_SUCCESS;
658 stream->fmt = clone_format(params->fmt);
659 if(!stream->fmt){
660 params->result = E_OUTOFMEMORY;
661 goto end;
664 stream->period = params->period;
665 stream->period_frames = muldiv(params->period, stream->fmt->nSamplesPerSec, 10000000);
666 stream->dev_id = dev_id_from_device(params->device);
667 stream->flow = params->flow;
668 stream->flags = params->flags;
669 stream->share = params->share;
671 stream->bufsize_frames = muldiv(params->duration, stream->fmt->nSamplesPerSec, 10000000);
672 if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE)
673 stream->bufsize_frames -= stream->bufsize_frames % stream->period_frames;
675 if(!(stream->unit = get_audiounit(stream->flow, stream->dev_id))){
676 params->result = AUDCLNT_E_DEVICE_INVALIDATED;
677 goto end;
680 params->result = ca_setup_audiounit(stream->flow, stream->unit, stream->fmt, &stream->dev_desc, &stream->converter);
681 if(FAILED(params->result)) goto end;
683 input.inputProcRefCon = stream;
684 if(stream->flow == eCapture){
685 input.inputProc = ca_capture_cb;
686 sc = AudioUnitSetProperty(stream->unit, kAudioOutputUnitProperty_SetInputCallback,
687 kAudioUnitScope_Output, 1, &input, sizeof(input));
688 }else{
689 input.inputProc = ca_render_cb;
690 sc = AudioUnitSetProperty(stream->unit, kAudioUnitProperty_SetRenderCallback,
691 kAudioUnitScope_Input, 0, &input, sizeof(input));
693 if(sc != noErr){
694 WARN("Couldn't set callback: %x\n", (int)sc);
695 params->result = osstatus_to_hresult(sc);
696 goto end;
699 sc = AudioUnitInitialize(stream->unit);
700 if(sc != noErr){
701 WARN("Couldn't initialize: %x\n", (int)sc);
702 params->result = osstatus_to_hresult(sc);
703 goto end;
706 /* we play audio continuously because AudioOutputUnitStart sometimes takes
707 * a while to return */
708 sc = AudioOutputUnitStart(stream->unit);
709 if(sc != noErr){
710 WARN("Unit failed to start: %x\n", (int)sc);
711 params->result = osstatus_to_hresult(sc);
712 goto end;
715 size = stream->bufsize_frames * stream->fmt->nBlockAlign;
716 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer, zero_bits(),
717 &size, MEM_COMMIT, PAGE_READWRITE)){
718 params->result = E_OUTOFMEMORY;
719 goto end;
721 silence_buffer(stream, stream->local_buffer, stream->bufsize_frames);
723 if(stream->flow == eCapture){
724 stream->cap_bufsize_frames = muldiv(params->duration, stream->dev_desc.mSampleRate, 10000000);
725 stream->cap_buffer = malloc(stream->cap_bufsize_frames * stream->fmt->nBlockAlign);
727 params->result = S_OK;
729 end:
730 if(FAILED(params->result)){
731 if(stream->converter) AudioConverterDispose(stream->converter);
732 if(stream->unit) AudioComponentInstanceDispose(stream->unit);
733 free(stream->fmt);
734 free(stream);
735 } else
736 *params->stream = (stream_handle)(UINT_PTR)stream;
738 return STATUS_SUCCESS;
741 static NTSTATUS unix_release_stream( void *args )
743 struct release_stream_params *params = args;
744 struct coreaudio_stream *stream = handle_get_stream(params->stream);
745 SIZE_T size;
747 if(stream->unit){
748 AudioOutputUnitStop(stream->unit);
749 AudioComponentInstanceDispose(stream->unit);
752 if(stream->converter) AudioConverterDispose(stream->converter);
753 free(stream->resamp_buffer);
754 free(stream->wrap_buffer);
755 free(stream->cap_buffer);
756 if(stream->local_buffer){
757 size = 0;
758 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->local_buffer,
759 &size, MEM_RELEASE);
761 if(stream->tmp_buffer){
762 size = 0;
763 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer,
764 &size, MEM_RELEASE);
766 free(stream->fmt);
767 free(stream);
768 params->result = S_OK;
769 return STATUS_SUCCESS;
772 static UINT ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
774 int i;
775 UINT mask = 0;
777 for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
778 switch (layout->mChannelDescriptions[i].mChannelLabel) {
779 default: FIXME("Unhandled channel 0x%x\n",
780 (unsigned int)layout->mChannelDescriptions[i].mChannelLabel); break;
781 case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
782 case kAudioChannelLabel_Mono:
783 case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
784 case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
785 case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
786 case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
787 case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
788 case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
789 case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
790 case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
791 case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
792 case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
793 case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
794 case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
795 case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
796 case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
797 case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
798 case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
799 case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
803 return mask;
806 /* For most hardware on Windows, users must choose a configuration with an even
807 * number of channels (stereo, quad, 5.1, 7.1). Users can then disable
808 * channels, but those channels are still reported to applications from
809 * GetMixFormat! Some applications behave badly if given an odd number of
810 * channels (e.g. 2.1). Here, we find the nearest configuration that Windows
811 * would report for a given channel layout. */
812 static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
814 UINT ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
816 TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
818 if (ca_layout->mNumberChannelDescriptions == 1)
820 fmt->Format.nChannels = 1;
821 fmt->dwChannelMask = ca_mask;
822 return;
825 /* compare against known configurations and find smallest configuration
826 * which is a superset of the given speakers */
828 if (ca_layout->mNumberChannelDescriptions <= 2 &&
829 (ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
831 fmt->Format.nChannels = 2;
832 fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
833 return;
836 if (ca_layout->mNumberChannelDescriptions <= 4 &&
837 (ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
839 fmt->Format.nChannels = 4;
840 fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
841 return;
844 if (ca_layout->mNumberChannelDescriptions <= 4 &&
845 (ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
847 fmt->Format.nChannels = 4;
848 fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
849 return;
852 if (ca_layout->mNumberChannelDescriptions <= 6 &&
853 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
855 fmt->Format.nChannels = 6;
856 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
857 return;
860 if (ca_layout->mNumberChannelDescriptions <= 6 &&
861 (ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
863 fmt->Format.nChannels = 6;
864 fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
865 return;
868 if (ca_layout->mNumberChannelDescriptions <= 8 &&
869 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
871 fmt->Format.nChannels = 8;
872 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
873 return;
876 if (ca_layout->mNumberChannelDescriptions <= 8 &&
877 (ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
879 fmt->Format.nChannels = 8;
880 fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
881 return;
884 /* oddball format, report truthfully */
885 fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
886 fmt->dwChannelMask = ca_mask;
889 static DWORD get_channel_mask(unsigned int channels)
891 switch(channels){
892 case 0:
893 return 0;
894 case 1:
895 return KSAUDIO_SPEAKER_MONO;
896 case 2:
897 return KSAUDIO_SPEAKER_STEREO;
898 case 3:
899 return KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY;
900 case 4:
901 return KSAUDIO_SPEAKER_QUAD; /* not _SURROUND */
902 case 5:
903 return KSAUDIO_SPEAKER_QUAD | SPEAKER_LOW_FREQUENCY;
904 case 6:
905 return KSAUDIO_SPEAKER_5POINT1; /* not 5POINT1_SURROUND */
906 case 7:
907 return KSAUDIO_SPEAKER_5POINT1 | SPEAKER_BACK_CENTER;
908 case 8:
909 return KSAUDIO_SPEAKER_7POINT1_SURROUND; /* Vista deprecates 7POINT1 */
911 FIXME("Unknown speaker configuration: %u\n", channels);
912 return 0;
915 static NTSTATUS unix_get_mix_format(void *args)
917 struct get_mix_format_params *params = args;
918 AudioObjectPropertyAddress addr;
919 AudioChannelLayout *layout;
920 AudioBufferList *buffers;
921 Float64 rate;
922 UInt32 size;
923 OSStatus sc;
924 int i;
925 const AudioDeviceID dev_id = dev_id_from_device(params->device);
927 params->fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
929 addr.mScope = get_scope(params->flow);
930 addr.mElement = 0;
931 addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
933 sc = AudioObjectGetPropertyDataSize(dev_id, &addr, 0, NULL, &size);
934 if(sc == noErr){
935 layout = malloc(size);
936 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, layout);
937 if(sc == noErr){
938 TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
939 (unsigned int)layout->mChannelLayoutTag, (unsigned int)layout->mChannelBitmap,
940 (unsigned int)layout->mNumberChannelDescriptions);
942 if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
943 convert_channel_layout(layout, params->fmt);
944 }else{
945 WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n",
946 (unsigned int)layout->mChannelLayoutTag);
947 params->fmt->Format.nChannels = 0;
949 }else{
950 TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
951 params->fmt->Format.nChannels = 0;
954 free(layout);
955 }else{
956 TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
957 params->fmt->Format.nChannels = 0;
960 if(params->fmt->Format.nChannels == 0){
961 addr.mScope = get_scope(params->flow);
962 addr.mElement = 0;
963 addr.mSelector = kAudioDevicePropertyStreamConfiguration;
965 sc = AudioObjectGetPropertyDataSize(dev_id, &addr, 0, NULL, &size);
966 if(sc != noErr){
967 WARN("Unable to get size for _StreamConfiguration property: %x\n", (int)sc);
968 params->result = osstatus_to_hresult(sc);
969 return STATUS_SUCCESS;
972 buffers = malloc(size);
973 if(!buffers){
974 params->result = E_OUTOFMEMORY;
975 return STATUS_SUCCESS;
978 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, buffers);
979 if(sc != noErr){
980 free(buffers);
981 WARN("Unable to get _StreamConfiguration property: %x\n", (int)sc);
982 params->result = osstatus_to_hresult(sc);
983 return STATUS_SUCCESS;
986 for(i = 0; i < buffers->mNumberBuffers; ++i)
987 params->fmt->Format.nChannels += buffers->mBuffers[i].mNumberChannels;
989 free(buffers);
991 params->fmt->dwChannelMask = get_channel_mask(params->fmt->Format.nChannels);
994 addr.mSelector = kAudioDevicePropertyNominalSampleRate;
995 size = sizeof(Float64);
996 sc = AudioObjectGetPropertyData(dev_id, &addr, 0, NULL, &size, &rate);
997 if(sc != noErr){
998 WARN("Unable to get _NominalSampleRate property: %x\n", (int)sc);
999 params->result = osstatus_to_hresult(sc);
1000 return STATUS_SUCCESS;
1002 params->fmt->Format.nSamplesPerSec = rate;
1004 params->fmt->Format.wBitsPerSample = 32;
1005 params->fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1007 params->fmt->Format.nBlockAlign = (params->fmt->Format.wBitsPerSample *
1008 params->fmt->Format.nChannels) / 8;
1009 params->fmt->Format.nAvgBytesPerSec = params->fmt->Format.nSamplesPerSec *
1010 params->fmt->Format.nBlockAlign;
1012 params->fmt->Samples.wValidBitsPerSample = params->fmt->Format.wBitsPerSample;
1013 params->fmt->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1014 params->result = S_OK;
1015 return STATUS_SUCCESS;
1018 static NTSTATUS unix_is_format_supported(void *args)
1020 struct is_format_supported_params *params = args;
1021 const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)params->fmt_in;
1022 AudioStreamBasicDescription dev_desc;
1023 AudioConverterRef converter;
1024 AudioComponentInstance unit;
1025 const AudioDeviceID dev_id = dev_id_from_device(params->device);
1027 params->result = S_OK;
1029 if(!params->fmt_in || (params->share == AUDCLNT_SHAREMODE_SHARED && !params->fmt_out))
1030 params->result = E_POINTER;
1031 else if(params->share != AUDCLNT_SHAREMODE_SHARED && params->share != AUDCLNT_SHAREMODE_EXCLUSIVE)
1032 params->result = E_INVALIDARG;
1033 else if(params->fmt_in->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
1034 if(params->fmt_in->cbSize < sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX))
1035 params->result = E_INVALIDARG;
1036 else if(params->fmt_in->nAvgBytesPerSec == 0 || params->fmt_in->nBlockAlign == 0 ||
1037 fmtex->Samples.wValidBitsPerSample > params->fmt_in->wBitsPerSample)
1038 params->result = E_INVALIDARG;
1039 else if(fmtex->Samples.wValidBitsPerSample < params->fmt_in->wBitsPerSample)
1040 goto unsupported;
1041 else if(params->share == AUDCLNT_SHAREMODE_EXCLUSIVE &&
1042 (fmtex->dwChannelMask == 0 || fmtex->dwChannelMask & SPEAKER_RESERVED))
1043 goto unsupported;
1045 if(FAILED(params->result)) return STATUS_SUCCESS;
1047 if(params->fmt_in->nBlockAlign != params->fmt_in->nChannels * params->fmt_in->wBitsPerSample / 8 ||
1048 params->fmt_in->nAvgBytesPerSec != params->fmt_in->nBlockAlign * params->fmt_in->nSamplesPerSec)
1049 goto unsupported;
1051 if(params->fmt_in->nChannels == 0){
1052 params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
1053 return STATUS_SUCCESS;
1055 unit = get_audiounit(params->flow, dev_id);
1057 converter = NULL;
1058 params->result = ca_setup_audiounit(params->flow, unit, params->fmt_in, &dev_desc, &converter);
1059 AudioComponentInstanceDispose(unit);
1060 if(FAILED(params->result)) goto unsupported;
1061 if(converter) AudioConverterDispose(converter);
1063 params->result = S_OK;
1064 return STATUS_SUCCESS;
1066 unsupported:
1067 if(params->fmt_out){
1068 struct get_mix_format_params get_mix_params =
1070 .device = params->device,
1071 .flow = params->flow,
1072 .fmt = params->fmt_out,
1075 unix_get_mix_format(&get_mix_params);
1076 params->result = get_mix_params.result;
1077 if(SUCCEEDED(params->result)) params->result = S_FALSE;
1079 else params->result = AUDCLNT_E_UNSUPPORTED_FORMAT;
1080 return STATUS_SUCCESS;
1083 static UINT buf_ptr_diff(UINT left, UINT right, UINT bufsize)
1085 if(left <= right)
1086 return right - left;
1087 return bufsize - (left - right);
1090 /* place data from cap_buffer into provided AudioBufferList */
1091 static OSStatus feed_cb(AudioConverterRef converter, UInt32 *nframes, AudioBufferList *data,
1092 AudioStreamPacketDescription **packets, void *user)
1094 struct coreaudio_stream *stream = user;
1096 *nframes = min(*nframes, stream->cap_held_frames);
1097 if(!*nframes){
1098 data->mBuffers[0].mData = NULL;
1099 data->mBuffers[0].mDataByteSize = 0;
1100 data->mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1101 return noErr;
1104 data->mBuffers[0].mDataByteSize = *nframes * stream->fmt->nBlockAlign;
1105 data->mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1107 if(stream->cap_offs_frames + *nframes > stream->cap_bufsize_frames){
1108 UINT32 chunk_frames = stream->cap_bufsize_frames - stream->cap_offs_frames;
1110 if(stream->wrap_bufsize_frames < *nframes){
1111 free(stream->wrap_buffer);
1112 stream->wrap_buffer = malloc(data->mBuffers[0].mDataByteSize);
1113 stream->wrap_bufsize_frames = *nframes;
1116 memcpy(stream->wrap_buffer, stream->cap_buffer + stream->cap_offs_frames * stream->fmt->nBlockAlign,
1117 chunk_frames * stream->fmt->nBlockAlign);
1118 memcpy(stream->wrap_buffer + chunk_frames * stream->fmt->nBlockAlign, stream->cap_buffer,
1119 (*nframes - chunk_frames) * stream->fmt->nBlockAlign);
1121 data->mBuffers[0].mData = stream->wrap_buffer;
1122 }else
1123 data->mBuffers[0].mData = stream->cap_buffer + stream->cap_offs_frames * stream->fmt->nBlockAlign;
1125 stream->cap_offs_frames += *nframes;
1126 stream->cap_offs_frames %= stream->cap_bufsize_frames;
1127 stream->cap_held_frames -= *nframes;
1129 if(packets)
1130 *packets = NULL;
1132 return noErr;
1135 static void capture_resample(struct coreaudio_stream *stream)
1137 UINT32 resamp_period_frames = muldiv(stream->period_frames, stream->dev_desc.mSampleRate,
1138 stream->fmt->nSamplesPerSec);
1139 OSStatus sc;
1141 /* the resampling process often needs more source frames than we'd
1142 * guess from a straight conversion using the sample rate ratio. so
1143 * only convert if we have extra source data. */
1144 while(stream->cap_held_frames > resamp_period_frames * 2){
1145 AudioBufferList converted_list;
1146 UInt32 wanted_frames = stream->period_frames;
1148 converted_list.mNumberBuffers = 1;
1149 converted_list.mBuffers[0].mNumberChannels = stream->fmt->nChannels;
1150 converted_list.mBuffers[0].mDataByteSize = wanted_frames * stream->fmt->nBlockAlign;
1152 if(stream->resamp_bufsize_frames < wanted_frames){
1153 free(stream->resamp_buffer);
1154 stream->resamp_buffer = malloc(converted_list.mBuffers[0].mDataByteSize);
1155 stream->resamp_bufsize_frames = wanted_frames;
1158 converted_list.mBuffers[0].mData = stream->resamp_buffer;
1160 sc = AudioConverterFillComplexBuffer(stream->converter, feed_cb,
1161 stream, &wanted_frames, &converted_list, NULL);
1162 if(sc != noErr){
1163 WARN("AudioConverterFillComplexBuffer failed: %x\n", (int)sc);
1164 break;
1167 ca_wrap_buffer(stream->local_buffer,
1168 stream->wri_offs_frames * stream->fmt->nBlockAlign,
1169 stream->bufsize_frames * stream->fmt->nBlockAlign,
1170 stream->resamp_buffer, wanted_frames * stream->fmt->nBlockAlign);
1172 stream->wri_offs_frames += wanted_frames;
1173 stream->wri_offs_frames %= stream->bufsize_frames;
1174 if(stream->held_frames + wanted_frames > stream->bufsize_frames){
1175 stream->lcl_offs_frames += buf_ptr_diff(stream->lcl_offs_frames, stream->wri_offs_frames,
1176 stream->bufsize_frames);
1177 stream->held_frames = stream->bufsize_frames;
1178 }else
1179 stream->held_frames += wanted_frames;
1183 static NTSTATUS unix_get_buffer_size(void *args)
1185 struct get_buffer_size_params *params = args;
1186 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1188 OSSpinLockLock(&stream->lock);
1189 *params->frames = stream->bufsize_frames;
1190 OSSpinLockUnlock(&stream->lock);
1191 params->result = S_OK;
1192 return STATUS_SUCCESS;
1195 static HRESULT ca_get_max_stream_latency(struct coreaudio_stream *stream, UInt32 *max)
1197 AudioObjectPropertyAddress addr;
1198 AudioStreamID *ids;
1199 UInt32 size;
1200 OSStatus sc;
1201 int nstreams, i;
1203 addr.mScope = get_scope(stream->flow);
1204 addr.mElement = 0;
1205 addr.mSelector = kAudioDevicePropertyStreams;
1207 sc = AudioObjectGetPropertyDataSize(stream->dev_id, &addr, 0, NULL, &size);
1208 if(sc != noErr){
1209 WARN("Unable to get size for _Streams property: %x\n", (int)sc);
1210 return osstatus_to_hresult(sc);
1213 ids = malloc(size);
1214 if(!ids)
1215 return E_OUTOFMEMORY;
1217 sc = AudioObjectGetPropertyData(stream->dev_id, &addr, 0, NULL, &size, ids);
1218 if(sc != noErr){
1219 WARN("Unable to get _Streams property: %x\n", (int)sc);
1220 free(ids);
1221 return osstatus_to_hresult(sc);
1224 nstreams = size / sizeof(AudioStreamID);
1225 *max = 0;
1227 addr.mSelector = kAudioStreamPropertyLatency;
1228 for(i = 0; i < nstreams; ++i){
1229 UInt32 latency;
1231 size = sizeof(latency);
1232 sc = AudioObjectGetPropertyData(ids[i], &addr, 0, NULL, &size, &latency);
1233 if(sc != noErr){
1234 WARN("Unable to get _Latency property: %x\n", (int)sc);
1235 continue;
1238 if(latency > *max)
1239 *max = latency;
1242 free(ids);
1244 return S_OK;
1247 static NTSTATUS unix_get_latency(void *args)
1249 struct get_latency_params *params = args;
1250 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1251 UInt32 latency, stream_latency, size;
1252 AudioObjectPropertyAddress addr;
1253 OSStatus sc;
1255 OSSpinLockLock(&stream->lock);
1257 addr.mScope = get_scope(stream->flow);
1258 addr.mSelector = kAudioDevicePropertyLatency;
1259 addr.mElement = 0;
1261 size = sizeof(latency);
1262 sc = AudioObjectGetPropertyData(stream->dev_id, &addr, 0, NULL, &size, &latency);
1263 if(sc != noErr){
1264 WARN("Couldn't get _Latency property: %x\n", (int)sc);
1265 OSSpinLockUnlock(&stream->lock);
1266 params->result = osstatus_to_hresult(sc);
1267 return STATUS_SUCCESS;
1270 params->result = ca_get_max_stream_latency(stream, &stream_latency);
1271 if(FAILED(params->result)){
1272 OSSpinLockUnlock(&stream->lock);
1273 return STATUS_SUCCESS;
1276 latency += stream_latency;
1277 /* pretend we process audio in Period chunks, so max latency includes
1278 * the period time */
1279 *params->latency = muldiv(latency, 10000000, stream->fmt->nSamplesPerSec) + stream->period;
1281 OSSpinLockUnlock(&stream->lock);
1282 params->result = S_OK;
1283 return STATUS_SUCCESS;
1286 static UINT32 get_current_padding_nolock(struct coreaudio_stream *stream)
1288 if(stream->flow == eCapture) capture_resample(stream);
1289 return stream->held_frames;
1292 static NTSTATUS unix_get_current_padding(void *args)
1294 struct get_current_padding_params *params = args;
1295 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1297 OSSpinLockLock(&stream->lock);
1298 *params->padding = get_current_padding_nolock(stream);
1299 OSSpinLockUnlock(&stream->lock);
1300 params->result = S_OK;
1301 return STATUS_SUCCESS;
1304 static NTSTATUS unix_start(void *args)
1306 struct start_params *params = args;
1307 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1309 OSSpinLockLock(&stream->lock);
1311 if(stream->playing)
1312 params->result = AUDCLNT_E_NOT_STOPPED;
1313 else{
1314 stream->playing = TRUE;
1315 params->result = S_OK;
1318 OSSpinLockUnlock(&stream->lock);
1320 return STATUS_SUCCESS;
1323 static NTSTATUS unix_stop(void *args)
1325 struct stop_params *params = args;
1326 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1328 OSSpinLockLock(&stream->lock);
1330 if(!stream->playing)
1331 params->result = S_FALSE;
1332 else{
1333 stream->playing = FALSE;
1334 params->result = S_OK;
1337 OSSpinLockUnlock(&stream->lock);
1339 return STATUS_SUCCESS;
1342 static NTSTATUS unix_reset(void *args)
1344 struct reset_params *params = args;
1345 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1347 OSSpinLockLock(&stream->lock);
1349 if(stream->playing)
1350 params->result = AUDCLNT_E_NOT_STOPPED;
1351 else if(stream->getbuf_last)
1352 params->result = AUDCLNT_E_BUFFER_OPERATION_PENDING;
1353 else{
1354 if(stream->flow == eRender)
1355 stream->written_frames = 0;
1356 else
1357 stream->written_frames += stream->held_frames;
1358 stream->held_frames = 0;
1359 stream->lcl_offs_frames = 0;
1360 stream->wri_offs_frames = 0;
1361 stream->cap_offs_frames = 0;
1362 stream->cap_held_frames = 0;
1363 params->result = S_OK;
1366 OSSpinLockUnlock(&stream->lock);
1367 return STATUS_SUCCESS;
1370 static NTSTATUS unix_get_render_buffer(void *args)
1372 struct get_render_buffer_params *params = args;
1373 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1374 SIZE_T size;
1375 UINT32 pad;
1377 OSSpinLockLock(&stream->lock);
1379 pad = get_current_padding_nolock(stream);
1381 if(stream->getbuf_last){
1382 params->result = AUDCLNT_E_OUT_OF_ORDER;
1383 goto end;
1385 if(!params->frames){
1386 params->result = S_OK;
1387 goto end;
1389 if(pad + params->frames > stream->bufsize_frames){
1390 params->result = AUDCLNT_E_BUFFER_TOO_LARGE;
1391 goto end;
1394 if(stream->wri_offs_frames + params->frames > stream->bufsize_frames){
1395 if(stream->tmp_buffer_frames < params->frames){
1396 if(stream->tmp_buffer){
1397 size = 0;
1398 NtFreeVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer,
1399 &size, MEM_RELEASE);
1400 stream->tmp_buffer = NULL;
1402 size = params->frames * stream->fmt->nBlockAlign;
1403 if(NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
1404 &size, MEM_COMMIT, PAGE_READWRITE)){
1405 stream->tmp_buffer_frames = 0;
1406 params->result = E_OUTOFMEMORY;
1407 goto end;
1409 stream->tmp_buffer_frames = params->frames;
1411 *params->data = stream->tmp_buffer;
1412 stream->getbuf_last = -params->frames;
1413 }else{
1414 *params->data = stream->local_buffer + stream->wri_offs_frames * stream->fmt->nBlockAlign;
1415 stream->getbuf_last = params->frames;
1418 silence_buffer(stream, *params->data, params->frames);
1419 params->result = S_OK;
1421 end:
1422 OSSpinLockUnlock(&stream->lock);
1424 return STATUS_SUCCESS;
1427 static NTSTATUS unix_release_render_buffer(void *args)
1429 struct release_render_buffer_params *params = args;
1430 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1431 BYTE *buffer;
1433 OSSpinLockLock(&stream->lock);
1435 if(!params->written_frames){
1436 stream->getbuf_last = 0;
1437 params->result = S_OK;
1438 }else if(!stream->getbuf_last)
1439 params->result = AUDCLNT_E_OUT_OF_ORDER;
1440 else if(params->written_frames > (stream->getbuf_last >= 0 ? stream->getbuf_last : -stream->getbuf_last))
1441 params->result = AUDCLNT_E_INVALID_SIZE;
1442 else{
1443 if(stream->getbuf_last >= 0)
1444 buffer = stream->local_buffer + stream->wri_offs_frames * stream->fmt->nBlockAlign;
1445 else
1446 buffer = stream->tmp_buffer;
1448 if(params->flags & AUDCLNT_BUFFERFLAGS_SILENT)
1449 silence_buffer(stream, buffer, params->written_frames);
1451 if(stream->getbuf_last < 0)
1452 ca_wrap_buffer(stream->local_buffer,
1453 stream->wri_offs_frames * stream->fmt->nBlockAlign,
1454 stream->bufsize_frames * stream->fmt->nBlockAlign,
1455 buffer, params->written_frames * stream->fmt->nBlockAlign);
1457 stream->wri_offs_frames += params->written_frames;
1458 stream->wri_offs_frames %= stream->bufsize_frames;
1459 stream->held_frames += params->written_frames;
1460 stream->written_frames += params->written_frames;
1461 stream->getbuf_last = 0;
1463 params->result = S_OK;
1466 OSSpinLockUnlock(&stream->lock);
1468 return STATUS_SUCCESS;
1471 static NTSTATUS unix_get_capture_buffer(void *args)
1473 struct get_capture_buffer_params *params = args;
1474 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1475 UINT32 chunk_bytes, chunk_frames;
1476 LARGE_INTEGER stamp, freq;
1477 SIZE_T size;
1479 OSSpinLockLock(&stream->lock);
1481 if(stream->getbuf_last){
1482 params->result = AUDCLNT_E_OUT_OF_ORDER;
1483 goto end;
1486 capture_resample(stream);
1488 *params->frames = 0;
1490 if(stream->held_frames < stream->period_frames){
1491 params->result = AUDCLNT_S_BUFFER_EMPTY;
1492 goto end;
1495 *params->flags = 0;
1496 chunk_frames = stream->bufsize_frames - stream->lcl_offs_frames;
1497 if(chunk_frames < stream->period_frames){
1498 chunk_bytes = chunk_frames * stream->fmt->nBlockAlign;
1499 if(!stream->tmp_buffer){
1500 size = stream->period_frames * stream->fmt->nBlockAlign;
1501 NtAllocateVirtualMemory(GetCurrentProcess(), (void **)&stream->tmp_buffer, zero_bits(),
1502 &size, MEM_COMMIT, PAGE_READWRITE);
1504 *params->data = stream->tmp_buffer;
1505 memcpy(*params->data, stream->local_buffer + stream->lcl_offs_frames * stream->fmt->nBlockAlign,
1506 chunk_bytes);
1507 memcpy(*params->data + chunk_bytes, stream->local_buffer,
1508 stream->period_frames * stream->fmt->nBlockAlign - chunk_bytes);
1509 }else
1510 *params->data = stream->local_buffer + stream->lcl_offs_frames * stream->fmt->nBlockAlign;
1512 stream->getbuf_last = *params->frames = stream->period_frames;
1514 if(params->devpos)
1515 *params->devpos = stream->written_frames;
1516 if(params->qpcpos){ /* fixme: qpc of recording time */
1517 NtQueryPerformanceCounter(&stamp, &freq);
1518 *params->qpcpos = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1520 params->result = S_OK;
1522 end:
1523 OSSpinLockUnlock(&stream->lock);
1524 return STATUS_SUCCESS;
1527 static NTSTATUS unix_release_capture_buffer(void *args)
1529 struct release_capture_buffer_params *params = args;
1530 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1532 OSSpinLockLock(&stream->lock);
1534 if(!params->done){
1535 stream->getbuf_last = 0;
1536 params->result = S_OK;
1537 }else if(!stream->getbuf_last)
1538 params->result = AUDCLNT_E_OUT_OF_ORDER;
1539 else if(stream->getbuf_last != params->done)
1540 params->result = AUDCLNT_E_INVALID_SIZE;
1541 else{
1542 stream->written_frames += params->done;
1543 stream->held_frames -= params->done;
1544 stream->lcl_offs_frames += params->done;
1545 stream->lcl_offs_frames %= stream->bufsize_frames;
1546 stream->getbuf_last = 0;
1547 params->result = S_OK;
1550 OSSpinLockUnlock(&stream->lock);
1552 return STATUS_SUCCESS;
1555 static NTSTATUS unix_get_next_packet_size(void *args)
1557 struct get_next_packet_size_params *params = args;
1558 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1560 OSSpinLockLock(&stream->lock);
1562 capture_resample(stream);
1564 if(stream->held_frames >= stream->period_frames)
1565 *params->frames = stream->period_frames;
1566 else
1567 *params->frames = 0;
1569 OSSpinLockUnlock(&stream->lock);
1571 params->result = S_OK;
1572 return STATUS_SUCCESS;
1575 static NTSTATUS unix_get_position(void *args)
1577 struct get_position_params *params = args;
1578 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1579 LARGE_INTEGER stamp, freq;
1581 OSSpinLockLock(&stream->lock);
1583 *params->pos = stream->written_frames - stream->held_frames;
1585 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1586 *params->pos *= stream->fmt->nBlockAlign;
1588 if(params->qpctime){
1589 NtQueryPerformanceCounter(&stamp, &freq);
1590 *params->qpctime = (stamp.QuadPart * (INT64)10000000) / freq.QuadPart;
1593 OSSpinLockUnlock(&stream->lock);
1595 params->result = S_OK;
1596 return STATUS_SUCCESS;
1599 static NTSTATUS unix_get_frequency(void *args)
1601 struct get_frequency_params *params = args;
1602 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1604 if(stream->share == AUDCLNT_SHAREMODE_SHARED)
1605 *params->freq = (UINT64)stream->fmt->nSamplesPerSec * stream->fmt->nBlockAlign;
1606 else
1607 *params->freq = stream->fmt->nSamplesPerSec;
1609 params->result = S_OK;
1610 return STATUS_SUCCESS;
1613 static NTSTATUS unix_is_started(void *args)
1615 struct is_started_params *params = args;
1616 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1618 if(stream->playing)
1619 params->result = S_OK;
1620 else
1621 params->result = S_FALSE;
1623 return STATUS_SUCCESS;
1626 static NTSTATUS unix_set_volumes(void *args)
1628 struct set_volumes_params *params = args;
1629 struct coreaudio_stream *stream = handle_get_stream(params->stream);
1630 Float32 level = 1.0, tmp;
1631 OSStatus sc;
1632 UINT32 i;
1634 if(params->channel >= stream->fmt->nChannels || params->channel < -1){
1635 ERR("Incorrect channel %d\n", params->channel);
1636 return STATUS_SUCCESS;
1639 if(params->channel == -1){
1640 for(i = 0; i < stream->fmt->nChannels; ++i){
1641 tmp = params->master_volume * params->volumes[i] * params->session_volumes[i];
1642 level = tmp < level ? tmp : level;
1644 }else
1645 level = params->master_volume * params->volumes[params->channel] *
1646 params->session_volumes[params->channel];
1648 sc = AudioUnitSetParameter(stream->unit, kHALOutputParam_Volume,
1649 kAudioUnitScope_Global, 0, level, 0);
1650 if(sc != noErr)
1651 WARN("Couldn't set volume: %x\n", (int)sc);
1653 return STATUS_SUCCESS;
1656 unixlib_entry_t __wine_unix_call_funcs[] =
1658 unix_not_implemented,
1659 unix_not_implemented,
1660 unix_not_implemented,
1661 unix_get_endpoint_ids,
1662 unix_create_stream,
1663 unix_release_stream,
1664 unix_start,
1665 unix_stop,
1666 unix_reset,
1667 unix_not_implemented,
1668 unix_get_render_buffer,
1669 unix_release_render_buffer,
1670 unix_get_capture_buffer,
1671 unix_release_capture_buffer,
1672 unix_is_format_supported,
1673 unix_get_mix_format,
1674 unix_not_implemented,
1675 unix_get_buffer_size,
1676 unix_get_latency,
1677 unix_get_current_padding,
1678 unix_get_next_packet_size,
1679 unix_get_frequency,
1680 unix_get_position,
1681 unix_set_volumes,
1682 unix_not_implemented,
1683 unix_not_implemented,
1684 unix_is_started,
1685 unix_not_implemented,
1686 unix_midi_init,
1687 unix_midi_release,
1688 unix_midi_out_message,
1689 unix_midi_in_message,
1690 unix_midi_notify_wait,
1691 unix_not_implemented,
1694 #ifdef _WIN64
1696 typedef UINT PTR32;
1698 static NTSTATUS unix_wow64_get_endpoint_ids(void *args)
1700 struct
1702 EDataFlow flow;
1703 PTR32 endpoints;
1704 unsigned int size;
1705 HRESULT result;
1706 unsigned int num;
1707 unsigned int default_idx;
1708 } *params32 = args;
1709 struct get_endpoint_ids_params params =
1711 .flow = params32->flow,
1712 .endpoints = ULongToPtr(params32->endpoints),
1713 .size = params32->size
1715 unix_get_endpoint_ids(&params);
1716 params32->size = params.size;
1717 params32->result = params.result;
1718 params32->num = params.num;
1719 params32->default_idx = params.default_idx;
1720 return STATUS_SUCCESS;
1723 static NTSTATUS unix_wow64_create_stream(void *args)
1725 struct
1727 PTR32 name;
1728 PTR32 device;
1729 EDataFlow flow;
1730 AUDCLNT_SHAREMODE share;
1731 DWORD flags;
1732 REFERENCE_TIME duration;
1733 REFERENCE_TIME period;
1734 PTR32 fmt;
1735 HRESULT result;
1736 PTR32 channel_count;
1737 PTR32 stream;
1738 } *params32 = args;
1739 struct create_stream_params params =
1741 .name = ULongToPtr(params32->name),
1742 .device = ULongToPtr(params32->device),
1743 .flow = params32->flow,
1744 .share = params32->share,
1745 .flags = params32->flags,
1746 .duration = params32->duration,
1747 .period = params32->period,
1748 .fmt = ULongToPtr(params32->fmt),
1749 .channel_count = ULongToPtr(params32->channel_count),
1750 .stream = ULongToPtr(params32->stream)
1752 unix_create_stream(&params);
1753 params32->result = params.result;
1754 return STATUS_SUCCESS;
1757 static NTSTATUS unix_wow64_release_stream(void *args)
1759 struct
1761 stream_handle stream;
1762 PTR32 timer_thread;
1763 HRESULT result;
1764 } *params32 = args;
1765 struct release_stream_params params =
1767 .stream = params32->stream,
1768 .timer_thread = ULongToHandle(params32->timer_thread)
1770 unix_release_stream(&params);
1771 params32->result = params.result;
1772 return STATUS_SUCCESS;
1775 static NTSTATUS unix_wow64_get_render_buffer(void *args)
1777 struct
1779 stream_handle stream;
1780 UINT32 frames;
1781 HRESULT result;
1782 PTR32 data;
1783 } *params32 = args;
1784 BYTE *data = NULL;
1785 struct get_render_buffer_params params =
1787 .stream = params32->stream,
1788 .frames = params32->frames,
1789 .data = &data
1791 unix_get_render_buffer(&params);
1792 params32->result = params.result;
1793 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1794 return STATUS_SUCCESS;
1797 static NTSTATUS unix_wow64_get_capture_buffer(void *args)
1799 struct
1801 stream_handle stream;
1802 HRESULT result;
1803 PTR32 data;
1804 PTR32 frames;
1805 PTR32 flags;
1806 PTR32 devpos;
1807 PTR32 qpcpos;
1808 } *params32 = args;
1809 BYTE *data = NULL;
1810 struct get_capture_buffer_params params =
1812 .stream = params32->stream,
1813 .data = &data,
1814 .frames = ULongToPtr(params32->frames),
1815 .flags = ULongToPtr(params32->flags),
1816 .devpos = ULongToPtr(params32->devpos),
1817 .qpcpos = ULongToPtr(params32->qpcpos)
1819 unix_get_capture_buffer(&params);
1820 params32->result = params.result;
1821 *(unsigned int *)ULongToPtr(params32->data) = PtrToUlong(data);
1822 return STATUS_SUCCESS;
1825 static NTSTATUS unix_wow64_is_format_supported(void *args)
1827 struct
1829 PTR32 device;
1830 EDataFlow flow;
1831 AUDCLNT_SHAREMODE share;
1832 PTR32 fmt_in;
1833 PTR32 fmt_out;
1834 HRESULT result;
1835 } *params32 = args;
1836 struct is_format_supported_params params =
1838 .device = ULongToPtr(params32->device),
1839 .flow = params32->flow,
1840 .share = params32->share,
1841 .fmt_in = ULongToPtr(params32->fmt_in),
1842 .fmt_out = ULongToPtr(params32->fmt_out)
1844 unix_is_format_supported(&params);
1845 params32->result = params.result;
1846 return STATUS_SUCCESS;
1849 static NTSTATUS unix_wow64_get_mix_format(void *args)
1851 struct
1853 PTR32 device;
1854 EDataFlow flow;
1855 PTR32 fmt;
1856 HRESULT result;
1857 } *params32 = args;
1858 struct get_mix_format_params params =
1860 .device = ULongToPtr(params32->device),
1861 .flow = params32->flow,
1862 .fmt = ULongToPtr(params32->fmt)
1864 unix_get_mix_format(&params);
1865 params32->result = params.result;
1866 return STATUS_SUCCESS;
1869 static NTSTATUS unix_wow64_get_buffer_size(void *args)
1871 struct
1873 stream_handle stream;
1874 HRESULT result;
1875 PTR32 frames;
1876 } *params32 = args;
1877 struct get_buffer_size_params params =
1879 .stream = params32->stream,
1880 .frames = ULongToPtr(params32->frames)
1882 unix_get_buffer_size(&params);
1883 params32->result = params.result;
1884 return STATUS_SUCCESS;
1887 static NTSTATUS unix_wow64_get_latency(void *args)
1889 struct
1891 stream_handle stream;
1892 HRESULT result;
1893 PTR32 latency;
1894 } *params32 = args;
1895 struct get_latency_params params =
1897 .stream = params32->stream,
1898 .latency = ULongToPtr(params32->latency)
1900 unix_get_latency(&params);
1901 params32->result = params.result;
1902 return STATUS_SUCCESS;
1905 static NTSTATUS unix_wow64_get_current_padding(void *args)
1907 struct
1909 stream_handle stream;
1910 HRESULT result;
1911 PTR32 padding;
1912 } *params32 = args;
1913 struct get_current_padding_params params =
1915 .stream = params32->stream,
1916 .padding = ULongToPtr(params32->padding)
1918 unix_get_current_padding(&params);
1919 params32->result = params.result;
1920 return STATUS_SUCCESS;
1923 static NTSTATUS unix_wow64_get_next_packet_size(void *args)
1925 struct
1927 stream_handle stream;
1928 HRESULT result;
1929 PTR32 frames;
1930 } *params32 = args;
1931 struct get_next_packet_size_params params =
1933 .stream = params32->stream,
1934 .frames = ULongToPtr(params32->frames)
1936 unix_get_next_packet_size(&params);
1937 params32->result = params.result;
1938 return STATUS_SUCCESS;
1941 static NTSTATUS unix_wow64_get_position(void *args)
1943 struct
1945 stream_handle stream;
1946 HRESULT result;
1947 PTR32 pos;
1948 PTR32 qpctime;
1949 } *params32 = args;
1950 struct get_position_params params =
1952 .stream = params32->stream,
1953 .pos = ULongToPtr(params32->pos),
1954 .qpctime = ULongToPtr(params32->qpctime)
1956 unix_get_position(&params);
1957 params32->result = params.result;
1958 return STATUS_SUCCESS;
1961 static NTSTATUS unix_wow64_get_frequency(void *args)
1963 struct
1965 stream_handle stream;
1966 HRESULT result;
1967 PTR32 freq;
1968 } *params32 = args;
1969 struct get_frequency_params params =
1971 .stream = params32->stream,
1972 .freq = ULongToPtr(params32->freq)
1974 unix_get_frequency(&params);
1975 params32->result = params.result;
1976 return STATUS_SUCCESS;
1979 static NTSTATUS unix_wow64_set_volumes(void *args)
1981 struct
1983 stream_handle stream;
1984 float master_volume;
1985 PTR32 volumes;
1986 PTR32 session_volumes;
1987 int channel;
1988 } *params32 = args;
1989 struct set_volumes_params params =
1991 .stream = params32->stream,
1992 .master_volume = params32->master_volume,
1993 .volumes = ULongToPtr(params32->volumes),
1994 .session_volumes = ULongToPtr(params32->session_volumes),
1995 .channel = params32->channel
1997 return unix_set_volumes(&params);
2000 unixlib_entry_t __wine_unix_call_wow64_funcs[] =
2002 unix_not_implemented,
2003 unix_not_implemented,
2004 unix_not_implemented,
2005 unix_wow64_get_endpoint_ids,
2006 unix_wow64_create_stream,
2007 unix_wow64_release_stream,
2008 unix_start,
2009 unix_stop,
2010 unix_reset,
2011 unix_not_implemented,
2012 unix_wow64_get_render_buffer,
2013 unix_release_render_buffer,
2014 unix_wow64_get_capture_buffer,
2015 unix_release_capture_buffer,
2016 unix_wow64_is_format_supported,
2017 unix_wow64_get_mix_format,
2018 unix_not_implemented,
2019 unix_wow64_get_buffer_size,
2020 unix_wow64_get_latency,
2021 unix_wow64_get_current_padding,
2022 unix_wow64_get_next_packet_size,
2023 unix_wow64_get_frequency,
2024 unix_wow64_get_position,
2025 unix_wow64_set_volumes,
2026 unix_not_implemented,
2027 unix_not_implemented,
2028 unix_is_started,
2029 unix_not_implemented,
2030 unix_wow64_midi_init,
2031 unix_midi_release,
2032 unix_wow64_midi_out_message,
2033 unix_wow64_midi_in_message,
2034 unix_wow64_midi_notify_wait,
2035 unix_not_implemented,
2038 #endif /* _WIN64 */