From ce675b143047fbf5a54d2f276dda6c5535d574de Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 24 Jun 2026 00:38:49 +0200 Subject: [PATCH] virtual ports working - ui needs some work --- src/app-manager.cpp | 23 ++++++++++++++---- src/datatypes.h | 6 +++++ src/main.cpp | 3 ++- src/midi-node.cpp | 3 ++- src/midi-node.h | 4 ++-- src/midi.cpp | 53 ++++++++++++++++++++++++++++++++++-------- src/midi.h | 10 ++++++-- src/signal-manager.cpp | 4 ++-- src/ui.cpp | 13 ++++++++++- 9 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/app-manager.cpp b/src/app-manager.cpp index 89abaa8..da73ef0 100644 --- a/src/app-manager.cpp +++ b/src/app-manager.cpp @@ -3,6 +3,7 @@ #include "interaction-manager.h" #include "nlohmann/json.hpp" #include "graph-manager.h" +#include "signal-manager.h" #include "midi-node.h" #include "state.h" #include "ui.h" @@ -22,6 +23,12 @@ namespace VISEQ::APP{ std::ofstream file("state.json"); file << json(*this); std::cout << std::setw(4) << json(*this) << std::endl; + + sm->midi.all_notes_off(); + + for(auto &p : sm->midi.midi_outs){ + p.second->close_port(); + } } void AppManager::startSignals(){ @@ -110,11 +117,19 @@ namespace VISEQ::APP{ for(auto& n : gm->getMidiNodes()){ auto nc = std::dynamic_pointer_cast(n.lock()); for(auto& mo : sm->midi.midi_out_ports){ - if(!nc->midi_outs.contains(mo.number) && mo.enabled){ - nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; + if(!nc->selected_midi_outs.contains(mo.number) && mo.enabled){ + nc->selected_midi_outs[mo.number] = {mo.number, false, mo.port.port_name}; } - if(nc->midi_outs.contains(mo.number) && !mo.enabled){ - nc->midi_outs.erase(mo.number); + if(nc->selected_midi_outs.contains(mo.number) && !mo.enabled){ + nc->selected_midi_outs.erase(mo.number); + } + } + for(auto& mo : sm->midi.virtual_midi_out_ports){ + if(!nc->selected_midi_outs.contains(mo.number) && mo.enabled){ + nc->selected_midi_outs[mo.number] = {mo.number, false, mo.name}; + } + if(nc->selected_midi_outs.contains(mo.number) && !mo.enabled){ + nc->selected_midi_outs.erase(mo.number); } } } diff --git a/src/datatypes.h b/src/datatypes.h index 0719a69..29cad7c 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -88,6 +88,12 @@ namespace VISEQ::MIDI{ int number; bool enabled; }; + + struct VirtualMidiOutPort{ + int number; + bool enabled; + std::string name; + }; struct MidiOut{ libremidi::midi_out out; diff --git a/src/main.cpp b/src/main.cpp index e98f515..9839b5f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,8 @@ int main(){ } app.close(); - + app.sm->midi.update(); + CloseWindow(); return 0; diff --git a/src/midi-node.cpp b/src/midi-node.cpp index 6a5a95d..b9c543a 100644 --- a/src/midi-node.cpp +++ b/src/midi-node.cpp @@ -54,7 +54,8 @@ namespace VISEQ::GRAPH::NODES { } void MidiNode::setMidiPort(int midi_port, bool _state){ - midi_outs[midi_port].enabled = _state; + selected_midi_outs[midi_port].enabled = _state; + std::cout << midi_port << " " << _state << std::endl; } void MidiNode::trigger() { diff --git a/src/midi-node.h b/src/midi-node.h index e9cc84a..a724f44 100644 --- a/src/midi-node.h +++ b/src/midi-node.h @@ -33,8 +33,8 @@ namespace VISEQ::GRAPH::NODES { struct NodeMidiOut{ int i; - std::string name; bool enabled; + std::string name; }; MidiNode() {init();} @@ -51,7 +51,7 @@ namespace VISEQ::GRAPH::NODES { int midi_out = -1; - std::map midi_outs; + std::map selected_midi_outs; bool gui_midi_channel_edit = false; bool gui_midi_note_edit = false; diff --git a/src/midi.cpp b/src/midi.cpp index ccccba0..31e25d0 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -5,26 +5,29 @@ #include #include +#include #include "datatypes.h" namespace VISEQ::MIDI { + void Midi::init(){ + auto out_conf = libremidi::midi1::out_default_configuration(); + for(int i = 0; i < 5; i++){ + std::ostringstream port_name; + port_name << "viseq midi out " << i; + virtual_midi_out_ports.push_back({i+2000, false, port_name.str()}); + } + } + void Midi::enumerate(){ std::cout << (int)obs.get_current_api() << std::endl; - int i = 0; midi_out_ports.clear(); for(const libremidi::output_port& port : obs.get_output_ports()){ midi_out_ports.push_back({port, i, false}); i++; } - - auto out_conf = libremidi::midi1::out_default_configuration(); - virtual_midi_out = libremidi::midi_out(libremidi::output_configuration(), out_conf); - virtual_midi_out.open_virtual_port("viseq out"); - - //midi_out_ports.push_back({&virtual_midi_out, 99, false}); } void Midi::open_midi_out(MidiOutPort *out_port){ @@ -41,16 +44,31 @@ namespace VISEQ::MIDI { midi_outs.erase(out_port->number); } } + + void Midi::open_midi_out(VirtualMidiOutPort *out_port){ + if(!midi_outs.contains(out_port->number)){ + auto out = std::make_shared(); + out->open_virtual_port(out_port->name); + midi_outs[out_port->number] = out; + } + } + + void Midi::close_midi_out(VirtualMidiOutPort *out_port){ + if(midi_outs.contains(out_port->number)){ + midi_outs[out_port->number]->close_port(); + midi_outs.erase(out_port->number); + } + } void Midi::note(int midi_out, int channel, int note, int velocity, int gate){ + std::cout << "note on " << midi_out << " " << midi_outs.contains(midi_out) << std::endl; if(midi_outs.contains(midi_out)){ if(midi_outs[midi_out]->is_port_open()){ midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity)); notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate); + std::cout << "note on " << midi_out << std::endl; } } - virtual_midi_out.send_message(libremidi::channel_events::note_on(channel, note, velocity)); - notes.emplace_back(99, channel, note, velocity, GetTime(), gate); } void Midi::cc(int midi_out, int channel, int cc, int value){ @@ -69,16 +87,31 @@ namespace VISEQ::MIDI { } } + void Midi::all_notes_off(){ + for(auto &n : notes){ + if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity)); + n.erase = true; + } + std::erase_if(notes, [](const auto& item){return item.erase;}); + } + void Midi::update(){ float time = GetTime(); for(auto &n : notes){ if(time - n.start >= (float)n.gate / 1000.0f){ if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity)); - virtual_midi_out.send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity)); n.erase = true; } } std::erase_if(notes, [](const auto& item){return item.erase;}); + + for(auto& p : virtual_midi_out_ports){ + if(p.enabled) { + open_midi_out(&p); + } else if(!p.enabled) { + close_midi_out(&p); + } + } for(auto& p : midi_out_ports){ if(p.enabled) { diff --git a/src/midi.h b/src/midi.h index b2442fc..7ff3fa3 100644 --- a/src/midi.h +++ b/src/midi.h @@ -26,6 +26,7 @@ namespace VISEQ::MIDI { public: Midi() { + init(); enumerate(); } @@ -34,19 +35,24 @@ namespace VISEQ::MIDI { std::map> midi_outs; std::vector midi_in_ports; std::vector midi_out_ports; + std::vector virtual_midi_out_ports; - libremidi::midi_out virtual_midi_out; std::vector notes; float timer; - + + void init(); + void enumerate(); void open_midi_out(MidiOutPort *out_port); void close_midi_out(MidiOutPort *out_port); + void open_midi_out(VirtualMidiOutPort *out_port); + void close_midi_out(VirtualMidiOutPort *out_port); void note(int midi_out, int channel, int note, int velocity, int gate); void cc(int midi_out, int channel, int cc, int value); void pitch_bend(int midi_out, int channel, int pitch_value); + void all_notes_off(); void update(); }; diff --git a/src/signal-manager.cpp b/src/signal-manager.cpp index ff5c9a3..6f52cb1 100644 --- a/src/signal-manager.cpp +++ b/src/signal-manager.cpp @@ -98,14 +98,14 @@ namespace VISEQ::SIGNAL { if(next_node.lock() != s->next.lock() && !next_node.expired() && s->next.lock()->getNodeType() == NodeType::MIDINODE) { auto midinode = std::dynamic_pointer_cast(s->next.lock()); - for(auto &m : midinode->midi_outs){ + for(auto &m : midinode->selected_midi_outs){ + std::cout << m.second.name << " " << m.second.i << " " << m.second.enabled << std::endl; if(m.second.enabled) { if(midinode->send_note) midi.note(m.second.i, midinode->getNotes().channel, (int)Clamp(midinode->getNotes().note + s->midi_note_offset, 0, 127), midinode->getNotes().velocity, midinode->getNotes().gate); if(midinode->send_cc) midi.cc(m.second.i, midinode->getCC().channel, midinode->getCC().control, (int)Clamp(midinode->getCC().value + s->midi_cc_offset, 0, 127)); if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->getPitchBend().channel, midinode->getPitchBend().value); } } - if(midinode->send_note) midi.note(99, midinode->getNotes().channel, (int)Clamp(midinode->getNotes().note + s->midi_note_offset, 0, 127), midinode->getNotes().velocity, midinode->getNotes().gate); s->last = s->next; s->next = next_node; } diff --git a/src/ui.cpp b/src/ui.cpp index 5ce199d..2ac3be7 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -9,6 +9,7 @@ #include #include +#include namespace VISEQ::APP::UI{ void AppUI::init(){ @@ -37,6 +38,11 @@ namespace VISEQ::APP::UI{ if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate(); int i = 0; + for(auto& vo : sm->midi.virtual_midi_out_ports){ + GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, vo.name.substr(0,32).c_str(), &vo.enabled); + i++; + } + i++; for(auto& mo : sm->midi.midi_out_ports){ GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.substr(0,32).c_str(), &mo.enabled); i++; @@ -48,6 +54,11 @@ namespace VISEQ::APP::UI{ gm->setMidiPortForAll(midis.number, true); } } + for(auto &midis : sm->midi.virtual_midi_out_ports){ + if(midis.enabled){ + gm->setMidiPortForAll(midis.number, true); + } + } } int s_signal = s_midi + 100 + i*20; @@ -119,7 +130,7 @@ namespace VISEQ::APP::UI{ int i = 0; if(n->getNodeType() == NodeType::MIDINODE){ std::shared_ptr mn = std::dynamic_pointer_cast(n); - for(auto& mo : mn->midi_outs){ + for(auto& mo : mn->selected_midi_outs){ GuiCheckBox((Rectangle){np.x + 20, np.y + y_midi + 20 + i*20.0f, 20, 20}, mo.second.name.c_str(), &mo.second.enabled); i++; }