diff --git a/Makefile b/Makefile index 13c4406..343962e 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ CXX = g++ -CXXFLAGS = -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -CXXINC = -Iinclude +CXXFLAGS = -std=c++23 -Wall -g -fno-omit-frame-pointer -gdwarf-4 -Og -Wnarrowing -DLIBREMIDI_ALSA=1 -DLIBREMIDI_HEADER_ONLY=1 +CXXINC = -isysteminclude -LDLIBS = -lraylib -lasound -pthread +LDLIBS = -lraylib -lasound -llibremidi -pthread LDFLAGS = -Llib -L/usr/lib -L/usr/local/lib hdr_files = ${wildcard src/*.h} src_files = ${wildcard src/*.cpp} -src_files += ${wildcard include/libremidi/*.cpp} +#src_files += ${wildcard include/libremidi/*.cpp} all: viseq @@ -20,13 +20,13 @@ $(src_files): %.cpp : %_src $(CXX) -c $@ -o build/$(*F).o $(CXXFLAGS) $(CXXINC) $(hdr_files): %.h : %_hdr - @echo "G++ >---------- $(*F).h -----------<" + $(NOECHO) $(NOOP) %_src: - @echo "G++ >---------- $(*F).cpp -----------<" + $(NOECHO) $(NOOP) %_hdr: - @echo "G++ >---------- $(*F).h -----------<" + $(NOECHO) $(NOOP) clean: rm -f build/* diff --git a/lib/liblibremidi.a b/lib/liblibremidi.a new file mode 100644 index 0000000..f9894dd Binary files /dev/null and b/lib/liblibremidi.a differ diff --git a/src/state-manager.h b/src/app-manager.h similarity index 92% rename from src/state-manager.h rename to src/app-manager.h index e83e203..68dc4d8 100644 --- a/src/state-manager.h +++ b/src/app-manager.h @@ -14,16 +14,10 @@ namespace VISEQ::APP { - class AppState{ + class AppManager{ public: - AppState() : sm(&gm, &mutex), ui(&settings, &sm, &gm) {} - - enum States {IDLE, CONFIG}; - - AppSettings settings; - - States state; + AppManager() : sm(&gm, &mutex), ui(&sm, &gm) {} std::mutex mutex; @@ -144,11 +138,11 @@ namespace VISEQ::APP { } }; - inline void to_json(json& j, const AppState& A){ - j = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}, {"settings",A.settings}}; + inline void to_json(json& j, const AppManager& A){ + j = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}}; } - inline void from_json(const json& j, AppState& A){ + inline void from_json(const json& j, AppManager& A){ j.at("graphmanager").get_to(A.gm); // = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}}; } } diff --git a/src/button.h b/src/button.h index bc49f2f..5967067 100644 --- a/src/button.h +++ b/src/button.h @@ -1,6 +1,6 @@ #pragma once -#include "datatypes.h" +#include "vmath.h" #include "raylib.h" #include "raymath.h" diff --git a/src/connectable.h b/src/connectable.h index 1a885cf..e6c3a2b 100644 --- a/src/connectable.h +++ b/src/connectable.h @@ -25,7 +25,7 @@ namespace VISEQ::BASE { virtual void drag() override { Vec2Df mp = {GetMousePosition().x, GetMousePosition().y}; - DrawLineV(position, mp, (Color){229, 181, 103, 255}); + DrawLineV(position, state->mod_mouse, (Color){229, 181, 103, 255}); } void update() override{ diff --git a/src/datatypes.h b/src/datatypes.h index 71db971..a3fef9c 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -18,8 +18,6 @@ #define VS_COLOR_BROWN (Color){84, 75, 61, 255} #define VS_COLOR_SUN (Color){214, 170, 21, 255} -#include "vmath.h" - #include "nlohmann/json.hpp" using json = nlohmann::json; @@ -33,19 +31,6 @@ namespace VISEQ::GRAPH::NODES { enum Distribution {GEOMETRIC, ROUNDROBIN, RANDOM}; } -namespace VISEQ::APP{ - struct AppSettings{ - int mode = 0; - int width; - int height; - float grid; - float bpm; - float fps; - float speed = 16.0f; - int gui_selected_midi_out; - }; - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(AppSettings, mode, width, height, grid, bpm, fps, speed, gui_selected_midi_out) -} namespace VISEQ::GRAPH{ enum GraphItemType {NODE, EDGE}; diff --git a/src/draggable.h b/src/draggable.h index dd0dc28..21a0175 100644 --- a/src/draggable.h +++ b/src/draggable.h @@ -26,8 +26,7 @@ namespace VISEQ::BASE { } virtual void drag() { - Vec2Df mp = {GetMousePosition().x, GetMousePosition().y}; - position = mp + drag_offset; + position = state->mod_mouse + drag_offset; } virtual void putDown(){ diff --git a/src/graph-item.h b/src/graph-item.h index e0c208e..033de38 100644 --- a/src/graph-item.h +++ b/src/graph-item.h @@ -1,7 +1,9 @@ #pragma once +#include "globals.h" #include "raylib.h" #include "datatypes.h" +#include namespace VISEQ::GRAPH { class GraphItem{ @@ -13,7 +15,9 @@ namespace VISEQ::GRAPH { graphItemType = G.graphItemType; } GraphItemType graphItemType; - + + std::shared_ptr state = APP::State::get(); + int id = 0; void newId(int start){ diff --git a/src/graph-manager.h b/src/graph-manager.h index a7dd299..d2ecd24 100644 --- a/src/graph-manager.h +++ b/src/graph-manager.h @@ -2,6 +2,7 @@ #include "datatypes.h" +#include "vmath.h" #include "raylib.h" #include "raymath.h" @@ -264,42 +265,44 @@ namespace VISEQ::GRAPH { } } - void drawNodes(Vec2Df mouse_pos, Camera2D camera){ + void drawNeighborLines(SPTR_Node node, Vec2Df mouse_pos, Camera2D camera){ float min_d = 480; + auto neighbors_local = nodes | RANGE_FILTER([mouse_pos](SPTR_Node n){ return !n->picked_up && !n->selected && n->position.dist(mouse_pos) < 960;}) | std::ranges::to(); + std::sort(neighbors_local.begin(), neighbors_local.end(), [node, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);}); + if(!neighbors_local.empty()){ + min_d = neighbors_local.front()->position.dist(mouse_pos); + DrawCircleLinesV(neighbors_local.front()->position, 22.0f, VS_COLOR_PINK); + node->neighbors = neighbors_local | std::ranges::views::filter([mouse_pos, min_d](SPTR_Node n){return abs(n->position.dist(mouse_pos) - min_d) < 5;}) | std::ranges::to(); + for(auto nb : node->neighbors){ + DrawCircleLinesV(nb->position, 20.0f, VS_COLOR_RED); + float d = nb->position.dist(mouse_pos); + float dn = nb->position.dist(node->position); + if(d > 0 && d < min_d*1.5 && d < 960) { + std::string frac = MIDI::Fractions.at(std::clamp(round(dn/60) - 1, 0, 31)); + Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",dn).c_str(), 10.0f, 2.0f); + Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f); + + Vec2Df middle_handle = nb->position + (node->position - nb->position) * ((dn - text_length.x)*0.5f)/dn; + Vec2Df middle_handle_frac = nb->position + (node->position - nb->position) * ((dn - text_length_frac.x)*0.5f)/dn; + Vec2Df text_position = middle_handle + (nb->position - node->position).norm().orth() * 12.0f; + Vec2Df text_position_frac = middle_handle_frac - (nb->position - node->position).norm().orth() * 4.0f; + + float rotation = Vector2Angle((Vector2){1,0}, (node->position - nb->position)) * RAD2DEG; + DrawTextPro(GetFontDefault(), std::format("{:.1f}",dn).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + if(IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); + Vec2Df n_position = node->position - ((node->position - nb->position)).norm() * 20.0f; + Vec2Df nb_position = nb->position - ((nb->position - node->position)).norm() * 20.0f; + DrawLineV(n_position, nb_position, VS_COLOR_PINK); + } + + } + } + } + + void drawNodes(Vec2Df mouse_pos, Camera2D camera){ for(auto n : nodes){ n->update(); - if(n->picked_up){ - auto neighbors_local = nodes | RANGE_FILTER([mouse_pos](SPTR_Node n){ return !n->picked_up && !n->selected && n->position.dist(mouse_pos) < 960;}) | std::ranges::to(); - std::sort(neighbors_local.begin(), neighbors_local.end(), [n, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);}); - if(!neighbors_local.empty()){ - min_d = neighbors_local.front()->position.dist(mouse_pos); - DrawCircleLinesV(neighbors_local.front()->position, 22.0f, VS_COLOR_PINK); - n->neighbors = neighbors_local | std::ranges::views::filter([mouse_pos, min_d](SPTR_Node n){return abs(n->position.dist(mouse_pos) - min_d) < 5;}) | std::ranges::to(); - for(auto nb : n->neighbors){ - DrawCircleLinesV(nb->position, 20.0f, VS_COLOR_RED); - float d = nb->position.dist(mouse_pos); - float dn = nb->position.dist(n->position); - if(d > 0 && d < min_d*1.5 && d < 960) { - std::string frac = MIDI::Fractions.at(std::clamp(round(dn/60) - 1, 0, 31)); - Vector2 text_length = MeasureTextEx(GetFontDefault(), std::format("{:.1f}",dn).c_str(), 10.0f, 2.0f); - Vector2 text_length_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f); - - Vec2Df middle_handle = nb->position + (n->position - nb->position) * ((dn - text_length.x)*0.5f)/dn; - Vec2Df middle_handle_frac = nb->position + (n->position - nb->position) * ((dn - text_length_frac.x)*0.5f)/dn; - Vec2Df text_position = middle_handle + (nb->position - n->position).norm().orth() * 12.0f; - Vec2Df text_position_frac = middle_handle_frac - (nb->position - n->position).norm().orth() * 4.0f; - - float rotation = Vector2Angle((Vector2){1,0}, (n->position - nb->position)) * RAD2DEG; - DrawTextPro(GetFontDefault(), std::format("{:.1f}",dn).c_str(), text_position, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - if(IsKeyDown(KEY_LEFT_SHIFT)) DrawTextPro(GetFontDefault(), frac.c_str(), text_position_frac, (Vector2){0,0}, rotation, 10.0f, 2.0f, VS_COLOR_PINK); - Vec2Df n_position = n->position - ((n->position - nb->position)).norm() * 20.0f; - Vec2Df nb_position = nb->position - ((nb->position - n->position)).norm() * 20.0f; - DrawLineV(n_position, nb_position, VS_COLOR_PINK); - } - - } - } - } + if(n->picked_up) drawNeighborLines(n, mouse_pos, camera); n->draw(mouse_pos, camera); } } diff --git a/src/main.cpp b/src/main.cpp index 4bce921..f107f3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,7 +5,8 @@ * 2024 Sebastian Kujas * */ - +#include "globals.h" +#include "datatypes.h" #include "graph-manager.h" #include "node.h" #include "vmath.h" @@ -15,7 +16,7 @@ #include #include -#include "state-manager.h" +#include "app-manager.h" #include "datatypes.h" #include "midi-node.h" @@ -29,10 +30,13 @@ using json = nlohmann::json; -VISEQ::APP::AppState app; +VISEQ::APP::AppManager app; + +std::shared_ptr state = VISEQ::APP::State::get(); Vec2Df mouse_pos; Vec2Df drag_start; +//Vec2Df global_mouse; Camera2D camera = { {0} }; @@ -59,19 +63,19 @@ int main(){ app.ui.init(); - app.settings.fps = 60.0f; - app.settings.bpm = 120.0f; - app.settings.grid = 60.0f; // 120px for a 16th note + state->settings.fps = 60.0f; + state->settings.bpm = 120.0f; + state->settings.grid = 60.0f; // 120px for a 16th note - app.settings.speed = (app.settings.grid * 16.0f) * (app.settings.bpm / 120.f); + state->settings.speed = (state->settings.grid * 16.0f) * (state->settings.bpm / 120.f); - app.settings.width = monitor_width - 300; - app.settings.height = monitor_height - 300; + state->settings.width = monitor_width - 300; + state->settings.height = monitor_height - 300; - SetTargetFPS(app.settings.fps); + SetTargetFPS(state->settings.fps); - 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.target = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f }; + camera.offset = (Vector2){ state->settings.width/2.0f, state->settings.height/2.0f }; camera.rotation = 0.0f; camera.zoom = 1.0f; @@ -79,7 +83,7 @@ int main(){ std::ifstream f("state.json"); json graph = json::parse(f); - app.settings = graph.at("settings"); + state->settings = graph.at("settings"); int highest_id = 0; std::shared_ptr hid; @@ -139,10 +143,10 @@ int main(){ } else { for(int i = 0; i < 4; ++i){ - app.gm.addMidiNode(Vec2Df{(float)i * app.settings.grid*4 + 7 * app.settings.grid, 2 * app.settings.grid*4}); + app.gm.addMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 2 * state->settings.grid*4}); } for(int i = 3; i >= 0; --i){ - app.gm.addMidiNode(Vec2Df{(float)i * app.settings.grid*4 + 7 * app.settings.grid, 3 * app.settings.grid*4}); + app.gm.addMidiNode(Vec2Df{(float)i * state->settings.grid*4 + 7 * state->settings.grid, 3 * state->settings.grid*4}); } for(int i = 0; i < 8; ++i){ @@ -176,7 +180,7 @@ int main(){ ClearBackground((Color){46,46,46}); BeginMode2D(camera); - app.settings.speed = (app.settings.grid * 16.0f) * (app.settings.bpm / 120.f); + state->settings.speed = (state->settings.grid * 16.0f) * (state->settings.bpm / 120.f); Vector2 mp = GetScreenToWorld2D(GetMousePosition(), camera); Vec2Df mouse_pos = {(float)mp.x, (float)mp.y}; @@ -193,34 +197,38 @@ int main(){ Vec2Df mouse_delta = {GetMouseDelta().x, GetMouseDelta().y}; Vec2Df delta = mouse_delta * (-1.0f/camera.zoom); + state->raw_mouse = mouse_pos; + state->mod_mouse = mouse_pos; + if(IsKeyDown(KEY_LEFT_CONTROL)) { - modified_mouse.x = round(mouse_pos.x / app.settings.grid) * app.settings.grid; - modified_mouse.y = round(mouse_pos.y / app.settings.grid) * app.settings.grid; - } else if(IsKeyDown(KEY_LEFT_SHIFT)){ - float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / app.settings.grid) * app.settings.grid; - if(dragNode->neighbors.size() > 1){ - Vec2Df dn1 = dragNode->neighbors[0]->position; - Vec2Df dn2 = dragNode->neighbors[1]->position; - Vec2Df v1_orth = dn1.center(dn2); - - if(dn1.x < dn2.x) std::swap(dn1,dn2); - - float dist = dn1.dist(dn2); - float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2)); - - if(mouse_pos.y < v1_orth.y) dist_orth *= -1; - - Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth; - DrawLineV(dn1, dn2, VS_COLOR_RED); - DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED); - DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED); - dragNode->position = v1_orth_t; - } else if(!dragNode->neighbors.empty()){ - float pol_r = steps; - float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbors.front()->position)); - Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta); - dragNode->position = dragNode->neighbors.front()->position + polar; - } + state->mod_mouse.x = round(mouse_pos.x / state->settings.grid) * state->settings.grid; + state->mod_mouse.y = round(mouse_pos.y / state->settings.grid) * state->settings.grid; + } + /*else if(IsKeyDown(KEY_LEFT_SHIFT)){*/ + /* float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/ + /* if(dragNode->neighbors.size() > 1){*/ + /* Vec2Df dn1 = dragNode->neighbors[0]->position;*/ + /* Vec2Df dn2 = dragNode->neighbors[1]->position;*/ + /* Vec2Df v1_orth = dn1.center(dn2);*/ + /**/ + /* if(dn1.x < dn2.x) std::swap(dn1,dn2);*/ + /**/ + /* float dist = dn1.dist(dn2);*/ + /* float dist_orth = std::sqrt(std::pow(steps,2) - std::pow(dist/2,2));*/ + /**/ + /* if(mouse_pos.y < v1_orth.y) dist_orth *= -1;*/ + /**/ + /* Vec2Df v1_orth_t = v1_orth + (dn1-dn2).norm().orth() * dist_orth;*/ + /* DrawLineV(dn1, dn2, VS_COLOR_RED);*/ + /* DrawLineV(v1_orth, v1_orth_t, VS_COLOR_RED);*/ + /* DrawCircleV(v1_orth, 3.0f, VS_COLOR_RED);*/ + /* dragNode->position = v1_orth_t;*/ + /* } else if(!dragNode->neighbors.empty()){*/ + /* float pol_r = steps;*/ + /* float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbors.front()->position));*/ + /* Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta); */ + /* dragNode->position = dragNode->neighbors.front()->position + polar;*/ + /* }*/ // auto dragging_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->dragging;}); /*auto connecting_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->connecting;});*/ /*auto selected_nodes = app.gm.nodes | RANGE_FILTER([](SPTR_Node n) { return n->selected && !n->picked_up;}) | std::ranges::to();*/ @@ -245,8 +253,8 @@ int main(){ /*if(!copied_nodes.empty() && IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_V)) istate = InteractionState::PASTE_SELECTED_NODES;*/ /**/ /*if(IsKeyPressed(KEY_TAB)) {*/ - /* app.settings.mode = (app.settings.mode + 1) % 3;*/ - /* app.gm.setNodeMode(app.settings.mode);*/ + /* state->settings.mode = (state->settings.mode + 1) % 3;*/ + /* app.gm.setNodeMode(state->settings.mode);*/ /*}*/ /*if(!selected_nodes.empty()){*/ @@ -381,10 +389,10 @@ int main(){ /* }*/ /**/ /* if(IsKeyDown(KEY_LEFT_CONTROL)) {*/ - /* dragNode->position.x = round((mouse_pos.x + dragNode->drag_offset.x) / app.settings.grid) * app.settings.grid;*/ - /* dragNode->position.y = round((mouse_pos.y + dragNode->drag_offset.y) / app.settings.grid) * app.settings.grid;*/ + /* dragNode->position.x = round((mouse_pos.x + dragNode->drag_offset.x) / state->settings.grid) * state->settings.grid;*/ + /* dragNode->position.y = round((mouse_pos.y + dragNode->drag_offset.y) / state->settings.grid) * state->settings.grid;*/ /* } else if(IsKeyDown(KEY_LEFT_SHIFT)){*/ - /* float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / app.settings.grid) * app.settings.grid;*/ + /* float steps = round((dragNode->neighbors.front()->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/ /* if(dragNode->neighbors.size() > 1){*/ /* Vec2Df dn1 = dragNode->neighbors[0]->position;*/ /* Vec2Df dn2 = dragNode->neighbors[1]->position;*/ @@ -443,11 +451,11 @@ int main(){ /* Vec2Df target_position = mouse_pos;*/ /**/ /* if(IsKeyDown(KEY_LEFT_CONTROL)) {*/ - /* target_position.x = round((mouse_pos.x) / app.settings.grid) * app.settings.grid;*/ - /* target_position.y = round((mouse_pos.y) / app.settings.grid) * app.settings.grid;*/ + /* target_position.x = round((mouse_pos.x) / state->settings.grid) * state->settings.grid;*/ + /* target_position.y = round((mouse_pos.y) / state->settings.grid) * state->settings.grid;*/ /* } else if(IsKeyDown(KEY_LEFT_SHIFT)){*/ /* std::shared_ptr closest = app.gm.getClosestNode(mouse_pos);*/ - /* float steps = round((closest->position.dist(mouse_pos)) / app.settings.grid) * app.settings.grid;*/ + /* float steps = round((closest->position.dist(mouse_pos)) / state->settings.grid) * state->settings.grid;*/ /* float pol_r = steps;*/ /* float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - closest->position));*/ /**/ @@ -506,74 +514,29 @@ int main(){ /* }*/ /*};*/ - /*float wheel = GetMouseWheelMove();*/ - /*if(wheel != 0){*/ - /* if(!hovering_nodes.empty() && hovering_nodes.front()->getNodeType() == NodeType::MIDINODE) {*/ - /* VEC_SPTR_Node modify_nodes;*/ - /* std::copy_if(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(modify_nodes), [](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE;});*/ - /* if(selected_nodes.empty()) std::copy_if(hovering_nodes.begin(), hovering_nodes.end(), std::back_inserter(modify_nodes), [modify_nodes](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE && !std::ranges::contains(modify_nodes, n);});*/ - /**/ - /* for(auto n : modify_nodes){*/ - /* std::shared_ptr midiNode = std::dynamic_pointer_cast(n);*/ - /* if(IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){*/ - /* switch(app.settings.mode){*/ - /* case 0:{*/ - /* midiNode->midi_note += wheel;*/ - /* break;}*/ - /* case 1:{*/ - /* midiNode->midi_cc += wheel;*/ - /* break;}*/ - /* case 2:{*/ - /* midiNode->midi_pitch_bend += wheel;*/ - /* break;}*/ - /* };*/ - /* if(midiNode->midi_note < 0 ) midiNode->midi_note = VISEQ::MIDI::Notes.size()-1;*/ - /* if(midiNode->midi_note >= VISEQ::MIDI::Notes.size() ) midiNode->midi_note = 0;*/ - /**/ - /* if(midiNode->midi_cc < 0 ) midiNode->midi_cc = 127;*/ - /* if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0;*/ - /**/ - /* if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0;*/ - /* if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383;*/ - /**/ - /* } else if(IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) {*/ - /* midiNode->midi_velocity += wheel;*/ - /* if(midiNode->midi_velocity < 0 ) midiNode->midi_velocity = 127;*/ - /* if(midiNode->midi_velocity > 127 ) midiNode->midi_velocity = 0; */ - /* } else if(IsKeyDown(KEY_LEFT_ALT)) {*/ - /* switch(app.settings.mode){*/ - /* case 0:{*/ - /* float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f);*/ - /* midiNode->midi_gate += incr;*/ - /* if(midiNode->midi_gate < 0 ) midiNode->midi_gate = 0;*/ - /* break;}*/ - /* case 1:{*/ - /* midiNode->midi_cc_value += wheel;*/ - /* if(midiNode->midi_cc_value < 0 ) midiNode->midi_cc_value = 0;*/ - /* if(midiNode->midi_cc_value > 127 ) midiNode->midi_cc_value = 127;*/ - /* break;}*/ - /* case 2:{*/ - /* float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f);*/ - /* midiNode->midi_pitch_bend += incr;*/ - /* if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0;*/ - /* if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383;*/ - /* break;}*/ - /* };*/ - /* }*/ - /* }*/ - /* }*/ - /* if(!IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){*/ - /* Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera);*/ - /* camera.offset = GetMousePosition();*/ - /* camera.target = mouseWorldPos;*/ - /* float scaleFactor = 1.0f + (0.25f*fabsf(wheel));*/ - /* if (wheel < 0) scaleFactor = 1.0f/scaleFactor;*/ - /* camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f);*/ - /* }*/ - /*}*/ + float wheel = GetMouseWheelMove(); + if(wheel != 0){ + if(!hovering_nodes.empty() && hovering_nodes.front()->getNodeType() == NodeType::MIDINODE) { + VEC_SPTR_Node modify_nodes; + std::copy_if(selected_nodes.begin(), selected_nodes.end(), std::back_inserter(modify_nodes), [](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE;}); + if(selected_nodes.empty()) std::copy_if(hovering_nodes.begin(), hovering_nodes.end(), std::back_inserter(modify_nodes), [modify_nodes](SPTR_Node n){ return n->getNodeType() == NodeType::MIDINODE && !std::ranges::contains(modify_nodes, n);}); - 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){ + for(auto n : modify_nodes){ + std::shared_ptr midiNode = std::dynamic_pointer_cast(n); + } + } + if(!IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){ + Vector2 mouseWorldPos = GetScreenToWorld2D(GetMousePosition(), camera); + camera.offset = GetMousePosition(); + camera.target = mouseWorldPos; + float scaleFactor = 1.0f + (0.25f*fabsf(wheel)); + if (wheel < 0) scaleFactor = 1.0f/scaleFactor; + camera.zoom = Clamp(camera.zoom*scaleFactor, 0.125f, 64.0f); + } + } + + for(int x = -state->settings.grid*100; x < state->settings.grid*100; x+=state->settings.grid){ + for(int y = -state->settings.grid*100; y < state->settings.grid*100; y+=state->settings.grid){ DrawPixel(x, y, (Color){130,130,130,200}); } } diff --git a/src/midi-node.h b/src/midi-node.h index eb0bc70..0ceabba 100644 --- a/src/midi-node.h +++ b/src/midi-node.h @@ -82,6 +82,54 @@ namespace VISEQ::GRAPH::NODES { int Ggate(){ return midi_gate; } + void interact(){ + + if(IsKeyDown(KEY_LEFT_CONTROL) && !IsKeyDown(KEY_LEFT_ALT)){ + switch(state->settings.mode){ + case 0:{ + midiNode->midi_note += wheel; + break;} + case 1:{ + midiNode->midi_cc += wheel; + break;} + case 2:{ + midiNode->midi_pitch_bend += wheel; + break;} + }; + if(midiNode->midi_note < 0 ) midiNode->midi_note = VISEQ::MIDI::Notes.size()-1; + if(midiNode->midi_note >= VISEQ::MIDI::Notes.size() ) midiNode->midi_note = 0; + + if(midiNode->midi_cc < 0 ) midiNode->midi_cc = 127; + if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0; + + if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0; + if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383; + + } else if(IsKeyDown(KEY_LEFT_SHIFT) && !IsKeyDown(KEY_LEFT_ALT)) { + midiNode->midi_velocity += wheel; + if(midiNode->midi_velocity < 0 ) midiNode->midi_velocity = 127; + if(midiNode->midi_velocity > 127 ) midiNode->midi_velocity = 0; + } else if(IsKeyDown(KEY_LEFT_ALT)) { + switch(state->settings.mode){ + case 0:{ + float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); + midiNode->midi_gate += incr; + if(midiNode->midi_gate < 0 ) midiNode->midi_gate = 0; + break;} + case 1:{ + midiNode->midi_cc_value += wheel; + if(midiNode->midi_cc_value < 0 ) midiNode->midi_cc_value = 0; + if(midiNode->midi_cc_value > 127 ) midiNode->midi_cc_value = 127; + break;} + case 2:{ + float incr = (float)wheel * (IsKeyDown(KEY_LEFT_SHIFT) ? 100.0f : IsKeyDown(KEY_LEFT_CONTROL) ? 1000.0f : 10.0f); + midiNode->midi_pitch_bend += incr; + if(midiNode->midi_pitch_bend < 0 ) midiNode->midi_pitch_bend = 0; + if(midiNode->midi_pitch_bend > 16383 ) midiNode->midi_pitch_bend = 16383; + break;} + }; + } + } void draw(Vec2Df mouse, Camera2D cam) override { Node::draw(); diff --git a/src/moveable.h b/src/moveable.h index 3977481..db5818d 100644 --- a/src/moveable.h +++ b/src/moveable.h @@ -1,6 +1,8 @@ #pragma once +#include "globals.h" #include "vmath.h" +#include namespace VISEQ::BASE { class Movable{ @@ -12,6 +14,8 @@ namespace VISEQ::BASE { Movable(Vec2Df p, Vec2Df d, float s) : speed(s), position(p), direction(d) {} Movable(Vec2Df p, float s) : speed(s), position(p) {} + std::shared_ptr state = APP::State::get(); + float speed = 960.0f; Vec2Df position; diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..a50afb1 --- /dev/null +++ b/src/state.h @@ -0,0 +1,38 @@ +#pragma once + +#include "vmath.h" +#include + +namespace VISEQ::APP{ + + struct AppSettings{ + int mode = 0; + int width; + int height; + float grid; + float bpm; + float fps; + float speed = 16.0f; + int gui_selected_midi_out; + }; + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(AppSettings, mode, width, height, grid, bpm, fps, speed, gui_selected_midi_out) + + class State{ + public: + State(){} + State(State &state) = delete; + void operator=(const State &) = delete; + + AppSettings settings; + Vec2Df raw_mouse; + Vec2Df mod_mouse; + + inline static std::shared_ptr instance; + static std::shared_ptr get() { + if(!instance){ + instance.reset(new State()); + } + return instance; + } + }; +} diff --git a/src/ui.h b/src/ui.h index 52071a5..fe19f77 100644 --- a/src/ui.h +++ b/src/ui.h @@ -1,5 +1,6 @@ #pragma once +#include "globals.h" #include "midi-node.h" #include "raylib.h" #include "raygui/raygui.h" @@ -11,15 +12,15 @@ #include #include -#include namespace VISEQ::APP::UI{ class AppUI{ public: AppUI() {} - AppUI(AppSettings *settings, SIGNAL::SignalManager *sm, GraphManager *gm) : settings(settings), gm(gm), sm(sm) {} + AppUI(SIGNAL::SignalManager *sm, GraphManager *gm) : gm(gm), sm(sm) {} + + std::shared_ptr state = State::get(); - AppSettings *settings; GraphManager *gm; SIGNAL::SignalManager *sm; @@ -31,7 +32,7 @@ namespace VISEQ::APP::UI{ void drawSideBar(){ - GuiPanel((Rectangle){-1,-30,280,(float)settings->height + 31}, ""); + GuiPanel((Rectangle){-1,-30,280,(float)state->settings.height + 31}, ""); const int s_controls = 25; //GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF); if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start(); @@ -40,11 +41,11 @@ namespace VISEQ::APP::UI{ const int s_bpm = s_controls + 45; GuiLabel((Rectangle){20, (float)s_bpm, 120, 20}, "TIMING"); - GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",settings->bpm).c_str()); - int _bpm = settings->bpm; + GuiLabel((Rectangle){200, (float)s_bpm, 40, 20}, std::format("{}",state->settings.bpm).c_str()); + int _bpm = state->settings.bpm; // GuiSlider((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", "", &_bpm, 1, 300); if(GuiSpinner((Rectangle){20, (float)s_bpm + 25, 240, 20}, "", &_bpm, 1, 300, bpm_spinner_edit_mode)) bpm_spinner_edit_mode = !bpm_spinner_edit_mode; - settings->bpm = _bpm; + state->settings.bpm = _bpm; int s_midi = s_bpm + 75; GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT"); @@ -79,11 +80,11 @@ namespace VISEQ::APP::UI{ DrawCircle(55, s_signal+80 + signal_configs*20, 6.0F, s->color); int s_speed = round(s->speed); - s->multiplier = settings->bpm / 120.0f; - float signal_bpm = round(settings->bpm * s->multiplier * (s->speed / settings->speed)); - if(GuiSpinner((Rectangle){70, (float)s_signal+70 + signal_configs*20, 100, 20}, "", &s_speed, 1, 16 * settings->grid * 4, s->ui_edit_speed)) s->ui_edit_speed = !s->ui_edit_speed; - if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20}, "1/2")) s_speed = settings->speed / 2; - //if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20},"1/4")) s_speed = settings->speed / 4; + s->multiplier = state->settings.bpm / 120.0f; + float signal_bpm = round(state->settings.bpm * s->multiplier * (s->speed / state->settings.speed)); + if(GuiSpinner((Rectangle){70, (float)s_signal+70 + signal_configs*20, 100, 20}, "", &s_speed, 1, 16 * state->settings.grid * 4, s->ui_edit_speed)) s->ui_edit_speed = !s->ui_edit_speed; + if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20}, "1/2")) s_speed = state->settings.speed / 2; + //if(GuiButton((Rectangle){200,(float)s_signal+70 + signal_configs*20, 30, 20},"1/4")) s_speed = state->settings.speed / 4; GuiLabel((Rectangle){195, (float)s_signal+70 + signal_configs*20, 30, 20}, std::format("{}",signal_bpm).c_str()); GuiToggle((Rectangle){240, (float)s_signal+70 + signal_configs*20, 20, 20},"P", &s->run); s->speed = s_speed; @@ -95,8 +96,8 @@ namespace VISEQ::APP::UI{ if(GuiButton((Rectangle){20, (float)s_nodes + 60, 240, 20}, "ADD RANDOM NODE")){ gm->addMidiNode(Vec2Df{200.0f, 200.0f}); } - DrawText(std::format("{}", gm->nodes.size()).c_str(), 20, settings->height - 70, 15, VS_COLOR_WHITE); - DrawFPS(20, settings->height - 30); + DrawText(std::format("{}", gm->nodes.size()).c_str(), 20, state->settings.height - 70, 15, VS_COLOR_WHITE); + DrawFPS(20, state->settings.height - 30); } void DrawCenteredText(std::string text, Vec2Df position, int size, Color color){ @@ -136,7 +137,7 @@ namespace VISEQ::APP::UI{ GuiCheckBox((Rectangle){np.x + 160, np.y + y_channel, 20, 20}, "PITCH", &mn->send_pitch_bend); int y_cc_note = y_channel + 120; - switch(settings->mode){ + switch(state->settings.mode){ case 0:{ GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "NOTE CHANNEL"); GuiToggleGroup((Rectangle){np.x + 20, np.y + y_cc_note - 50, 25, 20}, "1;2;3;4;5;6;7;8\n9;10;11;12;13;14;15;16", &mn->midi_note_channel); diff --git a/src/vmath.h b/src/vmath.h index c944a03..c2723d0 100644 --- a/src/vmath.h +++ b/src/vmath.h @@ -162,6 +162,3 @@ namespace VISEQ::VMATH{ using Vec2Df = VISEQ::VMATH::Vec2D; - - -