#include "MidiController.hpp" #include #include MidiController::MidiController(NoteQueue& queue) : noteQueue_(queue) { try { midiIn_ = std::make_unique(RtMidi::LINUX_ALSA); midiIn_->ignoreTypes(false, false, false); } catch (RtMidiError& e) { std::cout << "RtMidi init failed: " << e.getMessage() << std::endl; } // TODO: this still doesnt work on windows } MidiController::~MidiController() { close(); } // open the first for thats successful bool MidiController::openDefaultPort() { if (!midiIn_) return false; if (midiIn_->getPortCount() == 0) { std::cout << "No MIDI input ports available" << std::endl; return false; } uint32_t portCount = midiIn_->getPortCount(); std::cout << "Available MidiIn ports: " << portCount << std::endl; for (int i = 0; i < portCount; i++) { std::cout << "#" << i << " : " << midiIn_->getPortName(i) << std::endl; if(openPort(i)) return true; } return false; } bool MidiController::openPort(unsigned int index) { if (!midiIn_) return false; try { midiIn_->openPort(index); midiIn_->setCallback(&MidiController::midiCallback, this); std::cout << "Opened MIDI port: " << midiIn_->getPortName(index) << std::endl; return true; } catch (RtMidiError& e) { std::cout << "Midi Port error" << std::endl; std::cerr << e.getMessage() << std::endl; return false; } } void MidiController::close() { if (midiIn_ && midiIn_->isPortOpen()) { midiIn_->closePort(); } } // called by RtMidi on receive of a midi message. deltaTime is time since last midi message, not useful atm void MidiController::midiCallback(double /*deltaTime*/, std::vector* message, void* userData) { auto* self = static_cast(userData); if (!message || message->empty()) return; self->handleMessage(*message); // pass to parsing function if valid } void MidiController::handleMessage(const std::vector& msg) { if(msg.size() <= 1) return; // msg doesn't contain useful note info uint8_t status = msg[0] & 0xF0; uint8_t data1 = msg[1]; uint8_t data2 = msg[2]; if(status == 0xFE) return; // "Active Sensing" -> 300ms heartbeat. could be useful to sense if this is missing for device failure detection if(status == 0xF8) return; // "Timing Clock" -> 24 pulses per quarter note, for steady rhythm. not useful for this instrument // sustain pedal message event if(status == 0xB0 && data1 == 64) { handleSustain(data2 >= 64); return; } unsigned char note = msg.size() > 1 ? msg[1] : 0; // note number unsigned char vel = msg.size() > 2 ? msg[2] : 0; // velocity // note on (velocity > 0) if (status == 0x90 && vel > 0) { noteOn(note, vel); } // note off (or note on with 0 velocity) else if (status == 0x80 || (status == 0x90 && vel == 0)) { noteOff(note); } } // construct note on event and add to noteQueue void MidiController::noteOn(uint8_t note, uint8_t vel) { sustainedNotes_.erase(note); noteQueue_.push({ NoteEventType::NoteOn, static_cast(note), vel / 127.0f, std::chrono::high_resolution_clock::now() }); } // add note off event to noteQueue if no sustain active void MidiController::noteOff(uint8_t note) { if(sustainDown_) { sustainedNotes_.insert(note); return; } noteQueue_.push({ NoteEventType::NoteOff, static_cast(note), 0.0f, std::chrono::high_resolution_clock::now() }); } // if sustain goes from on->off, then noteOff all the active ntoes void MidiController::handleSustain(bool down) { if(down == sustainDown_) return; sustainDown_ = down; if(!sustainDown_) { for(uint8_t note : sustainedNotes_) { noteOff(note); } sustainedNotes_.clear(); } }