diff --git a/src/button.h b/src/button.h index f7aed10..c86fcfa 100644 --- a/src/button.h +++ b/src/button.h @@ -9,3 +9,9 @@ inline bool ViButton(Vec2D *mouse_pos, float x, float y, float radius, Co DrawCircle(x,y,radius, color); return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); } + +inline bool ViSegButton(Vec2D *mouse_pos, float x, float y, float start_angle, float end_angle, float radius, Color color){ + Vector2 mp = {mouse_pos->x, mouse_pos->y}; //GetMousePosition(); TODO: Refactor my own vectors to raylib vectors + DrawCircleSector((Vector2){x,y},radius, start_angle, end_angle, 5, color); + return (Vector2Distance(mp, (Vector2){x,y}) < radius && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); +} diff --git a/src/connection-manager.h b/src/connection-manager.h index ff8cbdc..b764aab 100644 --- a/src/connection-manager.h +++ b/src/connection-manager.h @@ -15,6 +15,10 @@ class ConnectionManager{ std::map counter; + void reset(){ + counter.clear(); + } + Node* next(Node* current, Node* last){ std::vector paths; std::vector nodes; diff --git a/src/connection.h b/src/connection.h index 5655063..b2901b9 100644 --- a/src/connection.h +++ b/src/connection.h @@ -19,6 +19,9 @@ class Connection{ bool remove = false; bool config = false; + float connector_offset = 14.0f; + float switches_offset = 25.0f; + void flip(){ Node* vt = v1; v1 = v2; @@ -26,31 +29,49 @@ class Connection{ } void draw(Vec2D *mouse_pos, Camera2D *cam){ - Vec2D v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + 14.0f); - Vec2D v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 14.0f); + Vec2D v1_position = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + connector_offset); + Vec2D v2_position = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + connector_offset); + float v1_v2_distance = v1_position.dist(&v2_position); + // Line with small connector circles Color c = {229, 181, 103, 255}; if(active) c = {108, 153, 187, 255}; DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, c); DrawCircle(v1_position.x, v1_position.y, 2.0f, c); DrawCircle(v2_position.x, v2_position.y, 2.0f, c); - - Vec2D from_to_handle = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + 35); - Vec2D to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + 35); + + if(v1_v2_distance < 100) switches_offset = v1_v2_distance/2.3f; + Vec2D from_to_handle = v1->position - ((v1->position - v2->position)).norm() * (v1->radius + switches_offset); + Vec2D to_from_handle = v2->position - ((v2->position - v1->position)).norm() * (v2->radius + switches_offset); 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; +// Vec2D config_handle = v2->position - ((v2->position - v1->position))*0.66f; + Vec2D flip_handle = middle_handle + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*0.2, 0.f, 20.f) : 1.0f); + + // Dynamic opacity by distance to mouse unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f); unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 120.0f); unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 120.0f); + // On/Off switches on both sides if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, v1_v2 ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) v1_v2 = !v1_v2; if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, v2_v1 ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) v2_v1 = !v2_v1; + + if(!v1_v2){ + Vec2D block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle; + Vec2D block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle; + DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from}); + } - if(ViButton(mouse_pos, middle_handle.x, middle_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) remove = true; - if(ViButton(mouse_pos, config_handle.x, config_handle.y, 8.0f, (Color){158, 134, 200, opacity})) config = !config; + if(!v2_v1){ + Vec2D block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle; + Vec2D block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + to_from_handle; + DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_to}); + } + + if(ViButton(mouse_pos, flip_handle.x, flip_handle.y, 8.0f, (Color){232, 50, 32, opacity} )) remove = true; + //if(ViButton(mouse_pos, config_handle.x, config_handle.y, 8.0f, (Color){158, 134, 200, opacity})) config = !config; //if(ViButton(mouse_pos, config_handle.x + flip_handle.x, config_handle.y + flip_handle.y, 8.0f, (Color){158, 0, 200, opacity})) flip(); } }; diff --git a/src/datatypes.h b/src/datatypes.h index b9bd5de..2e0c9e4 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -25,7 +25,7 @@ class Vec2D{ T length() { return (T)sqrt(pow(x, 2) + pow(y, 2));}; - T angle(Vec2D &v){ + T angle(Vec2D v){ T angle = atan2(v.y, v.x) - atan2(y,x); if(angle > M_PI) angle -= M_PI_2; if(angle < -M_PI) angle += M_PI_2; @@ -33,11 +33,10 @@ class Vec2D{ //return acos(dot(v)); }; - T angleDeg(Vec2D &v){ - + T angleDeg(Vec2D v){ return angle(v) * 180 / M_PI; }; - + T dot(Vec2D &v){ return (x*v.x) + (y*v.y); }; diff --git a/src/main.cpp b/src/main.cpp index 8ed8594..37e18ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,6 +19,7 @@ #include "datatypes.h" #include "signal-manager.h" #include +#include #include #include #include @@ -70,12 +71,16 @@ int main(){ SetWindowMonitor(monitor); - app.fps = 60.0f; + 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.speed = (app.grid * 4.0f) / (app.fps / (app.bpm / 60.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); // 120 fps // 120bpm = 120 1/4 Noten pro Minute = 2 1/4 Noten / s @@ -93,14 +98,14 @@ int main(){ app.width = GetMonitorWidth(monitor); app.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.rotation = 0.0f; camera.zoom = 1.0f; - - SetTargetFPS(app.fps); nodes.reserve(1000); @@ -126,7 +131,8 @@ int main(){ double drag_timer = GetTime(); - enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE}; + enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE, DRAW_SELECTION}; + InteractionState istate = HOVER_CANVAS; Vec2D select_start; Rectangle selection; @@ -135,8 +141,12 @@ int main(){ while(!WindowShouldClose()){ + 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 * 4.0f) / (app.fps / (app.bpm / 60.0f)); + app.speed = (app.grid * 8.0f) * (120.0f / app.bpm); Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera); Vec2D mouse_pos = {(float)mp.x, (float)mp.y}; @@ -150,25 +160,114 @@ int main(){ bool mouse_right_clicked = IsMouseButtonPressed(MOUSE_BUTTON_RIGHT); bool mouse_right_released = IsMouseButtonReleased(MOUSE_BUTTON_RIGHT); - for(auto &n : nodes){ - n.hovering = false; - n.hover_connect = false; - } + 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);}); + 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; + if(!hovering_nodes.empty() && !mouse_down && !mouse_right_down) istate = InteractionState::HOVER_NODE; + if(!dragging_nodes.empty()) istate = InteractionState::DRAG_NODE; + if(!hovering_connectors.empty() && istate == InteractionState::HOVER_CANVAS) istate = InteractionState::HOVER_CONNECTOR; + if(!connecting_nodes.empty()) istate = InteractionState::CONNECT_NODE; + switch(istate){ + case InteractionState::HOVER_CANVAS: + if(mouse_right_down) { + camera.target = Vector2Add(camera.target, delta); + } + break; + case InteractionState::DRAW_SELECTION: + if(mouse_clicked){ + select_start = mouse_pos; + } + if(mouse_down){ + Vec2D select1 = {mouse_pos.x > select_start.x ? select_start.x : mouse_pos.x, mouse_pos.y > select_start.y ? select_start.y : mouse_pos.y}; + Vec2D select2 = {mouse_pos.x > select_start.x ? mouse_pos.x - select_start.x : select_start.x - mouse_pos.x, mouse_pos.y > select_start.y ? mouse_pos.y - select_start.y : select_start.y - mouse_pos.y}; + 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){ + if(CheckCollisionPointRec(n.position, selection)) { + n.in_rect = true; + } else { + n.in_rect = false; + } + } + } - if(hovering_nodes.empty() && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)){ - Vector2 delta = GetMouseDelta(); - delta = Vector2Scale(delta, -1.0f/camera.zoom); - camera.target = Vector2Add(camera.target, delta); - } + if(mouse_released){ + for(auto& n : nodes){ + if(CheckCollisionPointRec(n.position, selection)) { + n.selected = true; + n.in_rect = false; + } else { + if(!IsKeyDown(KEY_LEFT_SHIFT)) n.selected = false; + } + } + selection = (Rectangle){0,0,0,0}; + } + break; + case InteractionState::HOVER_NODE: + if(mouse_clicked) { + if(IsKeyDown(KEY_LEFT_SHIFT)) { + hovering_nodes.front().selected = true; + } else { + hovering_nodes.front().dragging = true; + hovering_nodes.front().drag_offset = hovering_nodes.front().position - mouse_pos; + } + } + if(mouse_right_clicked) hovering_nodes.front().config = true; + 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}; + } else { + dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; + } + + for(auto &n : selected_nodes){ + if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position; + n.picked_up = true; + n.position = dragging_nodes.front().position + n.drag_offset; + } + + if(mouse_released) { + dragging_nodes.front().dragging = false; + for(auto &n : selected_nodes){ + n.picked_up = false; + } + } + break; + case InteractionState::HOVER_CONNECTOR: + if(mouse_clicked) hovering_connectors.front().connecting = true; + break; + case InteractionState::CONNECT_NODE: + Vec2D connector_position = connecting_nodes.front().position - ((connecting_nodes.front().position - mouse_pos)).norm() * (connecting_nodes.front().radius + 14.0f); + + if(connecting_nodes.front().connecting && mouse_down){ + DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255}); + } + + 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()); + } + connecting_nodes.front().connecting = false; + } + break; + }; float wheel = GetMouseWheelMove(); if(wheel != 0){ @@ -186,269 +285,154 @@ int main(){ } } - if(mouse_clicked){ - drag_start = mouse_pos; + 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){ + DrawPixel(x, y, (Color){130,130,130,200}); + } } - BeginDrawing(); - DrawFPS(20, app.height - 100); - ClearBackground((Color){46,46,46}); + for(auto& n : nodes){ + n.draw(&mouse_pos, &camera); + } - BeginMode2D(camera); - bool connecting = false; + cm.drawConnections(&mouse_pos, &camera); + sm.drawSignals(&mouse_pos, &camera); - 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){ - DrawPixel(x, y, (Color){130,130,130,200}); + midi.update(); + + 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; + } } } + } - for(auto &h : hovering_connectors){ - h.hover_connect = 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(dragging_nodes.empty() && connecting_nodes.empty()) { + 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"); - if(!hovering_connectors.empty()){ - if(mouse_clicked){ - hovering_connectors.front().connecting = true; - } - } - - if(hovering_nodes.empty() || selection.width > 0){ - if(mouse_clicked){ - select_start = mouse_pos; - for(auto &n : nodes){ - n.selected = false; - } - } - - if(mouse_down){ - Vec2D select1 = {mouse_pos.x > select_start.x ? select_start.x : mouse_pos.x, mouse_pos.y > select_start.y ? select_start.y : mouse_pos.y}; - Vec2D select2 = {mouse_pos.x > select_start.x ? mouse_pos.x - select_start.x : select_start.x - mouse_pos.x, mouse_pos.y > select_start.y ? mouse_pos.y - select_start.y : select_start.y - mouse_pos.y}; - 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){ - if(CheckCollisionPointRec(n.position, selection)) { - n.in_rect = true; - } else { - n.in_rect = false; - } - } - } - - if(mouse_released){ - for(auto& n : nodes){ - if(CheckCollisionPointRec(n.position, selection)) { - n.selected = true; - } - } - selection = (Rectangle){0,0,0,0}; - } - } - - if(!hovering_nodes.empty()){ - hovering_nodes.front().hovering = true; - if(selection.width == 0.0f){ - if(mouse_clicked){ - drag_timer = GetTime(); - } - if(mouse_right_down && GetTime() - drag_timer > 0.1){ - hovering_nodes.front().drag_offset = hovering_nodes.front().position - mouse_pos; - hovering_nodes.front().dragging = true; - } - if(mouse_right_released && GetTime() - drag_timer <= 0.1){ - hovering_nodes.front().config = true; - } - } - } - - } else if(!dragging_nodes.empty()){ - - if(mouse_down){ - 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}; - } else { - dragging_nodes.front().position = mouse_pos + dragging_nodes.front().drag_offset; - } - } - if(mouse_released) { - dragging_nodes.front().dragging = false; - } - - auto selected_nodes = nodes | std::ranges::views::filter([](Node n) { return n.selected && !n.dragging;}); - if(!selected_nodes.empty() && mouse_down){ - for(auto &n : selected_nodes){ - if(!n.picked_up) n.drag_offset = n.position - dragging_nodes.front().position; - n.picked_up = true; - n.position = dragging_nodes.front().position + n.drag_offset; - } - } - if(mouse_released) { - dragging_nodes.front().dragging = false; - for(auto &n : selected_nodes){ - n.picked_up = false; - } - } - - } else if(!connecting_nodes.empty() || connecting){ - - Vec2D connector_position = connecting_nodes.front().position - ((connecting_nodes.front().position - mouse_pos)).norm() * (connecting_nodes.front().radius + 14.0f); - - if(connecting_nodes.front().connecting && mouse_down){ - DrawLine(connector_position.x, connector_position.y, mouse_pos.x, mouse_pos.y, (Color){229, 181, 103, 255}); - } - - 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()); - } - connecting_nodes.front().connecting = false; - } + 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); } - for(auto& n : nodes){ - n.draw(&mouse_pos, &camera); - } + int y_midi = 120; + GuiLabel((Rectangle){np.x + 20, np.y + y_midi, 220, 20}, "MIDI OUT"); - cm.drawConnections(&mouse_pos, &camera); - sm.drawSignals(&mouse_pos, &camera); - - midi.update(); - EndMode2D(); - - // Main Config Window - GuiWindowBox((Rectangle){0,0,280,(float)app.height}, "config"); - { - const int s_bpm = 55; - 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 = 100; - 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); + 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++; } - - 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_speed = round(s.speed*10.0f)/10.0f; - float signal_bpm = (app.bpm / app.speed) * s_speed; - GuiSlider((Rectangle){70, (float)s_signal+70 + signal_configs*20, 160, 20}, "", std::format("{:.1f}",signal_bpm).c_str(), &s_speed, 0.1, 24); - s.speed = s_speed; - signal_configs++; - } + 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); - 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); - } - + 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); } - - // 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); - - } - } + } EndDrawing(); } diff --git a/src/moveable.h b/src/moveable.h index 38744d4..de03511 100644 --- a/src/moveable.h +++ b/src/moveable.h @@ -18,6 +18,7 @@ class Movable{ Vec2D direction; virtual void update(int fps) {}; + virtual void update() {}; virtual void draw(Vec2D *mouse, Camera2D *cam) {}; }; diff --git a/src/signal-manager.h b/src/signal-manager.h index a17b520..f0dea2b 100644 --- a/src/signal-manager.h +++ b/src/signal-manager.h @@ -21,6 +21,29 @@ class SignalManager{ std::vector signals; bool loop = true; + bool play = false; + + void pause(){ + play = false; + } + + void stop(){ + play = false; + reset(); + } + + void start(){ + play = true; + } + + void reset(){ + for(auto &s : signals){ + s.position = s.start->position + Vec2D{0.1, 0.1}; + s.next = s.start; + s.last = s.start; + } + cm->reset(); + } void startSignalThread(){ std::thread t1(&SignalManager::updateSignals, this); @@ -36,6 +59,7 @@ class SignalManager{ 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.back().next = node; signals.back().last = node; + signals.back().start = node; } void removeSignal(int n){ @@ -45,19 +69,22 @@ class SignalManager{ void updateSignals(){ auto last = std::chrono::steady_clock::now(); while(loop){ - std::chrono::duration diff = std::chrono::steady_clock::now() - last; - if(diff.count() > 10){ - for(auto& s : signals){ - s.direction = (s.next->position - s.position).norm(); - if(s.position.dist(&s.next->position) < s.speed * 1.5) { - Node* n = cm->next(s.next, s.last); - - 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); + std::chrono::duration diff = std::chrono::steady_clock::now() - last; + if(diff.count() > 500){ + if(play){ + for(auto& s : signals){ + s.direction = (s.next->position - s.position).norm(); + s.update(diff.count()); + if(s.position.dist(&s.next->position) < 1.0f) { + Node* n = cm->next(s.next, s.last); + + 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); + } + s.last = s.next; + s.next = n; } - s.last = s.next; - s.next = n; } } } diff --git a/src/signal.h b/src/signal.h index 0f61db5..a32232c 100644 --- a/src/signal.h +++ b/src/signal.h @@ -10,7 +10,7 @@ class Signal : public Movable{ public: Signal(); - Signal(float x, float y, float r, float s, Color color) : Movable(Vec2D(x, y), Vec2D(0,0), s), radius(r), color(color) {} + Signal(float x, float y, float r, float _speed, Color color) : Movable(Vec2D(x, y), Vec2D(0,0), _speed), radius(r), color(color) {} int id = 0; @@ -25,17 +25,18 @@ class Signal : public Movable{ float radius; + float multiplier = 1.0f; + Color color; - void update(int fps){ - + void update(int t_delta){ + // 120px / 16th = 1920px / whole note + // whole note = 2s at 120bpm + // 960px / s + position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f; } void draw(Vec2D *mouse, Camera2D *cam) { - - position += direction.norm() * speed; - DrawCircle(position.x, position.y, radius, color); - } };