make it compile again :)
[wineasio.git] / asio.c
blob478380f828ad6a55e2c88882657398cb37f03901
1 /*
2 * Copyright (C) 2006 Robert Reif
3 * Portions copyright (C) 2007 Ralf Beck
4 * Portions copyright (C) 2007 Johnny Petrantoni
5 * Portions copyright (C) 2007 Stephane Letz
6 * Portions copyright (C) 2008 William Steidtmann
7 * Portions copyright (C) 2010 Peter L Jones
8 * Portions copyright (C) 2010 Torben Hohn
9 * Portions copyright (C) 2010 Nedko Arnaudov
10 * Portions copyright (C) 2010 Joakim Hernberg
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * Todo (maybe):
29 * create a freely distributable asio.h replacement
30 * add ASIOOutputReady() support
33 #include <stdio.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <sys/mman.h>
37 #include <pthread.h>
39 #include "wine/debug.h"
40 #include "objbase.h"
41 #include "mmsystem.h"
42 #include "winreg.h"
43 #include "wine/unicode.h"
45 #include <jack/jack.h>
46 #include <jack/thread.h>
48 #define IEEE754_64FLOAT 1
49 #include "asio.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(asio);
53 #define MAX_ENVIRONMENT_SIZE 6
54 #define ASIO_MAX_NAME_LENGTH 32
55 #define ASIO_MINIMUM_BUFFERSIZE 16
56 #define ASIO_MAXIMUM_BUFFERSIZE 8192
57 #define ASIO_PREFERRED_BUFFERSIZE 1024
59 /* ASIO drivers (breaking the COM specification) use the Microsoft variety of
60 * thiscall calling convention which gcc is unable to produce. These macros
61 * add an extra layer to fixup the registers. Borrowed from config.h and the
62 * wine source code.
65 /* From config.h */
66 #define __ASM_DEFINE_FUNC(name,suffix,code) asm(".text\n\t.align 4\n\t.globl " #name suffix "\n\t.type " #name suffix ",@function\n" #name suffix ":\n\t.cfi_startproc\n\t" code "\n\t.cfi_endproc\n\t.previous");
67 #define __ASM_GLOBAL_FUNC(name,code) __ASM_DEFINE_FUNC(name,"",code)
68 #define __ASM_NAME(name) name
69 #define __ASM_STDCALL(args) ""
71 /* From wine source */
72 #ifdef __i386__ /* thiscall functions are i386-specific */
74 #define THISCALL(func) __thiscall_ ## func
75 #define THISCALL_NAME(func) __ASM_NAME("__thiscall_" #func)
76 #define __thiscall __stdcall
77 #define DEFINE_THISCALL_WRAPPER(func,args) \
78 extern void THISCALL(func)(void); \
79 __ASM_GLOBAL_FUNC(__thiscall_ ## func, \
80 "popl %eax\n\t" \
81 "pushl %ecx\n\t" \
82 "pushl %eax\n\t" \
83 "jmp " __ASM_NAME(#func) __ASM_STDCALL(args) )
84 #else /* __i386__ */
86 #define THISCALL(func) func
87 #define THISCALL_NAME(func) __ASM_NAME(#func)
88 #define __thiscall __cdecl
89 #define DEFINE_THISCALL_WRAPPER(func,args) /* nothing */
91 #endif /* __i386__ */
93 /* Hide ELF symbols for the COM members - No need to to export them */
94 #define HIDDEN __attribute__ ((visibility("hidden")))
96 /*****************************************************************************
97 * IWineAsio interface
100 #define INTERFACE IWineASIO
101 DECLARE_INTERFACE_(IWineASIO,IUnknown)
103 STDMETHOD_(HRESULT, QueryInterface) (THIS_ IID riid, void** ppvObject) PURE;
104 STDMETHOD_(ULONG, AddRef) (THIS) PURE;
105 STDMETHOD_(ULONG, Release) (THIS) PURE;
106 STDMETHOD_(ASIOBool, Init) (THIS_ void *sysRef) PURE;
107 STDMETHOD_(void, GetDriverName) (THIS_ char *name) PURE;
108 STDMETHOD_(LONG, GetDriverVersion) (THIS) PURE;
109 STDMETHOD_(void, GetErrorMessage) (THIS_ char *string) PURE;
110 STDMETHOD_(ASIOError, Start) (THIS) PURE;
111 STDMETHOD_(ASIOError, Stop) (THIS) PURE;
112 STDMETHOD_(ASIOError, GetChannels) (THIS_ LONG *numInputChannels, LONG *numOutputChannels) PURE;
113 STDMETHOD_(ASIOError, GetLatencies) (THIS_ LONG *inputLatency, LONG *outputLatency) PURE;
114 STDMETHOD_(ASIOError, GetBufferSize) (THIS_ LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity) PURE;
115 STDMETHOD_(ASIOError, CanSampleRate) (THIS_ ASIOSampleRate sampleRate) PURE;
116 STDMETHOD_(ASIOError, GetSampleRate) (THIS_ ASIOSampleRate *sampleRate) PURE;
117 STDMETHOD_(ASIOError, SetSampleRate) (THIS_ ASIOSampleRate sampleRate) PURE;
118 STDMETHOD_(ASIOError, GetClockSources) (THIS_ ASIOClockSource *clocks, LONG *numSources) PURE;
119 STDMETHOD_(ASIOError, SetClockSource) (THIS_ LONG index) PURE;
120 STDMETHOD_(ASIOError, GetSamplePosition) (THIS_ ASIOSamples *sPos, ASIOTimeStamp *tStamp) PURE;
121 STDMETHOD_(ASIOError, GetChannelInfo) (THIS_ ASIOChannelInfo *info) PURE;
122 STDMETHOD_(ASIOError, CreateBuffers) (THIS_ ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks) PURE;
123 STDMETHOD_(ASIOError, DisposeBuffers) (THIS) PURE;
124 STDMETHOD_(ASIOError, ControlPanel) (THIS) PURE;
125 STDMETHOD_(ASIOError, Future) (THIS_ LONG selector,void *opt) PURE;
126 STDMETHOD_(ASIOError, OutputReady) (THIS) PURE;
128 #undef INTERFACE
130 typedef struct IWineASIO *LPWINEASIO;
132 typedef struct IOChannel
134 ASIOBool active;
135 jack_default_audio_sample_t *audio_buffer;
136 char port_name[ASIO_MAX_NAME_LENGTH];
137 jack_port_t *port;
138 } IOChannel;
140 typedef struct IWineASIOImpl
142 /* COM stuff */
143 const IWineASIOVtbl *lpVtbl;
144 LONG ref;
146 /* The app's main window handle on windows, 0 on OS/X */
147 HWND sys_ref;
149 /* ASIO stuff */
150 LONG asio_active_inputs;
151 LONG asio_active_outputs;
152 BOOL asio_buffer_index;
153 ASIOCallbacks *asio_callbacks;
154 BOOL asio_can_time_code;
155 LONG asio_current_buffersize;
156 INT asio_driver_state;
157 ASIOSamples asio_sample_position;
158 ASIOSampleRate asio_sample_rate;
159 ASIOTime asio_time;
160 BOOL asio_time_info_mode;
161 ASIOTimeStamp asio_time_stamp;
162 LONG asio_version;
164 /* WineASIO configuration options */
165 LONG wineasio_number_inputs;
166 LONG wineasio_number_outputs;
167 BOOL wineasio_autostart_server;
168 BOOL wineasio_connect_to_hardware;
169 LONG wineasio_fixed_buffersize;
170 LONG wineasio_preferred_buffersize;
172 /* JACK stuff */
173 jack_client_t *jack_client;
174 char jack_client_name[ASIO_MAX_NAME_LENGTH];
175 int jack_num_input_ports;
176 int jack_num_output_ports;
177 const char **jack_input_ports;
178 const char **jack_output_ports;
180 /* process callback buffers */
181 jack_default_audio_sample_t *callback_audio_buffer;
182 IOChannel *input_channel;
183 IOChannel *output_channel;
184 } IWineASIOImpl;
186 enum { Loaded, Initialized, Prepared, Running };
188 /****************************************************************************
189 * Interface Methods
193 * as seen from the WineASIO source
196 HIDDEN HRESULT STDMETHODCALLTYPE QueryInterface(LPWINEASIO iface, REFIID riid, void **ppvObject);
197 HIDDEN ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface);
198 HIDDEN ULONG STDMETHODCALLTYPE Release(LPWINEASIO iface);
199 HIDDEN ASIOBool STDMETHODCALLTYPE Init(LPWINEASIO iface, void *sysRef);
200 HIDDEN void STDMETHODCALLTYPE GetDriverName(LPWINEASIO iface, char *name);
201 HIDDEN LONG STDMETHODCALLTYPE GetDriverVersion(LPWINEASIO iface);
202 HIDDEN void STDMETHODCALLTYPE GetErrorMessage(LPWINEASIO iface, char *string);
203 HIDDEN ASIOError STDMETHODCALLTYPE Start(LPWINEASIO iface);
204 HIDDEN ASIOError STDMETHODCALLTYPE Stop(LPWINEASIO iface);
205 HIDDEN ASIOError STDMETHODCALLTYPE GetChannels (LPWINEASIO iface, LONG *numInputChannels, LONG *numOutputChannels);
206 HIDDEN ASIOError STDMETHODCALLTYPE GetLatencies(LPWINEASIO iface, LONG *inputLatency, LONG *outputLatency);
207 HIDDEN ASIOError STDMETHODCALLTYPE GetBufferSize(LPWINEASIO iface, LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity);
208 HIDDEN ASIOError STDMETHODCALLTYPE CanSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate);
209 HIDDEN ASIOError STDMETHODCALLTYPE GetSampleRate(LPWINEASIO iface, ASIOSampleRate *sampleRate);
210 HIDDEN ASIOError STDMETHODCALLTYPE SetSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate);
211 HIDDEN ASIOError STDMETHODCALLTYPE GetClockSources(LPWINEASIO iface, ASIOClockSource *clocks, LONG *numSources);
212 HIDDEN ASIOError STDMETHODCALLTYPE SetClockSource(LPWINEASIO iface, LONG index);
213 HIDDEN ASIOError STDMETHODCALLTYPE GetSamplePosition(LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp);
214 HIDDEN ASIOError STDMETHODCALLTYPE GetChannelInfo(LPWINEASIO iface, ASIOChannelInfo *info);
215 HIDDEN ASIOError STDMETHODCALLTYPE CreateBuffers(LPWINEASIO iface, ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks);
216 HIDDEN ASIOError STDMETHODCALLTYPE DisposeBuffers(LPWINEASIO iface);
217 HIDDEN ASIOError STDMETHODCALLTYPE ControlPanel(LPWINEASIO iface);
218 HIDDEN ASIOError STDMETHODCALLTYPE Future(LPWINEASIO iface, LONG selector, void *opt);
219 HIDDEN ASIOError STDMETHODCALLTYPE OutputReady(LPWINEASIO iface);
222 * thiscall wrappers for the vtbl (as seen from app side 32bit)
225 HIDDEN void __thiscall_Init(void);
226 HIDDEN void __thiscall_GetDriverName(void);
227 HIDDEN void __thiscall_GetDriverVersion(void);
228 HIDDEN void __thiscall_GetErrorMessage(void);
229 HIDDEN void __thiscall_Start(void);
230 HIDDEN void __thiscall_Stop(void);
231 HIDDEN void __thiscall_GetChannels(void);
232 HIDDEN void __thiscall_GetLatencies(void);
233 HIDDEN void __thiscall_GetBufferSize(void);
234 HIDDEN void __thiscall_CanSampleRate(void);
235 HIDDEN void __thiscall_GetSampleRate(void);
236 HIDDEN void __thiscall_SetSampleRate(void);
237 HIDDEN void __thiscall_GetClockSources(void);
238 HIDDEN void __thiscall_SetClockSource(void);
239 HIDDEN void __thiscall_GetSamplePosition(void);
240 HIDDEN void __thiscall_GetChannelInfo(void);
241 HIDDEN void __thiscall_CreateBuffers(void);
242 HIDDEN void __thiscall_DisposeBuffers(void);
243 HIDDEN void __thiscall_ControlPanel(void);
244 HIDDEN void __thiscall_Future(void);
245 HIDDEN void __thiscall_OutputReady(void);
248 * Jack callbacks
251 static int bufsize_callback (jack_nframes_t nframes, void *arg);
252 static int process_callback (jack_nframes_t nframes, void *arg);
253 static int srate_callback (jack_nframes_t nframes, void *arg);
254 static int xrun_callback(void *arg);
257 * Support functions
260 HRESULT WINAPI WineASIOCreateInstance(REFIID riid, LPVOID *ppobj);
261 static BOOL configure_driver(IWineASIOImpl *This);
263 #ifdef __WINESRC__
264 static DWORD WINAPI jack_thread_creator_helper(LPVOID arg);
265 static int jack_thread_creator(pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg);
266 #endif
268 /* {48D0C522-BFCC-45cc-8B84-17F25F33E6E8} */
269 static GUID const CLSID_WineASIO = {
270 0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } };
272 static const IWineASIOVtbl WineASIO_Vtbl =
274 (void *) QueryInterface,
275 (void *) AddRef,
276 (void *) Release,
278 (void *) THISCALL(Init),
279 (void *) THISCALL(GetDriverName),
280 (void *) THISCALL(GetDriverVersion),
281 (void *) THISCALL(GetErrorMessage),
282 (void *) THISCALL(Start),
283 (void *) THISCALL(Stop),
284 (void *) THISCALL(GetChannels),
285 (void *) THISCALL(GetLatencies),
286 (void *) THISCALL(GetBufferSize),
287 (void *) THISCALL(CanSampleRate),
288 (void *) THISCALL(GetSampleRate),
289 (void *) THISCALL(SetSampleRate),
290 (void *) THISCALL(GetClockSources),
291 (void *) THISCALL(SetClockSource),
292 (void *) THISCALL(GetSamplePosition),
293 (void *) THISCALL(GetChannelInfo),
294 (void *) THISCALL(CreateBuffers),
295 (void *) THISCALL(DisposeBuffers),
296 (void *) THISCALL(ControlPanel),
297 (void *) THISCALL(Future),
298 (void *) THISCALL(OutputReady)
301 #ifdef __WINESRC__
302 /* structure needed to create the JACK callback thread in the wine process context */
303 struct {
304 void *(*jack_callback_thread) (void*);
305 void *arg;
306 pthread_t jack_callback_pthread_id;
307 HANDLE jack_callback_thread_created;
308 } jack_thread_creator_privates;
309 #endif
311 /*****************************************************************************
312 * Interface method definitions
316 HIDDEN HRESULT STDMETHODCALLTYPE QueryInterface(LPWINEASIO iface, REFIID riid, void **ppvObject)
318 IWineASIOImpl *This = (IWineASIOImpl *)iface;
320 TRACE("iface: %p, riid: %s, ppvObject: %p)\n", iface, debugstr_guid(riid), ppvObject);
322 if (ppvObject == NULL)
323 return E_INVALIDARG;
325 if (IsEqualIID(&CLSID_WineASIO, riid))
327 AddRef(iface);
328 *ppvObject = This;
329 return S_OK;
332 return E_NOINTERFACE;
336 * ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface);
337 * Function: Increment the reference count on the object
338 * Returns: Ref count
341 HIDDEN ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface)
343 IWineASIOImpl *This = (IWineASIOImpl *)iface;
344 ULONG ref = InterlockedIncrement(&(This->ref));
346 TRACE("iface: %p, ref count is %d\n", iface, ref);
347 return ref;
351 * ULONG Release (LPWINEASIO iface);
352 * Function: Destroy the interface
353 * Returns: Ref count
354 * Implies: ASIOStop() and ASIODisposeBuffers()
357 HIDDEN ULONG STDMETHODCALLTYPE Release(LPWINEASIO iface)
359 IWineASIOImpl *This = (IWineASIOImpl *)iface;
360 ULONG ref = InterlockedDecrement(&This->ref);
361 int i;
363 TRACE("iface: %p, ref count is %d\n", iface, ref);
365 if (This->asio_driver_state == Running)
366 Stop(iface);
367 if (This->asio_driver_state == Prepared)
368 DisposeBuffers(iface);
370 if (This->asio_driver_state == Initialized)
372 /* just for good measure we deinitialize IOChannel structures and unregister JACK ports */
373 for (i = 0; i < This->wineasio_number_inputs; i++)
375 if(jack_port_unregister (This->jack_client, This->input_channel[i].port))
376 MESSAGE("Error trying to unregister port %s\n", This->input_channel[i].port_name);
377 This->input_channel[i].active = ASIOFalse;
378 This->input_channel[i].port = NULL;
380 for (i = 0; i < This->wineasio_number_outputs; i++)
382 if(jack_port_unregister (This->jack_client, This->output_channel[i].port))
383 MESSAGE("Error trying to unregister port %s\n", This->output_channel[i].port_name);
384 This->output_channel[i].active = ASIOFalse;
385 This->output_channel[i].port = NULL;
387 This->asio_active_inputs = This->asio_active_outputs = 0;
388 TRACE("%i IOChannel structures released\n", This->wineasio_number_inputs + This->wineasio_number_outputs);
390 if (This->jack_output_ports)
391 jack_free (This->jack_output_ports);
392 if (This->jack_input_ports)
393 jack_free (This->jack_input_ports);
395 if (This->jack_client)
396 if (jack_client_close(This->jack_client))
397 MESSAGE("Error trying to close JACK client\n");
399 if (This->input_channel)
400 HeapFree(GetProcessHeap(), 0, This->input_channel);
402 TRACE("WineASIO terminated\n\n");
403 if (ref == 0)
404 HeapFree(GetProcessHeap(), 0, This);
405 return ref;
409 * ASIOBool Init (void *sysRef);
410 * Function: Initialize the driver
411 * Parameters: Pointer to "This"
412 * sysHanle is 0 on OS/X and on windows it contains the applications main window handle
413 * Returns: ASIOFalse on error, and ASIOTrue on success
416 DEFINE_THISCALL_WRAPPER(Init,8)
417 HIDDEN ASIOBool STDMETHODCALLTYPE Init(LPWINEASIO iface, void *sysRef)
419 IWineASIOImpl *This = (IWineASIOImpl *)iface;
420 jack_status_t jack_status;
421 jack_options_t jack_options = JackNullOption;
422 int i;
424 TRACE("iface: %p, sysRef: %p\n", iface, sysRef);
426 This->sys_ref = sysRef;
428 #ifdef __WINESRC__
429 mlockall(MCL_FUTURE);
430 #endif
432 if (!configure_driver(This))
434 WARN("Unable to configure WineASIO\n");
435 return ASIOFalse;
438 if (!This->wineasio_autostart_server)
439 jack_options |= JackNoStartServer;
440 This->jack_client = jack_client_open(This->jack_client_name, jack_options, &jack_status);
442 if (This->jack_client == NULL)
444 WARN("Unable to open a JACK client as: %s\n", This->jack_client_name);
445 return ASIOFalse;
447 TRACE("JACK client opened as: '%s'\n", jack_get_client_name(This->jack_client));
449 if (!(This->asio_sample_rate = jack_get_sample_rate(This->jack_client)))
451 WARN("Unable to get samplerate from JACK\n");
452 return ASIOFalse;
455 if (!(This->asio_current_buffersize = jack_get_buffer_size(This->jack_client)))
457 WARN("Unable to get buffer size from JACK\n");
458 return ASIOFalse;
461 /* Allocate IOChannel structures */
462 This->input_channel = HeapAlloc(GetProcessHeap(), 0, (This->wineasio_number_inputs + This->wineasio_number_outputs) * sizeof(IOChannel));
463 if (!This->input_channel)
465 jack_client_close(This->jack_client);
466 ERR("Unable to allocate IOChannel structures for %i channels\n", This->wineasio_number_inputs);
467 return ASIOFalse;
469 This->output_channel = This->input_channel + This->wineasio_number_inputs;
470 TRACE("%i IOChannel structures allocated\n", This->wineasio_number_inputs + This->wineasio_number_outputs);
472 /* Get and count physical JACK ports */
473 This->jack_input_ports = jack_get_ports(This->jack_client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput);
474 for (This->jack_num_input_ports = 0; This->jack_input_ports && This->jack_input_ports[This->jack_num_input_ports]; This->jack_num_input_ports++)
476 This->jack_output_ports = jack_get_ports(This->jack_client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
477 for (This->jack_num_output_ports = 0; This->jack_output_ports && This->jack_output_ports[This->jack_num_output_ports]; This->jack_num_output_ports++)
480 /* Initialize IOChannel structures */
481 for (i = 0; i < This->wineasio_number_inputs; i++)
483 This->input_channel[i].active = ASIOFalse;
484 This->input_channel[i].port = NULL;
485 snprintf(This->input_channel[i].port_name, ASIO_MAX_NAME_LENGTH, "in_%i", i + 1);
486 This->input_channel[i].port = jack_port_register(This->jack_client,
487 This->input_channel[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i);
488 /* TRACE("IOChannel structure initialized for input %d: '%s'\n", i, This->input_channel[i].port_name); */
490 for (i = 0; i < This->wineasio_number_outputs; i++)
492 This->output_channel[i].active = ASIOFalse;
493 This->output_channel[i].port = NULL;
494 snprintf(This->output_channel[i].port_name, ASIO_MAX_NAME_LENGTH, "out_%i", i + 1);
495 This->output_channel[i].port = jack_port_register(This->jack_client,
496 This->output_channel[i].port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i);
497 /* TRACE("IOChannel structure initialized for output %d: '%s'\n", i, This->output_channel[i].port_name); */
499 TRACE("%i IOChannel structures initialized\n", This->wineasio_number_inputs + This->wineasio_number_outputs);
501 #ifdef __WINESRC__
502 jack_set_thread_creator(jack_thread_creator);
503 #endif
505 if (jack_set_process_callback(This->jack_client, process_callback, This))
507 jack_client_close(This->jack_client);
508 HeapFree(GetProcessHeap(), 0, This->input_channel);
509 ERR("Unable to register JACK process callback\n");
510 return ASIOFalse;
513 if (jack_set_buffer_size_callback(This->jack_client, bufsize_callback, This))
515 jack_client_close(This->jack_client);
516 HeapFree(GetProcessHeap(), 0, This->input_channel);
517 ERR("Unable to register JACK buffersize change callback\n");
518 return ASIOFalse;
521 if (jack_set_sample_rate_callback (This->jack_client, srate_callback, This))
523 jack_client_close(This->jack_client);
524 HeapFree(GetProcessHeap(), 0, This->input_channel);
525 ERR("Unable to register JACK samplerate change callback\n");
526 return ASIOFalse;
530 if (jack_set_xrun_callback(This->jack_client, xrun_callback, This))
532 jack_client_close(This->jack_client);
533 HeapFree(GetProcessHeap(), 0, This->input_channel);
534 ERR("Unable to register JACK xrun callback\n");
535 return ASIOFalse;
538 This->asio_driver_state = Initialized;
539 TRACE("WineASIO 0.%.1f initialized\n",(float) This->asio_version / 10);
540 return ASIOTrue;
544 * void GetDriverName(char *name);
545 * Function: Returns the driver name in name
548 DEFINE_THISCALL_WRAPPER(GetDriverName,8)
549 HIDDEN void STDMETHODCALLTYPE GetDriverName(LPWINEASIO iface, char *name)
551 TRACE("iface: %p, name: %p\n", iface, name);
552 strcpy(name, "WineASIO");
553 return;
557 * LONG GetDriverVersion (void);
558 * Function: Returns the driver version number
561 DEFINE_THISCALL_WRAPPER(GetDriverVersion,4)
562 HIDDEN LONG STDMETHODCALLTYPE GetDriverVersion(LPWINEASIO iface)
564 IWineASIOImpl *This = (IWineASIOImpl*)iface;
566 TRACE("iface: %p\n", iface);
567 return This->asio_version;
571 * void GetErrorMessage(char *string);
572 * Function: Returns an error message for the last occured error in string
575 DEFINE_THISCALL_WRAPPER(GetErrorMessage,8)
576 HIDDEN void STDMETHODCALLTYPE GetErrorMessage(LPWINEASIO iface, char *string)
578 TRACE("iface: %p, string: %p)\n", iface, string);
579 strcpy(string, "WineASIO does not return error messages\n");
580 return;
584 * ASIOError Start(void);
585 * Function: Start JACK IO processing and reset the sample counter to zero
586 * Returns: ASE_NotPresent if IO is missing
587 * ASE_HWMalfunction if JACK fails to start
590 DEFINE_THISCALL_WRAPPER(Start,4)
591 HIDDEN ASIOError STDMETHODCALLTYPE Start(LPWINEASIO iface)
593 IWineASIOImpl *This = (IWineASIOImpl*)iface;
594 int i;
596 #ifndef _WIN64
597 DWORD temp_time;
598 #endif
600 TRACE("iface: %p\n", iface);
602 if (This->asio_driver_state != Prepared)
604 ERR("Unable to start WineASIO\n");
605 return ASE_NotPresent;
608 /* Zero the audio buffer */
609 for (i = 0; i < (This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize; i++)
610 This->callback_audio_buffer[i] = 0;
612 /* prime the callback */
613 This->asio_buffer_index = 0;
614 if (This->asio_callbacks)
616 #ifdef _WIN64
617 This->asio_sample_position = 0;
618 This->asio_time_stamp = timeGetTime() * 1000000;
619 #else
620 This->asio_sample_position.hi = This->asio_sample_position.lo = 0;
621 temp_time = timeGetTime();
622 This->asio_time_stamp.lo = temp_time * 1000000;
623 This->asio_time_stamp.hi = ((unsigned long long) temp_time * 1000000) >> 32;
624 #endif
625 This->asio_time_info_mode = FALSE;
626 This->asio_can_time_code = FALSE;
628 if (This->asio_callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0))
630 TRACE("TimeInfo mode enabled\n");
631 This->asio_time_info_mode = TRUE;
632 #ifdef _WIN64
633 This->asio_time.timeInfo.systemTime = This->asio_time_stamp;
634 This->asio_time.timeInfo.samplePosition = 0;
635 #else
636 This->asio_time.timeInfo.systemTime.hi = This->asio_time_stamp.hi;
637 This->asio_time.timeInfo.systemTime.lo = This->asio_time_stamp.lo;
638 This->asio_time.timeInfo.samplePosition.hi = This->asio_time.timeInfo.samplePosition.lo = 0;
639 #endif
640 This->asio_time.timeCode.speed = 0;
641 This->asio_time.timeInfo.sampleRate = This->asio_sample_rate;
642 This->asio_time.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
643 if (This->asio_callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0))
645 TRACE("TimeCode supported\n");
646 This->asio_can_time_code = TRUE;
647 #ifdef _WIN64
648 This->asio_time.timeCode.timeCodeSamples = This->asio_time_stamp;
649 #else
650 This->asio_time.timeCode.timeCodeSamples.hi = This->asio_time_stamp.hi;
651 This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time_stamp.lo;
652 #endif
653 This->asio_time.timeCode.flags = ~(kTcValid | kTcRunning);
655 This->asio_callbacks->bufferSwitchTimeInfo(&This->asio_time, This->asio_buffer_index, ASIOTrue);
657 else
659 TRACE("Runnning simple BufferSwitch() callback\n");
660 This->asio_callbacks->bufferSwitch(This->asio_buffer_index, ASIOTrue);
662 This->asio_buffer_index = This->asio_buffer_index ? 0 : 1;
664 else
666 WARN("The ASIO host supplied no callback structure\n");
667 return ASE_NotPresent;
670 if (jack_activate(This->jack_client))
672 ERR("Unable to activate JACK client\n");
673 return ASE_NotPresent;
676 /* connect to the hardware io */
677 if (This->wineasio_connect_to_hardware)
679 for (i = 0; i < This->jack_num_input_ports && i < This->wineasio_number_inputs; i++)
681 /* TRACE("Connecting JACK port: %s to asio: %s\n", This->jack_input_ports[i], jack_port_name(This->input_channel[i].port)); */
682 if (strstr(jack_port_type(jack_port_by_name(This->jack_client, This->jack_input_ports[i])), "audio"))
683 if (jack_connect(This->jack_client, This->jack_input_ports[i], jack_port_name(This->input_channel[i].port)))
684 WARN("Unable to connect %s to %s\n", This->jack_input_ports[i], jack_port_name(This->input_channel[i].port));
686 for (i = 0; i < This->jack_num_output_ports && i < This->wineasio_number_outputs; i++)
688 /* TRACE("Connecting asio: %s to jack port: %s\n", jack_port_name(This->output_channel[i].port), This->jack_output_ports[i]); */
689 if (strstr(jack_port_type(jack_port_by_name(This->jack_client, This->jack_output_ports[i])), "audio"))
690 if (jack_connect(This->jack_client, jack_port_name(This->output_channel[i].port), This->jack_output_ports[i]))
691 WARN("Unable to connect to %s\n", jack_port_name(This->output_channel[i].port));
695 This->asio_driver_state = Running;
696 TRACE("WineASIO successfully loaded\n");
697 return ASE_OK;
701 * ASIOError Stop(void);
702 * Function: Stop JACK IO processing
703 * Returns: ASE_NotPresent on missing IO
704 * Note: BufferSwitch() must not called after returning
707 DEFINE_THISCALL_WRAPPER(Stop,4)
708 HIDDEN ASIOError STDMETHODCALLTYPE Stop(LPWINEASIO iface)
710 IWineASIOImpl *This = (IWineASIOImpl*)iface;
712 TRACE("iface: %p\n", iface);
714 if (This->asio_driver_state != Running)
716 WARN("Unable to stop WineASIO, not running\n");
717 return ASE_NotPresent;
720 This->asio_driver_state = Prepared;
722 if (jack_deactivate(This->jack_client))
724 ERR("Unable to deactivate JACK client\n");
725 return ASE_NotPresent;
727 return ASE_OK;
731 * ASIOError GetChannels(LONG *numInputChannels, LONG *numOutputChannels);
732 * Function: Report number of IO channels
733 * Parameters: numInputChannels and numOutputChannels will hold number of channels on returning
734 * Returns: ASE_NotPresent if no channels are available, otherwise AES_OK
737 DEFINE_THISCALL_WRAPPER(GetChannels,12)
738 HIDDEN ASIOError STDMETHODCALLTYPE GetChannels (LPWINEASIO iface, LONG *numInputChannels, LONG *numOutputChannels)
740 IWineASIOImpl *This = (IWineASIOImpl*)iface;
742 if (!numInputChannels && !numOutputChannels)
744 WARN("Nullpointer argument\n");
745 return ASE_InvalidParameter;
748 *numInputChannels = This->wineasio_number_inputs;
749 *numOutputChannels = This->wineasio_number_outputs;
750 TRACE("iface: %p, inputs: %i, outputs: %i\n", iface, This->wineasio_number_inputs, This->wineasio_number_outputs);
751 return ASE_OK;
755 * ASIOError GetLatencies(LONG *inputLatency, LONG *outputLatency);
756 * Function: Return latency in frames
757 * Returns: ASE_NotPresent if no IO is available, otherwise AES_OK
760 DEFINE_THISCALL_WRAPPER(GetLatencies,12)
761 HIDDEN ASIOError STDMETHODCALLTYPE GetLatencies(LPWINEASIO iface, LONG *inputLatency, LONG *outputLatency)
763 IWineASIOImpl *This = (IWineASIOImpl*)iface;
765 if (!inputLatency && !outputLatency)
767 WARN("Nullpointer argument\n");
768 return ASE_InvalidParameter;
771 *inputLatency = *outputLatency = This->asio_current_buffersize;
772 TRACE("iface: %p Latency = %i frames\n", iface, This->asio_current_buffersize);
773 return ASE_OK;
777 * ASIOError GetBufferSize(LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity);
778 * Function: Return minimum, maximum, preferred buffer sizes, and granularity
779 * At the moment return all the same, and granularity 0
780 * Returns: ASE_NotPresent on missing IO
783 DEFINE_THISCALL_WRAPPER(GetBufferSize,20)
784 HIDDEN ASIOError STDMETHODCALLTYPE GetBufferSize(LPWINEASIO iface, LONG *minSize, LONG *maxSize, LONG *preferredSize, LONG *granularity)
786 IWineASIOImpl *This = (IWineASIOImpl*)iface;
788 TRACE("iface: %p, minSize: %p, maxSize: %p, preferredSize: %p, granularity: %p\n", iface, minSize, maxSize, preferredSize, granularity);
790 if (!minSize && !maxSize && !preferredSize && !granularity)
792 WARN("Nullpointer argument\n");
793 return ASE_InvalidParameter;
796 if (This->wineasio_fixed_buffersize)
798 *minSize = *maxSize = *preferredSize = This->asio_current_buffersize;
799 *granularity = 0;
800 TRACE("Buffersize fixed at %i\n", This->asio_current_buffersize);
801 return ASE_OK;
804 *minSize = ASIO_MINIMUM_BUFFERSIZE;
805 *maxSize = ASIO_MAXIMUM_BUFFERSIZE;
806 *preferredSize = This->wineasio_preferred_buffersize;
807 *granularity = -1;
808 TRACE("The ASIO host can control buffersize\nMinimum: %i, maximum: %i, preferred: %i, granularity: %i, current: %i\n",
809 *minSize, *maxSize, *preferredSize, *granularity, This->asio_current_buffersize);
810 return ASE_OK;
814 * ASIOError CanSampleRate(ASIOSampleRate sampleRate);
815 * Function: Ask if specific SR is available
816 * Returns: ASE_NoClock if SR isn't available, ASE_NotPresent on missing IO
819 DEFINE_THISCALL_WRAPPER(CanSampleRate,12)
820 HIDDEN ASIOError STDMETHODCALLTYPE CanSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate)
822 IWineASIOImpl *This = (IWineASIOImpl*)iface;
824 TRACE("iface: %p, Samplerate = %li, requested samplerate = %li\n", iface, (long) This->asio_sample_rate, (long) sampleRate);
826 if (sampleRate != This->asio_sample_rate)
827 return ASE_NoClock;
828 return ASE_OK;
832 * ASIOError GetSampleRate(ASIOSampleRate *currentRate);
833 * Function: Return current SR
834 * Parameters: currentRate will hold SR on return, 0 if unknown
835 * Returns: ASE_NoClock if SR is unknown, ASE_NotPresent on missing IO
838 DEFINE_THISCALL_WRAPPER(GetSampleRate,8)
839 HIDDEN ASIOError STDMETHODCALLTYPE GetSampleRate(LPWINEASIO iface, ASIOSampleRate *sampleRate)
841 IWineASIOImpl *This = (IWineASIOImpl*)iface;
843 TRACE("iface: %p, Sample rate is %i\n", iface, (int) This->asio_sample_rate);
845 if (!sampleRate)
847 WARN("Nullpointer argument\n");
848 return ASE_InvalidParameter;
850 *sampleRate = This->asio_sample_rate;
851 return ASE_OK;
855 * ASIOError SetSampleRate(ASIOSampleRate sampleRate);
856 * Function: Set requested SR, enable external sync if SR == 0
857 * Returns: ASE_NoClock if unknown SR
858 * ASE_InvalidMode if current clock is external and SR != 0
859 * ASE_NotPresent on missing IO
862 DEFINE_THISCALL_WRAPPER(SetSampleRate,12)
863 HIDDEN ASIOError STDMETHODCALLTYPE SetSampleRate(LPWINEASIO iface, ASIOSampleRate sampleRate)
865 IWineASIOImpl *This = (IWineASIOImpl*)iface;
867 TRACE("iface: %p, Sample rate %f requested\n", iface, sampleRate);
869 if (sampleRate != This->asio_sample_rate)
870 return ASE_NoClock;
871 return ASE_OK;
875 * ASIOError GetClockSources(ASIOClockSource *clocks, LONG *numSources);
876 * Function: Return available clock sources
877 * Parameters: clocks - a pointer to an array of ASIOClockSource structures.
878 * numSources - when called: number of allocated members
879 * - on return: number of clock sources, the minimum is 1 - the internal clock
880 * Returns: ASE_NotPresent on missing IO
883 DEFINE_THISCALL_WRAPPER(GetClockSources,12)
884 HIDDEN ASIOError STDMETHODCALLTYPE GetClockSources(LPWINEASIO iface, ASIOClockSource *clocks, LONG *numSources)
886 TRACE("iface: %p, clocks: %p, numSources: %p\n", iface, clocks, numSources);
888 if (!clocks && !numSources)
890 WARN("Nullpointer argument\n");
891 return ASE_InvalidParameter;
893 clocks->index = 0;
894 clocks->associatedChannel = -1;
895 clocks->associatedGroup = -1;
896 clocks->isCurrentSource = ASIOTrue;
897 strcpy(clocks->name, "Internal");
898 *numSources = 1;
899 return ASE_OK;
903 * ASIOError SetClockSource(LONG index);
904 * Function: Set clock source
905 * Parameters: index returned by ASIOGetClockSources() - See asio.h for more details
906 * Returns: ASE_NotPresent on missing IO
907 * ASE_InvalidMode may be returned if a clock can't be selected
908 * ASE_NoClock should not be returned
911 DEFINE_THISCALL_WRAPPER(SetClockSource,8)
912 HIDDEN ASIOError STDMETHODCALLTYPE SetClockSource(LPWINEASIO iface, LONG index)
914 TRACE("iface: %p, index: %i\n", iface, index);
916 if (index != 0)
917 return ASE_NotPresent;
918 return ASE_OK;
922 * ASIOError GetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);
923 * Function: Return sample position and timestamp
924 * Parameters: sPos holds the position on return, reset to 0 on ASIOStart()
925 * tStamp holds the system time of sPos
926 * Return: ASE_NotPresent on missing IO
927 * ASE_SPNotAdvancing on missing clock
930 DEFINE_THISCALL_WRAPPER(GetSamplePosition,12)
931 HIDDEN ASIOError STDMETHODCALLTYPE GetSamplePosition(LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp)
933 IWineASIOImpl *This = (IWineASIOImpl*)iface;
935 TRACE("iface: %p, sPos: %p, tStamp: %p\n", iface, sPos, tStamp);
937 if (!sPos && !tStamp)
939 WARN("Nullpointer argument\n");
940 return ASE_InvalidParameter;
942 #ifdef _WIN64
943 *tStamp = This->asio_time_stamp;
944 *sPos = This->asio_sample_position;
945 #else
946 tStamp->lo = This->asio_time_stamp.lo;
947 tStamp->hi = This->asio_time_stamp.hi;
948 sPos->lo = This->asio_sample_position.lo;
949 sPos->hi = 0;
950 #endif
951 return ASE_OK;
955 * ASIOError GetChannelInfo (ASIOChannelInfo *info);
956 * Function: Retrive channel info. - See asio.h for more detail
957 * Returns: ASE_NotPresent on missing IO
960 DEFINE_THISCALL_WRAPPER(GetChannelInfo,8)
961 HIDDEN ASIOError STDMETHODCALLTYPE GetChannelInfo(LPWINEASIO iface, ASIOChannelInfo *info)
963 IWineASIOImpl *This = (IWineASIOImpl*)iface;
965 /* TRACE("(iface: %p, info: %p\n", iface, info); */
967 if (info->channel < 0 || (info->isInput ? info->channel >= This->wineasio_number_inputs : info->channel >= This->wineasio_number_outputs))
969 TRACE("Invalid Parameter\n");
970 return ASE_InvalidParameter;
973 info->channelGroup = 0;
975 #ifdef ASIOST32INT
976 info->type = ASIOSTInt32LSB;
977 #else
978 info->type = ASIOSTFloat32LSB;
979 #endif
981 if (info->isInput)
983 info->isActive = This->input_channel[info->channel].active;
984 memcpy(info->name, This->input_channel[info->channel].port_name, ASIO_MAX_NAME_LENGTH);
986 else
988 info->isActive = This->output_channel[info->channel].active;
989 memcpy(info->name, This->output_channel[info->channel].port_name, ASIO_MAX_NAME_LENGTH);
991 return ASE_OK;
995 * ASIOError CreateBuffers(ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks);
996 * Function: Allocate buffers for IO channels
997 * Parameters: bufferInfo - pointer to an array of ASIOBufferInfo structures
998 * numChannels - the total number of IO channels to be allocated
999 * bufferSize - one of the buffer sizes retrieved with ASIOGetBufferSize()
1000 * asioCallbacks - pointer to an ASIOCallbacks structure
1001 * See asio.h for more detail
1002 * Returns: ASE_NoMemory if impossible to allocate enough memory
1003 * ASE_InvalidMode on unsupported bufferSize or invalid bufferInfo data
1004 * ASE_NotPresent on missing IO
1007 DEFINE_THISCALL_WRAPPER(CreateBuffers,20)
1008 HIDDEN ASIOError STDMETHODCALLTYPE CreateBuffers(LPWINEASIO iface, ASIOBufferInfo *bufferInfo, LONG numChannels, LONG bufferSize, ASIOCallbacks *asioCallbacks)
1010 IWineASIOImpl *This = (IWineASIOImpl*)iface;
1011 ASIOBufferInfo *buffer_info = bufferInfo;
1012 int i, j, k;
1014 TRACE("iface: %p, bufferInfo: %p, numChannels: %i, bufferSize: %i, asioCallbacks: %p\n", iface, bufferInfo, numChannels, bufferSize, asioCallbacks);
1016 if (This->asio_driver_state != Initialized)
1018 WARN("Unable to create buffers, WineASIO is not in the initialized state\n");
1019 return ASE_NotPresent;
1022 if (This->wineasio_fixed_buffersize)
1024 if (This->asio_current_buffersize != bufferSize)
1026 WARN("Invalid buffersize (%i) requested\n", bufferSize);
1027 return ASE_InvalidMode;
1029 TRACE("Buffersize fixed at %i\n", This->asio_current_buffersize);
1031 else
1032 { /* fail if not a power of two and if out of range */
1033 if (!(bufferSize > 0 && !(bufferSize&(bufferSize-1))
1034 && bufferSize >= ASIO_MINIMUM_BUFFERSIZE
1035 && bufferSize <= ASIO_MAXIMUM_BUFFERSIZE))
1037 WARN("Invalid buffersize %i requested\n", bufferSize);
1038 return ASE_InvalidMode;
1040 else
1042 if (This->asio_current_buffersize == bufferSize)
1043 TRACE("Buffer size already set to %i\n", This->asio_current_buffersize);
1044 else
1046 This->asio_current_buffersize = bufferSize;
1047 if (jack_set_buffer_size(This->jack_client, This->asio_current_buffersize))
1049 WARN("JACK is unable to set buffersize to %i\n", This->asio_current_buffersize);
1050 return ASE_HWMalfunction;
1052 TRACE("Buffer size changed to %i\n", This->asio_current_buffersize);
1057 This->asio_callbacks = asioCallbacks;
1058 if (!This->asio_callbacks->asioMessage)
1060 WARN("No asioMessage callback supplied\n");
1061 return ASE_InvalidMode;
1064 TRACE("The ASIO host supports ASIO v%i\n", (int) This->asio_callbacks->asioMessage(kAsioEngineVersion, 0, 0, 0));
1066 if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResetRequest, 0 , 0))
1067 TRACE("The ASIO host supports kAsioResetRequest\n");
1069 if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResyncRequest, 0 , 0))
1070 TRACE("The ASIO host supports kAsioResyncRequest\n");
1072 if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioBufferSizeChange, 0 , 0))
1073 TRACE("The ASIO host supports kAsioBufferSizeChange\n");
1075 /* Check for invalid channel numbers */
1076 for (i = j = k = 0; i < numChannels; i++, buffer_info++)
1078 if (buffer_info->isInput)
1080 if (j++ >= This->wineasio_number_inputs)
1082 WARN("Invalid input channel requested\n");
1083 return ASE_InvalidMode;
1086 else
1088 if (k++ >= This->wineasio_number_outputs)
1090 WARN("Invalid output channel requested\n");
1091 return ASE_InvalidMode;
1096 /* Allocate audio buffers */
1097 This->callback_audio_buffer = HeapAlloc(GetProcessHeap(), 0,
1098 (This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize * sizeof(jack_default_audio_sample_t));
1099 if (!This->callback_audio_buffer)
1101 ERR("Unable to allocate %i ASIO audio buffers\n", This->wineasio_number_inputs + This->wineasio_number_outputs);
1102 return ASE_NoMemory;
1104 TRACE("%i ASIO audio buffers allocated (%i kB)\n", This->wineasio_number_inputs + This->wineasio_number_outputs,
1105 (int) ((This->wineasio_number_inputs + This->wineasio_number_outputs) * 2 * This->asio_current_buffersize * sizeof(jack_default_audio_sample_t) / 1024));
1107 for (i = 0; i < This->wineasio_number_inputs; i++)
1108 This->input_channel[i].audio_buffer = This->callback_audio_buffer + (i * 2 * This->asio_current_buffersize);
1109 for (i = 0; i < This->wineasio_number_outputs; i++)
1110 This->output_channel[i].audio_buffer = This->callback_audio_buffer + ((This->wineasio_number_inputs + i) * 2 * This->asio_current_buffersize);
1112 /* initialize ASIOBufferInfo structures */
1113 buffer_info = bufferInfo;
1114 This->asio_active_inputs = This->asio_active_outputs = 0;
1115 for (i = 0; i < numChannels; i++, buffer_info++)
1117 if (buffer_info->isInput)
1119 buffer_info->buffers[0] = &This->input_channel[This->asio_active_inputs].audio_buffer[0];
1120 buffer_info->buffers[1] = &This->input_channel[This->asio_active_inputs].audio_buffer[This->asio_current_buffersize];
1121 This->input_channel[This->asio_active_inputs].active = ASIOTrue;
1122 This->asio_active_inputs++;
1123 /* TRACE("ASIO audio buffer for channel %i as input %li created\n", i, This->asio_active_inputs); */
1125 else
1127 buffer_info->buffers[0] = &This->output_channel[This->asio_active_outputs].audio_buffer[0];
1128 buffer_info->buffers[1] = &This->output_channel[This->asio_active_outputs].audio_buffer[This->asio_current_buffersize];
1129 This->output_channel[This->asio_active_outputs].active = ASIOTrue;
1130 This->asio_active_outputs++;
1131 /* TRACE("ASIO audio buffer for channel %i as output %li created\n", i, This->asio_active_outputs); */
1134 TRACE("%i audio channels initialized\n", This->asio_active_inputs + This->asio_active_outputs);
1136 /* check for TimeInfo or TimeCode mode */
1137 if (This->asio_callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0))
1139 This->asio_time_info_mode = TRUE;
1140 if (This->asio_callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0))
1141 This->asio_can_time_code = TRUE;
1143 else
1144 This->asio_time_info_mode = FALSE;
1146 This->asio_driver_state = Prepared;
1147 return ASE_OK;
1151 * ASIOError DisposeBuffers(void);
1152 * Function: Release allocated buffers
1153 * Returns: ASE_InvalidMode if no buffers were previously allocated
1154 * ASE_NotPresent on missing IO
1155 * Implies: ASIOStop()
1158 DEFINE_THISCALL_WRAPPER(DisposeBuffers,4)
1159 HIDDEN ASIOError STDMETHODCALLTYPE DisposeBuffers(LPWINEASIO iface)
1161 IWineASIOImpl *This = (IWineASIOImpl*)iface;
1162 int i;
1164 TRACE("iface: %p\n", iface);
1166 if (This->asio_driver_state == Running)
1167 Stop (iface);
1168 if (This->asio_driver_state != Prepared)
1170 WARN("Unable to dispose buffers, wrong driver state\n");
1171 return ASE_NotPresent;
1174 This->asio_callbacks = NULL;
1176 for (i = 0; i < This->wineasio_number_inputs; i++)
1178 This->input_channel[i].audio_buffer = NULL;
1179 This->input_channel[i].active = ASIOFalse;
1181 for (i = 0; i < This->wineasio_number_outputs; i++)
1183 This->output_channel[i].audio_buffer = NULL;
1184 This->output_channel[i].active = ASIOFalse;
1186 This->asio_active_inputs = This->asio_active_outputs = 0;
1188 if (This->callback_audio_buffer)
1189 HeapFree(GetProcessHeap(), 0, This->callback_audio_buffer);
1191 This->asio_driver_state = Initialized;
1192 return ASE_OK;
1196 * ASIOError ControlPanel(void);
1197 * Function: Open a control panel for driver settings
1198 * Returns: ASE_NotPresent if no control panel exists. Actually return code should be ignored
1199 * Note: Call the asioMessage callback if something has changed
1202 DEFINE_THISCALL_WRAPPER(ControlPanel,4)
1203 HIDDEN ASIOError STDMETHODCALLTYPE ControlPanel(LPWINEASIO iface)
1205 char *arg_list[] = { strdup ("qjackctl"), NULL };
1207 TRACE("iface: %p\n", iface);
1209 if (!fork())
1210 execvp (arg_list[0], arg_list);
1211 return ASE_OK;
1215 * ASIOError Future(LONG selector, void *opt);
1216 * Function: Various, See asio.h for more detail
1217 * Returns: Depends on the selector but in general ASE_InvalidParameter on invalid selector
1218 * ASE_InvalidParameter if function is unsupported to disable further calls
1219 * ASE_SUCCESS on success, do not use AES_OK
1222 DEFINE_THISCALL_WRAPPER(Future,12)
1223 HIDDEN ASIOError STDMETHODCALLTYPE Future(LPWINEASIO iface, LONG selector, void *opt)
1225 IWineASIOImpl *This = (IWineASIOImpl *) iface;
1227 TRACE("iface: %p, selector: %i, opt: %p\n", iface, selector, opt);
1229 switch (selector)
1231 case kAsioEnableTimeCodeRead:
1232 This->asio_can_time_code = TRUE;
1233 TRACE("The ASIO host enabled TimeCode\n");
1234 return ASE_SUCCESS;
1235 case kAsioDisableTimeCodeRead:
1236 This->asio_can_time_code = FALSE;
1237 TRACE("The ASIO host disabled TimeCode\n");
1238 return ASE_SUCCESS;
1239 case kAsioSetInputMonitor:
1240 TRACE("The driver denied request to set input monitor\n");
1241 return ASE_NotPresent;
1242 case kAsioTransport:
1243 TRACE("The driver denied request for ASIO Transport control\n");
1244 return ASE_InvalidParameter;
1245 case kAsioSetInputGain:
1246 TRACE("The driver denied request to set input gain\n");
1247 return ASE_InvalidParameter;
1248 case kAsioGetInputMeter:
1249 TRACE("The driver denied request to get input meter \n");
1250 return ASE_InvalidParameter;
1251 case kAsioSetOutputGain:
1252 TRACE("The driver denied request to set output gain\n");
1253 return ASE_InvalidParameter;
1254 case kAsioGetOutputMeter:
1255 TRACE("The driver denied request to get output meter\n");
1256 return ASE_InvalidParameter;
1257 case kAsioCanInputMonitor:
1258 TRACE("The driver does not support input monitor\n");
1259 return ASE_InvalidParameter;
1260 case kAsioCanTimeInfo:
1261 TRACE("The driver supports TimeInfo\n");
1262 return ASE_SUCCESS;
1263 case kAsioCanTimeCode:
1264 TRACE("The driver supports TimeCode\n");
1265 return ASE_SUCCESS;
1266 case kAsioCanTransport:
1267 TRACE("The driver denied request for ASIO Transport\n");
1268 return ASE_InvalidParameter;
1269 case kAsioCanInputGain:
1270 TRACE("The driver does not support input gain\n");
1271 return ASE_InvalidParameter;
1272 case kAsioCanInputMeter:
1273 TRACE("The driver does not support input meter\n");
1274 return ASE_InvalidParameter;
1275 case kAsioCanOutputGain:
1276 TRACE("The driver does not support output gain\n");
1277 return ASE_InvalidParameter;
1278 case kAsioCanOutputMeter:
1279 TRACE("The driver does not support output meter\n");
1280 return ASE_InvalidParameter;
1281 case kAsioSetIoFormat:
1282 TRACE("The driver denied request to set DSD IO format\n");
1283 return ASE_NotPresent;
1284 case kAsioGetIoFormat:
1285 TRACE("The driver denied request to get DSD IO format\n");
1286 return ASE_NotPresent;
1287 case kAsioCanDoIoFormat:
1288 TRACE("The driver does not support DSD IO format\n");
1289 return ASE_NotPresent;
1290 default:
1291 TRACE("ASIOFuture() called with undocumented selector\n");
1292 return ASE_InvalidParameter;
1297 * ASIOError OutputReady(void);
1298 * Function: Tells the driver that output bufffers are ready
1299 * Returns: ASE_OK if supported
1300 * ASE_NotPresent to disable
1303 DEFINE_THISCALL_WRAPPER(OutputReady,4)
1304 HIDDEN ASIOError STDMETHODCALLTYPE OutputReady(LPWINEASIO iface)
1306 /* disabled to stop stand alone NI programs from spamming the console
1307 TRACE("iface: %p\n", iface); */
1308 return ASE_NotPresent;
1311 /****************************************************************************
1312 * JACK callbacks
1315 static int bufsize_callback(jack_nframes_t nframes, void *arg)
1317 IWineASIOImpl *This = (IWineASIOImpl*)arg;
1319 if(This->asio_driver_state != Running)
1320 return 1;
1322 if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResetRequest, 0 , 0))
1323 This->asio_callbacks->asioMessage(kAsioResetRequest, 0, 0, 0);
1324 return 0;
1327 static int process_callback(jack_nframes_t nframes, void *arg)
1329 IWineASIOImpl *This = (IWineASIOImpl*)arg;
1330 int i;
1331 jack_transport_state_t jack_transport_state;
1332 jack_position_t jack_position;
1334 #ifdef ASIOST32INT
1335 jack_default_audio_sample_t *in, *out;
1336 jack_nframes_t j;
1337 #endif
1339 #ifndef _WIN64
1340 DWORD temp_time;
1341 #endif
1343 if (This->asio_driver_state != Running)
1344 return 1;
1346 /* get the input data from JACK and copy it to the ASIO buffers */
1347 #ifdef ASIOST32INT
1348 for (i = 0; i < This->asio_active_inputs; i++)
1349 if (This->input_channel[i].active == ASIOTrue)
1351 in = jack_port_get_buffer(This->input_channel[i].port, nframes);
1352 for (j = 0; j < nframes; j++)
1353 ((int *)This->input_channel[i].audio_buffer)[nframes * This->asio_buffer_index + j] = in[j] * 0x7fffffff;
1355 #else
1356 for (i = 0; i < This->asio_active_inputs; i++)
1357 if (This->input_channel[i].active == ASIOTrue)
1358 memcpy (&This->input_channel[i].audio_buffer[nframes * This->asio_buffer_index],
1359 jack_port_get_buffer(This->input_channel[i].port, nframes),
1360 sizeof (jack_default_audio_sample_t) * nframes);
1361 #endif
1363 #ifdef _WIN64
1364 This->asio_sample_position += nframes;
1365 This->asio_sample_position & 0xffffffff; /* make 32bit since JACK's position is 32bit anyways */
1366 This->asio_time_stamp = timeGetTime() * 1000000;
1367 #else
1368 This->asio_sample_position.lo += nframes;
1369 temp_time = timeGetTime();
1370 This->asio_time_stamp.lo = temp_time * 1000000;
1371 This->asio_time_stamp.hi = ((unsigned long long) temp_time * 1000000) >> 32;
1372 #endif
1374 /* time info & time code */
1375 if (This->asio_time_info_mode)
1377 #ifdef _WIN64
1378 This->asio_time.timeInfo.samplePosition = This->asio_sample_position;
1379 This->asio_time.timeInfo.systemTime = This->asio_time_stamp;
1380 #else
1381 This->asio_time.timeInfo.samplePosition.lo = This->asio_sample_position.lo;
1382 This->asio_time.timeInfo.samplePosition.hi = 0;
1383 This->asio_time.timeInfo.systemTime.lo = This->asio_time_stamp.lo;
1384 This->asio_time.timeInfo.systemTime.hi = This->asio_time_stamp.hi;
1385 #endif
1386 This->asio_time.timeInfo.sampleRate = This->asio_sample_rate;
1387 This->asio_time.timeInfo.flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
1389 if (This->asio_can_time_code)
1391 jack_transport_state = jack_transport_query(This->jack_client, &jack_position);
1392 #ifdef _WIN64
1393 This->asio_time.timeCode.timeCodeSamples = jack_position.frame;
1394 #else
1395 This->asio_time.timeCode.timeCodeSamples.lo = jack_position.frame;
1396 This->asio_time.timeCode.timeCodeSamples.hi = 0;
1397 #endif
1398 This->asio_time.timeCode.flags = kTcValid;
1399 if (jack_transport_state == JackTransportRolling)
1400 This->asio_time.timeCode.flags |= kTcRunning;
1402 This->asio_callbacks->bufferSwitchTimeInfo(&This->asio_time, This->asio_buffer_index, ASIOTrue);
1404 else
1405 This->asio_callbacks->bufferSwitch(This->asio_buffer_index, ASIOTrue);
1407 /* copy the ASIO data to JACK */
1408 #ifdef ASIOST32INT
1409 for (i = 0; i < This->asio_active_outputs; i++)
1410 if (This->output_channel[i].active == ASIOTrue)
1412 out = jack_port_get_buffer(This->output_channel[i].port, nframes);
1413 for (j = 0; j < nframes; j++)
1414 out[j] = ((int *) This->output_channel[i].audio_buffer)[nframes * This->asio_buffer_index + j] / (float) 0x7fffffff;
1416 #else
1417 for (i = 0; i < This->asio_active_outputs; i++)
1418 if (This->output_channel[i].active == ASIOTrue)
1419 memcpy(jack_port_get_buffer(This->output_channel[i].port, nframes),
1420 &This->output_channel[i].audio_buffer[nframes * This->asio_buffer_index],
1421 sizeof (jack_default_audio_sample_t) * nframes);
1422 #endif
1423 This->asio_buffer_index = This->asio_buffer_index ? 0 : 1;
1424 return 0;
1427 static int srate_callback(jack_nframes_t nframes, void *arg)
1429 IWineASIOImpl *This = (IWineASIOImpl*)arg;
1431 if(This->asio_driver_state != Running)
1432 return 1;
1434 This->asio_sample_rate = nframes;
1435 This->asio_callbacks->sampleRateDidChange(nframes);
1436 return 0;
1439 static int xrun_callback(void *arg)
1441 IWineASIOImpl *This = (IWineASIOImpl*)arg;
1443 if(This->asio_driver_state != Running)
1444 return 1;
1446 if (This->asio_callbacks->asioMessage(kAsioSelectorSupported, kAsioResyncRequest, 0 , 0))
1447 This->asio_callbacks->asioMessage(kAsioResyncRequest, 0, 0, 0);
1448 return 0;
1451 /*****************************************************************************
1452 * Support functions
1455 #ifdef __WINESRC__
1456 /* Function called by JACK to create a thread in the wine process context,
1457 * uses the global structure jack_thread_creator_privates to communicate with jack_thread_creator_helper() */
1458 static int jack_thread_creator(pthread_t* thread_id, const pthread_attr_t* attr, void *(*function)(void*), void* arg)
1460 TRACE("arg: %p, thread_id: %p, attr: %p, function: %p\n", arg, thread_id, attr, function);
1462 jack_thread_creator_privates.jack_callback_thread = function;
1463 jack_thread_creator_privates.arg = arg;
1464 jack_thread_creator_privates.jack_callback_thread_created = CreateEventW(NULL, FALSE, FALSE, NULL);
1465 CreateThread( NULL, 0, jack_thread_creator_helper, arg, 0,0 );
1466 WaitForSingleObject(jack_thread_creator_privates.jack_callback_thread_created, INFINITE);
1467 *thread_id = jack_thread_creator_privates.jack_callback_pthread_id;
1468 return 0;
1471 /* internal helper function for returning the posix thread_id of the newly created callback thread */
1472 static DWORD WINAPI jack_thread_creator_helper(LPVOID arg)
1474 TRACE("arg: %p\n", arg);
1476 jack_thread_creator_privates.jack_callback_pthread_id = pthread_self();
1477 SetEvent(jack_thread_creator_privates.jack_callback_thread_created);
1478 jack_thread_creator_privates.jack_callback_thread(jack_thread_creator_privates.arg);
1479 return 0;
1481 #endif
1483 static BOOL configure_driver(IWineASIOImpl *This)
1485 HKEY hkey;
1486 LONG result, value;
1487 DWORD type, size;
1488 WCHAR application_path [MAX_PATH];
1489 WCHAR *application_name;
1490 char environment_variable[MAX_ENVIRONMENT_SIZE];
1492 /* Unicode strings used for the registry */
1493 static const WCHAR key_software_wine_wineasio[] =
1494 { 'S','o','f','t','w','a','r','e','\\',
1495 'W','i','n','e','\\',
1496 'W','i','n','e','A','S','I','O',0 };
1497 static const WCHAR value_wineasio_number_inputs[] =
1498 { 'N','u','m','b','e','r',' ','o','f',' ','i','n','p','u','t','s',0 };
1499 static const WCHAR value_wineasio_number_outputs[] =
1500 { 'N','u','m','b','e','r',' ','o','f',' ','o','u','t','p','u','t','s',0 };
1501 static const WCHAR value_wineasio_fixed_buffersize[] =
1502 { 'F','i','x','e','d',' ','b','u','f','f','e','r','s','i','z','e',0 };
1503 static const WCHAR value_wineasio_preferred_buffersize[] =
1504 { 'P','r','e','f','e','r','r','e','d',' ','b','u','f','f','e','r','s','i','z','e',0 };
1505 static const WCHAR wineasio_autostart_server[] =
1506 { 'A','u','t','o','s','t','a','r','t',' ','s','e','r','v','e','r',0 };
1507 static const WCHAR value_wineasio_connect_to_hardware[] =
1508 { 'C','o','n','n','e','c','t',' ','t','o',' ','h','a','r','d','w','a','r','e',0 };
1510 /* Initialise most member variables,
1511 * asio_sample_position, asio_time, & asio_time_stamp are initialized in Start()
1512 * jack_num_input_ports & jack_num_output_ports are initialized in Init() */
1513 This->asio_active_inputs = 0;
1514 This->asio_active_outputs = 0;
1515 This->asio_buffer_index = 0;
1516 This->asio_callbacks = NULL;
1517 This->asio_can_time_code = FALSE;
1518 This->asio_current_buffersize = 0;
1519 This->asio_driver_state = Loaded;
1520 This->asio_sample_rate = 0;
1521 This->asio_time_info_mode = FALSE;
1522 This->asio_version = 90;
1524 This->wineasio_number_inputs = 16;
1525 This->wineasio_number_outputs = 16;
1526 This->wineasio_autostart_server = FALSE;
1527 This->wineasio_connect_to_hardware = TRUE;
1528 This->wineasio_fixed_buffersize = TRUE;
1529 This->wineasio_preferred_buffersize = ASIO_PREFERRED_BUFFERSIZE;
1531 This->jack_client = NULL;
1532 This->jack_client_name[0] = 0;
1533 This->jack_input_ports = NULL;
1534 This->jack_output_ports = NULL;
1535 This->callback_audio_buffer = NULL;
1536 This->input_channel = NULL;
1537 This->output_channel = NULL;
1539 /* create registry entries with defaults if not present */
1540 result = RegCreateKeyExW(HKEY_CURRENT_USER, key_software_wine_wineasio, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1541 if (result != ERROR_SUCCESS)
1543 ERR("Unable to open registry\n");
1544 return FALSE;
1547 /* get/set number of asio inputs */
1548 size = sizeof(DWORD);
1549 if (RegQueryValueExW(hkey, value_wineasio_number_inputs, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1551 if (type == REG_DWORD)
1552 This->wineasio_number_inputs = value;
1554 else
1556 type = REG_DWORD;
1557 size = sizeof(DWORD);
1558 value = This->wineasio_number_inputs;
1559 result = RegSetValueExW(hkey, value_wineasio_number_inputs, 0, REG_DWORD, (LPBYTE) &value, size);
1562 /* get/set number of asio outputs */
1563 size = sizeof(DWORD);
1564 if (RegQueryValueExW(hkey, value_wineasio_number_outputs, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1566 if (type == REG_DWORD)
1567 This->wineasio_number_outputs = value;
1569 else
1571 type = REG_DWORD;
1572 size = sizeof(DWORD);
1573 value = This->wineasio_number_outputs;
1574 result = RegSetValueExW(hkey, value_wineasio_number_outputs, 0, REG_DWORD, (LPBYTE) &value, size);
1577 /* allow changing of asio buffer sizes */
1578 size = sizeof(DWORD);
1579 if (RegQueryValueExW(hkey, value_wineasio_fixed_buffersize, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1581 if (type == REG_DWORD)
1582 This->wineasio_fixed_buffersize = value;
1584 else
1586 type = REG_DWORD;
1587 size = sizeof(DWORD);
1588 value = This->wineasio_fixed_buffersize;
1589 result = RegSetValueExW(hkey, value_wineasio_fixed_buffersize, 0, REG_DWORD, (LPBYTE) &value, size);
1592 /* preferred buffer size (if changing buffersize is allowed) */
1593 size = sizeof(DWORD);
1594 if (RegQueryValueExW(hkey, value_wineasio_preferred_buffersize, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1596 if (type == REG_DWORD)
1597 This->wineasio_preferred_buffersize = value;
1599 else
1601 type = REG_DWORD;
1602 size = sizeof(DWORD);
1603 value = This->wineasio_preferred_buffersize;
1604 result = RegSetValueExW(hkey, value_wineasio_preferred_buffersize, 0, REG_DWORD, (LPBYTE) &value, size);
1607 /* get/set JACK autostart */
1608 size = sizeof(DWORD);
1609 if (RegQueryValueExW(hkey, wineasio_autostart_server, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1611 if (type == REG_DWORD)
1612 This->wineasio_autostart_server = value;
1614 else
1616 type = REG_DWORD;
1617 size = sizeof(DWORD);
1618 value = This->wineasio_autostart_server;
1619 result = RegSetValueExW(hkey, wineasio_autostart_server, 0, REG_DWORD, (LPBYTE) &value, size);
1622 /* get/set JACK connect to physical io */
1623 size = sizeof(DWORD);
1624 if (RegQueryValueExW(hkey, value_wineasio_connect_to_hardware, NULL, &type, (LPBYTE) &value, &size) == ERROR_SUCCESS)
1626 if (type == REG_DWORD)
1627 This->wineasio_connect_to_hardware = value;
1629 else
1631 type = REG_DWORD;
1632 size = sizeof(DWORD);
1633 value = This->wineasio_connect_to_hardware;
1634 result = RegSetValueExW(hkey, value_wineasio_connect_to_hardware, 0, REG_DWORD, (LPBYTE) &value, size);
1637 /* get client name by stripping path and extension */
1638 GetModuleFileNameW(0, application_path, MAX_PATH);
1639 application_name = strrchrW(application_path, L'.');
1640 *application_name = 0;
1641 application_name = strrchrW(application_path, L'\\');
1642 application_name++;
1643 WideCharToMultiByte(CP_ACP, WC_SEPCHARS, application_name, -1, This->jack_client_name, ASIO_MAX_NAME_LENGTH, NULL, NULL);
1645 RegCloseKey(hkey);
1647 /* Look for environment variables to override registry config values */
1649 if (GetEnvironmentVariableA("WINEASIO_NUMBER_INPUTS", environment_variable, MAX_ENVIRONMENT_SIZE))
1651 errno = 0;
1652 result = strtol(environment_variable, 0, 10);
1653 if (errno != ERANGE)
1654 This->wineasio_number_inputs = result;
1657 if (GetEnvironmentVariableA("WINEASIO_NUMBER_OUTPUTS", environment_variable, MAX_ENVIRONMENT_SIZE))
1659 errno = 0;
1660 result = strtol(environment_variable, 0, 10);
1661 if (errno != ERANGE)
1662 This->wineasio_number_outputs = result;
1665 if (GetEnvironmentVariableA("WINEASIO_AUTOSTART_SERVER", environment_variable, MAX_ENVIRONMENT_SIZE))
1667 if (!strcasecmp(environment_variable, "on"))
1668 This->wineasio_autostart_server = TRUE;
1669 else if (!strcasecmp(environment_variable, "off"))
1670 This->wineasio_autostart_server = FALSE;
1673 if (GetEnvironmentVariableA("WINEASIO_CONNECT_TO_HARDWARE", environment_variable, MAX_ENVIRONMENT_SIZE))
1675 if (!strcasecmp(environment_variable, "on"))
1676 This->wineasio_autostart_server = TRUE;
1677 else if (!strcasecmp(environment_variable, "off"))
1678 This->wineasio_autostart_server = FALSE;
1681 if (GetEnvironmentVariableA("WINEASIO_FIXED_BUFFERSIZE", environment_variable, MAX_ENVIRONMENT_SIZE))
1683 if (!strcasecmp(environment_variable, "on"))
1684 This->wineasio_fixed_buffersize = TRUE;
1685 if (!strcasecmp(environment_variable, "off"))
1686 This->wineasio_fixed_buffersize = FALSE;
1689 if (GetEnvironmentVariableA("WINEASIO_PREFERRED_BUFFERSIZE", environment_variable, MAX_ENVIRONMENT_SIZE))
1691 errno = 0;
1692 result = strtol(environment_variable, 0, 10);
1693 if (errno != ERANGE)
1694 This->wineasio_preferred_buffersize = result;
1697 /* over ride the JACK client name gotten from the application name */
1698 size = GetEnvironmentVariableA("WINEASIO_CLIENT_NAME", environment_variable, ASIO_MAX_NAME_LENGTH);
1699 if (size > 0 && size < ASIO_MAX_NAME_LENGTH)
1700 strcpy(This->jack_client_name, environment_variable);
1702 /* if wineasio_preferred_buffersize is not a power of two or if out of range, then set to ASIO_PREFERRED_BUFFERSIZE */
1703 if (!(This->wineasio_preferred_buffersize > 0 && !(This->wineasio_preferred_buffersize&(This->wineasio_preferred_buffersize-1))
1704 && This->wineasio_preferred_buffersize >= ASIO_MINIMUM_BUFFERSIZE
1705 && This->wineasio_preferred_buffersize <= ASIO_MAXIMUM_BUFFERSIZE))
1706 This->wineasio_preferred_buffersize = ASIO_PREFERRED_BUFFERSIZE;
1708 return TRUE;
1711 /* Allocate the interface pointer and associate it with the vtbl/WineASIO object */
1712 HRESULT WINAPI WineASIOCreateInstance(REFIID riid, LPVOID *ppobj)
1714 IWineASIOImpl *pobj;
1716 TRACE("riid: %s, ppobj: %p\n", debugstr_guid(riid), ppobj);
1718 pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj));
1719 if (pobj == NULL)
1721 WARN("out of memory\n");
1722 return E_OUTOFMEMORY;
1725 pobj->lpVtbl = &WineASIO_Vtbl;
1726 pobj->ref = 1;
1727 TRACE("pobj = %p\n", pobj);
1728 *ppobj = pobj;
1729 TRACE("return %p\n", *ppobj);
1730 return S_OK;