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.

288 lines
9.6KB

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