ASIO to JACK driver for WINE
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

883 lines
26KB

  1. /*
  2. * Copyright (C) 2006 Robert Reif
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Lesser General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2.1 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public
  15. * License along with this library; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  17. */
  18. #include "config.h"
  19. #include "wine/port.h"
  20. #include <stdarg.h>
  21. #include <stdio.h>
  22. #include "windef.h"
  23. #include "winbase.h"
  24. #include "objbase.h"
  25. #include "mmsystem.h"
  26. #include "wine/debug.h"
  27. #include <jack/jack.h>
  28. #include "asio.h"
  29. WINE_DEFAULT_DEBUG_CHANNEL(asio);
  30. /* JACK callback function */
  31. static int jack_process(jack_nframes_t nframes, void * arg);
  32. // {48D0C522-BFCC-45cc-8B84-17F25F33E6E8}
  33. static GUID const CLSID_WineASIO = {
  34. 0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } };
  35. #define twoRaisedTo32 4294967296.0
  36. #define twoRaisedTo32Reciprocal (1.0 / twoRaisedTo32)
  37. #define MAX_INPUTS 2
  38. #define MAX_OUTPUTS 2
  39. #ifdef __i386__ /* thiscall functions are i386-specific */
  40. #ifdef __GNUC__
  41. /* GCC erroneously warns that the newly wrapped function
  42. * isn't used, lets help it out of it's thinking
  43. */
  44. #define SUPPRESS_NOTUSED __attribute__((used))
  45. #else
  46. #define SUPPRESS_NOTUSED
  47. #endif /* __GNUC__ */
  48. #define WRAP_THISCALL(type, func, parm) \
  49. extern type func parm; \
  50. __ASM_GLOBAL_FUNC( func, \
  51. "popl %eax\n\t" \
  52. "pushl %ecx\n\t" \
  53. "pushl %eax\n\t" \
  54. "jmp " __ASM_NAME("__wrapped_" #func) ); \
  55. SUPPRESS_NOTUSED static type __wrapped_ ## func parm
  56. #else
  57. #define WRAP_THISCALL(functype, function, param) \
  58. functype function param
  59. #endif
  60. /*****************************************************************************
  61. * IWineAsio interface
  62. */
  63. #define INTERFACE IWineASIO
  64. DECLARE_INTERFACE_(IWineASIO,IUnknown)
  65. {
  66. STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
  67. STDMETHOD_(ULONG,AddRef)(THIS) PURE;
  68. STDMETHOD_(ULONG,Release)(THIS) PURE;
  69. STDMETHOD_(ASIOBool,init)(THIS_ void *sysHandle) PURE;
  70. STDMETHOD_(void,getDriverName)(THIS_ char *name) PURE;
  71. STDMETHOD_(long,getDriverVersion)(THIS) PURE;
  72. STDMETHOD_(void,getErrorMessage)(THIS_ char *string) PURE;
  73. STDMETHOD_(ASIOError,start)(THIS) PURE;
  74. STDMETHOD_(ASIOError,stop)(THIS) PURE;
  75. STDMETHOD_(ASIOError,getChannels)(THIS_ long *numInputChannels, long *numOutputChannels) PURE;
  76. STDMETHOD_(ASIOError,getLatencies)(THIS_ long *inputLatency, long *outputLatency) PURE;
  77. STDMETHOD_(ASIOError,getBufferSize)(THIS_ long *minSize, long *maxSize, long *preferredSize, long *granularity) PURE;
  78. STDMETHOD_(ASIOError,canSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE;
  79. STDMETHOD_(ASIOError,getSampleRate)(THIS_ ASIOSampleRate *sampleRate) PURE;
  80. STDMETHOD_(ASIOError,setSampleRate)(THIS_ ASIOSampleRate sampleRate) PURE;
  81. STDMETHOD_(ASIOError,getClockSources)(THIS_ ASIOClockSource *clocks, long *numSources) PURE;
  82. STDMETHOD_(ASIOError,setClockSource)(THIS_ long reference) PURE;
  83. STDMETHOD_(ASIOError,getSamplePosition)(THIS_ ASIOSamples *sPos, ASIOTimeStamp *tStamp) PURE;
  84. STDMETHOD_(ASIOError,getChannelInfo)(THIS_ ASIOChannelInfo *info) PURE;
  85. STDMETHOD_(ASIOError,createBuffers)(THIS_ ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) PURE;
  86. STDMETHOD_(ASIOError,disposeBuffers)(THIS) PURE;
  87. STDMETHOD_(ASIOError,controlPanel)(THIS) PURE;
  88. STDMETHOD_(ASIOError,future)(THIS_ long selector,void *opt) PURE;
  89. STDMETHOD_(ASIOError,outputReady)(THIS) PURE;
  90. };
  91. #undef INTERFACE
  92. typedef struct IWineASIO *LPWINEASIO, **LPLPWINEASIO;
  93. enum
  94. {
  95. Init,
  96. Run,
  97. Exit
  98. };
  99. struct IWineASIOImpl
  100. {
  101. /* COM stuff */
  102. const IWineASIOVtbl *lpVtbl;
  103. LONG ref;
  104. /* ASIO stuff */
  105. HWND hwnd;
  106. ASIOSampleRate sample_rate;
  107. long input_latency;
  108. long output_latency;
  109. long block_frames;
  110. ASIOTime asio_time;
  111. long miliseconds;
  112. ASIOTimeStamp system_time;
  113. double sample_position;
  114. ASIOBufferInfo *bufferInfos;
  115. ASIOCallbacks *callbacks;
  116. char error_message[256];
  117. long in_map[MAX_INPUTS];
  118. long out_map[MAX_OUTPUTS];
  119. long num_inputs;
  120. long num_outputs;
  121. short *input_buffers[MAX_INPUTS][2];
  122. short *output_buffers[MAX_OUTPUTS][2];
  123. long active_inputs;
  124. long active_outputs;
  125. BOOL time_info_mode;
  126. BOOL tc_read;
  127. /* JACK stuff */
  128. jack_port_t *input_port[MAX_INPUTS];
  129. jack_port_t *output_port[MAX_OUTPUTS];
  130. jack_client_t *client;
  131. long client_state;
  132. long toggle;
  133. };
  134. typedef struct IWineASIOImpl IWineASIOImpl;
  135. static ULONG WINAPI IWineASIOImpl_AddRef(LPWINEASIO iface)
  136. {
  137. IWineASIOImpl *This = (IWineASIOImpl *)iface;
  138. ULONG ref = InterlockedIncrement(&(This->ref));
  139. TRACE("(%p) ref was %ld\n", This, ref - 1);
  140. return ref;
  141. }
  142. static ULONG WINAPI IWineASIOImpl_Release(LPWINEASIO iface)
  143. {
  144. IWineASIOImpl *This = (IWineASIOImpl *)iface;
  145. ULONG ref = InterlockedDecrement(&(This->ref));
  146. TRACE("(%p) ref was %ld\n", This, ref + 1);
  147. if (!ref) {
  148. jack_client_close(This->client);
  149. TRACE("JACK client closed\n");
  150. HeapFree(GetProcessHeap(),0,This);
  151. TRACE("(%p) released\n", This);
  152. }
  153. return ref;
  154. }
  155. static HRESULT WINAPI IWineASIOImpl_QueryInterface(LPWINEASIO iface, REFIID riid, void** ppvObject)
  156. {
  157. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  158. TRACE("(%p, %s, %p)\n", iface, debugstr_guid(riid), ppvObject);
  159. if (ppvObject == NULL)
  160. return E_INVALIDARG;
  161. if (IsEqualIID(&CLSID_WineASIO, riid))
  162. {
  163. IWineASIOImpl_AddRef(iface);
  164. *ppvObject = This;
  165. return S_OK;
  166. }
  167. return E_NOINTERFACE;
  168. }
  169. WRAP_THISCALL( ASIOBool __stdcall, IWineASIOImpl_init, (LPWINEASIO iface, void *sysHandle))
  170. {
  171. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  172. jack_status_t status;
  173. int i;
  174. TRACE("(%p, %p)\n", iface, sysHandle);
  175. This->sample_rate = 48000;
  176. This->block_frames = 1024;
  177. This->input_latency = This->block_frames;
  178. This->output_latency = This->block_frames * 2;
  179. This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate);
  180. This->callbacks = NULL;
  181. This->sample_position = 0;
  182. strcpy(This->error_message, "No Error");
  183. This->num_inputs = 0;
  184. This->num_outputs = 0;
  185. This->active_inputs = 0;
  186. This->active_outputs = 0;
  187. This->toggle = 0;
  188. This->client_state = Init;
  189. This->time_info_mode = FALSE;
  190. This->tc_read = FALSE;
  191. for (i = 0; i < MAX_INPUTS; i++)
  192. {
  193. This->in_map[i] = 0;
  194. }
  195. for (i = 0; i < MAX_OUTPUTS; i++)
  196. {
  197. This->out_map[i] = 0;
  198. }
  199. This->client = jack_client_open("Wine_ASIO_Jack_Client", JackNullOption, &status, NULL);
  200. if (This->client == NULL)
  201. {
  202. WARN("failed ot open jack server\n");
  203. return ASIOFalse;
  204. }
  205. TRACE("JACK client opened\n");
  206. if (status & JackServerStarted)
  207. TRACE("JACK server started\n");
  208. jack_set_process_callback(This->client, jack_process, This);
  209. This->sample_rate = jack_get_sample_rate(This->client);
  210. This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate);
  211. TRACE("sample rate: %f\n", This->sample_rate);
  212. for (i = 0; i < MAX_INPUTS; i++)
  213. {
  214. char name[32];
  215. snprintf(name, sizeof(name), "Input%d", i);
  216. This->input_port[i] = jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, i);
  217. if (This->input_port[i] == 0)
  218. break;
  219. This->num_inputs++;
  220. }
  221. TRACE("found %ld inputs\n", This->num_inputs);
  222. for (i = 0; i < MAX_OUTPUTS; i++)
  223. {
  224. char name[32];
  225. snprintf(name, sizeof(name), "Output%d", i);
  226. This->output_port[i] = jack_port_register(This->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, i);
  227. if (This->output_port[i] == 0)
  228. break;
  229. This->num_outputs++;
  230. }
  231. TRACE("found %ld outputs\n", This->num_outputs);
  232. return ASIOTrue;
  233. }
  234. WRAP_THISCALL( void __stdcall, IWineASIOImpl_getDriverName, (LPWINEASIO iface, char *name))
  235. {
  236. TRACE("(%p, %p)\n", iface, name);
  237. strcpy(name, "Wine ASIO");
  238. }
  239. WRAP_THISCALL( long __stdcall, IWineASIOImpl_getDriverVersion, (LPWINEASIO iface))
  240. {
  241. TRACE("(%p)\n", iface);
  242. return 1;
  243. }
  244. WRAP_THISCALL( void __stdcall, IWineASIOImpl_getErrorMessage, (LPWINEASIO iface, char *string))
  245. {
  246. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  247. TRACE("(%p, %p)\n", iface, string);
  248. strcpy(string, This->error_message);
  249. }
  250. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_start, (LPWINEASIO iface))
  251. {
  252. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  253. const char ** ports;
  254. int i;
  255. TRACE("(%p)\n", iface);
  256. if (This->callbacks)
  257. {
  258. This->sample_position = 0;
  259. This->system_time.lo = 0;
  260. This->system_time.hi = 0;
  261. if (jack_activate(This->client))
  262. {
  263. WARN("couldn't activate client\n");
  264. return ASE_NotPresent;
  265. }
  266. ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsOutput);
  267. if (ports == NULL)
  268. {
  269. WARN("couldn't get input ports\n");
  270. return ASE_NotPresent;
  271. }
  272. for (i = 0; i < This->active_inputs; i++)
  273. {
  274. if (jack_connect(This->client, ports[i], jack_port_name(This->input_port[i])))
  275. {
  276. WARN("input %d connect failed\n", i);
  277. free(ports);
  278. return ASE_NotPresent;
  279. }
  280. }
  281. free(ports);
  282. ports = jack_get_ports(This->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
  283. if (ports == NULL)
  284. {
  285. WARN("couldn't get output ports\n");
  286. return ASE_NotPresent;
  287. }
  288. for (i = 0; i < This->active_outputs; i++)
  289. {
  290. if (jack_connect(This->client, jack_port_name(This->output_port[i]), ports[i]))
  291. {
  292. WARN("output %d connect failed\n", i);
  293. free(ports);
  294. return ASE_NotPresent;
  295. }
  296. }
  297. free(ports);
  298. TRACE("started\n");
  299. return ASE_OK;
  300. }
  301. return ASE_NotPresent;
  302. }
  303. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_stop, (LPWINEASIO iface))
  304. {
  305. TRACE("(%p) stub!\n", iface);
  306. return ASE_OK;
  307. }
  308. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannels, (LPWINEASIO iface, long *numInputChannels, long *numOutputChannels))
  309. {
  310. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  311. TRACE("(%p, %p, %p)\n", iface, numInputChannels, numOutputChannels);
  312. if (numInputChannels)
  313. *numInputChannels = This->num_inputs;
  314. if (numOutputChannels)
  315. *numOutputChannels = This->num_outputs;
  316. return ASE_OK;
  317. }
  318. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getLatencies, (LPWINEASIO iface, long *inputLatency, long *outputLatency))
  319. {
  320. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  321. TRACE("(%p, %p, %p)\n", iface, inputLatency, outputLatency);
  322. if (inputLatency)
  323. *inputLatency = This->input_latency;
  324. if (outputLatency)
  325. *outputLatency = This->output_latency;
  326. return ASE_OK;
  327. }
  328. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getBufferSize, (LPWINEASIO iface, long *minSize, long *maxSize, long *preferredSize, long *granularity))
  329. {
  330. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  331. TRACE("(%p, %p, %p, %p, %p)\n", iface, minSize, maxSize, preferredSize, granularity);
  332. if (minSize)
  333. *minSize = This->block_frames;
  334. if (maxSize)
  335. *maxSize = This->block_frames;
  336. if (preferredSize)
  337. *preferredSize = This->block_frames;
  338. if (granularity)
  339. *granularity = 0;
  340. return ASE_OK;
  341. }
  342. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_canSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate))
  343. {
  344. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  345. TRACE("(%p, %f)\n", iface, sampleRate);
  346. if (sampleRate == This->sample_rate)
  347. return ASE_OK;
  348. return ASE_NoClock;
  349. }
  350. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSampleRate, (LPWINEASIO iface, ASIOSampleRate *sampleRate))
  351. {
  352. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  353. TRACE("(%p, %p)\n", iface, sampleRate);
  354. if (sampleRate)
  355. *sampleRate = This->sample_rate;
  356. return ASE_OK;
  357. }
  358. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setSampleRate, (LPWINEASIO iface, ASIOSampleRate sampleRate))
  359. {
  360. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  361. TRACE("(%p, %f)\n", iface, sampleRate);
  362. if (sampleRate != This->sample_rate)
  363. return ASE_NoClock;
  364. if (sampleRate != This->sample_rate)
  365. {
  366. This->sample_rate = sampleRate;
  367. This->asio_time.timeInfo.sampleRate = sampleRate;
  368. This->asio_time.timeInfo.flags |= kSampleRateChanged;
  369. This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate);
  370. if (This->callbacks && This->callbacks->sampleRateDidChange)
  371. This->callbacks->sampleRateDidChange(This->sample_rate);
  372. }
  373. return ASE_OK;
  374. }
  375. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getClockSources, (LPWINEASIO iface, ASIOClockSource *clocks, long *numSources))
  376. {
  377. TRACE("(%p, %p, %p)\n", iface, clocks, numSources);
  378. if (clocks && numSources)
  379. {
  380. clocks->index = 0;
  381. clocks->associatedChannel = -1;
  382. clocks->associatedGroup = -1;
  383. clocks->isCurrentSource = ASIOTrue;
  384. strcpy(clocks->name, "Internal");
  385. *numSources = 1;
  386. return ASE_OK;
  387. }
  388. return ASE_InvalidParameter;
  389. }
  390. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_setClockSource, (LPWINEASIO iface, long reference))
  391. {
  392. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  393. TRACE("(%p, %ld)\n", iface, reference);
  394. if (reference == 0)
  395. {
  396. This->asio_time.timeInfo.flags |= kClockSourceChanged;
  397. return ASE_OK;
  398. }
  399. return ASE_NotPresent;
  400. }
  401. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getSamplePosition, (LPWINEASIO iface, ASIOSamples *sPos, ASIOTimeStamp *tStamp))
  402. {
  403. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  404. TRACE("(%p, %p, %p)\n", iface, sPos, tStamp);
  405. tStamp->lo = This->system_time.lo;
  406. tStamp->hi = This->system_time.hi;
  407. if (This->sample_position >= twoRaisedTo32)
  408. {
  409. sPos->hi = (unsigned long)(This->sample_position * twoRaisedTo32Reciprocal);
  410. sPos->lo = (unsigned long)(This->sample_position - (sPos->hi * twoRaisedTo32));
  411. }
  412. else
  413. {
  414. sPos->hi = 0;
  415. sPos->lo = (unsigned long)This->sample_position;
  416. }
  417. return ASE_OK;
  418. }
  419. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_getChannelInfo, (LPWINEASIO iface, ASIOChannelInfo *info))
  420. {
  421. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  422. int i;
  423. char name[32];
  424. TRACE("(%p, %p)\n", iface, info);
  425. if (info->channel < 0 || (info->isInput ? info->channel >= MAX_INPUTS : info->channel >= MAX_OUTPUTS))
  426. return ASE_InvalidParameter;
  427. info->type = ASIOSTInt16LSB;
  428. info->channelGroup = 0;
  429. info->isActive = ASIOFalse;
  430. if (info->isInput)
  431. {
  432. for (i = 0; i < This->active_inputs; i++)
  433. {
  434. if (This->in_map[i] == info->channel)
  435. {
  436. info->isActive = ASIOTrue;
  437. break;
  438. }
  439. }
  440. snprintf(name, sizeof(name), "Input %ld", info->channel);
  441. }
  442. else
  443. {
  444. for (i = 0; i < This->active_outputs; i++)
  445. {
  446. if (This->out_map[i] == info->channel)
  447. {
  448. info->isActive = ASIOTrue;
  449. break;
  450. }
  451. }
  452. snprintf(name, sizeof(name), "Output %ld", info->channel);
  453. }
  454. strcpy(info->name, name);
  455. return ASE_OK;
  456. }
  457. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_disposeBuffers, (LPWINEASIO iface))
  458. {
  459. int i;
  460. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  461. TRACE("(%p)\n", iface);
  462. This->callbacks = NULL;
  463. __wrapped_IWineASIOImpl_stop(iface);
  464. for (i = 0; i < This->active_inputs; i++)
  465. {
  466. HeapFree(GetProcessHeap(), 0, This->input_buffers[i][0]);
  467. HeapFree(GetProcessHeap(), 0, This->input_buffers[i][1]);
  468. }
  469. This->active_inputs = 0;
  470. for (i = 0; i < This->active_outputs; i++)
  471. {
  472. HeapFree(GetProcessHeap(), 0, This->output_buffers[i][0]);
  473. HeapFree(GetProcessHeap(), 0, This->output_buffers[i][1]);
  474. }
  475. This->active_outputs = 0;
  476. return ASE_OK;
  477. }
  478. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_createBuffers, (LPWINEASIO iface, ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks))
  479. {
  480. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  481. ASIOBufferInfo * info = bufferInfos;
  482. int i;
  483. TRACE("(%p, %p, %ld, %ld, %p)\n", iface, bufferInfos, numChannels, bufferSize, callbacks);
  484. This->active_inputs = 0;
  485. This->active_outputs = 0;
  486. This->block_frames = bufferSize;
  487. This->miliseconds = (long)((double)(This->block_frames * 1000) / This->sample_rate);
  488. for (i = 0; i < numChannels; i++, info++)
  489. {
  490. if (info->isInput)
  491. {
  492. if (info->channelNum < 0 || info->channelNum >= This->num_inputs)
  493. {
  494. WARN("invalid input channel: %ld\n", info->channelNum);
  495. goto ERROR_PARAM;
  496. }
  497. if (This->active_inputs >= This->num_inputs)
  498. {
  499. WARN("too many inputs\n");
  500. goto ERROR_PARAM;
  501. }
  502. This->in_map[This->active_inputs] = info->channelNum;
  503. This->input_buffers[This->active_inputs][0] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short));
  504. This->input_buffers[This->active_inputs][1] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short));
  505. if (This->input_buffers[This->active_inputs][0] && This->input_buffers[This->active_inputs][1])
  506. {
  507. info->buffers[0] = This->input_buffers[This->active_inputs][0];
  508. info->buffers[1] = This->input_buffers[This->active_inputs][1];
  509. }
  510. else
  511. {
  512. HeapFree(GetProcessHeap(), 0, This->input_buffers[This->active_inputs][0]);
  513. info->buffers[0] = 0;
  514. info->buffers[1] = 0;
  515. WARN("no input buffer memory\n");
  516. goto ERROR_MEM;
  517. }
  518. This->active_inputs++;
  519. }
  520. else
  521. {
  522. if (info->channelNum < 0 || info->channelNum >= This->num_outputs)
  523. {
  524. WARN("invalid output channel: %ld\n", info->channelNum);
  525. goto ERROR_PARAM;
  526. }
  527. if (This->active_outputs >= This->num_outputs)
  528. {
  529. WARN("too many outputs\n");
  530. goto ERROR_PARAM;
  531. }
  532. This->out_map[This->active_outputs] = info->channelNum;
  533. This->output_buffers[This->active_outputs][0] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short));
  534. This->output_buffers[This->active_outputs][1] = HeapAlloc(GetProcessHeap(), 0, This->block_frames * sizeof(short));
  535. if (This->output_buffers[This->active_outputs][0] && This->output_buffers[This->active_outputs][1])
  536. {
  537. info->buffers[0] = This->output_buffers[This->active_outputs][0];
  538. info->buffers[1] = This->output_buffers[This->active_outputs][1];
  539. }
  540. else
  541. {
  542. HeapFree(GetProcessHeap(), 0, This->output_buffers[This->active_outputs][0]);
  543. info->buffers[0] = 0;
  544. info->buffers[1] = 0;
  545. WARN("no output buffer memory\n");
  546. goto ERROR_MEM;
  547. }
  548. This->active_outputs++;
  549. }
  550. }
  551. This->callbacks = callbacks;
  552. if (This->callbacks->asioMessage)
  553. {
  554. if (This->callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0))
  555. {
  556. This->time_info_mode = TRUE;
  557. This->asio_time.timeInfo.speed = 1;
  558. This->asio_time.timeInfo.systemTime.hi = 0;
  559. This->asio_time.timeInfo.systemTime.lo = 0;
  560. This->asio_time.timeInfo.samplePosition.hi = 0;
  561. This->asio_time.timeInfo.samplePosition.lo = 0;
  562. This->asio_time.timeInfo.sampleRate = This->sample_rate;
  563. This->asio_time.timeInfo. flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
  564. This->asio_time.timeCode.speed = 1;
  565. This->asio_time.timeCode.timeCodeSamples.hi = 0;
  566. This->asio_time.timeCode.timeCodeSamples.lo = 0;
  567. This->asio_time.timeCode.flags = kTcValid | kTcRunning;
  568. }
  569. else
  570. This->time_info_mode = FALSE;
  571. }
  572. else
  573. {
  574. This->time_info_mode = FALSE;
  575. WARN("asioMessage callback not supplied\n");
  576. goto ERROR_PARAM;
  577. }
  578. return ASE_OK;
  579. ERROR_MEM:
  580. __wrapped_IWineASIOImpl_disposeBuffers(iface);
  581. WARN("no memory\n");
  582. return ASE_NoMemory;
  583. ERROR_PARAM:
  584. __wrapped_IWineASIOImpl_disposeBuffers(iface);
  585. WARN("invalid parameter\n");
  586. return ASE_InvalidParameter;
  587. }
  588. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_controlPanel, (LPWINEASIO iface))
  589. {
  590. TRACE("(%p) stub!\n", iface);
  591. return ASE_OK;
  592. }
  593. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_future, (LPWINEASIO iface, long selector, void *opt))
  594. {
  595. IWineASIOImpl * This = (IWineASIOImpl*)iface;
  596. TRACE("(%p, %ld, %p)\n", iface, selector, opt);
  597. switch (selector)
  598. {
  599. case kAsioEnableTimeCodeRead:
  600. This->tc_read = TRUE;
  601. return ASE_SUCCESS;
  602. case kAsioDisableTimeCodeRead:
  603. This->tc_read = FALSE;
  604. return ASE_SUCCESS;
  605. case kAsioSetInputMonitor:
  606. return ASE_SUCCESS;
  607. case kAsioCanInputMonitor:
  608. return ASE_SUCCESS;
  609. case kAsioCanTimeInfo:
  610. return ASE_SUCCESS;
  611. case kAsioCanTimeCode:
  612. return ASE_SUCCESS;
  613. }
  614. return ASE_NotPresent;
  615. }
  616. WRAP_THISCALL( ASIOError __stdcall, IWineASIOImpl_outputReady, (LPWINEASIO iface))
  617. {
  618. TRACE("(%p)\n", iface);
  619. return ASE_NotPresent;
  620. }
  621. static const IWineASIOVtbl WineASIO_Vtbl =
  622. {
  623. IWineASIOImpl_QueryInterface,
  624. IWineASIOImpl_AddRef,
  625. IWineASIOImpl_Release,
  626. IWineASIOImpl_init,
  627. IWineASIOImpl_getDriverName,
  628. IWineASIOImpl_getDriverVersion,
  629. IWineASIOImpl_getErrorMessage,
  630. IWineASIOImpl_start,
  631. IWineASIOImpl_stop,
  632. IWineASIOImpl_getChannels,
  633. IWineASIOImpl_getLatencies,
  634. IWineASIOImpl_getBufferSize,
  635. IWineASIOImpl_canSampleRate,
  636. IWineASIOImpl_getSampleRate,
  637. IWineASIOImpl_setSampleRate,
  638. IWineASIOImpl_getClockSources,
  639. IWineASIOImpl_setClockSource,
  640. IWineASIOImpl_getSamplePosition,
  641. IWineASIOImpl_getChannelInfo,
  642. IWineASIOImpl_createBuffers,
  643. IWineASIOImpl_disposeBuffers,
  644. IWineASIOImpl_controlPanel,
  645. IWineASIOImpl_future,
  646. IWineASIOImpl_outputReady,
  647. };
  648. HRESULT asioCreateInstance(REFIID riid, LPVOID *ppobj)
  649. {
  650. IWineASIOImpl * pobj;
  651. TRACE("(%s, %p)\n", debugstr_guid(riid), ppobj);
  652. pobj = HeapAlloc(GetProcessHeap(), 0, sizeof(*pobj));
  653. if (pobj == NULL) {
  654. WARN("out of memory\n");
  655. return E_OUTOFMEMORY;
  656. }
  657. pobj->lpVtbl = &WineASIO_Vtbl;
  658. pobj->ref = 1;
  659. TRACE("pobj = %p\n", pobj);
  660. *ppobj = pobj;
  661. TRACE("return %p\n", *ppobj);
  662. return S_OK;
  663. }
  664. static void getNanoSeconds(ASIOTimeStamp* ts)
  665. {
  666. double nanoSeconds = (double)((unsigned long)timeGetTime ()) * 1000000.;
  667. ts->hi = (unsigned long)(nanoSeconds / twoRaisedTo32);
  668. ts->lo = (unsigned long)(nanoSeconds - (ts->hi * twoRaisedTo32));
  669. }
  670. static int jack_process(jack_nframes_t nframes, void * arg)
  671. {
  672. IWineASIOImpl * This = (IWineASIOImpl*)arg;
  673. int i, j;
  674. jack_default_audio_sample_t * in, *out;
  675. jack_transport_state_t ts = jack_transport_query(This->client, NULL);
  676. if (ts == JackTransportRolling)
  677. {
  678. if (This->client_state == Init)
  679. This->client_state = Run;
  680. This->sample_position += nframes;
  681. /* get the input data from JACK and copy it to the ASIO buffers */
  682. for (i = 0; i < This->active_inputs; i++)
  683. {
  684. short * buffer = This->input_buffers[i][This->toggle];
  685. in = jack_port_get_buffer(This->input_port[i], nframes);
  686. for (j = 0; j < nframes; j++)
  687. buffer[j] = in[j] * 32767.0f;
  688. }
  689. /* call the ASIO user callback to read the input data and fill the output data */
  690. getNanoSeconds(&This->system_time);
  691. if (This->time_info_mode)
  692. {
  693. __wrapped_IWineASIOImpl_getSamplePosition((LPWINEASIO)This, &This->asio_time.timeInfo.samplePosition, &This->asio_time.timeInfo.systemTime);
  694. if (This->tc_read)
  695. {
  696. /* FIXME */
  697. This->asio_time.timeCode.timeCodeSamples.lo = This->asio_time.timeInfo.samplePosition.lo;
  698. This->asio_time.timeCode.timeCodeSamples.hi = 0;
  699. }
  700. This->callbacks->bufferSwitchTimeInfo(&This->asio_time, This->toggle, ASIOFalse);
  701. This->asio_time.timeInfo.flags &= ~(kSampleRateChanged | kClockSourceChanged);
  702. }
  703. else
  704. This->callbacks->bufferSwitch(This->toggle, ASIOFalse);
  705. /* copy the ASIO data to JACK */
  706. for (i = 0; i < This->active_outputs; i++)
  707. {
  708. short * buffer = This->output_buffers[i][This->toggle];
  709. out = jack_port_get_buffer(This->output_port[i], nframes);
  710. for (j = 0; j < nframes; j++)
  711. out[j] = buffer[j] / 32767.0f;
  712. }
  713. This->toggle = This->toggle ? 0 : 1;
  714. }
  715. else if (ts == JackTransportStopped)
  716. {
  717. if (This->client_state == Run)
  718. This->client_state = Exit;
  719. /* FIXME why is this needed ? */
  720. jack_transport_start(This->client);
  721. }
  722. return 0;
  723. }