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
29 * create a freely distributable asio.h replacement
30 * add ASIOOutputReady() support
39 #include "wine/debug.h"
43 #include "wine/unicode.h"
45 #include <jack/jack.h>
46 #include <jack/thread.h>
48 #define IEEE754_64FLOAT 1
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
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, \
83 "jmp " __ASM_NAME(#func) __ASM_STDCALL(args) )
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 */
93 /* Hide ELF symbols for the COM members - No need to to export them */
94 #define HIDDEN __attribute__ ((visibility("hidden")))
96 /*****************************************************************************
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
;
130 typedef struct IWineASIO
*LPWINEASIO
;
132 typedef struct IOChannel
135 jack_default_audio_sample_t
*audio_buffer
;
136 char port_name
[ASIO_MAX_NAME_LENGTH
];
140 typedef struct IWineASIOImpl
143 const IWineASIOVtbl
*lpVtbl
;
146 /* The app's main window handle on windows, 0 on OS/X */
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
;
160 BOOL asio_time_info_mode
;
161 ASIOTimeStamp asio_time_stamp
;
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
;
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
;
186 enum { Loaded
, Initialized
, Prepared
, Running
};
188 /****************************************************************************
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);
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
);
260 HRESULT WINAPI
WineASIOCreateInstance(REFIID riid
, LPVOID
*ppobj
);
261 static BOOL
configure_driver(IWineASIOImpl
*This
);
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
);
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
,
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
)
302 /* structure needed to create the JACK callback thread in the wine process context */
304 void *(*jack_callback_thread
) (void*);
306 pthread_t jack_callback_pthread_id
;
307 HANDLE jack_callback_thread_created
;
308 } jack_thread_creator_privates
;
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
)
325 if (IsEqualIID(&CLSID_WineASIO
, riid
))
332 return E_NOINTERFACE
;
336 * ULONG STDMETHODCALLTYPE AddRef(LPWINEASIO iface);
337 * Function: Increment the reference count on the object
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
);
351 * ULONG Release (LPWINEASIO iface);
352 * Function: Destroy the interface
354 * Implies: ASIOStop() and ASIODisposeBuffers()
357 HIDDEN ULONG STDMETHODCALLTYPE
Release(LPWINEASIO iface
)
359 IWineASIOImpl
*This
= (IWineASIOImpl
*)iface
;
360 ULONG ref
= InterlockedDecrement(&This
->ref
);
363 TRACE("iface: %p, ref count is %d\n", iface
, ref
);
365 if (This
->asio_driver_state
== Running
)
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");
404 HeapFree(GetProcessHeap(), 0, This
);
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
;
424 TRACE("iface: %p, sysRef: %p\n", iface
, sysRef
);
426 This
->sys_ref
= sysRef
;
429 mlockall(MCL_FUTURE
);
432 if (!configure_driver(This
))
434 WARN("Unable to configure WineASIO\n");
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
);
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");
455 if (!(This
->asio_current_buffersize
= jack_get_buffer_size(This
->jack_client
)))
457 WARN("Unable to get buffer size from JACK\n");
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
);
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
);
502 jack_set_thread_creator(jack_thread_creator
);
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");
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");
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");
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");
538 This
->asio_driver_state
= Initialized
;
539 TRACE("WineASIO 0.%.1f initialized\n",(float) This
->asio_version
/ 10);
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");
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");
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
;
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
)
617 This
->asio_sample_position
= 0;
618 This
->asio_time_stamp
= timeGetTime() * 1000000;
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;
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
;
633 This
->asio_time
.timeInfo
.systemTime
= This
->asio_time_stamp
;
634 This
->asio_time
.timeInfo
.samplePosition
= 0;
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;
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
;
648 This
->asio_time
.timeCode
.timeCodeSamples
= This
->asio_time_stamp
;
650 This
->asio_time
.timeCode
.timeCodeSamples
.hi
= This
->asio_time_stamp
.hi
;
651 This
->asio_time
.timeCode
.timeCodeSamples
.lo
= This
->asio_time_stamp
.lo
;
653 This
->asio_time
.timeCode
.flags
= ~(kTcValid
| kTcRunning
);
655 This
->asio_callbacks
->bufferSwitchTimeInfo(&This
->asio_time
, This
->asio_buffer_index
, ASIOTrue
);
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;
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");
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
;
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
);
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
);
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
;
800 TRACE("Buffersize fixed at %i\n", This
->asio_current_buffersize
);
804 *minSize
= ASIO_MINIMUM_BUFFERSIZE
;
805 *maxSize
= ASIO_MAXIMUM_BUFFERSIZE
;
806 *preferredSize
= This
->wineasio_preferred_buffersize
;
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
);
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
)
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
);
847 WARN("Nullpointer argument\n");
848 return ASE_InvalidParameter
;
850 *sampleRate
= This
->asio_sample_rate
;
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
)
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
;
894 clocks
->associatedChannel
= -1;
895 clocks
->associatedGroup
= -1;
896 clocks
->isCurrentSource
= ASIOTrue
;
897 strcpy(clocks
->name
, "Internal");
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
);
917 return ASE_NotPresent
;
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
;
943 *tStamp
= This
->asio_time_stamp
;
944 *sPos
= This
->asio_sample_position
;
946 tStamp
->lo
= This
->asio_time_stamp
.lo
;
947 tStamp
->hi
= This
->asio_time_stamp
.hi
;
948 sPos
->lo
= This
->asio_sample_position
.lo
;
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;
976 info
->type
= ASIOSTInt32LSB
;
978 info
->type
= ASIOSTFloat32LSB
;
983 info
->isActive
= This
->input_channel
[info
->channel
].active
;
984 memcpy(info
->name
, This
->input_channel
[info
->channel
].port_name
, ASIO_MAX_NAME_LENGTH
);
988 info
->isActive
= This
->output_channel
[info
->channel
].active
;
989 memcpy(info
->name
, This
->output_channel
[info
->channel
].port_name
, ASIO_MAX_NAME_LENGTH
);
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
;
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
);
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
;
1042 if (This
->asio_current_buffersize
== bufferSize
)
1043 TRACE("Buffer size already set to %i\n", This
->asio_current_buffersize
);
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
;
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); */
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
;
1144 This
->asio_time_info_mode
= FALSE
;
1146 This
->asio_driver_state
= Prepared
;
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
;
1164 TRACE("iface: %p\n", iface
);
1166 if (This
->asio_driver_state
== Running
)
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
;
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
);
1210 execvp (arg_list
[0], arg_list
);
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
);
1231 case kAsioEnableTimeCodeRead
:
1232 This
->asio_can_time_code
= TRUE
;
1233 TRACE("The ASIO host enabled TimeCode\n");
1235 case kAsioDisableTimeCodeRead
:
1236 This
->asio_can_time_code
= FALSE
;
1237 TRACE("The ASIO host disabled TimeCode\n");
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");
1263 case kAsioCanTimeCode
:
1264 TRACE("The driver supports TimeCode\n");
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
;
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 /****************************************************************************
1315 static int bufsize_callback(jack_nframes_t nframes
, void *arg
)
1317 IWineASIOImpl
*This
= (IWineASIOImpl
*)arg
;
1319 if(This
->asio_driver_state
!= Running
)
1322 if (This
->asio_callbacks
->asioMessage(kAsioSelectorSupported
, kAsioResetRequest
, 0 , 0))
1323 This
->asio_callbacks
->asioMessage(kAsioResetRequest
, 0, 0, 0);
1327 static int process_callback(jack_nframes_t nframes
, void *arg
)
1329 IWineASIOImpl
*This
= (IWineASIOImpl
*)arg
;
1331 jack_transport_state_t jack_transport_state
;
1332 jack_position_t jack_position
;
1335 jack_default_audio_sample_t
*in
, *out
;
1343 if (This
->asio_driver_state
!= Running
)
1346 /* get the input data from JACK and copy it to the ASIO buffers */
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;
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
);
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;
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;
1374 /* time info & time code */
1375 if (This
->asio_time_info_mode
)
1378 This
->asio_time
.timeInfo
.samplePosition
= This
->asio_sample_position
;
1379 This
->asio_time
.timeInfo
.systemTime
= This
->asio_time_stamp
;
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
;
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
);
1393 This
->asio_time
.timeCode
.timeCodeSamples
= jack_position
.frame
;
1395 This
->asio_time
.timeCode
.timeCodeSamples
.lo
= jack_position
.frame
;
1396 This
->asio_time
.timeCode
.timeCodeSamples
.hi
= 0;
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
);
1405 This
->asio_callbacks
->bufferSwitch(This
->asio_buffer_index
, ASIOTrue
);
1407 /* copy the ASIO data to JACK */
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;
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
);
1423 This
->asio_buffer_index
= This
->asio_buffer_index
? 0 : 1;
1427 static int srate_callback(jack_nframes_t nframes
, void *arg
)
1429 IWineASIOImpl
*This
= (IWineASIOImpl
*)arg
;
1431 if(This
->asio_driver_state
!= Running
)
1434 This
->asio_sample_rate
= nframes
;
1435 This
->asio_callbacks
->sampleRateDidChange(nframes
);
1439 static int xrun_callback(void *arg
)
1441 IWineASIOImpl
*This
= (IWineASIOImpl
*)arg
;
1443 if(This
->asio_driver_state
!= Running
)
1446 if (This
->asio_callbacks
->asioMessage(kAsioSelectorSupported
, kAsioResyncRequest
, 0 , 0))
1447 This
->asio_callbacks
->asioMessage(kAsioResyncRequest
, 0, 0, 0);
1451 /*****************************************************************************
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
;
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
);
1483 static BOOL
configure_driver(IWineASIOImpl
*This
)
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");
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
;
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
;
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
;
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
;
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
;
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
;
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
'\\');
1643 WideCharToMultiByte(CP_ACP
, WC_SEPCHARS
, application_name
, -1, This
->jack_client_name
, ASIO_MAX_NAME_LENGTH
, NULL
, NULL
);
1647 /* Look for environment variables to override registry config values */
1649 if (GetEnvironmentVariableA("WINEASIO_NUMBER_INPUTS", environment_variable
, MAX_ENVIRONMENT_SIZE
))
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
))
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
))
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
;
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
));
1721 WARN("out of memory\n");
1722 return E_OUTOFMEMORY
;
1725 pobj
->lpVtbl
= &WineASIO_Vtbl
;
1727 TRACE("pobj = %p\n", pobj
);
1729 TRACE("return %p\n", *ppobj
);