23 #include "JackMidiUtil.h"
25 #include "JackWinMMEOutputPort.h"
26 #include "JackGlobals.h"
27 #include "JackEngineControl.h"
36 JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message,
37 DWORD_PTR port, DWORD_PTR param1,
47 JackWinMMEOutputPort::JackWinMMEOutputPort(
const char *alias_name,
48 const char *client_name,
49 const char *driver_name,
54 read_queue =
new JackMidiBufferReadQueue();
55 std::unique_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
56 thread_queue =
new JackMidiAsyncQueue(max_bytes, max_messages);
57 std::unique_ptr<JackMidiAsyncQueue> thread_queue_ptr(thread_queue);
58 thread =
new JackThread(
this);
59 std::unique_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 strncpy(device_name, name_tmp,
sizeof(device_name) - 1);
91 read_queue_ptr.release();
92 thread_queue_ptr.release();
96 destroy_thread_queue_semaphore:
97 if (! CloseHandle(thread_queue_semaphore)) {
98 WriteOSError(
"JackWinMMEOutputPort [constructor]",
"CloseHandle");
101 result = midiOutClose(handle);
102 if (result != MMSYSERR_NOERROR) {
103 WriteOutError(
"JackWinMMEOutputPort [constructor]",
"midiOutClose",
107 throw std::runtime_error(error_message);
110 JackWinMMEOutputPort::~JackWinMMEOutputPort()
112 MMRESULT result = midiOutReset(handle);
113 if (result != MMSYSERR_NOERROR) {
114 WriteOutError(
"JackWinMMEOutputPort [destructor]",
"midiOutReset",
117 result = midiOutClose(handle);
118 if (result != MMSYSERR_NOERROR) {
119 WriteOutError(
"JackWinMMEOutputPort [destructor]",
"midiOutClose",
122 if (! CloseHandle(sysex_semaphore)) {
123 WriteOSError(
"JackWinMMEOutputPort [destructor]",
"CloseHandle");
125 if (! CloseHandle(thread_queue_semaphore)) {
126 WriteOSError(
"JackWinMMEOutputPort [destructor]",
"CloseHandle");
134 JackWinMMEOutputPort::Execute()
137 if (! Wait(thread_queue_semaphore)) {
138 jack_log(
"JackWinMMEOutputPort::Execute BREAK");
146 jack_time_t frame_time = GetTimeFromFrames(event->time);
147 jack_time_t current_time = GetMicroSeconds();
148 if (frame_time > current_time) {
149 LARGE_INTEGER due_time;
153 -((LONGLONG) ((frame_time - current_time) * 10));
154 if (! SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0)) {
155 WriteOSError(
"JackWinMMEOutputPort::Execute",
161 jack_log(
"JackWinMMEOutputPort::Execute - waiting at %f for %f "
162 "milliseconds before sending message (current frame: %d, "
164 ((
double) current_time) / 1000.0,
165 ((
double) (frame_time - current_time)) / 1000.0,
166 GetFramesFromTime(current_time), event->time);
174 jack_time_t wakeup_time = GetMicroSeconds();
175 jack_log(
"JackWinMMEOutputPort::Execute - woke up at %f.",
176 ((
double) wakeup_time) / 1000.0);
177 if (wakeup_time > frame_time) {
178 jack_log(
"JackWinMMEOutputPort::Execute - overslept by %f "
180 ((
double) (wakeup_time - frame_time)) / 1000.0);
181 }
else if (wakeup_time < frame_time) {
182 jack_log(
"JackWinMMEOutputPort::Execute - woke up %f "
183 "milliseconds too early.",
184 ((
double) (frame_time - wakeup_time)) / 1000.0);
189 jack_midi_data_t *data =
event->buffer;
192 size_t size =
event->size;
195 message |= (((DWORD) data[2]) << 16);
198 message |= (((DWORD) data[1]) << 8);
201 message |= (DWORD) data[0];
202 result = midiOutShortMsg(handle, message);
203 if (result != MMSYSERR_NOERROR) {
204 WriteOutError(
"JackWinMMEOutputPort::Execute",
205 "midiOutShortMsg", result);
210 header.dwBufferLength = size;
212 header.lpData = (LPSTR) data;
213 result = midiOutPrepareHeader(handle, &header,
sizeof(MIDIHDR));
214 if (result != MMSYSERR_NOERROR) {
215 WriteOutError(
"JackWinMMEOutputPort::Execute",
216 "midiOutPrepareHeader", result);
219 result = midiOutLongMsg(handle, &header,
sizeof(MIDIHDR));
220 if (result != MMSYSERR_NOERROR) {
221 WriteOutError(
"JackWinMMEOutputPort::Execute",
"midiOutLongMsg",
229 if (! Wait(sysex_semaphore)) {
233 result = midiOutUnprepareHeader(handle, &header,
sizeof(MIDIHDR));
234 if (result != MMSYSERR_NOERROR) {
235 WriteOutError(
"JackWinMMEOutputPort::Execute",
236 "midiOutUnprepareHeader", result);
244 JackWinMMEOutputPort::GetOutErrorString(MMRESULT error, LPTSTR text)
246 MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH);
247 if (result != MMSYSERR_NOERROR) {
248 snprintf(text, MAXERRORLENGTH,
"Unknown MM error code '%d'", error);
253 JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1,
256 set_threaded_log_function();
259 jack_log(
"JackWinMMEOutputPort::HandleMessage - MIDI device closed.");
262 Signal(sysex_semaphore);
265 jack_log(
"JackWinMMEOutputPort::HandleMessage - MIDI device opened.");
268 LPMIDIHDR header = (LPMIDIHDR) param1;
269 jack_log(
"JackWinMMEOutputPort::HandleMessage - %d bytes out of %d "
270 "bytes of the current sysex message have been sent.",
271 header->dwOffset, header->dwBytesRecorded);
278 set_threaded_log_function();
281 if (thread->AcquireSelfRealTime(GetEngineControl()->fServerPriority)) {
282 jack_error(
"JackWinMMEOutputPort::Init - could not acquire realtime "
283 "scheduling. Continuing anyway.");
290 jack_nframes_t frames)
297 case JackMidiWriteQueue::BUFFER_FULL:
298 jack_error(
"JackWinMMEOutputPort::ProcessJack - The thread queue "
299 "buffer is full. Dropping event.");
301 case JackMidiWriteQueue::BUFFER_TOO_SMALL:
302 jack_error(
"JackWinMMEOutputPort::ProcessJack - The thread queue "
303 "couldn't enqueue a %d-byte event. Dropping event.",
307 Signal(thread_queue_semaphore);
313 JackWinMMEOutputPort::Signal(HANDLE semaphore)
315 bool result = (bool) ReleaseSemaphore(semaphore, 1, NULL);
317 WriteOSError(
"JackWinMMEOutputPort::Signal",
"ReleaseSemaphore");
323 JackWinMMEOutputPort::Start()
325 if (thread->GetStatus() != JackThread::kIdle) {
328 timer = CreateWaitableTimer(NULL, FALSE, NULL);
330 WriteOSError(
"JackWinMMEOutputPort::Start",
"CreateWaitableTimer");
333 if (! thread->StartSync()) {
336 jack_error(
"JackWinMMEOutputPort::Start - failed to start MIDI processing "
338 if (! CloseHandle(timer)) {
339 WriteOSError(
"JackWinMMEOutputPort::Start",
"CloseHandle");
345 JackWinMMEOutputPort::Stop()
347 jack_log(
"JackWinMMEOutputPort::Stop - stopping MIDI output port "
348 "processing thread.");
352 switch (thread->GetStatus()) {
353 case JackThread::kIniting:
354 case JackThread::kStarting:
355 result = thread->Kill();
358 case JackThread::kRunning:
359 Signal(thread_queue_semaphore);
360 result = thread->Stop();
367 jack_error(
"JackWinMMEOutputPort::Stop - could not %s MIDI processing "
370 if (! CloseHandle(timer)) {
371 WriteOSError(
"JackWinMMEOutputPort::Stop",
"CloseHandle");
378 JackWinMMEOutputPort::Wait(HANDLE semaphore)
380 DWORD result = WaitForSingleObject(semaphore, INFINITE);
383 WriteOSError(
"JackWinMMEOutputPort::Wait",
"WaitForSingleObject");
388 jack_error(
"JackWinMMEOutputPort::Wait - unexpected result from "
389 "'WaitForSingleObject'.");
395 JackWinMMEOutputPort::WriteOutError(
const char *jack_func,
const char *mm_func,
398 char error_message[MAXERRORLENGTH];
399 GetOutErrorString(result, error_message);
400 jack_error(
"%s - %s: %s", jack_func, mm_func, error_message);
jack_midi_event_t * DequeueEvent()
void ResetMidiBuffer(JackMidiBuffer *buffer)
SERVER_EXPORT void jack_error(const char *fmt,...)
virtual jack_midi_event_t * DequeueEvent()
SERVER_EXPORT void jack_log(const char *fmt,...)
virtual EnqueueResult EnqueueEvent(jack_nframes_t time, size_t size, jack_midi_data_t *buffer)