void MidiPlugin::closeOutput(quint32 output) { qDebug() << Q_FUNC_INFO; MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) dev->close(); }
void MidiPlugin::sendSysEx(quint32 output, const QByteArray &data) { qDebug() << "sendSysEx data: " << data; MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) dev->writeSysEx(data); }
void MidiPlugin::writeUniverse(quint32 universe, quint32 output, const QByteArray &data) { Q_UNUSED(universe) MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) dev->writeUniverse(data); }
void MidiPlugin::openOutput(quint32 output) { qDebug() << "MIDI plugin open output: " << output; MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) dev->open(); }
void MidiPlugin::sendFeedBack(quint32 output, quint32 channel, uchar value) { MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) { qDebug() << "[sendFeedBack] Channel: " << channel << ", value: " << value; dev->writeChannel(channel, value); } }
void MidiPlugin::sendFeedBack(quint32 output, quint32 channel, uchar value, const QString &) { MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) { qDebug() << "[sendFeedBack] Channel:" << channel << ", value:" << value; uchar cmd = 0; uchar data1 = 0, data2 = 0; if (QLCMIDIProtocol::feedbackToMidi(channel, value, dev->midiChannel(), &cmd, &data1, &data2) == true) { qDebug() << Q_FUNC_INFO << "cmd:" << cmd << "data1:" << data1 << "data2:" << data2; dev->writeFeedback(cmd, data1, data2); } } }
void MidiPlayer::performCountIn(MidiOutputDevice &device, const SystemLocation &location, int beat_duration) { // Load preferences. uint8_t velocity; uint8_t preset; { auto settings = mySettingsManager.getReadHandle(); if (!settings->get(Settings::CountInEnabled)) return; velocity = settings->get(Settings::CountInVolume); preset = settings->get(Settings::CountInPreset) + Midi::MIDI_PERCUSSION_PRESET_OFFSET; } // Figure out the time signature where playback is starting. const System &system = myScore.getSystems()[location.getSystem()]; const Barline *barline = system.getPreviousBarline(location.getPosition()); if (!barline) barline = &system.getBarlines().front(); const TimeSignature &time_sig = barline->getTimeSignature(); const int tick_duration = boost::rational_cast<int>( boost::rational<int>(4, time_sig.getBeatValue()) * boost::rational<int>(time_sig.getBeatsPerMeasure(), time_sig.getNumPulses()) * beat_duration); // Play the count-in. device.setChannelMaxVolume(METRONOME_CHANNEL, Midi::MAX_MIDI_CHANNEL_VOLUME); for (int i = 0; i < time_sig.getNumPulses(); ++i) { if (!isPlaying()) break; device.playNote(METRONOME_CHANNEL, preset, velocity); usleep(tick_duration * (100.0 / myPlaybackSpeed)); device.stopNote(METRONOME_CHANNEL, preset); } }
void StopNoteEvent::performEvent(MidiOutputDevice &device) const { #if defined(LOG_MIDI_EVENTS) qDebug() << "Stop Note: " << mySystem << ", " << myPosition << " at " << myStartTime; #endif device.stopNote(myChannel, myPitch); }
void MidiPlugin::openOutput(quint32 output) { qDebug() << "MIDI plugin open output: " << output; MidiOutputDevice* dev = outputDevice(output); if (dev == NULL) return; dev->open(); if (dev->midiTemplateName() != "") { qDebug() << "Opening device with Midi template: " << dev->midiTemplateName(); MidiTemplate* templ = midiTemplate(dev->midiTemplateName()); if (templ != NULL) sendSysEx(output, templ->initMessage()); } }
QString MidiPlugin::outputInfo(quint32 output) { qDebug() << Q_FUNC_INFO; QString str; if (output == QLCIOPlugin::invalidLine()) { str += QString("<BR><B>%1</B>").arg(tr("No output support available.")); return str; } MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) { QString status; str += QString("<H3>%1 %2</H3>").arg(tr("Output")).arg(outputs()[output]); str += QString("<P>"); if (dev->isOpen() == true) status = tr("Open"); else status = tr("Not Open"); str += QString("%1: %2").arg(tr("Status")).arg(status); str += QString("</P>"); } else { if (output < (quint32)outputs().length()) str += QString("<H3>%1 %2</H3>").arg(tr("Invalid Output")).arg(outputs()[output]); } str += QString("</BODY>"); str += QString("</HTML>"); return str; }
void MidiPlayer::run() { // Workaround to fix errors with the Microsoft GS Wavetable Synth on // Windows 10 - see http://stackoverflow.com/a/32553208/586978 #ifdef _WIN32 CoInitializeEx(nullptr, COINIT_MULTITHREADED); BOOST_SCOPE_EXIT(this_) { CoUninitialize(); } BOOST_SCOPE_EXIT_END #endif boost::signals2::scoped_connection connection( mySettingsManager.subscribeToChanges([&]() { auto settings = mySettingsManager.getReadHandle(); myMetronomeEnabled = settings->get(Settings::MetronomeEnabled); })); setIsPlaying(true); MidiFile::LoadOptions options; options.myEnableMetronome = true; options.myRecordPositionChanges = true; // Load MIDI settings. int api; int port; { auto settings = mySettingsManager.getReadHandle(); myMetronomeEnabled = settings->get(Settings::MetronomeEnabled); api = settings->get(Settings::MidiApi); port = settings->get(Settings::MidiPort); options.myMetronomePreset = settings->get(Settings::MetronomePreset) + Midi::MIDI_PERCUSSION_PRESET_OFFSET; options.myStrongAccentVel = settings->get(Settings::MetronomeStrongAccent); options.myWeakAccentVel = settings->get(Settings::MetronomeWeakAccent); options.myVibratoStrength = settings->get(Settings::MidiVibratoLevel); options.myWideVibratoStrength = settings->get(Settings::MidiWideVibratoLevel); } MidiFile file; file.load(myScore, options); const int ticks_per_beat = file.getTicksPerBeat(); // Merge the MIDI evvents for each track. MidiEventList events; for (MidiEventList &track : file.getTracks()) { track.convertToAbsoluteTicks(); events.concat(track); } // TODO - since each track is already sorted, an n-way merge should be faster. std::stable_sort(events.begin(), events.end()); events.convertToDeltaTicks(); // Initialize RtMidi and set the port. MidiOutputDevice device; if (!device.initialize(api, port)) { emit error(tr("Error initializing MIDI output device.")); return; } bool started = false; int beat_duration = Midi::BEAT_DURATION_120_BPM; const SystemLocation start_location(myStartLocation.getSystemIndex(), myStartLocation.getPositionIndex()); SystemLocation current_location = start_location; for (auto event = events.begin(); event != events.end(); ++event) { if (!isPlaying()) break; if (event->isTempoChange()) beat_duration = event->getTempo(); // Skip events before the start location, except for events such as // instrument changes. Tempo changes are tracked above. if (!started) { if (event->getLocation() < start_location) { if (event->isProgramChange()) device.sendMessage(event->getData()); continue; } else { performCountIn(device, event->getLocation(), beat_duration); started = true; } } const int delta = event->getTicks(); assert(delta >= 0); const int duration_us = boost::rational_cast<int>( boost::rational<int>(delta, ticks_per_beat) * beat_duration); usleep(duration_us * (100.0 / myPlaybackSpeed)); // Don't play metronome events if the metronome is disabled. if (event->isNoteOnOff() && event->getChannel() == METRONOME_CHANNEL && !myMetronomeEnabled) { continue; } device.sendMessage(event->getData()); // Notify listeners of the current playback position. if (event->getLocation() != current_location) { const SystemLocation &new_location = event->getLocation(); // Don't move backwards unless a repeat occurred. if (new_location < current_location && !event->isPositionChange()) continue; if (new_location.getSystem() != current_location.getSystem()) emit playbackSystemChanged(new_location.getSystem()); emit playbackPositionChanged(new_location.getPosition()); current_location = new_location; } } }
void MidiPlugin::writeUniverse(quint32 output, const QByteArray& universe) { MidiOutputDevice* dev = outputDevice(output); if (dev != NULL) dev->writeUniverse(universe); }