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.
23 #include "JackMidiUtil.h"
25 #include "JackWinMMEOutputPort.h"
26 #include "JackGlobals.h"
27 #include "JackEngineControl.h"
29 using Jack::JackWinMMEOutputPort
;
31 ///////////////////////////////////////////////////////////////////////////////
33 ///////////////////////////////////////////////////////////////////////////////
36 JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle
, UINT message
,
37 DWORD_PTR port
, DWORD_PTR param1
,
40 ((JackWinMMEOutputPort
*) port
)->HandleMessage(message
, param1
, param2
);
43 ///////////////////////////////////////////////////////////////////////////////
45 ///////////////////////////////////////////////////////////////////////////////
47 JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name
,
48 const char *client_name
,
49 const char *driver_name
,
54 read_queue
= new JackMidiBufferReadQueue();
55 std::auto_ptr
<JackMidiBufferReadQueue
> read_queue_ptr(read_queue
);
56 thread_queue
= new JackMidiAsyncQueue(max_bytes
, max_messages
);
57 std::auto_ptr
<JackMidiAsyncQueue
> thread_queue_ptr(thread_queue
);
58 thread
= new JackThread(this);
59 std::auto_ptr
<JackThread
> thread_ptr(thread
);
60 char error_message
[MAXERRORLENGTH
];
61 MMRESULT result
= midiOutOpen(&handle
, index
, (DWORD_PTR
)HandleMessageEvent
,
62 (DWORD_PTR
)this, CALLBACK_FUNCTION
);
63 if (result
!= MMSYSERR_NOERROR
) {
64 GetOutErrorString(result
, error_message
);
67 thread_queue_semaphore
= CreateSemaphore(NULL
, 0, max_messages
, NULL
);
68 if (thread_queue_semaphore
== NULL
) {
69 GetOSErrorString(error_message
);
72 sysex_semaphore
= CreateSemaphore(NULL
, 0, 1, NULL
);
73 if (sysex_semaphore
== NULL
) {
74 GetOSErrorString(error_message
);
75 goto destroy_thread_queue_semaphore
;
77 MIDIOUTCAPS capabilities
;
79 result
= midiOutGetDevCaps(index
, &capabilities
, sizeof(capabilities
));
80 if (result
!= MMSYSERR_NOERROR
) {
81 WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps",
83 name_tmp
= (char*)driver_name
;
85 name_tmp
= capabilities
.szPname
;
87 snprintf(alias
, sizeof(alias
) - 1, "%s:%s:out%d", alias_name
, name_tmp
,
89 snprintf(name
, sizeof(name
) - 1, "%s:playback_%d", client_name
, index
+ 1);
90 read_queue_ptr
.release();
91 thread_queue_ptr
.release();
95 destroy_thread_queue_semaphore
:
96 if (! CloseHandle(thread_queue_semaphore
)) {
97 WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle");
100 result
= midiOutClose(handle
);
101 if (result
!= MMSYSERR_NOERROR
) {
102 WriteOutError("JackWinMMEOutputPort [constructor]", "midiOutClose",
106 throw std::runtime_error(error_message
);
109 JackWinMMEOutputPort::~JackWinMMEOutputPort()
111 MMRESULT result
= midiOutReset(handle
);
112 if (result
!= MMSYSERR_NOERROR
) {
113 WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutReset",
116 result
= midiOutClose(handle
);
117 if (result
!= MMSYSERR_NOERROR
) {
118 WriteOutError("JackWinMMEOutputPort [destructor]", "midiOutClose",
121 if (! CloseHandle(sysex_semaphore
)) {
122 WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
124 if (! CloseHandle(thread_queue_semaphore
)) {
125 WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle");
133 JackWinMMEOutputPort::Execute()
136 if (! Wait(thread_queue_semaphore
)) {
137 jack_log("JackWinMMEOutputPort::Execute BREAK");
141 jack_midi_event_t
*event
= thread_queue
->DequeueEvent();
145 jack_time_t frame_time
= GetTimeFromFrames(event
->time
);
146 jack_time_t current_time
= GetMicroSeconds();
147 if (frame_time
> current_time
) {
148 LARGE_INTEGER due_time
;
152 -((LONGLONG
) ((frame_time
- current_time
) * 10));
153 if (! SetWaitableTimer(timer
, &due_time
, 0, NULL
, NULL
, 0)) {
154 WriteOSError("JackWinMMEOutputPort::Execute",
160 jack_log("JackWinMMEOutputPort::Execute - waiting at %f for %f "
161 "milliseconds before sending message (current frame: %d, "
163 ((double) current_time
) / 1000.0,
164 ((double) (frame_time
- current_time
)) / 1000.0,
165 GetFramesFromTime(current_time
), event
->time
);
166 // End debugging code
173 jack_time_t wakeup_time
= GetMicroSeconds();
174 jack_log("JackWinMMEOutputPort::Execute - woke up at %f.",
175 ((double) wakeup_time
) / 1000.0);
176 if (wakeup_time
> frame_time
) {
177 jack_log("JackWinMMEOutputPort::Execute - overslept by %f "
179 ((double) (wakeup_time
- frame_time
)) / 1000.0);
180 } else if (wakeup_time
< frame_time
) {
181 jack_log("JackWinMMEOutputPort::Execute - woke up %f "
182 "milliseconds too early.",
183 ((double) (frame_time
- wakeup_time
)) / 1000.0);
185 // End debugging code
188 jack_midi_data_t
*data
= event
->buffer
;
191 size_t size
= event
->size
;
194 message
|= (((DWORD
) data
[2]) << 16);
195 // Fallthrough on purpose.
197 message
|= (((DWORD
) data
[1]) << 8);
198 // Fallthrough on purpose.
200 message
|= (DWORD
) data
[0];
201 result
= midiOutShortMsg(handle
, message
);
202 if (result
!= MMSYSERR_NOERROR
) {
203 WriteOutError("JackWinMMEOutputPort::Execute",
204 "midiOutShortMsg", result
);
209 header
.dwBufferLength
= size
;
211 header
.lpData
= (LPSTR
) data
;
212 result
= midiOutPrepareHeader(handle
, &header
, sizeof(MIDIHDR
));
213 if (result
!= MMSYSERR_NOERROR
) {
214 WriteOutError("JackWinMMEOutputPort::Execute",
215 "midiOutPrepareHeader", result
);
218 result
= midiOutLongMsg(handle
, &header
, sizeof(MIDIHDR
));
219 if (result
!= MMSYSERR_NOERROR
) {
220 WriteOutError("JackWinMMEOutputPort::Execute", "midiOutLongMsg",
225 // System exclusive messages may be sent synchronously or
226 // asynchronously. The choice is up to the WinMME driver. So, we wait
227 // until the message is sent, regardless of the driver's choice.
228 if (! Wait(sysex_semaphore
)) {
232 result
= midiOutUnprepareHeader(handle
, &header
, sizeof(MIDIHDR
));
233 if (result
!= MMSYSERR_NOERROR
) {
234 WriteOutError("JackWinMMEOutputPort::Execute",
235 "midiOutUnprepareHeader", result
);
243 JackWinMMEOutputPort::GetOutErrorString(MMRESULT error
, LPTSTR text
)
245 MMRESULT result
= midiOutGetErrorText(error
, text
, MAXERRORLENGTH
);
246 if (result
!= MMSYSERR_NOERROR
) {
247 snprintf(text
, MAXERRORLENGTH
, "Unknown MM error code '%d'", error
);
252 JackWinMMEOutputPort::HandleMessage(UINT message
, DWORD_PTR param1
,
255 set_threaded_log_function();
258 jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed.");
261 Signal(sysex_semaphore
);
264 jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened.");
267 LPMIDIHDR header
= (LPMIDIHDR
) param1
;
268 jack_info("JackWinMMEOutputPort::HandleMessage - %d bytes out of %d "
269 "bytes of the current sysex message have been sent.",
270 header
->dwOffset
, header
->dwBytesRecorded
);
275 JackWinMMEOutputPort::Init()
277 set_threaded_log_function();
278 // XX: Can more be done? Ideally, this thread should have the JACK server
279 // thread priority + 1.
280 if (thread
->AcquireSelfRealTime(GetEngineControl()->fServerPriority
)) {
281 jack_error("JackWinMMEOutputPort::Init - could not acquire realtime "
282 "scheduling. Continuing anyway.");
288 JackWinMMEOutputPort::ProcessJack(JackMidiBuffer
*port_buffer
,
289 jack_nframes_t frames
)
291 read_queue
->ResetMidiBuffer(port_buffer
);
292 for (jack_midi_event_t
*event
= read_queue
->DequeueEvent(); event
;
293 event
= read_queue
->DequeueEvent()) {
295 switch (thread_queue
->EnqueueEvent(event
, frames
)) {
296 case JackMidiWriteQueue::BUFFER_FULL
:
297 jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
298 "buffer is full. Dropping event.");
300 case JackMidiWriteQueue::BUFFER_TOO_SMALL
:
301 jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue "
302 "couldn't enqueue a %d-byte event. Dropping event.",
306 Signal(thread_queue_semaphore
);
312 JackWinMMEOutputPort::Signal(HANDLE semaphore
)
314 bool result
= (bool) ReleaseSemaphore(semaphore
, 1, NULL
);
316 WriteOSError("JackWinMMEOutputPort::Signal", "ReleaseSemaphore");
322 JackWinMMEOutputPort::Start()
324 if (thread
->GetStatus() != JackThread::kIdle
) {
327 timer
= CreateWaitableTimer(NULL
, FALSE
, NULL
);
329 WriteOSError("JackWinMMEOutputPort::Start", "CreateWaitableTimer");
332 if (! thread
->StartSync()) {
335 jack_error("JackWinMMEOutputPort::Start - failed to start MIDI processing "
337 if (! CloseHandle(timer
)) {
338 WriteOSError("JackWinMMEOutputPort::Start", "CloseHandle");
344 JackWinMMEOutputPort::Stop()
346 jack_info("JackWinMMEOutputPort::Stop - stopping MIDI output port "
347 "processing thread.");
351 switch (thread
->GetStatus()) {
352 case JackThread::kIniting
:
353 case JackThread::kStarting
:
354 result
= thread
->Kill();
357 case JackThread::kRunning
:
358 Signal(thread_queue_semaphore
);
359 result
= thread
->Stop();
366 jack_error("JackWinMMEOutputPort::Stop - could not %s MIDI processing "
369 if (! CloseHandle(timer
)) {
370 WriteOSError("JackWinMMEOutputPort::Stop", "CloseHandle");
377 JackWinMMEOutputPort::Wait(HANDLE semaphore
)
379 DWORD result
= WaitForSingleObject(semaphore
, INFINITE
);
382 WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject");
387 jack_error("JackWinMMEOutputPort::Wait - unexpected result from "
388 "'WaitForSingleObject'.");
394 JackWinMMEOutputPort::WriteOutError(const char *jack_func
, const char *mm_func
,
397 char error_message
[MAXERRORLENGTH
];
398 GetOutErrorString(result
, error_message
);
399 jack_error("%s - %s: %s", jack_func
, mm_func
, error_message
);