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.

302 lines
11KB

  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 <cassert>
  16. #include <memory>
  17. #include <stdexcept>
  18. #include "JackError.h"
  19. #include "JackMidiUtil.h"
  20. #include "JackWinMMEInputPort.h"
  21. #include "JackMidiWriteQueue.h"
  22. using Jack::JackWinMMEInputPort;
  23. ///////////////////////////////////////////////////////////////////////////////
  24. // Static callbacks
  25. ///////////////////////////////////////////////////////////////////////////////
  26. void CALLBACK
  27. JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message,
  28. DWORD port, DWORD param1,
  29. DWORD param2)
  30. {
  31. ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2);
  32. }
  33. ///////////////////////////////////////////////////////////////////////////////
  34. // Class
  35. ///////////////////////////////////////////////////////////////////////////////
  36. JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name,
  37. const char *client_name,
  38. const char *driver_name, UINT index,
  39. size_t max_bytes, size_t max_messages)
  40. {
  41. thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
  42. std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
  43. write_queue = new JackMidiBufferWriteQueue();
  44. std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
  45. sysex_buffer = new jack_midi_data_t[max_bytes];
  46. char error_message[MAXERRORLENGTH];
  47. MMRESULT result = midiInOpen(&handle, index,
  48. (DWORD_PTR) HandleMidiInputEvent,
  49. (DWORD_PTR)this,
  50. CALLBACK_FUNCTION | MIDI_IO_STATUS);
  51. if (result != MMSYSERR_NOERROR) {
  52. GetInErrorString(result, error_message);
  53. goto delete_sysex_buffer;
  54. }
  55. sysex_header.dwBufferLength = max_bytes;
  56. sysex_header.dwFlags = 0;
  57. sysex_header.lpData = (LPSTR)(((LPBYTE) &sysex_header) + sizeof(MIDIHDR));
  58. result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
  59. if (result != MMSYSERR_NOERROR) {
  60. GetInErrorString(result, error_message);
  61. goto close_handle;
  62. }
  63. result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR));
  64. if (result != MMSYSERR_NOERROR) {
  65. GetInErrorString(result, error_message);
  66. goto unprepare_header;
  67. }
  68. MIDIINCAPS capabilities;
  69. char *name_tmp;
  70. result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
  71. if (result != MMSYSERR_NOERROR) {
  72. WriteInError("JackWinMMEInputPort [constructor]", "midiInGetDevCaps",
  73. result);
  74. name_tmp = (char*) driver_name;
  75. } else {
  76. name_tmp = capabilities.szPname;
  77. }
  78. snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, name_tmp,
  79. index + 1);
  80. snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1);
  81. jack_event = 0;
  82. started = false;
  83. write_queue_ptr.release();
  84. thread_queue_ptr.release();
  85. return;
  86. unprepare_header:
  87. result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
  88. if (result != MMSYSERR_NOERROR) {
  89. WriteInError("JackWinMMEInputPort [constructor]",
  90. "midiInUnprepareHeader", result);
  91. }
  92. close_handle:
  93. result = midiInClose(handle);
  94. if (result != MMSYSERR_NOERROR) {
  95. WriteInError("JackWinMMEInputPort [constructor]", "midiInClose",
  96. result);
  97. }
  98. delete_sysex_buffer:
  99. delete[] sysex_buffer;
  100. throw std::runtime_error(error_message);
  101. }
  102. JackWinMMEInputPort::~JackWinMMEInputPort()
  103. {
  104. MMRESULT result = midiInReset(handle);
  105. if (result != MMSYSERR_NOERROR) {
  106. WriteInError("JackWinMMEInputPort [destructor]", "midiInReset", result);
  107. }
  108. result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
  109. if (result != MMSYSERR_NOERROR) {
  110. WriteInError("JackWinMMEInputPort [destructor]",
  111. "midiInUnprepareHeader", result);
  112. }
  113. result = midiInClose(handle);
  114. if (result != MMSYSERR_NOERROR) {
  115. WriteInError("JackWinMMEInputPort [destructor]", "midiInClose", result);
  116. }
  117. delete[] sysex_buffer;
  118. delete thread_queue;
  119. delete write_queue;
  120. }
  121. void
  122. JackWinMMEInputPort::EnqueueMessage(DWORD timestamp, size_t length,
  123. jack_midi_data_t *data)
  124. {
  125. jack_nframes_t frame = GetFramesFromTime(start_time + (timestamp * 1000));
  126. switch (thread_queue->EnqueueEvent(frame, length, data)) {
  127. case JackMidiWriteQueue::BUFFER_FULL:
  128. jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
  129. "cannot currently accept a %d-byte event. Dropping event.",
  130. length);
  131. break;
  132. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  133. jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
  134. "buffer is too small to enqueue a %d-byte event. Dropping "
  135. "event.", length);
  136. break;
  137. default:
  138. ;
  139. }
  140. }
  141. void
  142. JackWinMMEInputPort::GetInErrorString(MMRESULT error, LPTSTR text)
  143. {
  144. MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH);
  145. if (result != MMSYSERR_NOERROR) {
  146. snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error);
  147. }
  148. }
  149. void
  150. JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer,
  151. jack_nframes_t frames)
  152. {
  153. write_queue->ResetMidiBuffer(port_buffer, frames);
  154. if (! jack_event) {
  155. jack_event = thread_queue->DequeueEvent();
  156. }
  157. for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
  158. switch (write_queue->EnqueueEvent(jack_event, frames)) {
  159. case JackMidiWriteQueue::BUFFER_TOO_SMALL:
  160. jack_error("JackWinMMEMidiInputPort::Process - The buffer write "
  161. "queue couldn't enqueue a %d-byte event. Dropping "
  162. "event.", jack_event->size);
  163. // Fallthrough on purpose
  164. case JackMidiWriteQueue::OK:
  165. continue;
  166. }
  167. break;
  168. }
  169. }
  170. void
  171. JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2)
  172. {
  173. set_threaded_log_function();
  174. switch (message) {
  175. case MIM_CLOSE:
  176. jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
  177. break;
  178. case MIM_MOREDATA:
  179. jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
  180. "driver thinks that JACK is not processing messages fast "
  181. "enough.");
  182. // Fallthrough on purpose.
  183. case MIM_DATA: {
  184. jack_midi_data_t message_buffer[3];
  185. jack_midi_data_t status = param1 & 0xff;
  186. int length = GetMessageLength(status);
  187. switch (length) {
  188. case 3:
  189. message_buffer[2] = (param1 >> 16) & 0xff;
  190. // Fallthrough on purpose.
  191. case 2:
  192. message_buffer[1] = (param1 >> 8) & 0xff;
  193. // Fallthrough on purpose.
  194. case 1:
  195. message_buffer[0] = status;
  196. break;
  197. case 0:
  198. jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
  199. "input driver sent an MIM_DATA message with a sysex "
  200. "status byte.");
  201. return;
  202. case -1:
  203. jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
  204. "input driver sent an MIM_DATA message with an invalid "
  205. "status byte.");
  206. return;
  207. }
  208. EnqueueMessage(dwParam2, (size_t) length, message_buffer);
  209. break;
  210. }
  211. case MIM_LONGDATA: {
  212. LPMIDIHDR header = (LPMIDIHDR) param1;
  213. size_t byte_count = header->dwBytesRecorded;
  214. if (! byte_count) {
  215. jack_info("JackWinMMEInputPort::ProcessWinMME - WinMME driver has "
  216. "returned sysex header to us with no bytes. The JACK "
  217. "driver is probably being stopped.");
  218. break;
  219. }
  220. jack_midi_data_t *data = (jack_midi_data_t *) header->lpData;
  221. if ((data[0] != 0xf0) || (data[byte_count - 1] != 0xf7)) {
  222. jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding "
  223. "%d-byte sysex chunk.", byte_count);
  224. } else {
  225. EnqueueMessage(dwParam2, byte_count, data);
  226. }
  227. // Is this realtime-safe? This function isn't run in the JACK thread,
  228. // but we still want it to perform as quickly as possible. Even if
  229. // this isn't realtime safe, it may not be avoidable.
  230. MMRESULT result = midiInAddBuffer(handle, &sysex_header,
  231. sizeof(MIDIHDR));
  232. if (result != MMSYSERR_NOERROR) {
  233. WriteInError("JackWinMMEInputPort::ProcessWinMME",
  234. "midiInAddBuffer", result);
  235. }
  236. break;
  237. }
  238. case MIM_LONGERROR:
  239. jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or "
  240. "incomplete sysex message received.");
  241. break;
  242. case MIM_OPEN:
  243. jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
  244. }
  245. }
  246. bool
  247. JackWinMMEInputPort::Start()
  248. {
  249. if (! started) {
  250. start_time = GetMicroSeconds();
  251. MMRESULT result = midiInStart(handle);
  252. started = result == MMSYSERR_NOERROR;
  253. if (! started) {
  254. WriteInError("JackWinMMEInputPort::Start", "midiInStart", result);
  255. }
  256. }
  257. return started;
  258. }
  259. bool
  260. JackWinMMEInputPort::Stop()
  261. {
  262. if (started) {
  263. MMRESULT result = midiInStop(handle);
  264. started = result != MMSYSERR_NOERROR;
  265. if (started) {
  266. WriteInError("JackWinMMEInputPort::Stop", "midiInStop", result);
  267. }
  268. }
  269. return ! started;
  270. }
  271. void
  272. JackWinMMEInputPort::WriteInError(const char *jack_func, const char *mm_func,
  273. MMRESULT result)
  274. {
  275. char error_message[MAXERRORLENGTH];
  276. GetInErrorString(result, error_message);
  277. jack_error("%s - %s: %s", jack_func, mm_func, error_message);
  278. }