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.
24 #include "JackError.h"
25 #include "JackMidiUtil.h"
26 #include "JackWinMMEInputPort.h"
27 #include "JackMidiWriteQueue.h"
29 using Jack::JackWinMMEInputPort
;
31 ///////////////////////////////////////////////////////////////////////////////
33 ///////////////////////////////////////////////////////////////////////////////
36 JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle
, UINT message
,
37 DWORD port
, DWORD param1
,
40 ((JackWinMMEInputPort
*) port
)->ProcessWinMME(message
, param1
, param2
);
43 ///////////////////////////////////////////////////////////////////////////////
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
);
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
;
83 result
= midiInGetDevCaps(index
, &capabilities
, sizeof(capabilities
));
86 if (result != MMSYSERR_NOERROR) {
87 WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps",
89 name_tmp = driver_name;
91 name_tmp = capabilities.szPname;
94 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:in%d", alias_name
, driver_name
,
96 snprintf(name
, sizeof(name
) - 1, "%s:capture_%d", client_name
, index
+ 1);
100 write_queue_ptr
.release();
101 thread_queue_ptr
.release();
105 result
= midiInUnprepareHeader(handle
, &sysex_header
, sizeof(MIDIHDR
));
106 if (result
!= MMSYSERR_NOERROR
) {
107 WriteError("JackWinMMEInputPort [constructor]",
108 "midiInUnprepareHeader", result
);
111 result
= midiInClose(handle
);
112 if (result
!= MMSYSERR_NOERROR
) {
113 WriteError("JackWinMMEInputPort [constructor]", "midiInClose", result
);
116 delete[] sysex_buffer
;
117 throw std::runtime_error(error_message
);
120 JackWinMMEInputPort::~JackWinMMEInputPort()
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",
132 result
= midiInClose(handle
);
133 if (result
!= MMSYSERR_NOERROR
) {
134 WriteError("JackWinMMEInputPort [destructor]", "midiInClose", result
);
136 delete[] sysex_buffer
;
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.",
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 "
162 JackWinMMEInputPort::GetAlias()
168 JackWinMMEInputPort::GetName()
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
);
183 JackWinMMEInputPort::ProcessJack(JackMidiBuffer
*port_buffer
, jack_nframes_t frames
)
185 write_queue
->ResetMidiBuffer(port_buffer
, frames
);
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
:
204 JackWinMMEInputPort::ProcessWinMME(UINT message
, DWORD param1
, DWORD param2
)
206 set_threaded_log_function();
207 jack_nframes_t current_frame
= GetCurrentFrame();
210 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed.");
213 jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device "
214 "driver thinks that JACK is not processing messages fast "
216 // Fallthrough on purpose.
218 jack_midi_data_t message_buffer
[3];
219 jack_midi_data_t status
= param1
& 0xff;
220 int length
= GetMessageLength(status
);
223 message_buffer
[2] = param1
& 0xff0000;
224 // Fallthrough on purpose.
226 message_buffer
[1] = param1
& 0xff00;
227 // Fallthrough on purpose.
229 message_buffer
[0] = status
;
232 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
233 "input driver sent an MIM_DATA message with a sysex "
237 jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI "
238 "input driver sent an MIM_DATA message with an invalid "
242 EnqueueMessage(current_frame
, (size_t) length
, message_buffer
);
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
);
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
,
259 if (result
!= MMSYSERR_NOERROR
) {
260 WriteError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer",
265 jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or "
266 "incomplete sysex message received.");
269 jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened.");
274 JackWinMMEInputPort::Start()
277 MMRESULT result
= midiInStart(handle
);
278 started
= result
== MMSYSERR_NOERROR
;
280 WriteError("JackWinMMEInputPort::Start", "midiInStart", result
);
287 JackWinMMEInputPort::Stop()
290 MMRESULT result
= midiInStop(handle
);
291 started
= result
!= MMSYSERR_NOERROR
;
293 WriteError("JackWinMMEInputPort::Stop", "midiInStop", result
);
300 JackWinMMEInputPort::WriteError(const char *jack_func
, const char *mm_func
,
303 char error_message
[MAXERRORLENGTH
];
304 GetErrorString(result
, error_message
);
305 jack_error("%s - %s: %s", jack_func
, mm_func
, error_message
);