/* Copyright (C) 2009 Grame 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 "JackWinMMEDriver.h" #include "JackGraphManager.h" #include "JackEngineControl.h" #include "JackDriverLoader.h" #include #include #include #include #include #include #include namespace Jack { static bool InitHeaders(MidiSlot* slot) { 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; } void CALLBACK JackWinMMEDriver::MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD userData, DWORD dwParam1, DWORD dwParam2) { 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; } } 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) { 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); // Real input int devindex = 0; for (int i = 0; i < fRealCaptureChannels; i++) { HMIDIIN handle; fMidiDestination[devindex].fIndex = i; MMRESULT ret = midiInOpen(&handle, fMidiDestination[devindex].fIndex, (DWORD)MidiInProc, (DWORD)fRingBuffer[devindex], CALLBACK_FUNCTION); 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; } devindex += 1; } 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; } devindex += 1; } fRealPlaybackChannels = devindex; fPlaybackChannels = devindex; return 0; } void JackWinMMEDriver::CloseInput(MidiSlot* slot) { 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"); } res = midiInReset(handle); if (res != MMSYSERR_NOERROR) { jack_error("midiInReset error"); } res = midiInUnprepareHeader(handle, slot->fHeader, sizeof(MIDIHDR)); if (res != MMSYSERR_NOERROR) { jack_error("midiInUnprepareHeader error"); } do { res = midiInClose(handle); if (res != MMSYSERR_NOERROR) { jack_error("midiInClose error"); } if (res == MIDIERR_STILLPLAYING) midiInReset(handle); Sleep (10); retry++; } while ((res == MIDIERR_STILLPLAYING) && (retry < 10)); if (slot->fHeader) { GlobalFreePtr(slot->fHeader); } } void JackWinMMEDriver::CloseOutput(MidiSlot* slot) { 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); } } int JackWinMMEDriver::Close() { jack_log("JackWinMMEDriver::Close"); // Close input if (fMidiDestination) { for (int i = 0; i < fRealCaptureChannels; i++) { CloseInput(&fMidiDestination[i]); } delete[] fMidiDestination; } // Close output if (fMidiSource) { for (int i = 0; i < fRealPlaybackChannels; i++) { CloseOutput(&fMidiSource[i]); } delete[] fMidiSource; } return 0; } 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; } 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; } 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; } int JackWinMMEDriver::Read() { size_t size; for (int chan = 0; chan < fCaptureChannels; chan++) { 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) { //jack_info("jack_ringbuffer_read_space %d", size); int ev_count = 0; jack_ringbuffer_read(fRingBuffer[chan], (char*)&ev_count, sizeof(int)); 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])); } } return 0; } 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 { } } } } return 0; } } // end of namespace #ifdef __cplusplus extern "C" { #endif SERVER_EXPORT jack_driver_desc_t * driver_get_descriptor() { jack_driver_desc_t * desc; //unsigned int i; desc = (jack_driver_desc_t*)calloc (1, sizeof (jack_driver_desc_t)); strcpy(desc->name, "winmme"); // size MUST be less then JACK_DRIVER_NAME_MAX + 1 strcpy(desc->desc, "WinMME API based MIDI backend"); // size MUST be less then JACK_DRIVER_PARAM_DESC + 1 desc->nparams = 0; desc->params = (jack_driver_param_desc_t*)calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); return desc; } SERVER_EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine* engine, Jack::JackSynchro* table, const JSList* params) { /* unsigned int capture_ports = 2; unsigned int playback_ports = 2; unsigned long wait_time = 0; const JSList * node; const jack_driver_param_t * param; bool monitor = false; for (node = params; node; node = jack_slist_next (node)) { param = (const jack_driver_param_t *) node->data; switch (param->character) { case 'C': capture_ports = param->value.ui; break; case 'P': playback_ports = param->value.ui; break; case 'r': sample_rate = param->value.ui; break; case 'p': period_size = param->value.ui; break; case 'w': wait_time = param->value.ui; break; case 'm': monitor = param->value.i; break; } } */ Jack::JackDriverClientInterface* driver = new Jack::JackWinMMEDriver("system_midi", "winmme", engine, table); if (driver->Open(1, 1, 0, 0, false, "in", "out", 0, 0) == 0) { return driver; } else { delete driver; return NULL; } } #ifdef __cplusplus } #endif /* jack_connect system:midi_capture_1 system_midi:playback_1 jack_connect system:midi_capture_1 system_midi:playback_2 jack_connect system:midi_capture_1 system_midi:playback_1 jack_connect system:midi_capture_1 system_midi:playback_1 jack_connect system:midi_capture_1 system_midi:playback_1 jack_connect system_midi:capture_1 system:midi_playback_1 jack_connect system_midi:capture_2 system:midi_playback_1 jack_connect system_midi:capture_1 system_midi:playback_1 */