jack2 codebase
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.

401 lines
13KB

  1. /*
  2. Copyright (C) 2011 Devin Anderson
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #include <memory>
  16. #include <stdexcept>
  17. #include "JackMidiUtil.h"
  18. #include "JackTime.h"
  19. #include "JackWinMMEOutputPort.h"
  20. #include "JackGlobals.h"
  21. #include "JackEngineControl.h"
  22. using Jack::JackWinMMEOutputPort;
  23. ///////////////////////////////////////////////////////////////////////////////
  24. // Static callbacks
  25. ///////////////////////////////////////////////////////////////////////////////
  26. void CALLBACK
  27. JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message,
  28. DWORD_PTR port, DWORD_PTR param1,
  29. DWORD_PTR param2)
  30. {
  31. ((JackWinMMEOutputPort *) port)->HandleMessage(message, param1, param2);
  32. }
  33. ///////////////////////////////////////////////////////////////////////////////
  34. // Class
  35. ///////////////////////////////////////////////////////////////////////////////
  36. JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name,
  37. const char *client_name,
  38. const char *driver_name,
  39. UINT index,
  40. size_t max_bytes,
  41. size_t max_messages)
  42. {
  43. read_queue = new JackMidiBufferReadQueue();
  44. std::unique_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
  45. thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
  46. std::unique_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
  47. thread = new JackThread(this);
  48. std::unique_ptr<JackThread> thread_ptr(thread);
  49. char error_message[MAXERRORLENGTH];
  50. MMRESULT result = midiOutOpen(&handle, index, (DWORD_PTR)HandleMessageEvent,
  51. (DWORD_PTR)this, CALLBACK_FUNCTION);
  52. if (result != MMSYSERR_NOERROR) {
  53. GetOutErrorString(result, error_message);
  54. goto raise_exception;
  55. }
  56. thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL);
  57. if (thread_queue_semaphore == NULL) {
  58. GetOSErrorString(error_message);
  59. goto close_handle;
  60. }
  61. sysex_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
  62. if (sysex_semaphore == NULL) {
  63. GetOSErrorString(error_message);
  64. goto destroy_thread_queue_semaphore;
  65. }
  66. MIDIOUTCAPS capabilities;
  67. char *name_tmp;
  68. result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities));
  69. if (result != MMSYSERR_NOERROR) {
  70. WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps",
  71. result);
  72. name_tmp = (char*)driver_name;
  73. } else {
  74. name_tmp = capabilities.szPname;
  75. }
  76. snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, name_tmp,
  77. index + 1);
  78. snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1);
  79. read_queue_ptr.release();
  80. thread_queue_ptr.release();
  81. thread_ptr.release();
  82. return;
  83. destroy_thread_queue_semaphore:
  84. if (! CloseHandle(thread_queue_semaphore)) {
  85. WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle");
  86. }
  87. close_handle:
  88. result = midiOutClose(handle);
  89. if (result != MMSYSERR_NOERROR) {
  90. WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose",
  91. result);
  92. }
  93. raise_exception:
  94. throw std::runtime_error(error_message);
  95. }
  96. JackWinMMEOutputPort::~JackWinMMEOutputPort()
  97. {
  98. MMRESULT result = midiOutReset(handle);
  99. if (result != MMSYSERR_NOERROR) {
  100. WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset",
  101. result);
  102. }
  103. result = midiOutClose(handle);
  104. if (result != MMSYSERR_NOERROR) {
  105. WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose",
  106. result);
  107. }
  108. if (! CloseHandle(sysex_semaphore)) {
  109. WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
  110. }
  111. if (! CloseHandle(thread_queue_semaphore)) {
  112. WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
  113. }
  114. delete read_queue;
  115. delete thread_queue;
  116. delete thread;
  117. }
  118. bool
  119. JackWinMMEOutputPort::Execute()
  120. {
  121. for (;;) {
  122. if (! Wait(thread_queue_semaphore)) {
  123. jack_log("JackWinMMEOutputPort::Execute BREAK");
  124. break;
  125. }
  126. jack_midi_event_t *event = thread_queue->DequeueEvent();
  127. if (! event) {
  128. break;
  129. }
  130. jack_time_t frame_time = GetTimeFromFrames(event->time);
  131. jack_time_t current_time = GetMicroSeconds();
  132. if (frame_time > current_time) {
  133. LARGE_INTEGER due_time;
  134. // 100 ns resolution
  135. due_time.QuadPart =
  136. -((LONGLONG) ((frame_time - current_time) * 10));
  137. if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) {
  138. WriteOSError("JackWinMMEOutputPort::Execute",
  139. "SetWaitableTimer");
  140. break;
  141. }
  142. // Debugging code
  143. jack_log("JackWinMMEOutputPort::Execute - waiting at %f for %f "
  144. "milliseconds before sending message (current frame: %d, "
  145. "send frame: %d)",
  146. ((double) current_time) / 1000.0,
  147. ((double) (frame_time - current_time)) / 1000.0,
  148. GetFramesFromTime(current_time), event->time);
  149. // End debugging code
  150. if (! Wait(timer)) {
  151. break;
  152. }
  153. // Debugging code
  154. jack_time_t wakeup_time = GetMicroSeconds();
  155. jack_log("JackWinMMEOutputPort::Execute - woke up at %f.",
  156. ((double) wakeup_time) / 1000.0);
  157. if (wakeup_time > frame_time) {
  158. jack_log("JackWinMMEOutputPort::Execute - overslept by %f "
  159. "milliseconds.",
  160. ((double) (wakeup_time - frame_time)) / 1000.0);
  161. } else if (wakeup_time < frame_time) {
  162. jack_log("JackWinMMEOutputPort::Execute - woke up %f "
  163. "milliseconds too early.",
  164. ((double) (frame_time - wakeup_time)) / 1000.0);
  165. }
  166. // End debugging code
  167. }
  168. jack_midi_data_t *data = event->buffer;
  169. DWORD message = 0;
  170. MMRESULT result;
  171. size_t size = event->size;
  172. switch (size) {
  173. case 3:
  174. message |= (((DWORD) data[2]) << 16);
  175. // Fallthrough on purpose.
  176. case 2:
  177. message |= (((DWORD) data[1]) << 8);
  178. // Fallthrough on purpose.
  179. case 1:
  180. message |= (DWORD) data[0];
  181. result = midiOutShortMsg(handle, message);
  182. if (result != MMSYSERR_NOERROR) {
  183. WriteOutError("JackWinMMEOutputPort::Execute",
  184. "midiOutShortMsg", result);
  185. }
  186. continue;
  187. }
  188. MIDIHDR header;
  189. header.dwBufferLength = size;
  190. header.dwFlags = 0;
  191. header.lpData = (LPSTR) data;
  192. result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR));
  193. if (result != MMSYSERR_NOERROR) {
  194. WriteOutError("JackWinMMEOutputPort::Execute",
  195. "midiOutPrepareHeader", result);
  196. continue;
  197. }
  198. result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR));
  199. if (result != MMSYSERR_NOERROR) {
  200. WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg",
  201. result);
  202. continue;
  203. }
  204. // System exclusive messages may be sent synchronously or
  205. // asynchronously. The choice is up to the WinMME driver. So, we wait
  206. // until the message is sent, regardless of the driver's choice.
  207. if (! Wait(sysex_semaphore)) {
  208. break;
  209. }
  210. result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR));
  211. if (result != MMSYSERR_NOERROR) {
  212. WriteOutError("JackWinMMEOutputPort::Execute",
  213. "midiOutUnprepareHeader", result);
  214. break;
  215. }
  216. }
  217. return false;
  218. }
  219. void
  220. JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
  221. {
  222. MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
  223. if (result != MMSYSERR_NOERROR) {
  224. snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error);
  225. }
  226. }
  227. void
  228. JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
  229. DWORD_PTR param2)
  230. {
  231. set_threaded_log_function();
  232. switch (message) {
  233. case MOM_CLOSE:
  234. jack_log("JackWinMMEOutputPort::HandleMessage - MIDI device closed.");
  235. break;
  236. case MOM_DONE:
  237. Signal(sysex_semaphore);
  238. break;
  239. case MOM_OPEN:
  240. jack_log("JackWinMMEOutputPort::HandleMessage - MIDI device opened.");
  241. break;
  242. case MOM_POSITIONCB:
  243. LPMIDIHDR header = (LPMIDIHDR) param1;
  244. jack_log("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d "
  245. "bytes of the current sysex message have been sent.",
  246. header->dwOffset, header->dwBytesRecorded);
  247. }
  248. }
  249. bool
  250. JackWinMMEOutputPort::Init()
  251. {
  252. set_threaded_log_function();
  253. // XX: Can more be done? Ideally, this thread should have the JACK server
  254. // thread priority + 1.
  255. if (thread->AcquireSelfRealTime(GetEngineControl()->fServerPriority)) {
  256. jack_error("JackWinMMEOutputPort::Init - could not acquire realtime "
  257. "scheduling. Continuing anyway.");
  258. }
  259. return true;
  260. }
  261. void
  262. JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
  263. jack_nframes_t frames)
  264. {
  265. read_queue->ResetMidiBuffer(port_buffer);
  266. for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
  267. event = read_queue->DequeueEvent()) {
  268. switch (thread_queue->EnqueueEvent(event, frames)) {
  269. case JackMidiWriteQueue::BUFFER_FULL:
  270. jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
  271. "buffer is full. Dropping event.");
  272. break;
  273. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  274. jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
  275. "couldn't enqueue a %d-byte event. Dropping event.",
  276. event->size);
  277. break;
  278. default:
  279. Signal(thread_queue_semaphore);
  280. }
  281. }
  282. }
  283. bool
  284. JackWinMMEOutputPort::Signal(HANDLE semaphore)
  285. {
  286. bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL);
  287. if (! result) {
  288. WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore");
  289. }
  290. return result;
  291. }
  292. bool
  293. JackWinMMEOutputPort::Start()
  294. {
  295. if (thread->GetStatus() != JackThread::kIdle) {
  296. return true;
  297. }
  298. timer = CreateWaitableTimer(NULL, FALSE, NULL);
  299. if (! timer) {
  300. WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer");
  301. return false;
  302. }
  303. if (! thread->StartSync()) {
  304. return true;
  305. }
  306. jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing "
  307. "thread.");
  308. if (! CloseHandle(timer)) {
  309. WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle");
  310. }
  311. return false;
  312. }
  313. bool
  314. JackWinMMEOutputPort::Stop()
  315. {
  316. jack_log("JackWinMMEOutputPort::Stop - stopping MIDI output port "
  317. "processing thread.");
  318. int result;
  319. const char *verb;
  320. switch (thread->GetStatus()) {
  321. case JackThread::kIniting:
  322. case JackThread::kStarting:
  323. result = thread->Kill();
  324. verb = "kill";
  325. break;
  326. case JackThread::kRunning:
  327. Signal(thread_queue_semaphore);
  328. result = thread->Stop();
  329. verb = "stop";
  330. break;
  331. default:
  332. return true;
  333. }
  334. if (result) {
  335. jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
  336. "thread.", verb);
  337. }
  338. if (! CloseHandle(timer)) {
  339. WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
  340. result = -1;
  341. }
  342. return ! result;
  343. }
  344. bool
  345. JackWinMMEOutputPort::Wait(HANDLE semaphore)
  346. {
  347. DWORD result = WaitForSingleObject(semaphore, INFINITE);
  348. switch (result) {
  349. case WAIT_FAILED:
  350. WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject");
  351. break;
  352. case WAIT_OBJECT_0:
  353. return true;
  354. default:
  355. jack_error("JackWinMMEOutputPort::Wait - unexpected result from "
  356. "'WaitForSingleObject'.");
  357. }
  358. return false;
  359. }
  360. void
  361. JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func,
  362. MMRESULT result)
  363. {
  364. char error_message[MAXERRORLENGTH];
  365. GetOutErrorString(result, error_message);
  366. jack_error("%s - %s: %s", jack_func, mm_func, error_message);
  367. }