From 18f12eb9f355066f83527c949282a51d0015ec63 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 25 Oct 2024 16:41:37 +0200 Subject: [PATCH] refactored main a bit --- src/datatypes.h | 10 +- src/main.cpp | 407 ++++++++++++++++++++----------------------- src/node-manager.h | 29 +++ src/node.h | 5 + src/signal-manager.h | 17 +- src/state-manager.h | 55 ++++++ src/ui.h | 148 ++++++++++++++++ 7 files changed, 438 insertions(+), 233 deletions(-) create mode 100644 src/node-manager.h create mode 100644 src/state-manager.h create mode 100644 src/ui.h diff --git a/src/datatypes.h b/src/datatypes.h index 2e0c9e4..8c0abeb 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -1,5 +1,7 @@ #pragma once +//#include "connection-manager.h" +//#include "signal-manager.h" #ifdef _WIN32 #define _USE_MATH_DEFINES #endif @@ -133,11 +135,8 @@ Vec2D operator*(const Vec2D& left, T scalar){ return Vec2D(left.x * scalar, left.y * scalar); }; -// State - -enum States {IDLE, CONFIG}; - -struct AppState{ +// App Settings +struct AppSettings{ int width; int height; float grid; @@ -145,7 +144,6 @@ struct AppState{ float fps; float speed = 16.0f; int gui_selected_midi_out; - States state; }; // MIDI diff --git a/src/main.cpp b/src/main.cpp index 37e18ef..08452ed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,113 +16,79 @@ /*Pink: (176, 82, 121); #b05279*/ /*Blue: (108, 153, 187); #6c99bb*/ +#define RAYGUI_IMPLEMENTATION + #include "datatypes.h" -#include "signal-manager.h" -#include -#include -#include #include #include #include -#include - -#define RAYGUI_IMPLEMENTATION -#include "raygui/raygui.h" -#include "raygui/dark/style_dark.h" #include #include #include - -#include "midi.h" -#include "node.h" - -#include "connection-manager.h" -#include "signal-manager.h" +#include "state-manager.h" AppState app; -std::vector nodes; - -ConnectionManager cm; - -Midi midi; - -SignalManager sm(&app, &cm, &midi); - Vec2D mouse_pos; Vec2D drag_start; +Camera2D camera = { {0} }; + bool mouse_clicked = false; bool mouse_touched = false; bool mouse_dragging = false; +//#include "ui.h" int main(){ srand(time(NULL)); InitWindow(2560, 1440, "VISEQ"); - - GuiLoadStyleDark(); + + app.ui.init(); int monitor = 0; //GetCurrentMonitor(); SetWindowMonitor(monitor); - app.fps = 1.0f; - app.bpm = 120.0f; - app.grid = 120.0f; //round(((app.fps / (app.bpm / 60.0f)) * app.speed) / 4.0f); // from quarter notes to 16th notes = div by 4 -// app.speed = round((((app.fps / (app.bpm / 60.0f)) / app.grid) / 4.0f) / 10.0f) * 10.0f; + app.settings.fps = 1.0f; + app.settings.bpm = 120.0f; + app.settings.grid = 120.0f; //round(((app.settings.fps / (app.settings.bpm / 60.0f)) * app.settings.speed) / 4.0f); // from quarter notes to 16th notes = div by 4 +// app.settings.speed = round((((app.settings.fps / (app.settings.bpm / 60.0f)) / app.settings.grid) / 4.0f) / 10.0f) * 10.0f; // 16th note = 120px (festgelegt) // ganze note = 1920px // ganze note bei 120bpm = 2s // 120bpm = 960px/s - app.speed = (app.grid * 8.0f) * (120.0f / app.bpm); + app.settings.speed = (app.settings.grid * 8.0f) * (app.settings.bpm / 120.f); - // 120 fps - // 120bpm = 120 1/4 Noten pro Minute = 2 1/4 Noten / s - // 2px / frame bei 120fps = 240px / s = 14400 px / minute - // 1/4 Note / 120 pixel - // 1/16 Note alle 30 pixel - - // 60 fps bei 120 bpm - // 2px / frame bei 60fps = 120px / s = 7200 px / minute - // 1/16 Note alle 15 pixel - // 1/8 Note alle 30 pixel - // 1/4 Note alle 60 pixel - // 1/2 Note alle 120 pixel - // 1/1 Note alle 240 pixel - - app.width = GetMonitorWidth(monitor); - app.height = GetMonitorHeight(monitor); + app.settings.width = GetMonitorWidth(monitor); + app.settings.height = GetMonitorHeight(monitor); SetTargetFPS(60.0f); - Camera2D camera = { {0} }; - camera.target = (Vector2){ app.width/2.0f, app.height/2.0f }; - camera.offset = (Vector2){ app.width/2.0f, app.height/2.0f }; + camera.target = (Vector2){ app.settings.width/2.0f, app.settings.height/2.0f }; + camera.offset = (Vector2){ app.settings.width/2.0f, app.settings.height/2.0f }; camera.rotation = 0.0f; - camera.zoom = 1.0f; - - nodes.reserve(1000); + camera.zoom = 1.0f; for(int i = 0; i < 4; ++i){ - nodes.emplace_back((float)i * app.grid + 10 * app.grid, 5 * app.grid, 15.0f); + app.nm.addNode((float)i * app.settings.grid + 10 * app.settings.grid, 5 * app.settings.grid); } for(int i = 3; i >= 0; --i){ - nodes.emplace_back((float)i * app.grid + 10 * app.grid, 6 * app.grid, 15.0f); + app.nm.addNode((float)i * app.settings.grid + 10 * app.settings.grid, 6 * app.settings.grid); } for(int i = 0; i < 8; ++i){ - cm.addConnection(&nodes[i], &nodes[(i+1) % 8]); + app.cm.addConnection(&app.nm.nodes[i], &app.nm.nodes[(i+1) % 8]); } - sm.addSignalAtNode(&nodes.front()); + app.sm.addSignalAtNode(&app.nm.nodes.front()); - sm.startSignalThread(); + app.sm.startSignalThread(); Vec2D drag_offset; @@ -144,9 +110,8 @@ int main(){ BeginDrawing(); ClearBackground((Color){46,46,46}); BeginMode2D(camera); - - //app.grid = round(((app.fps / (app.bpm / 60.0f)) * app.speed) / 4.0f); // from quarter notes to 16th notes = div by 4 - app.speed = (app.grid * 8.0f) * (120.0f / app.bpm); + //app.settings.mutex.lock(); + app.settings.speed = (app.settings.grid * 8.0f) * (app.settings.bpm / 120.f); Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera); Vec2D mouse_pos = {(float)mp.x, (float)mp.y}; @@ -163,11 +128,11 @@ int main(){ Vec2D mouse_delta = {GetMouseDelta().x, GetMouseDelta().y}; Vec2D delta = mouse_delta * (-1.0f/camera.zoom); - auto dragging_nodes = nodes | std::ranges::views::filter([](Node n) { return n.dragging;}); - auto connecting_nodes = nodes | std::ranges::views::filter([](Node n) { return n.connecting;}); - auto selected_nodes = nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;}); - auto hovering_nodes = nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, n.position, n.radius);}); - auto hovering_connectors = nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, (n.position - ((n.position - mouse_pos)).norm() * (n.radius + 14.0f)), 8.0f);}); + auto dragging_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.dragging;}); + auto connecting_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.connecting;}); + auto selected_nodes = app.nm.nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;}); + auto hovering_nodes = app.nm.nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, n.position, n.radius);}); + auto hovering_connectors = app.nm.nodes | std::ranges::views::filter([&mouse_pos](Node n) { return CheckCollisionPointCircle(mouse_pos, (n.position - ((n.position - mouse_pos)).norm() * (n.radius + 14.0f)), 8.0f);}); if(!mouse_down && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::HOVER_CANVAS; if((mouse_clicked || mouse_down || mouse_released) && !mouse_right_down && dragging_nodes.empty() && connecting_nodes.empty() && hovering_nodes.empty() && hovering_connectors.empty()) istate = InteractionState::DRAW_SELECTION; @@ -193,7 +158,7 @@ int main(){ DrawRectangleLines(select1.x, select1.y, select2.x, select2.y, (Color){150,150,150,150}); selection = (Rectangle){select1.x, select1.y, select2.x, select2.y}; - for(auto& n : nodes){ + for(auto& n : app.nm.nodes){ if(CheckCollisionPointRec(n.position, selection)) { n.in_rect = true; } else { @@ -203,7 +168,7 @@ int main(){ } if(mouse_released){ - for(auto& n : nodes){ + for(auto& n : app.nm.nodes){ if(CheckCollisionPointRec(n.position, selection)) { n.selected = true; n.in_rect = false; @@ -227,7 +192,7 @@ int main(){ break; case InteractionState::DRAG_NODE: if(IsKeyDown(KEY_LEFT_CONTROL)) { - dragging_nodes.front().position = (Vec2D){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.grid)*app.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.grid)*app.grid}; + dragging_nodes.front().position = (Vec2D){round((mouse_pos.x+dragging_nodes.front().drag_offset.x)/app.settings.grid)*app.settings.grid, round((mouse_pos.y+dragging_nodes.front().drag_offset.y)/app.settings.grid)*app.settings.grid}; } else { dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; } @@ -256,13 +221,15 @@ int main(){ } if(mouse_released) { - if(!hovering_nodes.empty()){ - cm.addConnection(&connecting_nodes.front(), &hovering_nodes.front()); - } else if(!hovering_connectors.empty()){ - cm.addConnection(&connecting_nodes.front(), &hovering_connectors.front()); - } else { - nodes.emplace_back(mouse_pos.x, mouse_pos.y, 15.0f); - cm.addConnection(&connecting_nodes.front(), &nodes.back()); + if(&hovering_nodes.front() != &connecting_nodes.front() && &connecting_nodes.front() != &hovering_connectors.front()){ + if(!hovering_nodes.empty()){ + app.cm.addConnection(&connecting_nodes.front(), &hovering_nodes.front()); + } else if(!hovering_connectors.empty()){ + app.cm.addConnection(&connecting_nodes.front(), &hovering_connectors.front()); + } else { + app.nm.addNode(mouse_pos.x, mouse_pos.y); + app.cm.addConnection(&connecting_nodes.front(), &app.nm.nodes.back()); + } } connecting_nodes.front().connecting = false; } @@ -273,8 +240,8 @@ int main(){ if(wheel != 0){ if(!hovering_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL)){ hovering_nodes.front().midi_note += wheel; - if(hovering_nodes.front().midi_note < 0 ) hovering_nodes.front().midi_note = midi.midiNotes.size()-1; - if(hovering_nodes.front().midi_note >= midi.midiNotes.size() ) hovering_nodes.front().midi_note = 0; + if(hovering_nodes.front().midi_note < 0 ) hovering_nodes.front().midi_note = app.sm.midi.midiNotes.size()-1; + if(hovering_nodes.front().midi_note >= app.sm.midi.midiNotes.size() ) hovering_nodes.front().midi_note = 0; } else { Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera); camera.offset = GetMousePosition(); @@ -285,154 +252,18 @@ int main(){ } } - for(int x = -app.grid*100; x < app.grid*100; x+=app.grid){ - for(int y = -app.grid*100; y < app.grid*100; y+=app.grid){ + for(int x = -app.settings.grid*100; x < app.settings.grid*100; x+=app.settings.grid){ + for(int y = -app.settings.grid*100; y < app.settings.grid*100; y+=app.settings.grid){ DrawPixel(x, y, (Color){130,130,130,200}); } } - for(auto& n : nodes){ - n.draw(&mouse_pos, &camera); - } - - cm.drawConnections(&mouse_pos, &camera); - sm.drawSignals(&mouse_pos, &camera); - - midi.update(); + + app.draw(&mouse_pos, &camera); EndMode2D(); - - // Main Config Window - GuiWindowBox((Rectangle){0,0,280,(float)app.height}, "config"); - { - const int s_controls = 55; - if(GuiButton((Rectangle){20, (float)s_controls, 60, 30}, "PLAY")) sm.play ? sm.pause() : sm.start(); - if(GuiButton((Rectangle){100, (float)s_controls, 60, 30}, "STOP")) sm.stop(); - if(GuiButton((Rectangle){180, (float)s_controls, 60, 30}, "RESET")) sm.reset(); - const int s_bpm = s_controls + 35; - GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING"); - GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",app.bpm).c_str()); - float _bpm = (float)app.bpm; - GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300); - app.bpm = _bpm; - - int s_midi = s_bpm + 55; - GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT"); - int i = 0; - for(auto& mo : midi.midi_out_ports){ - GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled); - i++; - } - - if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) { - for(auto &midis : midi.midi_out_ports){ - if(midis.enabled){ - for(auto &n : nodes){ - n.midi_outs[midis.number].enabled = true; - } - } - } - } - - int s_signal = s_midi + 90 + i*20; - GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS"); - if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){ - int ni = rand() % nodes.size(); - sm.addSignalAtNode(&nodes[ni]); - } - - int signal_configs = 0; - for(auto &s : sm.signals){ - if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm.removeSignal(signal_configs); - DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color); - float s_multiplier = round(s.multiplier*10.0f)/10.0f; - float signal_bpm = app.bpm * s.multiplier; - GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 2); - s.multiplier = s_multiplier; - signal_configs++; - } - - int s_nodes = s_signal + 60 + signal_configs*20; - GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES"); - if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){ - nodes.emplace_back(200.0f, 200.0f, 15.0f); - } - - DrawFPS(20, app.height - 30); - } - - // Per Node Config Windows - for(auto& n : nodes){ - Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, camera); - - for(auto& mo : midi.midi_out_ports){ - if(!n.midi_outs.contains(mo.number) && mo.enabled){ - n.midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; - } - if(n.midi_outs.contains(mo.number) && !mo.enabled){ - n.midi_outs.erase(mo.number); - } - } - - if(n.config){ - n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config"); - - int option = n.mode; - GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 110, 20 }, "Round Robin;Random", &option); - n.mode = (Distribution)option; - - GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS"); - if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){ - sm.addSignalAtNode(&n); - } - - int y_midi = 120; - GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT"); - - int i = 0; - for(auto& mo : n.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++; - } - - int y_channel = y_midi + 30 + i*20; - GuiLabel((Rectangle){np.x + 20, np.y + y_channel, 220, 20}, "CHANNEL"); - GuiToggleGroup((Rectangle){np.x + 20, np.y + y_channel + 30, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_channel); - - GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 90, 100, 20}, "NOTE"); - float v = n.midi_note; - GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 90, 20, 20}, midi.midiNotes[(int)round(v)].c_str()); - GuiLabel((Rectangle){np.x + 160, np.y + y_channel + 90, 40, 20}, std::format("{}",n.midi_note).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 120, 220, 20}, "", "", &v, 0, 127); - n.midi_note = round(v); - - GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 150, 100, 20}, "VELOCITY"); - v = n.midi_velocity; - GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 150, 20, 20}, std::format("{}",n.midi_velocity).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 180, 220, 20}, "", "", &v, 0, 127); - n.midi_velocity = round(v); - - GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 210, 100, 20}, "GATE"); - v = n.midi_gate; - GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 210, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str()); - GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 240, 220, 20}, "", "", &v, 0, 5000); - n.midi_gate = round(v); - - //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 40, 140, 20}, "VELOCITY", &n.midi_velocity, 0, 127, n.gui_midi_velocity_edit)) n.gui_midi_velocity_edit = !n.gui_midi_velocity_edit; - //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 60, 140, 20}, "GATE", &n.midi_gate, 0, 5000, n.gui_midi_gate_edit)) n.gui_midi_gate_edit = !n.gui_midi_gate_edit; - } - } - - for(auto& c : cm.connections){ - if(c.config){ - Vec2D config_handle = c.v2->position - ((c.v2->position - c.v1->position))*0.75f; - c.config = !GuiWindowBox((Rectangle){config_handle.x + 10, config_handle.y + 10, 200, 300}, "connection"); - GuiToggle((Rectangle){config_handle.x + 10, config_handle.y + 50, 80, 20}, "v1v2", &c.v1_v2); - GuiToggle((Rectangle){config_handle.x + 100, config_handle.y + 50, 80, 20}, "v2v1", &c.v2_v1); - - } - } + app.drawUI(&mouse_pos, &camera); EndDrawing(); } @@ -442,6 +273,140 @@ int main(){ return 0; } - - - +/*void drawGUI(){*/ +/**/ +/* // Main Config Window*/ +/* GuiWindowBox((Rectangle){0,0,280,(float)app.settings.height}, "config");*/ +/* {*/ +/* const int s_controls = 55;*/ +/* if(GuiButton((Rectangle){20, (float)s_controls, 60, 30}, "PLAY")) sm.play ? sm.pause() : sm.start();*/ +/* if(GuiButton((Rectangle){100, (float)s_controls, 60, 30}, "STOP")) sm.stop();*/ +/* if(GuiButton((Rectangle){180, (float)s_controls, 60, 30}, "RESET")) sm.reset();*/ +/**/ +/* const int s_bpm = s_controls + 35;*/ +/* GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING");*/ +/* GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",app.settings.bpm).c_str());*/ +/* float _bpm = (float)app.settings.bpm;*/ +/* GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300);*/ +/* app.settings.bpm = _bpm;*/ +/**/ +/* int s_midi = s_bpm + 55;*/ +/* GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");*/ +/* int i = 0;*/ +/* for(auto& mo : midi.midi_out_ports){*/ +/* GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled);*/ +/* i++;*/ +/* }*/ +/**/ +/* if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) {*/ +/* for(auto &midis : midi.midi_out_ports){*/ +/* if(midis.enabled){*/ +/* for(auto &n : nodes){*/ +/* n.midi_outs[midis.number].enabled = true;*/ +/* }*/ +/* }*/ +/* }*/ +/* }*/ +/**/ +/* int s_signal = s_midi + 90 + i*20;*/ +/* GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");*/ +/* if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){*/ +/* int ni = rand() % nodes.size();*/ +/* sm.addSignalAtNode(&nodes[ni]);*/ +/* }*/ +/**/ +/* int signal_configs = 0;*/ +/* for(auto &s : sm.signals){*/ +/* if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm.removeSignal(signal_configs);*/ +/* DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color);*/ +/* float s_multiplier = round(s.multiplier*10.0f)/10.0f;*/ +/* float signal_bpm = app.settings.bpm * s.multiplier;*/ +/* GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 2);*/ +/* s.multiplier = s_multiplier;*/ +/* s.speed = app.settings.speed;*/ +/* signal_configs++;*/ +/* }*/ +/**/ +/* int s_nodes = s_signal + 60 + signal_configs*20;*/ +/* GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES");*/ +/* if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){*/ +/* nodes.emplace_back(200.0f, 200.0f, 15.0f);*/ +/* }*/ +/**/ +/* DrawFPS(20, app.settings.height - 30);*/ +/* }*/ +/**/ +/* // Per Node Config Windows*/ +/* for(auto& n : nodes){*/ +/* Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, camera);*/ +/**/ +/* for(auto& mo : midi.midi_out_ports){*/ +/* if(!n.midi_outs.contains(mo.number) && mo.enabled){*/ +/* n.midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; */ +/* }*/ +/* if(n.midi_outs.contains(mo.number) && !mo.enabled){*/ +/* n.midi_outs.erase(mo.number);*/ +/* }*/ +/* }*/ +/**/ +/* if(n.config){*/ +/* n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config");*/ +/**/ +/* int option = n.mode;*/ +/* GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 110, 20 }, "Round Robin;Random", &option);*/ +/* n.mode = (Distribution)option;*/ +/**/ +/* GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS");*/ +/* if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){*/ +/* sm.addSignalAtNode(&n);*/ +/* }*/ +/**/ +/* int y_midi = 120;*/ +/* GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT");*/ +/**/ +/* int i = 0;*/ +/* for(auto& mo : n.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++;*/ +/* }*/ +/**/ +/* int y_channel = y_midi + 30 + i*20;*/ +/* GuiLabel((Rectangle){np.x + 20, np.y + y_channel, 220, 20}, "CHANNEL");*/ +/* GuiToggleGroup((Rectangle){np.x + 20, np.y + y_channel + 30, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_channel);*/ +/**/ +/* GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 90, 100, 20}, "NOTE");*/ +/* float v = n.midi_note;*/ +/* GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 90, 20, 20}, midi.midiNotes[(int)round(v)].c_str());*/ +/* GuiLabel((Rectangle){np.x + 160, np.y + y_channel + 90, 40, 20}, std::format("{}",n.midi_note).c_str());*/ +/* GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 120, 220, 20}, "", "", &v, 0, 127);*/ +/* n.midi_note = round(v);*/ +/**/ +/* GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 150, 100, 20}, "VELOCITY");*/ +/* v = n.midi_velocity;*/ +/* GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 150, 20, 20}, std::format("{}",n.midi_velocity).c_str());*/ +/* GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 180, 220, 20}, "", "", &v, 0, 127);*/ +/* n.midi_velocity = round(v);*/ +/**/ +/* GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 210, 100, 20}, "GATE");*/ +/* v = n.midi_gate;*/ +/* GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 210, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str());*/ +/* GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 240, 220, 20}, "", "", &v, 0, 5000);*/ +/* n.midi_gate = round(v);*/ +/**/ +/* //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 40, 140, 20}, "VELOCITY", &n.midi_velocity, 0, 127, n.gui_midi_velocity_edit)) n.gui_midi_velocity_edit = !n.gui_midi_velocity_edit;*/ +/* //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 60, 140, 20}, "GATE", &n.midi_gate, 0, 5000, n.gui_midi_gate_edit)) n.gui_midi_gate_edit = !n.gui_midi_gate_edit;*/ +/* }*/ +/* }*/ +/**/ +/* for(auto& c : cm.connections){*/ +/* if(c.config){*/ +/* Vec2D config_handle = c.v2->position - ((c.v2->position - c.v1->position))*0.75f;*/ +/* c.config = !GuiWindowBox((Rectangle){config_handle.x + 10, config_handle.y + 10, 200, 300}, "connection");*/ +/* GuiToggle((Rectangle){config_handle.x + 10, config_handle.y + 50, 80, 20}, "v1v2", &c.v1_v2);*/ +/* GuiToggle((Rectangle){config_handle.x + 100, config_handle.y + 50, 80, 20}, "v2v1", &c.v2_v1);*/ +/**/ +/* }*/ +/* }*/ +/**/ +/**/ +/*}*/ diff --git a/src/node-manager.h b/src/node-manager.h new file mode 100644 index 0000000..db99f4b --- /dev/null +++ b/src/node-manager.h @@ -0,0 +1,29 @@ +#pragma once + +#include "node.h" +#include + +class NodeManager{ + + public: + NodeManager() { + nodes.reserve(1000); + } + + std::vector nodes; + + void addNode(float x, float y){ + addNode(Vec2D(x,y)); + } + + void addNode(Vec2D position){ + nodes.emplace_back(position, 15.0f); + } + + void drawNodes(Vec2D *mouse_pos, Camera2D *camera){ + for(auto& n : nodes){ + n.draw(mouse_pos, camera); + } + } + +}; diff --git a/src/node.h b/src/node.h index c67e7dc..d5d3935 100644 --- a/src/node.h +++ b/src/node.h @@ -20,6 +20,7 @@ class Node : public Movable{ public: Node() {} Node(float x, float y, float r) : Movable(Vec2D(x, y), 0.0f), radius(r) {} + Node(Vec2D p, float r) : Movable(p, 0.0f), radius(r) {} float radius; @@ -52,6 +53,10 @@ class Node : public Movable{ Distribution mode = Distribution::ROUNDROBIN; + void setMidiPort(int midi_port, bool state){ + midi_outs[midi_port].enabled = state; + } + void draw(Vec2D *mouse, Camera2D *cam) { if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, (Color){180, 210, 115, 255}); if(dragging || selected || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255}); diff --git a/src/signal-manager.h b/src/signal-manager.h index f0dea2b..a063fc4 100644 --- a/src/signal-manager.h +++ b/src/signal-manager.h @@ -12,11 +12,13 @@ class SignalManager{ public: - SignalManager(AppState *app, ConnectionManager *cm, Midi *midi) : app(app), cm(cm), midi(midi) {} + SignalManager() {} + SignalManager(ConnectionManager *cm, std::mutex *mutex) : mutex(mutex), cm(cm) {} - AppState *app; + Midi midi; + + std::mutex *mutex; ConnectionManager *cm; - Midi *midi; std::vector signals; @@ -53,10 +55,10 @@ class SignalManager{ void stopSignalThread(){ loop = false; } - + void addSignalAtNode(Node *node){ Color c = {(unsigned char)(232 - (rand() % 120)), (unsigned char)(125 + (int)(rand() % 120)), 62, 255}; - signals.emplace(signals.end(), Signal(node->position.x+1, node->position.y+1, 5.0f, app->speed, c)); // position + 1 or else it does not render. why? no idea! + signals.emplace(signals.end(), Signal(node->position.x+1, node->position.y+1, 5.0f, 0, c)); // position + 1 or else it does not render. why? no idea! signals.back().next = node; signals.back().last = node; signals.back().start = node; @@ -71,6 +73,7 @@ class SignalManager{ while(loop){ std::chrono::duration diff = std::chrono::steady_clock::now() - last; if(diff.count() > 500){ + //mutex->lock(); if(play){ for(auto& s : signals){ s.direction = (s.next->position - s.position).norm(); @@ -80,7 +83,7 @@ class SignalManager{ if(n != s.next && n != nullptr) { for(auto &m : s.next->midi_outs){ - if(m.second.enabled) midi->note(m.second.i, s.next->midi_channel, s.next->midi_note, s.next->midi_velocity, s.next->midi_gate); + if(m.second.enabled) midi.note(m.second.i, s.next->midi_channel, s.next->midi_note, s.next->midi_velocity, s.next->midi_gate); } s.last = s.next; s.next = n; @@ -88,7 +91,9 @@ class SignalManager{ } } } + //mutex->unlock(); last = std::chrono::steady_clock::now(); + midi.update(); } } } diff --git a/src/state-manager.h b/src/state-manager.h new file mode 100644 index 0000000..967aec8 --- /dev/null +++ b/src/state-manager.h @@ -0,0 +1,55 @@ +#pragma once + +#include "datatypes.h" +#include "node-manager.h" +#include "connection-manager.h" +#include "signal-manager.h" +#include "ui.h" + +class AppState{ + public: + + AppState() : sm(&cm, &mutex), ui(&settings, &nm, &cm, &sm) {} + + enum States {IDLE, CONFIG}; + + AppSettings settings; + + States state; + + std::mutex mutex; + + ConnectionManager cm; + SignalManager sm; + NodeManager nm; + + AppUI ui; + + void updateMidiPorts(){ + for(auto&n : nm.nodes){ + for(auto& mo : sm.midi.midi_out_ports){ + if(!n.midi_outs.contains(mo.number) && mo.enabled){ + n.midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; + } + if(n.midi_outs.contains(mo.number) && !mo.enabled){ + n.midi_outs.erase(mo.number); + } + } + } + } + + void draw(Vec2D *mouse_pos, Camera2D *camera){ + mutex.lock(); + updateMidiPorts(); + nm.drawNodes(mouse_pos, camera); + cm.drawConnections(mouse_pos, camera); + sm.drawSignals(mouse_pos, camera); + mutex.unlock(); + } + + void drawUI(Vec2D *mouse_pos, Camera2D *camera){ + mutex.lock(); + ui.draw(mouse_pos, camera); + mutex.unlock(); + } +}; diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..fa1028f --- /dev/null +++ b/src/ui.h @@ -0,0 +1,148 @@ +#pragma once + +#include "raygui/raygui.h" +#include "raygui/dark/style_dark.h" + +#include "datatypes.h" + +#include "connection-manager.h" +#include "node-manager.h" +#include "raylib.h" +#include "signal-manager.h" +#include + +class AppUI{ + public: + + AppUI(AppSettings *settings, NodeManager *nm, ConnectionManager *cm, SignalManager *sm) : settings(settings), nm(nm), sm(sm), cm(cm) {} + + AppSettings *settings; + NodeManager *nm; + SignalManager *sm; + ConnectionManager *cm; + + void init(){ + GuiLoadStyleDark(); + } + + void drawSideBar(){ + + GuiWindowBox((Rectangle){0,0,280,(float)settings->height}, "config"); + { + const int s_controls = 55; + if(GuiButton((Rectangle){20, (float)s_controls, 60, 30}, "PLAY")) sm->play ? sm->pause() : sm->start(); + if(GuiButton((Rectangle){100, (float)s_controls, 60, 30}, "STOP")) sm->stop(); + if(GuiButton((Rectangle){180, (float)s_controls, 60, 30}, "RESET")) sm->reset(); + + const int s_bpm = s_controls + 35; + GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING"); + GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",settings->bpm).c_str()); + float _bpm = (float)settings->bpm; + GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300); + settings->bpm = _bpm; + + int s_midi = s_bpm + 55; + GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT"); + int i = 0; + for(auto& mo : sm->midi.midi_out_ports){ + GuiCheckBox((Rectangle){20, (float)s_midi + 25 + i*20.0f, 20, 20}, mo.port.port_name.c_str(), &mo.enabled); + i++; + } + + if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) { + for(auto &midis : sm->midi.midi_out_ports){ + if(midis.enabled){ + for(auto &n : nm->nodes){ + n.setMidiPort(midis.number, true); + } + } + } + } + + int s_signal = s_midi + 90 + i*20; + GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS"); + if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){ + int ni = rand() % nm->nodes.size(); + sm->addSignalAtNode(&nm->nodes[ni]); + } + + int signal_configs = 0; + for(auto &s : sm->signals){ + if(GuiButton((Rectangle){20,(float)s_signal+70 + signal_configs*20, 20, 20},"X")) sm->removeSignal(signal_configs); + DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s.color); + float s_multiplier = round(s.multiplier*10.0f)/10.0f; + float signal_bpm = settings->bpm * s.multiplier; + GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_multiplier, 0.1, 2); + s.multiplier = s_multiplier; + s.speed = settings->speed; + signal_configs++; + } + + int s_nodes = s_signal + 60 + signal_configs*20; + GuiLabel((Rectangle){20, (float)s_nodes + 30, 240, 20}, "NODES"); + if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){ + nm->nodes.emplace_back(200.0f, 200.0f, 15.0f); + } + + DrawFPS(20, settings->height - 30); + } + } + + void drawPerNodeConfig(Camera2D *camera){ + for(auto& n : nm->nodes | std::ranges::views::filter([](Node n){ return n.config; })){ + Vector2 np = GetWorldToScreen2D((Vector2){n.position.x, n.position.y}, *camera); + + n.config = !GuiWindowBox((Rectangle){np.x + 10, np.y + 10, 240, 440}, "config"); + + int option = n.mode; + GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 110, 20 }, "Round Robin;Random", &option); + n.mode = (Distribution)option; + + GuiLabel((Rectangle){np.x + 20, np.y + 65, 220, 20}, "SIGNALS"); + if(GuiButton((Rectangle){np.x + 20, np.y + 90, 220, 20}, "ADD SIGNAL")){ + sm->addSignalAtNode(&n); + } + + int y_midi = 120; + GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT"); + + int i = 0; + for(auto& mo : n.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++; + } + + int y_channel = y_midi + 30 + i*20; + GuiLabel((Rectangle){np.x + 20, np.y + y_channel, 220, 20}, "CHANNEL"); + GuiToggleGroup((Rectangle){np.x + 20, np.y + y_channel + 30, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &n.midi_channel); + + GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 90, 100, 20}, "NOTE"); + float v = n.midi_note; + GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 90, 20, 20}, sm->midi.midiNotes[(int)round(v)].c_str()); + GuiLabel((Rectangle){np.x + 160, np.y + y_channel + 90, 40, 20}, std::format("{}",n.midi_note).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 120, 220, 20}, "", "", &v, 0, 127); + n.midi_note = round(v); + + GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 150, 100, 20}, "VELOCITY"); + v = n.midi_velocity; + GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 150, 20, 20}, std::format("{}",n.midi_velocity).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 180, 220, 20}, "", "", &v, 0, 127); + n.midi_velocity = round(v); + + GuiLabel((Rectangle){np.x + 20, np.y + y_channel + 210, 100, 20}, "GATE"); + v = n.midi_gate; + GuiLabel((Rectangle){np.x + 210, np.y + y_channel + 210, 20, 20}, std::format("{}",(float)n.midi_gate/1000.0f).c_str()); + GuiSlider((Rectangle){np.x + 20, np.y + y_channel + 240, 220, 20}, "", "", &v, 0, 5000); + n.midi_gate = round(v); + + //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 40, 140, 20}, "VELOCITY", &n.midi_velocity, 0, 127, n.gui_midi_velocity_edit)) n.gui_midi_velocity_edit = !n.gui_midi_velocity_edit; + //if(GuiValueBox((Rectangle){n.position.x + 100, n.position.y + y_channel + 60, 140, 20}, "GATE", &n.midi_gate, 0, 5000, n.gui_midi_gate_edit)) n.gui_midi_gate_edit = !n.gui_midi_gate_edit; + } + } + + void draw(Vec2D *mouse_pos, Camera2D *camera){ + drawSideBar(); + drawPerNodeConfig(camera); + } + +};