diff --git a/windows/winmme/JackWinMMEDriver.cpp b/windows/winmme/JackWinMMEDriver.cpp index d3635c64..455118a2 100644 --- a/windows/winmme/JackWinMMEDriver.cpp +++ b/windows/winmme/JackWinMMEDriver.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,401 +18,285 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "JackWinMMEDriver.h" -#include "JackGraphManager.h" #include "JackEngineControl.h" -#include "JackDriverLoader.h" - -#include -#include -#include -#include - -#include -#include -#include +#include "JackWinMMEDriver.h" -namespace Jack -{ +using Jack::JackWinMMEDriver; -static bool InitHeaders(MidiSlot* slot) +JackWinMMEDriver::JackWinMMEDriver(const char *name, const char *alias, + JackLockedEngine *engine, + JackSynchro *table): + JackMidiDriver(name, alias, engine, table) { - slot->fHeader = (LPMIDIHDR)GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE|GMEM_ZEROINIT, sizeof(MIDIHDR) + kBuffSize); - if (!slot->fHeader) - return false; - - slot->fHeader->lpData = (LPSTR)((LPBYTE)slot->fHeader + sizeof(MIDIHDR)); - slot->fHeader->dwBufferLength = kBuffSize; - slot->fHeader->dwFlags = 0; - slot->fHeader->dwUser = 0; - slot->fHeader->lpNext = 0; - slot->fHeader->dwBytesRecorded = 0; - return true; + fCaptureChannels = 0; + fPlaybackChannels = 0; + input_ports = 0; + output_ports = 0; } -void CALLBACK JackWinMMEDriver::MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD userData, DWORD dwParam1, DWORD dwParam2) +JackWinMMEDriver::~JackWinMMEDriver() { - jack_ringbuffer_t* ringbuffer = (jack_ringbuffer_t*)userData; - //jack_info("JackWinMMEDriver::MidiInProc 0\n"); - - switch (wMsg) { - case MIM_OPEN: - break; - - case MIM_ERROR: - case MIM_DATA: { - - //jack_info("JackWinMMEDriver::MidiInProc"); - - // One event - unsigned int num_packet = 1; - jack_ringbuffer_write(ringbuffer, (char*)&num_packet, sizeof(unsigned int)); - - // Write event actual data - jack_ringbuffer_write(ringbuffer, (char*)&dwParam1, 3); - break; - } - - case MIM_LONGERROR: - case MIM_LONGDATA: - /* - Nothing for now - */ - break; - } + Stop(); + Close(); } -JackWinMMEDriver::JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) - : JackMidiDriver(name, alias, engine, table), - fRealCaptureChannels(0), - fRealPlaybackChannels(0), - fMidiSource(NULL), - fMidiDestination(NULL) -{} - -JackWinMMEDriver::~JackWinMMEDriver() -{} - -int JackWinMMEDriver::Open(bool capturing, - bool playing, - int inchannels, - int outchannels, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency) +int +JackWinMMEDriver::Attach() { + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + jack_port_id_t index; + jack_nframes_t latency = buffer_size; + jack_latency_range_t latency_range; + const char *name; + JackPort *port; + latency_range.max = latency; + latency_range.min = latency; + + // Inputs + for (int i = 0; i < fCaptureChannels; i++) { + JackWinMMEInputPort *input_port = input_ports[i]; + name = input_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + CaptureDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackWinMMEDriver::Attach - cannot register input port " + "with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(input_port->GetAlias()); + port->SetLatencyRange(JackCaptureLatency, &latency_range); + fCapturePortList[i] = index; + } - jack_log("JackWinMMEDriver::Open"); - - fRealCaptureChannels = midiInGetNumDevs(); - fRealPlaybackChannels = midiOutGetNumDevs(); - - // Generic JackMidiDriver Open - if (JackMidiDriver::Open(capturing, playing, fRealCaptureChannels, fRealPlaybackChannels, monitor, capture_driver_name, playback_driver_name, capture_latency, playback_latency) != 0) - return -1; - - fMidiDestination = new MidiSlot[fRealCaptureChannels]; - assert(fMidiDestination); + if (! fEngineControl->fSyncMode) { + latency += buffer_size; + latency_range.max = latency; + latency_range.min = latency; + } - // Real input - int devindex = 0; - for (int i = 0; i < fRealCaptureChannels; i++) { + // Outputs + for (int i = 0; i < fPlaybackChannels; i++) { + JackWinMMEOutputPort *output_port = output_ports[i]; + name = output_port->GetName(); + index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, + JACK_DEFAULT_MIDI_TYPE, + PlaybackDriverFlags, buffer_size); + if (index == NO_PORT) { + jack_error("JackWinMMEDriver::Attach - cannot register output " + "port with name '%s'.", name); + // X: Do we need to deallocate ports? + return -1; + } + port = fGraphManager->GetPort(index); + port->SetAlias(output_port->GetAlias()); + port->SetLatencyRange(JackPlaybackLatency, &latency_range); + fPlaybackPortList[i] = index; + } - HMIDIIN handle; - fMidiDestination[devindex].fIndex = i; - MMRESULT ret = midiInOpen(&handle, fMidiDestination[devindex].fIndex, (DWORD)MidiInProc, (DWORD)fRingBuffer[devindex], CALLBACK_FUNCTION); + return 0; +} - if (ret == MMSYSERR_NOERROR) { - fMidiDestination[devindex].fHandle = handle; - if (!InitHeaders(&fMidiDestination[devindex])) { - jack_error("memory allocation failed"); - midiInClose(handle); - continue; - } - ret = midiInPrepareHeader(handle, fMidiDestination[devindex].fHeader, sizeof(MIDIHDR)); - - if (ret == MMSYSERR_NOERROR) { - fMidiDestination[devindex].fHeader->dwUser = 1; - ret = midiInAddBuffer(handle, fMidiDestination[devindex].fHeader, sizeof(MIDIHDR)); - if (ret == MMSYSERR_NOERROR) { - ret = midiInStart(handle); - if (ret != MMSYSERR_NOERROR) { - jack_error("midiInStart error"); - CloseInput(&fMidiDestination[devindex]); - continue; - } - } else { - jack_error ("midiInAddBuffer error"); - CloseInput(&fMidiDestination[devindex]); - continue; - } - } else { - jack_error("midiInPrepareHeader error"); - midiInClose(handle); - continue; - } - } else { - jack_error ("midiInOpen error"); - continue; +int +JackWinMMEDriver::Close() +{ + int result = JackMidiDriver::Close(); + if (input_ports) { + for (int i = 0; i < fCaptureChannels; i++) { + delete input_ports[i]; } - devindex += 1; + delete[] input_ports; + input_ports = 0; } - fRealCaptureChannels = devindex; - fCaptureChannels = devindex; - - fMidiSource = new MidiSlot[fRealPlaybackChannels]; - assert(fMidiSource); - - // Real output - devindex = 0; - for (int i = 0; i < fRealPlaybackChannels; i++) { - MMRESULT res; - HMIDIOUT handle; - fMidiSource[devindex].fIndex = i; - UINT ret = midiOutOpen(&handle, fMidiSource[devindex].fIndex, 0L, 0L, CALLBACK_NULL); - if (ret == MMSYSERR_NOERROR) { - fMidiSource[devindex].fHandle = handle; - if (!InitHeaders(&fMidiSource[devindex])) { - jack_error("memory allocation failed"); - midiOutClose(handle); - continue; - } - res = midiOutPrepareHeader(handle, fMidiSource[devindex].fHeader, sizeof(MIDIHDR)); - if (res != MMSYSERR_NOERROR) { - jack_error("midiOutPrepareHeader error %d %d %d", i, handle, res); - continue; - } else { - fMidiSource[devindex].fHeader->dwUser = 1; - } - } else { - jack_error("midiOutOpen error"); - continue; + if (output_ports) { + for (int i = 0; i < fPlaybackChannels; i++) { + delete output_ports[i]; } - devindex += 1; + delete[] output_ports; + output_ports = 0; } - fRealPlaybackChannels = devindex; - fPlaybackChannels = devindex; - return 0; + return result; } -void JackWinMMEDriver::CloseInput(MidiSlot* slot) +int +JackWinMMEDriver::Open(bool capturing, bool playing, int in_channels, + int out_channels, bool monitor, + const char* capture_driver_name, + const char* playback_driver_name, + jack_nframes_t capture_latency, + jack_nframes_t playback_latency) { - MMRESULT res; - int retry = 0; - - if (slot->fHandle == 0) - return; - - HMIDIIN handle = (HMIDIIN)slot->fHandle; - slot->fHeader->dwUser = 0; - res = midiInStop(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInStop error"); + const char *client_name = fClientControl.fName; + int input_count = 0; + int num_potential_inputs = midiInGetNumDevs(); + int num_potential_outputs = midiOutGetNumDevs(); + int output_count = 0; + if (num_potential_inputs) { + try { + input_ports = new JackWinMMEInputPort *[num_potential_inputs]; + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating input port " + "array: %s", e.what()); + return -1; + } + for (int i = 0; i < num_potential_inputs; i++) { + try { + input_ports[input_count] = + new JackWinMMEInputPort(fAliasName, client_name, + capture_driver_name, i); + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating input " + "port: %s", e.what()); + continue; + } + input_count++; + } } - res = midiInReset(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInReset error"); + if (num_potential_outputs) { + try { + output_ports = new JackWinMMEOutputPort *[num_potential_outputs]; + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating output port " + "array: %s", e.what()); + goto destroy_input_ports; + } + for (int i = 0; i < num_potential_outputs; i++) { + try { + output_ports[output_count] = + new JackWinMMEOutputPort(fAliasName, client_name, + playback_driver_name, i); + } catch (std::exception e) { + jack_error("JackWinMMEDriver::Open - while creating output " + "port: %s", e.what()); + continue; + } + output_count++; + } } - res = midiInUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInUnprepareHeader error"); + if (! (input_count || output_count)) { + jack_error("JackWinMMEDriver::Open - no WinMME inputs or outputs " + "allocated."); + } else if (! JackMidiDriver::Open(capturing, playing, input_count, + output_count, monitor, + capture_driver_name, + playback_driver_name, capture_latency, + playback_latency)) { + return 0; } - do { - res = midiInClose(handle); - if (res != MMSYSERR_NOERROR) { - jack_error("midiInClose error"); + + destroy_input_ports: + if (input_ports) { + for (int i = 0; i < input_count; i++) { + delete input_ports[i]; } - if (res == MIDIERR_STILLPLAYING) - midiInReset(handle); - Sleep (10); - retry++; - } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); - - if (slot->fHeader) { - GlobalFreePtr(slot->fHeader); + delete[] input_ports; + input_ports = 0; } + return -1; } -void JackWinMMEDriver::CloseOutput(MidiSlot* slot) +int +JackWinMMEDriver::Read() { - MMRESULT res; - int retry = 0; - - if (slot->fHandle == 0) - return; - - HMIDIOUT handle = (HMIDIOUT)slot->fHandle; - res = midiOutReset(handle); - if (res != MMSYSERR_NOERROR) - jack_error("midiOutReset error"); - midiOutUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); - do { - res = midiOutClose(handle); - if (res != MMSYSERR_NOERROR) - jack_error("midiOutClose error"); - Sleep(10); - retry++; - } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); - - if (slot->fHeader) { - GlobalFreePtr(slot->fHeader); + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fCaptureChannels; i++) { + input_ports[i]->ProcessJack(GetInputBuffer(i), buffer_size); } + return 0; } -int JackWinMMEDriver::Close() +int +JackWinMMEDriver::Start() { - jack_log("JackWinMMEDriver::Close"); + jack_info("JackWinMMEDriver::Start - Starting driver."); + + JackMidiDriver::Start(); + + int input_count = 0; + int output_count = 0; - // Generic midi driver close - int res = JackMidiDriver::Close(); + jack_info("JackWinMMEDriver::Start - Enabling input ports."); - // Close input - if (fMidiDestination) { - for (int i = 0; i < fRealCaptureChannels; i++) { - CloseInput(&fMidiDestination[i]); + for (; input_count < fCaptureChannels; input_count++) { + if (input_ports[input_count]->Start() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to enable input " + "port."); + goto stop_input_ports; } - delete[] fMidiDestination; } - // Close output - if (fMidiSource) { - for (int i = 0; i < fRealPlaybackChannels; i++) { - CloseOutput(&fMidiSource[i]); + jack_info("JackWinMMEDriver::Start - Enabling output ports."); + + for (; output_count < fPlaybackChannels; output_count++) { + if (output_ports[output_count]->Start() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to enable output " + "port."); + goto stop_output_ports; } - delete[] fMidiSource; } - return res; -} + jack_info("JackWinMMEDriver::Start - Driver started."); -int JackWinMMEDriver::Attach() -{ - JackPort* port; - jack_port_id_t port_index; - char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; - MMRESULT res; - int i; - - jack_log("JackMidiDriver::Attach fBufferSize = %ld fSampleRate = %ld", fEngineControl->fBufferSize, fEngineControl->fSampleRate); - - for (i = 0; i < fCaptureChannels; i++) { - MIDIINCAPS caps; - res = midiInGetDevCaps(fMidiDestination[i].fIndex, &caps, sizeof(caps)); - if (res == MMSYSERR_NOERROR) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fCaptureDriverName, i + 1); - } - snprintf(name, sizeof(name) - 1, "%s:capture_%d", fClientControl.fName, i + 1); - - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, CaptureDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; + return 0; + + stop_output_ports: + for (int i = 0; i < output_count; i++) { + if (output_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to disable output " + "port."); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fCapturePortList[i] = port_index; - jack_log("JackMidiDriver::Attach fCapturePortList[i] port_index = %ld", port_index); } - - for (i = 0; i < fPlaybackChannels; i++) { - MIDIOUTCAPS caps; - res = midiOutGetDevCaps(fMidiSource[i].fIndex, &caps, sizeof(caps)); - if (res == MMSYSERR_NOERROR) { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, caps.szPname, i + 1); - } else { - snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName, fPlaybackDriverName, i + 1); - } - snprintf(name, sizeof(name) - 1, "%s:playback_%d", fClientControl.fName, i + 1); - - if ((port_index = fGraphManager->AllocatePort(fClientControl.fRefNum, name, JACK_DEFAULT_MIDI_TYPE, PlaybackDriverFlags, fEngineControl->fBufferSize)) == NO_PORT) { - jack_error("driver: cannot register port for %s", name); - return -1; + stop_input_ports: + for (int i = 0; i < input_count; i++) { + if (input_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Start - Failed to disable input " + "port."); } - port = fGraphManager->GetPort(port_index); - port->SetAlias(alias); - fPlaybackPortList[i] = port_index; - jack_log("JackMidiDriver::Attach fPlaybackPortList[i] port_index = %ld", port_index); } - return 0; + return -1; } -int JackWinMMEDriver::Read() +int +JackWinMMEDriver::Stop() { - size_t size; + int result = 0; - for (int chan = 0; chan < fCaptureChannels; chan++) { + jack_info("JackWinMMEDriver::Stop - disabling input ports."); - if (fGraphManager->GetConnectionsNum(fCapturePortList[chan]) > 0) { - - JackMidiBuffer* midi_buffer = GetInputBuffer(chan); - - if (jack_ringbuffer_read_space (fRingBuffer[chan]) == 0) { - // Reset buffer - midi_buffer->Reset(midi_buffer->nframes); - } else { - - while ((size = jack_ringbuffer_read_space (fRingBuffer[chan])) > 0) { + for (int i = 0; i < fCaptureChannels; i++) { + if (input_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Stop - Failed to disable input " + "port."); + result = -1; + } + } - //jack_info("jack_ringbuffer_read_space %d", size); - int ev_count = 0; - jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); + jack_info("JackWinMMEDriver::Stop - disabling output ports."); - if (ev_count > 0) { - for (int j = 0; j < ev_count; j++) { - unsigned int event_len = 3; - // Read event actual data - jack_midi_data_t* dest = midi_buffer->ReserveEvent(0, event_len); - jack_ringbuffer_read(fRingBuffer[chan], (char*)dest, event_len); - } - } - } - } - } else { - //jack_info("Consume ring buffer"); - jack_ringbuffer_read_advance(fRingBuffer[chan], jack_ringbuffer_read_space(fRingBuffer[chan])); + for (int i = 0; i < fPlaybackChannels; i++) { + if (output_ports[i]->Stop() < 0) { + jack_error("JackWinMMEDriver::Stop - Failed to disable output " + "port."); + result = -1; } } - return 0; + + return result; } -int JackWinMMEDriver::Write() +int +JackWinMMEDriver::Write() { - for (int chan = 0; chan < fPlaybackChannels; chan++) { - - if (fGraphManager->GetConnectionsNum(fPlaybackPortList[chan]) > 0) { - - JackMidiBuffer* midi_buffer = GetOutputBuffer(chan); - - // TODO : use timestamp - - for (unsigned int j = 0; j < midi_buffer->event_count; j++) { - JackMidiEvent* ev = &midi_buffer->events[j]; - if (ev->size <= 3) { - jack_midi_data_t *d = ev->GetData(midi_buffer); - DWORD winev = 0; - if (ev->size > 0) winev |= d[0]; - if (ev->size > 1) winev |= (d[1] << 8); - if (ev->size > 2) winev |= (d[2] << 16); - MMRESULT res = midiOutShortMsg((HMIDIOUT)fMidiSource[chan].fHandle, winev); - if (res != MMSYSERR_NOERROR) - jack_error ("midiOutShortMsg error res %d", res); - } else { - - } - } - } + jack_nframes_t buffer_size = fEngineControl->fBufferSize; + for (int i = 0; i < fPlaybackChannels; i++) { + output_ports[i]->ProcessJack(GetOutputBuffer(i), buffer_size); } - return 0; } -} // end of namespace - #ifdef __cplusplus extern "C" { diff --git a/windows/winmme/JackWinMMEDriver.h b/windows/winmme/JackWinMMEDriver.h index 6c28a0ef..52ae5f2b 100644 --- a/windows/winmme/JackWinMMEDriver.h +++ b/windows/winmme/JackWinMMEDriver.h @@ -1,5 +1,6 @@ /* Copyright (C) 2009 Grame +Copyright (C) 2011 Devin Anderson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,67 +22,51 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. #define __JackWinMMEDriver__ #include "JackMidiDriver.h" -#include "JackTime.h" +#include "JackWinMMEInputPort.h" +#include "JackWinMMEOutputPort.h" -namespace Jack -{ +namespace Jack { -/*! -\brief The WinMME driver. -*/ - -#define kBuffSize 512 - -struct MidiSlot { - - LPVOID fHandle; // MMSystem handler - short fIndex; // MMSystem dev index - LPMIDIHDR fHeader; // for long msg output - - MidiSlot():fHandle(0),fIndex(0) - {} + class JackWinMMEDriver : public JackMidiDriver { -}; + private: -class JackWinMMEDriver : public JackMidiDriver -{ + JackWinMMEInputPort **input_ports; + JackWinMMEOutputPort **output_ports; - private: + public: - int fRealCaptureChannels; - int fRealPlaybackChannels; + JackWinMMEDriver(const char* name, const char* alias, + JackLockedEngine* engine, JackSynchro* table); - MidiSlot* fMidiSource; - MidiSlot* fMidiDestination; + ~JackWinMMEDriver(); - void CloseInput(MidiSlot* slot); - void CloseOutput(MidiSlot* slot); + int + Attach(); - static void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); + int + Close(); - public: + int + Open(bool capturing, bool playing, int num_inputs, int num_outputs, + bool monitor, const char* capture_driver_name, + const char* playback_driver_name, jack_nframes_t capture_latency, + jack_nframes_t playback_latency); - JackWinMMEDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table); - virtual ~JackWinMMEDriver(); + int + Read(); - int Open(bool capturing, - bool playing, - int chan_in, - int chan_out, - bool monitor, - const char* capture_driver_name, - const char* playback_driver_name, - jack_nframes_t capture_latency, - jack_nframes_t playback_latency); - int Close(); + int + Start(); - int Attach(); + int + Stop(); - int Read(); - int Write(); + int + Write(); -}; + }; -} // end of namespace +} #endif diff --git a/windows/winmme/JackWinMMEInputPort.cpp b/windows/winmme/JackWinMMEInputPort.cpp new file mode 100644 index 00000000..f5c74023 --- /dev/null +++ b/windows/winmme/JackWinMMEInputPort.cpp @@ -0,0 +1,287 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include + +#include "JackError.h" +#include "JackMidiUtil.h" +#include "JackWinMMEInputPort.h" + +using Jack::JackWinMMEInputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void CALLBACK +JackWinMMEInputPort::HandleMidiInputEvent(HMIDIIN handle, UINT message, + DWORD port, DWORD param1, + DWORD param2) +{ + ((JackWinMMEInputPort *) port)->ProcessWinMME(message, param1, param2); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackWinMMEInputPort::JackWinMMEInputPort(const char *alias_name, + const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes, size_t max_messages) +{ + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + write_queue = new JackMidiBufferWriteQueue(); + std::auto_ptr write_queue_ptr(write_queue); + sysex_buffer = new jack_midi_data_t[max_bytes]; + char error_message[MAXERRORLENGTH]; + MMRESULT result = midiInOpen(&handle, index, HandleMidiInputEvent, this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto delete_sysex_buffer; + } + sysex_header.dwBufferLength = max_bytes; + sysex_header.dwBytesRecorded = 0; + sysex_header.dwFlags = 0; + sysex_header.dwUser = 0; + sysex_header.lpData = (((LPBYTE) sysex_header) + sizeof(MIDIHDR)); + sysex_header.lpNext = 0; + result = midiInPrepareHeader(handle, &sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto close_handle; + } + result = midiInAddBuffer(handle, &sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto unprepare_header; + } + + jack_event = 0; + started = false; + write_queue_ptr.release(); + thread_queue_ptr.release(); + return; + + unprepare_header: + result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [constructor]", + "midiInUnprepareHeader", result); + } + close_handle: + result = midiInClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [constructor]", "midiInClose", result); + } + delete_sysex_buffer: + delete[] sysex_buffer; + throw std::runtime_error(error_message); +} + +JackWinMMEInputPort::~JackWinMMEInputPort() +{ + Stop(); + MMRESULT result = midiInReset(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInReset", result); + } + result = midiInUnprepareHeader(handle, sysex_header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInUnprepareHeader", + result); + } + result = midiInClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort [destructor]", "midiInClose", result); + } + delete[] sysex_buffer; + delete thread_queue; + delete write_queue; +} + +void +JackWinMMEInputPort::EnqueueMessage(jack_nframes_t time, size_t length, + jack_midi_data_t *data) +{ + switch (thread_queue->EnqueueEvent(time, length, data)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " + "cannot currently accept a %d-byte event. Dropping event.", + size); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackWinMMEInputPort::EnqueueMessage - The thread queue " + "buffer is too small to enqueue a %d-byte event. Dropping " + "event.", size); + break; + default: + ; + } +} + +const char * +JackWinMMEInputPort::GetAlias() +{ + return alias; +} + +void +JackWinMMEInputPort::GetErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiInGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown error code '%d'", error); + } +} + +const char * +JackWinMMEInputPort::GetName() +{ + return name; +} + +void +JackWinMMEInputPort::ProcessJack() +{ + write_queue->ResetMidiBuffer(port_buffer, frames); + if (! jack_event) { + jack_event = thread_queue->DequeueEvent(); + } + for (; jack_event; jack_event = thread_queue->DequeueEvent()) { + switch (write_queue->EnqueueEvent(event)) { + case BUFFER_TOO_SMALL: + jack_error("JackWinMMEMidiInputPort::Process - The buffer write " + "queue couldn't enqueue a %d-byte event. Dropping " + "event.", event->size); + // Fallthrough on purpose + case OK: + continue; + } + break; + } +} + +void +JackWinMMEInputPort::ProcessWinMME(UINT message, DWORD param1, DWORD param2) +{ + jack_nframes_t current_frame = GetCurrentFrame(); + switch (message) { + case MIM_CLOSE: + jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device closed."); + break; + case MIM_MOREDATA: + jack_info("JackWinMMEInputPort::ProcessWinMME - The MIDI input device " + "driver thinks that JACK is not processing messages fast " + "enough."); + // Fallthrough on purpose. + case MIM_DATA: + jack_midi_data_t message_buffer[3]; + jack_midi_data_t status = param1 & 0xff; + int length = GetMessageLength(status); + switch (length) { + case 3: + message_buffer[2] = param1 & 0xff0000; + // Fallthrough on purpose. + case 2: + message_buffer[1] = param1 & 0xff00; + // Fallthrough on purpose. + case 1: + message_buffer[0] = status; + break; + case 0: + jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " + "input driver sent an MIM_DATA message with a sysex " + "status byte."); + return; + case -1: + jack_error("JackWinMMEInputPort::ProcessWinMME - **BUG** MIDI " + "input driver sent an MIM_DATA message with an invalid " + "status byte."); + return; + } + EnqueueMessage(current_frame, (size_t) length, message_buffer); + break; + case MIM_LONGDATA: + LPMIDIHDR header = (LPMIDIHDR) dwParam1; + jack_midi_data_t *data = (jack_midi_data_t *) header->lpData; + size_t length = header->dwBytesRecorded; + if ((data[0] != 0xf0) || (data[length - 1] != 0xf7)) { + jack_error("JackWinMMEInputPort::ProcessWinMME - Discarding " + "%d-byte sysex chunk.", length); + } else { + EnqueueMessage(current_frame, length, data); + } + // Is this realtime-safe? This function isn't run in the JACK thread, + // but we still want it to perform as quickly as possible. Even if + // this isn't realtime safe, it may not be avoidable. + MMRESULT result = midiInAddBuffer(handle, &sysex_header, + sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteError("JackWinMMEInputPort::ProcessWinMME", "midiInAddBuffer", + result); + } + break; + case MIM_LONGERROR: + jack_error("JackWinMMEInputPort::ProcessWinMME - Invalid or " + "incomplete sysex message received."); + break; + case MIM_OPEN: + jack_info("JackWinMMEInputPort::ProcessWinMME - MIDI device opened."); + } +} + +bool +JackWinMMEInputPort::Start() +{ + if (! started) { + MMRESULT result = midiInStart(handle); + started = result == MMSYSERR_NOERROR; + if (! started) { + WriteError("JackWinMMEInputPort::Start", "midiInStart", result); + } + } + return started; +} + +bool +JackWinMMEInputPort::Stop() +{ + if (started) { + MMRESULT result = midiInStop(handle); + started = result != MMSYSERR_NOERROR; + if (started) { + WriteError("JackWinMMEInputPort::Stop", "midiInStop", result); + } + } + return ! started; +} + +void +JackWinMMEInputPort::WriteError(const char *jack_func, const char *mm_func, + MMRESULT result) +{ + const char error_message[MAXERRORLENGTH]; + GetErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, mm_func, error_message); +} diff --git a/windows/winmme/JackWinMMEInputPort.h b/windows/winmme/JackWinMMEInputPort.h new file mode 100644 index 00000000..5ce042b9 --- /dev/null +++ b/windows/winmme/JackWinMMEInputPort.h @@ -0,0 +1,89 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEInputPort__ +#define __JackWinMMEInputPort__ + +#include + +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferWriteQueue.h" + +namespace Jack { + + class JackWinMMEInputPort { + + private: + + static void CALLBACK + HandleMidiInputEvent(HMIDIIN handle, UINT message, DWORD port, + DWORD param1, DWORD param2); + + void + EnqueueMessage(jack_nframes_t time, size_t length, + jack_midi_data_t *data); + + void + GetErrorString(MMRESULT error, LPTSTR text); + + void + ProcessWinMME(UINT message, DWORD param1, DWORD param2); + + void + WriteError(const char *jack_func, const char *mm_func, + MMRESULT result); + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + HMIDIIN handle; + jack_midi_event_t *jack_event; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + bool started; + jack_midi_data_t *sysex_buffer; + MIDIHDR sysex_header + JackMidiAsyncQueue *thread_queue; + JackMidiBufferWriteQueue *write_queue; + + public: + + JackWinMMEInputPort(const char *alias_name, const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes=4096, size_t max_messages=1024); + + ~JackWinMMEInputPort(); + + const char * + GetAlias(); + + const char * + GetName(); + + void + ProcessJack(); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif diff --git a/windows/winmme/JackWinMMEOutputPort.cpp b/windows/winmme/JackWinMMEOutputPort.cpp new file mode 100644 index 00000000..8f25feaf --- /dev/null +++ b/windows/winmme/JackWinMMEOutputPort.cpp @@ -0,0 +1,352 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +#include "JackMidiUtil.h" +#include "JackTime.h" +#include "JackWinMMEOutputPort.h" + +using Jack::JackWinMMEOutputPort; + +/////////////////////////////////////////////////////////////////////////////// +// Static callbacks +/////////////////////////////////////////////////////////////////////////////// + +void CALLBACK +JackWinMMEOutputPort::HandleMessageEvent(HMIDIOUT handle, UINT message, + DWORD_PTR port, DWORD_PTR param1, + DWORD_PTR param2) +{ + ((JackWinMMEOutputPort *) port)->HandleMessage(message, param1, param2); +} + +/////////////////////////////////////////////////////////////////////////////// +// Class +/////////////////////////////////////////////////////////////////////////////// + +JackWinMMEOutputPort::JackWinMMEOutputPort(const char *alias_name, + const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes, + size_t max_messages) +{ + read_queue = new JackMidiBufferReadQueue(); + std::auto_ptr read_queue_ptr(read_queue); + thread_queue = new JackMidiAsyncQueue(max_bytes, max_messages); + std::auto_ptr thread_queue_ptr(thread_queue); + char error_message[MAXERRORLENGTH]; + MMRESULT result = midiOutOpen(&handle, index, HandleMessageEvent, this, + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) { + GetErrorString(result, error_message); + goto raise_exception; + } + thread_queue_semaphore = CreateSemaphore(NULL, 0, max_messages, NULL); + if (thread_queue_semaphore == NULL) { + GetOSErrorString(error_message); + goto close_handle; + } + sysex_semaphore = CreateSemaphore(NULL, 0, 1, NULL); + if (sysex_semaphore == NULL) { + GetOSErrorString(error_message); + goto destroy_thread_queue_semaphore; + } + MIDIINCAPS capabilities; + char *name; + result = midiOutGetDevCaps(index, &capabilities, sizeof(capabilities)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutGetDevCaps", + result); + name = driver_name; + } else { + name = capabilities.szPname; + } + snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", alias_name, driver_name, + index + 1); + snprintf(name, sizeof(name) - 1, "%s:playback_%d", client_name, index + 1); + write_queue_ptr.release(); + thread_queue_ptr.release(); + return; + + destroy_thread_queue_semaphore: + if (! CloseHandle(thread_queue_semaphore)) { + WriteOSError("JackWinMMEOutputPort [constructor]", "CloseHandle"); + } + close_handle: + result = midiOutClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [constructor]", "midiOutClose", + result); + } + raise_exception: + throw std::runtime_error(error_message); +} + +JackWinMMEOutputPort::~JackWinMMEOutputPort() +{ + Stop(); + MMRESULT result = midiOutReset(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutReset", + result); + } + result = midiOutClose(handle); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort [destructor]", "midiOutClose", + result); + } + if (! CloseHandle(sysex_semaphore)) { + WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); + } + if (! CloseHandle(thread_queue_semaphore)) { + WriteOSError("JackWinMMEOutputPort [destructor]", "CloseHandle"); + } + delete read_queue; + delete thread_queue; +} + +bool +JackWinMMEOutputPort::Execute() +{ + for (;;) { + if (! Wait(thread_queue_semaphore)) { + break; + } + jack_midi_event_t *event = thread_queue->DequeueEvent(); + if (! event) { + break; + } + jack_time_t frame_time = GetTimeFromFrames(event->time); + for (jack_time_t current_time = GetMicroSeconds(); + frame_time > current_time; current_time = GetMicroSeconds()) { + jack_time_t sleep_time = frame_time - current_time; + + // Windows has a millisecond sleep resolution for its Sleep calls. + // This is unfortunate, as MIDI timing often requires a higher + // resolution. For now, we attempt to compensate by letting an + // event be sent if we're less than 500 microseconds from sending + // the event. We assume that it's better to let an event go out + // 499 microseconds early than let an event go out 501 microseconds + // late. Of course, that's assuming optimal sleep times, which is + // a whole different Windows issue ... + if (sleep_time < 500) { + break; + } + + if (sleep_time < 1000) { + sleep_time = 1000; + } + JackSleep(sleep_time); + } + jack_midi_data_t *data = event->buffer; + DWORD message = 0; + MMRESULT result; + size_t size = event->size; + switch (size) { + case 3: + message |= (((DWORD) data[2]) << 16); + // Fallthrough on purpose. + case 2: + message |= (((DWORD) data[1]) << 8); + // Fallthrough on purpose. + case 1: + message |= (DWORD) data[0]; + result = midiOutShortMsg(handle, message); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutShortMsg", result); + } + continue; + } + MIDIHDR header; + header.dwFlags = 0; + header.dwLength = size; + header.lpData = data; + result = midiOutPrepareHeader(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutPrepareHeader", result); + continue; + } + result = midiOutLongMsg(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", "midiOutLongMsg", + result); + continue; + } + + // System exclusive messages may be sent synchronously or + // asynchronously. The choice is up to the WinMME driver. So, we wait + // until the message is sent, regardless of the driver's choice. + if (! Wait(sysex_semaphore)) { + break; + } + + result = midiOutUnprepareHeader(handle, &header, sizeof(MIDIHDR)); + if (result != MMSYSERR_NOERROR) { + WriteMMError("JackWinMMEOutputPort::Execute", + "midiOutUnprepareHeader", result); + break; + } + } + stop_execution: + return false; +} + +void +JackWinMMEOutputPort::GetMMErrorString(MMRESULT error, LPTSTR text) +{ + MMRESULT result = midiOutGetErrorText(error, text, MAXERRORLENGTH); + if (result != MMSYSERR_NOERROR) { + snprintf(text, MAXERRORLENGTH, "Unknown MM error code '%d'", error); + } +} + +void +JackWinMMEOutputPort::GetOSErrorString(LPTSTR text) +{ + DWORD error = GetLastError(); + if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &text, + MAXERRORLENGTH, NULL)) { + snprintf(text, MAXERRORLENGTH, "Unknown OS error code '%d'", error); + } +} + +void +JackWinMMEOutputPort::HandleMessage(UINT message, DWORD_PTR param1, + DWORD_PTR param2) +{ + switch (message) { + case MOM_CLOSE: + jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device closed."); + break; + case MOM_DONE: + if (! ReleaseSemaphore(sysex_semaphore, 1, NULL)) { + WriteOSError("JackWinMMEOutputPort::HandleMessage", + "ReleaseSemaphore"); + } + break; + case MOM_OPEN: + jack_info("JackWinMMEOutputPort::HandleMessage - MIDI device opened."); + } +} + +bool +JackWinMMEOutputPort::Init() +{ + set_threaded_log_function(); + // XX: Can more be done? Ideally, this thread should have the JACK server + // thread priority + 1. + if (thread->AcquireSelfRealTime()) { + jack_error("JackWinMMEOutputPort::Init - could not acquire realtime " + "scheduling. Continuing anyway."); + } + return true; +} + +void +JackWinMMEOutputPort::ProcessJack(JackMidiBuffer *port_buffer, + jack_nframes_t frames) +{ + read_queue->ResetMidiBuffer(port_buffer); + for (jack_midi_event_t *event = read_queue->DequeueEvent(); event; + event = read_queue->DequeueEvent()) { + switch (thread_queue->EnqueueEvent(event, frames)) { + case JackMidiWriteQueue::BUFFER_FULL: + jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " + "buffer is full. Dropping event."); + break; + case JackMidiWriteQueue::BUFFER_TOO_SMALL: + jack_error("JackWinMMEOutputPort::ProcessJack - The thread queue " + "couldn't enqueue a %d-byte event. Dropping event.", + event->size); + break; + default: + if (! ReleaseSemaphore(thread_queue_semaphore, 1, NULL)) { + WriteOSError("JackWinMMEOutputPort::ProcessJack", + "ReleaseSemaphore"); + } + } + } +} + +bool +JackWinMMEOutputPort::Start() +{ + bool result = thread->GetStatus() != JackThread::kIdle; + if (! result) { + result = ! thread->StartSync(); + if (! result) { + jack_error("JackWinMMEOutputPort::Start - failed to start MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackWinMMEOutputPort::Stop() +{ + bool result = thread->GetStatus() == JackThread::kIdle; + if (! result) { + result = ! thread->Kill(); + if (! result) { + jack_error("JackWinMMEOutputPort::Stop - failed to stop MIDI " + "processing thread."); + } + } + return result; +} + +bool +JackWinMMEOutputPort::Wait(Handle semaphore) +{ + DWORD result = WaitForSingleObject(semaphore, INFINITE); + switch (result) { + case WAIT_FAILED: + WriteOSError("JackWinMMEOutputPort::Wait", "WaitForSingleObject"); + break; + case WAIT_OBJECT_0: + return true; + default: + jack_error("JackWinMMEOutputPort::Wait - unexpected result from " + "'WaitForSingleObject'."); + } + return false; +} + +void +JackWinMMEOutputPort::WriteMMError(const char *jack_func, const char *mm_func, + MMRESULT result) +{ + const char error_message[MAXERRORLENGTH]; + GetMMErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, mm_func, error_message); +} + +void +JackWinMMEOutputPort::WriteOSError(const char *jack_func, const char *os_func) +{ + const char error_message[MAXERRORLENGTH]; + GetOSErrorString(result, error_message); + jack_error("%s - %s: %s", jack_func, os_func, error_message); +} diff --git a/windows/winmme/JackWinMMEOutputPort.h b/windows/winmme/JackWinMMEOutputPort.h new file mode 100644 index 00000000..8da32066 --- /dev/null +++ b/windows/winmme/JackWinMMEOutputPort.h @@ -0,0 +1,93 @@ +/* +Copyright (C) 2011 Devin Anderson + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __JackWinMMEOutputPort__ +#define __JackWinMMEOutputPort__ + +#include +#include + +#include "JackMidiAsyncQueue.h" +#include "JackMidiBufferReadQueue.h" + +namespace Jack { + + class JackWinMMEOutputPort { + + private: + + static void CALLBACK + HandleMessageEvent(HMIDIOUT handle, UINT message, DWORD_PTR port, + DWORD_PTR param1, DWORD_PTR param2); + + void + GetMMErrorString(MMRESULT error, LPTSTR text); + + void + GetOSErrorString(LPTSTR text); + + void + HandleMessage(UINT message, DWORD_PTR param1, DWORD_PTR param2); + + bool + Wait(Handle semaphore); + + void + WriteMMError(const char *jack_func, const char *mm_func, + MMRESULT result); + + void + WriteOSError(const char *jack_func, const char *os_func); + + char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + HMIDIOUT handle; + char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE]; + JackMidiBufferReadQueue *read_queue; + HANDLE sysex_semaphore; + JackMidiAsyncQueue *thread_queue; + HANDLE thread_queue_semaphore; + + public: + + JackWinMMEOutputPort(const char *alias_name, const char *client_name, + const char *driver_name, UINT index, + size_t max_bytes=4096, size_t max_messages=1024); + + ~JackWinMMEOutputPort(); + + bool + Execute(); + + bool + Init(); + + void + ProcessJack(JackMidiBuffer *port_buffer, jack_nframes_t frames); + + bool + Start(); + + bool + Stop(); + + }; + +} + +#endif