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.

402 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. strncpy(device_name, name_tmp, sizeof(device_name) - 1);
  80. read_queue_ptr.release();
  81. thread_queue_ptr.release();
  82. thread_ptr.release();
  83. return;
  84. destroy_thread_queue_semaphore:
  85. if (! CloseHandle(thread_queue_semaphore)) {
  86. WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle");
  87. }
  88. close_handle:
  89. result = midiOutClose(handle);
  90. if (result != MMSYSERR_NOERROR) {
  91. WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose",
  92. result);
  93. }
  94. raise_exception:
  95. throw std::runtime_error(error_message);
  96. }
  97. JackWinMMEOutputPort::~JackWinMMEOutputPort()
  98. {
  99. MMRESULT result = midiOutReset(handle);
  100. if (result != MMSYSERR_NOERROR) {
  101. WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset",
  102. result);
  103. }
  104. result = midiOutClose(handle);
  105. if (result != MMSYSERR_NOERROR) {
  106. WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose",
  107. result);
  108. }
  109. if (! CloseHandle(sysex_semaphore)) {
  110. WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
  111. }
  112. if (! CloseHandle(thread_queue_semaphore)) {
  113. WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
  114. }
  115. delete read_queue;
  116. delete thread_queue;
  117. delete thread;
  118. }
  119. bool
  120. JackWinMMEOutputPort::Execute()
  121. {
  122. for (;;) {
  123. if (! Wait(thread_queue_semaphore)) {
  124. jack_log("JackWinMMEOutputPort::Execute BREAK");
  125. break;
  126. }
  127. jack_midi_event_t *event = thread_queue->DequeueEvent();
  128. if (! event) {
  129. break;
  130. }
  131. jack_time_t frame_time = GetTimeFromFrames(event->time);
  132. jack_time_t current_time = GetMicroSeconds();
  133. if (frame_time > current_time) {
  134. LARGE_INTEGER due_time;
  135. // 100 ns resolution
  136. due_time.QuadPart =
  137. -((LONGLONG) ((frame_time - current_time) * 10));
  138. if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) {
  139. WriteOSError("JackWinMMEOutputPort::Execute",
  140. "SetWaitableTimer");
  141. break;
  142. }
  143. // Debugging code
  144. jack_log("JackWinMMEOutputPort::Execute - waiting at %f for %f "
  145. "milliseconds before sending message (current frame: %d, "
  146. "send frame: %d)",
  147. ((double) current_time) / 1000.0,
  148. ((double) (frame_time - current_time)) / 1000.0,
  149. GetFramesFromTime(current_time), event->time);
  150. // End debugging code
  151. if (! Wait(timer)) {
  152. break;
  153. }
  154. // Debugging code
  155. jack_time_t wakeup_time = GetMicroSeconds();
  156. jack_log("JackWinMMEOutputPort::Execute - woke up at %f.",
  157. ((double) wakeup_time) / 1000.0);
  158. if (wakeup_time > frame_time) {
  159. jack_log("JackWinMMEOutputPort::Execute - overslept by %f "
  160. "milliseconds.",
  161. ((double) (wakeup_time - frame_time)) / 1000.0);
  162. } else if (wakeup_time < frame_time) {
  163. jack_log("JackWinMMEOutputPort::Execute - woke up %f "
  164. "milliseconds too early.",
  165. ((double) (frame_time - wakeup_time)) / 1000.0);
  166. }
  167. // End debugging code
  168. }
  169. jack_midi_data_t *data = event->buffer;
  170. DWORD message = 0;
  171. MMRESULT result;
  172. size_t size = event->size;
  173. switch (size) {
  174. case 3:
  175. message |= (((DWORD) data[2]) << 16);
  176. // Fallthrough on purpose.
  177. case 2:
  178. message |= (((DWORD) data[1]) << 8);
  179. // Fallthrough on purpose.
  180. case 1:
  181. message |= (DWORD) data[0];
  182. result = midiOutShortMsg(handle, message);
  183. if (result != MMSYSERR_NOERROR) {
  184. WriteOutError("JackWinMMEOutputPort::Execute",
  185. "midiOutShortMsg", result);
  186. }
  187. continue;
  188. }
  189. MIDIHDR header;
  190. header.dwBufferLength = size;
  191. header.dwFlags = 0;
  192. header.lpData = (LPSTR) data;
  193. result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR));
  194. if (result != MMSYSERR_NOERROR) {
  195. WriteOutError("JackWinMMEOutputPort::Execute",
  196. "midiOutPrepareHeader", result);
  197. continue;
  198. }
  199. result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR));
  200. if (result != MMSYSERR_NOERROR) {
  201. WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg",
  202. result);
  203. continue;
  204. }
  205. // System exclusive messages may be sent synchronously or
  206. // asynchronously. The choice is up to the WinMME driver. So, we wait
  207. // until the message is sent, regardless of the driver's choice.
  208. if (! Wait(sysex_semaphore)) {
  209. break;
  210. }
  211. result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR));
  212. if (result != MMSYSERR_NOERROR) {
  213. WriteOutError("JackWinMMEOutputPort::Execute",
  214. "midiOutUnprepareHeader", result);
  215. break;
  216. }
  217. }
  218. return false;
  219. }
  220. void
  221. JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
  222. {
  223. MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
  224. if (result != MMSYSERR_NOERROR) {
  225. snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error);
  226. }
  227. }
  228. void
  229. JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
  230. DWORD_PTR param2)
  231. {
  232. set_threaded_log_function();
  233. switch (message) {
  234. case MOM_CLOSE:
  235. jack_log("JackWinMMEOutputPort::HandleMessage - MIDI device closed.");
  236. break;
  237. case MOM_DONE:
  238. Signal(sysex_semaphore);
  239. break;
  240. case MOM_OPEN:
  241. jack_log("JackWinMMEOutputPort::HandleMessage - MIDI device opened.");
  242. break;
  243. case MOM_POSITIONCB:
  244. LPMIDIHDR header = (LPMIDIHDR) param1;
  245. jack_log("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d "
  246. "bytes of the current sysex message have been sent.",
  247. header->dwOffset, header->dwBytesRecorded);
  248. }
  249. }
  250. bool
  251. JackWinMMEOutputPort::Init()
  252. {
  253. set_threaded_log_function();
  254. // XX: Can more be done? Ideally, this thread should have the JACK server
  255. // thread priority + 1.
  256. if (thread->AcquireSelfRealTime(GetEngineControl()->fServerPriority)) {
  257. jack_error("JackWinMMEOutputPort::Init - could not acquire realtime "
  258. "scheduling. Continuing anyway.");
  259. }
  260. return true;
  261. }
  262. void
  263. JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer,
  264. jack_nframes_t frames)
  265. {
  266. read_queue->ResetMidiBuffer(port_buffer);
  267. for (jack_midi_event_t *event = read_queue->DequeueEvent(); event;
  268. event = read_queue->DequeueEvent()) {
  269. switch (thread_queue->EnqueueEvent(event, frames)) {
  270. case JackMidiWriteQueue::BUFFER_FULL:
  271. jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
  272. "buffer is full. Dropping event.");
  273. break;
  274. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  275. jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
  276. "couldn't enqueue a %d-byte event. Dropping event.",
  277. event->size);
  278. break;
  279. default:
  280. Signal(thread_queue_semaphore);
  281. }
  282. }
  283. }
  284. bool
  285. JackWinMMEOutputPort::Signal(HANDLE semaphore)
  286. {
  287. bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL);
  288. if (! result) {
  289. WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore");
  290. }
  291. return result;
  292. }
  293. bool
  294. JackWinMMEOutputPort::Start()
  295. {
  296. if (thread->GetStatus() != JackThread::kIdle) {
  297. return true;
  298. }
  299. timer = CreateWaitableTimer(NULL, FALSE, NULL);
  300. if (! timer) {
  301. WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer");
  302. return false;
  303. }
  304. if (! thread->StartSync()) {
  305. return true;
  306. }
  307. jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing "
  308. "thread.");
  309. if (! CloseHandle(timer)) {
  310. WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle");
  311. }
  312. return false;
  313. }
  314. bool
  315. JackWinMMEOutputPort::Stop()
  316. {
  317. jack_log("JackWinMMEOutputPort::Stop - stopping MIDI output port "
  318. "processing thread.");
  319. int result;
  320. const char *verb;
  321. switch (thread->GetStatus()) {
  322. case JackThread::kIniting:
  323. case JackThread::kStarting:
  324. result = thread->Kill();
  325. verb = "kill";
  326. break;
  327. case JackThread::kRunning:
  328. Signal(thread_queue_semaphore);
  329. result = thread->Stop();
  330. verb = "stop";
  331. break;
  332. default:
  333. return true;
  334. }
  335. if (result) {
  336. jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
  337. "thread.", verb);
  338. }
  339. if (! CloseHandle(timer)) {
  340. WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
  341. result = -1;
  342. }
  343. return ! result;
  344. }
  345. bool
  346. JackWinMMEOutputPort::Wait(HANDLE semaphore)
  347. {
  348. DWORD result = WaitForSingleObject(semaphore, INFINITE);
  349. switch (result) {
  350. case WAIT_FAILED:
  351. WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject");
  352. break;
  353. case WAIT_OBJECT_0:
  354. return true;
  355. default:
  356. jack_error("JackWinMMEOutputPort::Wait - unexpected result from "
  357. "'WaitForSingleObject'.");
  358. }
  359. return false;
  360. }
  361. void
  362. JackWinMMEOutputPort::WriteOutError(const char *jack_func, const char *mm_func,
  363. MMRESULT result)
  364. {
  365. char error_message[MAXERRORLENGTH];
  366. GetOutErrorString(result, error_message);
  367. jack_error("%s - %s: %s", jack_func, mm_func, error_message);
  368. }