void CALLBACK Win32MidiIn::midiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { Q_UNUSED(dwParam2); MidiSession *midiSession = (MidiSession *)dwInstance; LPMIDIHDR pMIDIhdr = (LPMIDIHDR)dwParam1; if (wMsg == MIM_LONGDATA) { if (pMIDIhdr->dwBytesRecorded == 0) { // 0 length means returning the buffer to the application when closing return; } // Use QMidiStreamParser to extract well-formed SysEx messages from buffer QMidiStreamParser &qMidiStreamParser = *midiSession->getQMidiStreamParser(); qMidiStreamParser.setTimestamp(MasterClock::getClockNanos()); qMidiStreamParser.parseStream((BYTE *)pMIDIhdr->lpData, pMIDIhdr->dwBytesRecorded); // Add SysEx Buffer for reuse if (midiInAddBuffer(hMidiIn, pMIDIhdr, sizeof(MIDIHDR)) != MMSYSERR_NOERROR) { QMessageBox::critical(NULL, "Win32MidiIn Error", "Failed to add SysEx Buffer for reuse"); return; } return; } if (wMsg == MIM_DATA) { // No need to use QMidiStreamParser for short messages: they are guaranteed to have explicit status byte // if ((dwParam1 & 0xF8) == 0xF8) // No support for System Realtime yet midiSession->getSynthRoute()->pushMIDIShortMessage(dwParam1, MasterClock::getClockNanos()); } }
LRESULT CALLBACK Win32MidiDriver::midiInProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_APP: { // Closing session MidiSession *midiSession = (MidiSession *)wParam; if (driver->midiSessions.indexOf(midiSession) < 0) { qDebug() << "Win32MidiDriver: Invalid midiSession handle supplied"; return 0; } qDebug() << "Win32MidiDriver: Session" << midiSession << "finished"; driver->deleteMidiSession(midiSession); return 1; } case WM_COPYDATA: { COPYDATASTRUCT *cds = (COPYDATASTRUCT *)lParam; MidiSession *midiSession = (MidiSession *)cds->dwData; DWORD *data = (DWORD *)cds->lpData; if (data[0] == 0) { // Special value, mark of a non-Sysex message if (data[1] == (DWORD)-1) { // Special value, mark of a handshaking message // Sync the timesource in the driver with MasterClock LARGE_INTEGER t = {{data[3], (LONG)data[4]}}; startMasterClock = t.QuadPart - MasterClock::getClockNanos(); // Process handshaking message QString appName = QFileInfo(QString((const char *)&data[5])).fileName(); midiSession = driver->createMidiSession(appName); driver->showBalloon("Connected application:", appName); qDebug() << "Win32MidiDriver: Connected application" << appName; qDebug() << "Win32MidiDriver: Session" << midiSession << "protocol version" << data[2]; if (!midiSession) { qDebug() << "Win32MidiDriver: Failed to create new session"; return 0; } return (LRESULT)midiSession; } else if (data[1] == 0) { // Special value, mark of a short MIDI message // Process short MIDI message if (driver->midiSessions.indexOf(midiSession) < 0) { qDebug() << "Win32MidiDriver: Invalid midiSession handle supplied"; return 0; } LARGE_INTEGER t = {{data[2], (LONG)data[3]}}; // qDebug() << "D" << 1e-6 * ((t.QuadPart - startMasterClock) - MasterClock::getClockNanos()); midiSession->getSynthRoute()->pushMIDIShortMessage(data[4], t.QuadPart - startMasterClock); return 1; } } else { // Process Sysex if (driver->midiSessions.indexOf(midiSession) < 0) { qDebug() << "Win32MidiDriver: Invalid midiSession handle supplied"; return 0; } midiSession->getSynthRoute()->pushMIDISysex((MT32Emu::Bit8u *)cds->lpData, cds->cbData, MasterClock::getClockNanos()); return 1; } } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } }
void SMFProcessor::run() { MidiSession *session = driver->createMidiSession(QFileInfo(fileName).fileName()); SynthRoute *synthRoute = session->getSynthRoute(); const MidiEventList &midiEvents = parser.getMIDIEvents(); midiTick = parser.getMidiTick(); quint32 totalSeconds = estimateRemainingTime(midiEvents, 0); MasterClockNanos startNanos = MasterClock::getClockNanos(); MasterClockNanos currentNanos = startNanos; for (int i = 0; i < midiEvents.count(); i++) { const MidiEvent &e = midiEvents.at(i); currentNanos += e.getTimestamp() * midiTick; while (!stopProcessing && synthRoute->getState() == SynthRouteState_OPEN) { if (bpmUpdated) { bpmUpdated = false; totalSeconds = (currentNanos - startNanos) / MasterClock::NANOS_PER_SECOND + estimateRemainingTime(midiEvents, i + 1); } if (driver->seekPosition > -1) { SMFProcessor::sendAllNotesOff(synthRoute); MasterClockNanos seekNanos = totalSeconds * driver->seekPosition * MasterClock::NANOS_PER_MILLISECOND; MasterClockNanos eventNanos = currentNanos - e.getTimestamp() * midiTick - startNanos; if (seekNanos < eventNanos) { i = 0; eventNanos = 0; midiTick = parser.getMidiTick(); emit driver->tempoUpdated(0); } i = seek(synthRoute, midiEvents, i, seekNanos, eventNanos) - 1; currentNanos = MasterClock::getClockNanos(); startNanos = currentNanos - seekNanos; break; } emit driver->playbackTimeChanged(MasterClock::getClockNanos() - startNanos, totalSeconds); MasterClockNanos delay = currentNanos - MasterClock::getClockNanos(); if (driver->fastForwardingFactor > 1) { MasterClockNanos timeShift = delay - (delay / driver->fastForwardingFactor); delay -= timeShift; currentNanos -= timeShift; startNanos -= timeShift; } if (delay < MasterClock::NANOS_PER_MILLISECOND) break; usleep(((delay < MAX_SLEEP_TIME ? delay : MAX_SLEEP_TIME) - MasterClock::NANOS_PER_MILLISECOND) / MasterClock::NANOS_PER_MICROSECOND); } if (stopProcessing || synthRoute->getState() != SynthRouteState_OPEN) break; if (driver->seekPosition > -1) { driver->seekPosition = -1; continue; } switch (e.getType()) { case SHORT_MESSAGE: synthRoute->pushMIDIShortMessage(e.getShortMessage(), currentNanos); break; case SYSEX: synthRoute->pushMIDISysex(e.getSysexData(), e.getSysexLen(), currentNanos); break; case SET_TEMPO: { uint tempo = e.getShortMessage(); midiTick = parser.getMidiTick(tempo); emit driver->tempoUpdated(MidiParser::MICROSECONDS_PER_MINUTE / tempo); break; } default: break; } } SMFProcessor::sendAllNotesOff(synthRoute); emit driver->playbackTimeChanged(0, 0); qDebug() << "SMFDriver: processor thread stopped"; driver->deleteMidiSession(session); if (!stopProcessing) emit driver->playbackFinished(); }