Correct JackWinMMEInputPort.cpp.
[jack2.git] / windows / winmme / JackWinMMEInputPort.cpp
blob6c573cf0731d111e0224069b9a2af622f9cf00b3
1 /*
2 Copyright (C) 2011 Devin Anderson
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program 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
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <cassert>
21 #include <memory>
22 #include <stdexcept>
24 #include "JackError.h"
25 #include "JackMidiUtil.h"
26 #include "JackWinMMEInputPort.h"
27 #include "JackMidiWriteQueue.h"
29 using Jack::JackWinMMEInputPort;
31 ///////////////////////////////////////////////////////////////////////////////
32 // Static callbacks
33 ///////////////////////////////////////////////////////////////////////////////
35 void CALLBACK
36 JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message,
37 DWORD port, DWORD param1,
38 DWORD param2)
40 ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2);
43 ///////////////////////////////////////////////////////////////////////////////
44 // Class
45 ///////////////////////////////////////////////////////////////////////////////
47 JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name,
48 const char *client_name,
49 const char *driver_name, UINT index,
50 size_t max_bytes, size_t max_messages)
52 thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages);
53 std::auto_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
54 write_queue = new JackMidiBufferWriteQueue();
55 std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
56 sysex_buffer = new jack_midi_data_t[max_bytes];
57 char error_message[MAXERRORLENGTH];
58 MMRESULT result = midiInOpen(&handle, index, (DWORD)HandleMidiInputEvent, (DWORD)this,
59 CALLBACK_FUNCTION | MIDI_IO_STATUS);
60 if (result != MMSYSERR_NOERROR) {
61 GetErrorString(result, error_message);
62 goto delete_sysex_buffer;
64 sysex_header.dwBufferLength = max_bytes;
65 sysex_header.dwBytesRecorded = 0;
66 sysex_header.dwFlags = 0;
67 sysex_header.dwUser = 0;
68 sysex_header.lpData = (LPSTR)(((LPBYTE) &sysex_header) + sizeof(MIDIHDR));
69 sysex_header.lpNext = 0;
70 result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
71 if (result != MMSYSERR_NOERROR) {
72 GetErrorString(result, error_message);
73 goto close_handle;
75 result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR));
76 if (result != MMSYSERR_NOERROR) {
77 GetErrorString(result, error_message);
78 goto unprepare_header;
81 MIDIINCAPS capabilities;
82 char *name_tmp;
83 result = midiInGetDevCaps(index, &capabilities, sizeof(capabilities));
85 Devin : FIXME
86 if (result != MMSYSERR_NOERROR) {
87 WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps",
88 result);
89 name_tmp = driver_name;
90 } else {
91 name_tmp = capabilities.szPname;
94 snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", alias_name, driver_name,
95 index + 1);
96 snprintf(name, sizeof(name) - 1, "%s:capture_%d", client_name, index + 1);
98 jack_event = 0;
99 started = false;
100 write_queue_ptr.release();
101 thread_queue_ptr.release();
102 return;
104 unprepare_header:
105 result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
106 if (result != MMSYSERR_NOERROR) {
107 WriteError("JackWinMMEInputPort [constructor]",
108 "midiInUnprepareHeader", result);
110 close_handle:
111 result = midiInClose(handle);
112 if (result != MMSYSERR_NOERROR) {
113 WriteError("JackWinMMEInputPort [constructor]", "midiInClose", result);
115 delete_sysex_buffer:
116 delete[] sysex_buffer;
117 throw std::runtime_error(error_message);
120 JackWinMMEInputPort::~JackWinMMEInputPort()
122 Stop();
123 MMRESULT result = midiInReset(handle);
124 if (result != MMSYSERR_NOERROR) {
125 WriteError("JackWinMMEInputPort [destructor]", "midiInReset", result);
127 result = midiInUnprepareHeader(handle, &sysex_header, sizeof(MIDIHDR));
128 if (result != MMSYSERR_NOERROR) {
129 WriteError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader",
130 result);
132 result = midiInClose(handle);
133 if (result != MMSYSERR_NOERROR) {
134 WriteError("JackWinMMEInputPort [destructor]", "midiInClose", result);
136 delete[] sysex_buffer;
137 delete thread_queue;
138 delete write_queue;
141 void
142 JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length,
143 jack_midi_data_t *data)
145 switch (thread_queue->EnqueueEvent(time, length, data)) {
146 case JackMidiWriteQueue::BUFFER_FULL:
147 jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
148 "cannot currently accept a %d-byte event. Dropping event.",
149 length);
150 break;
151 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
152 jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue "
153 "buffer is too small to enqueue a %d-byte event. Dropping "
154 "event.", length);
155 break;
156 default:
161 const char *
162 JackWinMMEInputPort::GetAlias()
164 return alias;
167 const char *
168 JackWinMMEInputPort::GetName()
170 return name;
173 void
174 JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text)
176 MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH);
177 if (result != MMSYSERR_NOERROR) {
178 snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error);
182 void
183 JackWinMMEInputPort::ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames)
185 write_queue->ResetMidiBuffer(port_buffer, frames);
186 if (! jack_event) {
187 jack_event = thread_queue->DequeueEvent();
189 for (; jack_event; jack_event = thread_queue->DequeueEvent()) {
190 switch (write_queue->EnqueueEvent(jack_event)) {
191 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
192 jack_error("JackWinMMEMidiInputPort::Process - The buffer write "
193 "queue couldn't enqueue a %d-byte event. Dropping "
194 "event.", jack_event->size);
195 // Fallthrough on purpose
196 case JackMidiWriteQueue::OK:
197 continue;
199 break;
203 void
204 JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2)
206 set_threaded_log_function();
207 jack_nframes_t current_frame = GetCurrentFrame();
208 switch (message) {
209 case MIM_CLOSE:
210 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
211 break;
212 case MIM_MOREDATA:
213 jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
214 "driver thinks that JACK is not processing messages fast "
215 "enough.");
216 // Fallthrough on purpose.
217 case MIM_DATA:
218 jack_midi_data_t message_buffer[3];
219 jack_midi_data_t status = param1 & 0xff;
220 int length = GetMessageLength(status);
221 switch (length) {
222 case 3:
223 message_buffer[2] = param1 & 0xff0000;
224 // Fallthrough on purpose.
225 case 2:
226 message_buffer[1] = param1 & 0xff00;
227 // Fallthrough on purpose.
228 case 1:
229 message_buffer[0] = status;
230 break;
231 case 0:
232 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
233 "input driver sent an MIM_DATA message with a sysex "
234 "status byte.");
235 return;
236 case -1:
237 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
238 "input driver sent an MIM_DATA message with an invalid "
239 "status byte.");
240 return;
242 EnqueueMessage(current_frame, (size_t) length, message_buffer);
243 break;
244 case MIM_LONGDATA:
245 LPMIDIHDR header = (LPMIDIHDR) param1;
246 jack_midi_data_t *data = (jack_midi_data_t *) header->lpData;
247 size_t length1 = header->dwBytesRecorded;
248 if ((data[0] != 0xf0) || (data[length1 - 1] != 0xf7)) {
249 jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding "
250 "%d-byte sysex chunk.", length);
251 } else {
252 EnqueueMessage(current_frame, length1, data);
254 // Is this realtime-safe? This function isn't run in the JACK thread,
255 // but we still want it to perform as quickly as possible. Even if
256 // this isn't realtime safe, it may not be avoidable.
257 MMRESULT result = midiInAddBuffer(handle, &sysex_header,
258 sizeof(MIDIHDR));
259 if (result != MMSYSERR_NOERROR) {
260 WriteError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer",
261 result);
263 break;
264 case MIM_LONGERROR:
265 jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or "
266 "incomplete sysex message received.");
267 break;
268 case MIM_OPEN:
269 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
273 bool
274 JackWinMMEInputPort::Start()
276 if (! started) {
277 MMRESULT result = midiInStart(handle);
278 started = result == MMSYSERR_NOERROR;
279 if (! started) {
280 WriteError("JackWinMMEInputPort::Start", "midiInStart", result);
283 return started;
286 bool
287 JackWinMMEInputPort::Stop()
289 if (started) {
290 MMRESULT result = midiInStop(handle);
291 started = result != MMSYSERR_NOERROR;
292 if (started) {
293 WriteError("JackWinMMEInputPort::Stop", "midiInStop", result);
296 return ! started;
299 void
300 JackWinMMEInputPort::WriteError(const char *jack_func, const char *mm_func,
301 MMRESULT result)
303 char error_message[MAXERRORLENGTH];
304 GetErrorString(result, error_message);
305 jack_error("%s - %s: %s", jack_func, mm_func, error_message);