diff --git a/Makefile b/Makefile index cdd7d80..1b002cd 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CXX = g++-13 +CXX = g++ CXXFLAGS = -std=c++20 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -DLIBREMIDI_ALSA=1 -Wnarrowing CXXINC = -I/usr/lib/gcc/x86_64-pc-linux-gnu/13.3.0/include -Iinclude diff --git a/src/connection-manager.h b/src/connection-manager.h index 4628c13..5b4ceb0 100644 --- a/src/connection-manager.h +++ b/src/connection-manager.h @@ -16,17 +16,23 @@ class ConnectionManager{ Node* next(Node* current, Node* last){ std::vector paths; - bool last_ok = false; + std::vector nodes; for(auto& p : connections){ - if(((p.v1 == current && p.v1_v2) || (p.v2 == current && p.v2_v1)) && (p.v1 != last || p.v2 != last)) { + if(((p.v1 == current && p.v1_v2) || (p.v2 == current && p.v2_v1))) { p.active = false; paths.push_back(&p); + nodes.push_back( p.v1 == current ? p.v2 : p.v1 ); } - if(((p.v1 == last && p.v2_v1) || (p.v2 == last && p.v1_v2))) last_ok = true; + } + + if(paths.size() == 2) { + Node* next = nodes[0] == last ? nodes[1] : nodes[0]; + if(paths[0]->v1 == next || paths[0]->v2 == next) paths[0]->active = true; + if(paths[1]->v1 == next || paths[1]->v2 == next) paths[1]->active = true; + return next; } - if(paths.empty() && last_ok) return last; if(paths.empty()) return nullptr; int return_path = 0; diff --git a/src/connection.h b/src/connection.h index 85d9e50..a6a2a2c 100644 --- a/src/connection.h +++ b/src/connection.h @@ -18,6 +18,12 @@ class Connection{ bool remove = false; bool config = false; + void flip(){ + Node* vt = v1; + v1 = v2; + v2 = vt; + } + void draw(){ Vec2D v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + 10.0f); Vec2D v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 10.0f); @@ -30,6 +36,7 @@ class Connection{ Vec2D to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 35); Vec2D middle_handle = v2->position - ((v2->position - v1->position))*0.5f; Vec2D config_handle = v2->position - ((v2->position - v1->position))*0.66f; + Vec2D flip_handle = (v1_position - v2_position).norm().orth()*20.0f; if(ViButton(from_to_handle.x, from_to_handle.y, 8.0f, v1_v2 ? (Color){232, 125, 62, 255} : (Color){232, 50, 32, 255} )) v1_v2 = !v1_v2; if(ViButton(to_from_handle.x, to_from_handle.y, 8.0f, v2_v1 ? (Color){232, 125, 62, 255} : (Color){232, 50, 32, 255} )) v2_v1 = !v2_v1; @@ -37,5 +44,8 @@ class Connection{ if(ViButton(middle_handle.x, middle_handle.y, 8.0f, (Color){232, 50, 32, 255} )) remove = true; if(ViButton(config_handle.x, config_handle.y, 8.0f, (Color){158, 134, 200, 255})) config = !config; + + + if(ViButton(config_handle.x + flip_handle.x, config_handle.y + flip_handle.y, 8.0f, (Color){158, 0, 200, 255})) flip(); } }; diff --git a/src/datatypes.h b/src/datatypes.h index 0ffb7f0..fa25f54 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -37,7 +37,16 @@ class Vec2D{ return (x*v.x) + (y*v.y); }; + Vec2D orth(Vec2D &v){ + return Vec2D(-v.y, v.x); + }; + + Vec2D orth(){ + return Vec2D(-y, x); + }; + Vec2D norm() { return *this / length();}; + Vec2D& operator=(const Vec2D& vec){ if (this == &vec){ diff --git a/src/main.cpp b/src/main.cpp index 38e37c8..fc8d590 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,7 @@ enum States {IDLE, CONFIG}; struct AppState{ int width; int height; + int gui_selected_midi_out; States state; } app; @@ -195,36 +196,52 @@ int main(){ midi.note(s.next->midi_out, 1, 50, 50, 500); s.last = s.next; s.next = n; - //std::cout << s.last << " " << s.next << std::endl; - //midi_out.send_message(libremidi::channel_events::control_change(2, 10, 50)); } } s.draw(&mouse_pos); } - + midi.update(); EndMode2D(); - GuiWindowBox((Rectangle){0,0,280,600}, "config"); - - if(GuiButton((Rectangle){20, 60, 240, 20}, "button")){ - signals.emplace_back(Signal(nodes.front().position.x + 1, nodes.front().position.y + 1, 5.0f, 2.0f)); // position + 1 or else it does not render. why? no idea! - signals.back().next = &nodes[0]; - signals.back().last = &nodes[0]; + + // Main Config Window + GuiWindowBox((Rectangle){0,0,280,(float)app.height}, "config"); + { + GuiLabel((Rectangle){20, 60, 240, 20}, "MIDI OUT"); + int i = 0; + for(auto& mo : midi.midi_out_ports){ + GuiCheckBox((Rectangle){20, 80 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled); + i++; + } + if(GuiButton((Rectangle){20, 100 + (float)midi.midi_out_ports.size()*20, 240, 20}, "OPEN PORT")) midi.open_midi_out(app.gui_selected_midi_out); + + if(GuiButton((Rectangle){20, 60, 240, 20}, "ADD SIGNAL")){ + signals.emplace_back(Signal(nodes.front().position.x + 1, nodes.front().position.y + 1, 5.0f, 2.0f)); // position + 1 or else it does not render. why? no idea! + signals.back().next = &nodes[0]; + signals.back().last = &nodes[0]; + } } - + for(auto& n : nodes){ if(n.config){ n.config = !GuiWindowBox((Rectangle){n.position.x + 10, n.position.y + 10,240,300}, "config"); int option = n.mode; GuiComboBox((Rectangle){ n.position.x + 20, n.position.y + 40, 220, 20 }, "Round Robin;Random", &option); n.mode = (Distribution)option; - std::string toggle_out_ports = ""; + + int i = 0; for(auto& mo : midi.midi_out_ports){ - toggle_out_ports += mo.port_name + "\n"; + GuiCheckBox((Rectangle){20, 80 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled); + i++; + } + + std::string toggle_out_ports = ""; + for(auto& mo : midi.midi_outs){ + toggle_out_ports += midi.midi_out_ports[mo.first].port.port_name + "\n"; } toggle_out_ports = toggle_out_ports.substr(0, toggle_out_ports.size()-2); GuiLabel((Rectangle){n.position.x + 20, n.position.y + 60, 220, 20}, "MIDI OUT"); GuiToggleGroup((Rectangle){n.position.x + 20, n.position.y + 80, 220, 20}, toggle_out_ports.c_str(), &n.midi_out); - if(GuiButton((Rectangle){n.position.x + 20, n.position.y + 100 + midi.midi_out_ports.size()*20, 220, 20}, "OPEN PORT")) midi.open_midi_out(n.midi_out); + } } diff --git a/src/midi.h b/src/midi.h index d8466f3..ba9d2b5 100644 --- a/src/midi.h +++ b/src/midi.h @@ -6,6 +6,7 @@ #include "raylib.h" #include +#include #include #include @@ -15,55 +16,70 @@ struct NoteTimer{ int channel; int note; int velocity; - int start; - int length; + float start; + int gate; + bool erase = false; }; +struct MidiOutPort{ + libremidi::output_port port; + int number; + bool enabled; +}; + +struct MidiOut{ + libremidi::midi_out out; + +}; class Midi { public: Midi() { - - for(const libremidi::input_port& port : obs.get_input_ports()){ - midi_in_ports.push_back(port); - } - + int i = 0; for(const libremidi::output_port& port : obs.get_output_ports()){ - midi_out_ports.push_back(port); + midi_out_ports.push_back({port, i, false}); + i++; } - std::cout << midi_out_ports.size() << std::endl; } libremidi::observer obs; - std::vector> midi_outs; + std::map> midi_outs; std::vector midi_in_ports; - std::vector midi_out_ports; + std::vector midi_out_ports; std::vector notes; float timer; - - void open_midi_out(int midi_n){ - std::cout << midi_n << std::endl; - - auto out = std::make_shared(); - out->open_port(midi_out_ports[midi_n]); - midi_outs.push_back(out); + + void open_midi_out(MidiOutPort *out_port){ + if(!midi_outs.contains(out_port->number)){ + auto out = std::make_shared(); + out->open_port(out_port->port); + midi_outs[out_port->number] = out; + } } - void note(int midi_out, int channel, int note, int velocity, int length){ - midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity)); - notes.emplace_back(midi_out, channel, note, velocity, GetTime(), length); + void note(int midi_out, int channel, int note, int velocity, int gate){ + if(!midi_outs.empty()){ + midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity)); + notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate); + } } void update(){ float time = GetTime(); - for( auto it = notes.begin(); it != notes.end(); ++it){ - if(time - it->start > ((float)it->length / 1000.0f)){ + for(auto it = notes.begin(); it != notes.end(); ++it){ + if(time - it->start >= (float)it->gate / 1000.0f){ midi_outs[it->midi_out]->send_message(libremidi::channel_events::note_off(it->channel, it->note, it->velocity)); - notes.erase(it); + it->erase = true; + } + } + std::erase_if(notes, [](const auto& item){return item.erase;}); + for(auto& p : midi_out_ports){ + if(p.enabled) { + open_midi_out(&p); } } } diff --git a/src/node.h b/src/node.h index 9fd7b2a..572b437 100644 --- a/src/node.h +++ b/src/node.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "datatypes.h" #include "moveable.h" @@ -21,7 +22,9 @@ class Node : public Movable{ bool connecting = false; bool config = false; - int midi_out = 0; + int midi_out = -1; + + std::vector midi_outs; int midi_note = 0; int midi_cc = 0;