json is loading, but does nothing yet

This commit is contained in:
Sebastian
2024-11-03 03:48:49 +01:00
parent a4ea20b2b3
commit 7bdd68bdcc
16 changed files with 1174 additions and 1104 deletions
+1
View File
@@ -3,3 +3,4 @@ build/**
viseq viseq
viseq.exe viseq.exe
compile_commands.json compile_commands.json
state.json
+33 -49
View File
@@ -27,64 +27,48 @@ using json = nlohmann::json;
#include <math.h> #include <math.h>
#include "libremidi/libremidi.hpp" #include "libremidi/libremidi.hpp"
enum Distribution {GEOMETRIC, ROUNDROBIN, RANDOM};
// Types
/*class GraphItem;*/
/*class Node;*/
/*class Edge;*/
/*class Signal;*/
#define RANGE_FILTER(L) std::ranges::views::filter(L) #define RANGE_FILTER(L) std::ranges::views::filter(L)
/*typedef std::shared_ptr<GraphItem> SPTR_GraphItem;*/ namespace VISEQ::GRAPH::NODES {
/*typedef std::shared_ptr<Node> SPTR_Node;*/ enum Distribution {GEOMETRIC, ROUNDROBIN, RANDOM};
/*typedef std::shared_ptr<Edge> SPTR_Edge;*/ }
/**/
/*typedef std::shared_ptr<Signal> SPTR_Signal;*/
/**/
/*typedef std::vector<SPTR_GraphItem> VEC_SPTR_GraphItem;*/
/*typedef std::vector<SPTR_Node> VEC_SPTR_Node;*/
/*typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;*/
/*typedef std::vector<SPTR_Signal> VEC_SPTR_Signal;*/
// App Settings namespace VISEQ::APP{
struct AppSettings{ struct AppSettings{
int mode = 0; int mode = 0;
int width; int width;
int height; int height;
float grid; float grid;
float bpm; float bpm;
float fps; float fps;
float speed = 16.0f; float speed = 16.0f;
int gui_selected_midi_out; int gui_selected_midi_out;
}; };
}
namespace VISEQ::GRAPH{
enum GraphItemType {NODE, EDGE};
enum NodeType {OSCNODE, MIDINODE};
}
// Graph namespace VISEQ::MIDI{
struct MidiOutPort{
libremidi::output_port port;
int number;
bool enabled;
};
enum GraphItemType {NODE, EDGE}; struct MidiOut{
enum NodeType {OSCNODE, MIDINODE}; libremidi::midi_out out;
int number;
};
// MIDI NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MidiOut, number)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MidiOutPort, number, enabled)
struct MidiOutPort{
libremidi::output_port port;
int number;
bool enabled;
};
struct MidiOut{
libremidi::midi_out out;
int number;
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MidiOut, number)
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MidiOutPort, number, enabled)
namespace MIDI{
inline std::vector<std::string> Fractions = {"1/32","1/16","3/32","1/8","5/32","3/16","7/32","1/4","9/32","5/16","11/32","3/8","13/32","7/16","15/32","1/2","17/32","9/16","19/32","5/8","21/32","11/16","23/32","3/4","25/32","13/16","27/32","7/8","29/32","15/16","31/32","1/1"}; inline std::vector<std::string> Fractions = {"1/32","1/16","3/32","1/8","5/32","3/16","7/32","1/4","9/32","5/16","11/32","3/8","13/32","7/16","15/32","1/2","17/32","9/16","19/32","5/8","21/32","11/16","23/32","3/4","25/32","13/16","27/32","7/8","29/32","15/16","31/32","1/1"};
inline std::vector<std::string> Notes = { inline std::vector<std::string> Notes = {
"C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1", "C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1",
"G#-1", "A-1", "A#-1", "B-1", "C0", "C#0", "D0", "D#0", "G#-1", "A-1", "A#-1", "B-1", "C0", "C#0", "D0", "D#0",
+26 -24
View File
@@ -1,38 +1,40 @@
#pragma once #pragma once
#include "datatypes.h"
#include "graph-item.h" #include "graph-item.h"
#include <memory>
#include "node.h"
class Edge : public GraphItem{ using namespace VISEQ::GRAPH;
public: namespace VISEQ::GRAPH::EDGES {
Edge() {} class Edge : public GraphItem{
Edge(SPTR_Node start, SPTR_Node end) : start(start), end(end) {}
SPTR_Node start; public:
SPTR_Node end; Edge() {}
Edge(std::shared_ptr<NODES::Node> start, std::shared_ptr<NODES::Node> end) : start(start), end(end) {}
bool pass_forward = true; std::shared_ptr<NODES::Node> start;
bool pass_backward = true; std::shared_ptr<NODES::Node> end;
bool active = false; bool pass_forward = true;
bool config = false; bool pass_backward = true;
bool split = false;
float connector_offset = 8.0f; bool active = false;
float switches_offset = 25.0f; bool config = false;
bool split = false;
virtual bool GActive() {return active;} float connector_offset = 8.0f;
float switches_offset = 25.0f;
SPTR_Node getNextNode(SPTR_Node origin){ virtual bool GActive() {return active;}
return start == origin ? end : start;
}
void draw(Vec2Df mouse_pos, Camera2D cam) {} std::shared_ptr<NODES::Node> getNextNode(std::shared_ptr<NODES::Node> origin){
}; return start == origin ? end : start;
}
void draw(Vec2Df mouse_pos, Camera2D cam) {}
};
/*typedef std::shared_ptr<Edge> SPTR_Edge;*/ typedef std::shared_ptr<Edge> SPTR_Edge;
/*typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;*/ typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;
}
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Edge, id, start, end, pass_forward, pass_backward)
+12 -18
View File
@@ -5,26 +5,20 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
class GraphItem{ namespace VISEQ::GRAPH {
class GraphItem{
public: public:
GraphItem() {static int _id; id=_id++;} GraphItem() {static int _id; id=_id++;}
std::vector<std::shared_ptr<GraphItem>> neighbors; std::vector<std::shared_ptr<GraphItem>> neighbors;
int id = 0; int id = 0;
GraphItemType graphItemType; GraphItemType graphItemType;
virtual void draw(Vec2Df mouse, Camera2D cam) {}; virtual void draw(Vec2Df mouse, Camera2D cam) {};
virtual GraphItemType getType() {return graphItemType;} virtual GraphItemType getType() {return graphItemType;}
virtual ~GraphItem() = default; virtual ~GraphItem() = default;
/*void to_json(json& j, const GraphItem& G){*/ };
/* j = json{{"id",id},{"graphItemType",graphItemType},{"neighbors",neighbors}};*/
/*}*/
}; }
/*typedef std::shared_ptr<GraphItem> SPTR_GraphItem;*/
/*typedef std::vector<SPTR_GraphItem> VEC_SPTR_GraphItem;*/
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GraphItem, id, graphItemType, neighbors)
+207 -214
View File
@@ -8,9 +8,9 @@
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include "button.h" #include "button.h"
#include "node.h"
#include "midi-node.h" #include "midi-node.h"
#include "edge.h" #include "edge.h"
#include "node.h"
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
@@ -26,271 +26,264 @@
using json = nlohmann::json; using json = nlohmann::json;
typedef std::shared_ptr<GraphItem> SPTR_GraphItem; using namespace VISEQ::GRAPH::NODES;
typedef std::shared_ptr<Node> SPTR_Node; using namespace VISEQ::GRAPH::EDGES;
typedef std::shared_ptr<Edge> SPTR_Edge;
typedef std::shared_ptr<Signal> SPTR_Signal; namespace VISEQ::GRAPH {
typedef std::vector<SPTR_GraphItem> VEC_SPTR_GraphItem; class GraphManager{
typedef std::vector<SPTR_Node> VEC_SPTR_Node;
typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;
typedef std::vector<SPTR_Signal> VEC_SPTR_Signal;
class GraphManager{ public:
public: GraphManager() {}
GraphManager() {} NODES::VEC_SPTR_Node nodes;
EDGES::VEC_SPTR_Edge edges;
VEC_SPTR_Node nodes; EDGES::VEC_SPTR_Edge remove_edges;
VEC_SPTR_Edge edges; EDGES::VEC_SPTR_Edge split_edges;
VEC_SPTR_Edge remove_edges; std::map<NODES::SPTR_Node, int> counter;
VEC_SPTR_Edge split_edges;
std::map<SPTR_Node, int> counter; NODES::SPTR_Node getRandomNode(){
VEC_SPTR_GraphItem o;
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
return std::dynamic_pointer_cast<VISEQ::GRAPH::NODES::Node>(o.front());
}
/*void to_json(json& j, const GraphManager& G){*/ EDGES::SPTR_Edge getRandomEdge(){
/* j = json{{"nodes", G.nodes}, {"edges", G.edges}};*/ VEC_SPTR_GraphItem o;
/*}*/ std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
/**/ return std::dynamic_pointer_cast<VISEQ::GRAPH::EDGES::Edge>(o.front());
/*void from_json(const json& j, GraphManager& G){*/ }
/**/
/*}*/
SPTR_Node getRandomNode(){ void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){
VEC_SPTR_GraphItem o; for(auto n: _nodes){
std::ranges::sample(nodes, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); nodes.insert(nodes.end(), std::make_shared<Node>(*n));
return std::dynamic_pointer_cast<Node>(o.front()); nodes.back()->setPosition(position + nodes.back()->drag_offset);
} nodes.back()->selected = false;
}
}
SPTR_Edge getRandomEdge(){ void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
VEC_SPTR_GraphItem o; for(auto n: _nodes){
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()}); nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
return std::dynamic_pointer_cast<Edge>(o.front()); nodes.back()->setPosition(position + nodes.back()->drag_offset);
} nodes.back()->selected = false;
}
}
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){ SPTR_Node copyNode(SPTR_Node n, Vec2Df position){
for(auto n: _nodes){ std::cout << "copy basenode" << std::endl;
nodes.insert(nodes.end(), std::make_shared<Node>(*n)); nodes.insert(nodes.end(), std::make_shared<Node>(*n));
nodes.back()->setPosition(position + nodes.back()->drag_offset); nodes.back()->setPosition(position);
nodes.back()->selected = false; return nodes.back();
} }
}
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){ SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position){
for(auto n: _nodes){ std::cout << "copy midinode" << std::endl;
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n)); nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
nodes.back()->setPosition(position + nodes.back()->drag_offset); nodes.back()->setPosition(position);
nodes.back()->selected = false; return nodes.back();
} }
}
SPTR_Node copyNode(SPTR_Node n, Vec2Df position){ SPTR_Node addMidiNode(Vec2Df position){
std::cout << "copy basenode" << std::endl; nodes.emplace_back(std::make_shared<MidiNode>(position));
nodes.insert(nodes.end(), std::make_shared<Node>(*n)); return nodes.back();
nodes.back()->setPosition(position);
return nodes.back();
}
SPTR_Node copyNode(std::shared_ptr<MidiNode> n, Vec2Df position){
std::cout << "copy midinode" << std::endl;
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
nodes.back()->setPosition(position);
return nodes.back();
}
SPTR_Node addMidiNode(Vec2Df position){
nodes.emplace_back(std::make_shared<MidiNode>(position));
return nodes.back();
}
void setNodeMode(int mode){
for(auto n: nodes){
n->SMode(mode);
} }
}
void reset(){ void setNodeMode(int mode){
counter.clear(); for(auto n: nodes){
} n->SMode(mode);
SPTR_Node next(SPTR_Node current, SPTR_Node last){
VEC_SPTR_Edge possible_connections = current->connections | RANGE_FILTER([current](SPTR_Edge c){
return (c->start == current && c->pass_forward) || (c->end == current && c->pass_backward);
}) | std::ranges::to<VEC_SPTR_Edge>();
if(possible_connections.empty()) return SPTR_Node(nullptr);
int return_path = 0;
float angle = -1;
if(current->distributionMode == Distribution::GEOMETRIC){
Vec2Df coming_from = (current->position - last->position).norm();
int i = 0;
for(auto n : possible_connections){
SPTR_Node next_node = n->getNextNode(current);
float angle_v1_v2 = coming_from.norm().dot((next_node->position - current->position).norm());
if(angle_v1_v2 > angle) {
return_path = i;
angle = angle_v1_v2;
}
++i;
} }
} }
if(current->distributionMode == Distribution::ROUNDROBIN){ void reset(){
if(counter.find(current) == counter.end()) { counter.clear();
counter[current] = 0; }
} else {
counter[current] = (counter[current] + 1) % possible_connections.size(); SPTR_Node next(SPTR_Node current, SPTR_Node last){
return_path = counter[current]; VEC_SPTR_Edge possible_connections = current->connections | RANGE_FILTER([current](SPTR_Edge c){
return (c->start == current && c->pass_forward) || (c->end == current && c->pass_backward);
}) | std::ranges::to<VEC_SPTR_Edge>();
if(possible_connections.empty()) return SPTR_Node(nullptr);
int return_path = 0;
float angle = -1;
if(current->distributionMode == Distribution::GEOMETRIC){
Vec2Df coming_from = (current->position - last->position).norm();
int i = 0;
for(auto n : possible_connections){
SPTR_Node next_node = n->getNextNode(current);
float angle_v1_v2 = coming_from.norm().dot((next_node->position - current->position).norm());
if(angle_v1_v2 > angle) {
return_path = i;
angle = angle_v1_v2;
}
++i;
}
}
if(current->distributionMode == Distribution::ROUNDROBIN){
if(counter.find(current) == counter.end()) {
counter[current] = 0;
} else {
counter[current] = (counter[current] + 1) % possible_connections.size();
return_path = counter[current];
}
}
if(current->distributionMode == Distribution::RANDOM){
return_path = rand() % possible_connections.size();
}
//possible_connections.at(return_path)->active = true;
return possible_connections.at(return_path)->getNextNode(current);
}
void addConnection(SPTR_Node from, SPTR_Node to){
if(from != to) {
edges.emplace_back(std::make_shared<Edge>());
edges.back()->start = from;
edges.back()->end = to;
from->connections.push_back(edges.back());
to->connections.push_back(edges.back());
} }
} }
if(current->distributionMode == Distribution::RANDOM){ void splitEdge(SPTR_Edge edge){
return_path = rand() % possible_connections.size(); split_edges.push_back(edge);
} }
//possible_connections.at(return_path)->active = true; void splitEdges(){
return possible_connections.at(return_path)->getNextNode(current); for(auto s : split_edges){
} Vec2Df center = s->end->position.center(s->start->position);
SPTR_Node cn = addMidiNode(center);
void addConnection(SPTR_Node from, SPTR_Node to){ addConnection(s->start, cn);
if(from != to) { addConnection(cn, s->end);
edges.emplace_back(std::make_shared<Edge>()); remove_edges.push_back(s);
edges.back()->start = from; }
edges.back()->end = to; split_edges.clear();
from->connections.push_back(edges.back());
to->connections.push_back(edges.back());
} }
}
void splitEdge(SPTR_Edge edge){ void removeEdge(SPTR_Edge edge){
split_edges.push_back(edge); remove_edges.push_back(edge);
}
void splitEdges(){
for(auto s : split_edges){
Vec2Df center = s->end->position.center(s->start->position);
SPTR_Node cn = addMidiNode(center);
addConnection(s->start, cn);
addConnection(cn, s->end);
remove_edges.push_back(s);
} }
split_edges.clear();
}
void removeEdge(SPTR_Edge edge){ void removeEdges(){
remove_edges.push_back(edge); for(auto edge : remove_edges){
} std::erase(edge->start->connections, edge);
std::erase(edge->end->connections, edge);
void removeEdges(){ std::erase(edges, edge);
for(auto edge : remove_edges){ }
std::erase(edge->start->connections, edge); remove_edges.clear();
std::erase(edge->end->connections, edge);
std::erase(edges, edge);
} }
remove_edges.clear();
}
void drawEdges(Vec2Df mouse_pos, Camera2D cam){ void drawEdges(Vec2Df mouse_pos, Camera2D cam){
if(!remove_edges.empty()) removeEdges(); if(!remove_edges.empty()) removeEdges();
if(!split_edges.empty()) splitEdges(); if(!split_edges.empty()) splitEdges();
for(auto c : edges){ for(auto c : edges){
Vec2Df v1_position = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->connector_offset); Vec2Df v1_position = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->connector_offset);
Vec2Df v2_position = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->connector_offset); Vec2Df v2_position = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->connector_offset);
float v1_v2_distance = v1_position.dist(&v2_position); float v1_v2_distance = v1_position.dist(&v2_position);
float midi_gate_v1 = Clamp((4.f / 1000.f) * c->start->GGate(), 1.f, 4.f); float midi_gate_v1 = Clamp((4.f / 1000.f) * c->start->GGate(), 1.f, 4.f);
float midi_gate_v2 = Clamp((4.f / 1000.f) * c->end->GGate(), 1.f, 4.f); float midi_gate_v2 = Clamp((4.f / 1000.f) * c->end->GGate(), 1.f, 4.f);
// Line with small connector circles // Line with small connector circles
Color col = {229, 181, 103, 255}; Color col = {229, 181, 103, 255};
if(c->GActive()) col = {108, 153, 187, 255}; if(c->GActive()) col = {108, 153, 187, 255};
DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col); DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col);
if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED; if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED;
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col); DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col);
col = {229, 181, 103, 255}; col = {229, 181, 103, 255};
if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED; if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED;
DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col); DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col);
if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f; if(v1_v2_distance < 100) c->switches_offset = v1_v2_distance/2.3f;
Vec2Df from_to_handle = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->switches_offset); Vec2Df from_to_handle = c->start->position - ((c->start->position - c->end->position)).norm() * (c->start->radius + c->switches_offset);
Vec2Df to_from_handle = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->switches_offset); Vec2Df to_from_handle = c->end->position - ((c->end->position - c->start->position)).norm() * (c->end->radius + c->switches_offset);
Vec2Df middle_handle = c->end->position - ((c->end->position - c->start->position))*0.4f; Vec2Df middle_handle = c->end->position - ((c->end->position - c->start->position))*0.4f;
Vec2Df middle_handle_2 = c->end->position - ((c->end->position - c->start->position))*0.6f; Vec2Df middle_handle_2 = c->end->position - ((c->end->position - c->start->position))*0.6f;
Vec2Df 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); Vec2Df 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);
Vec2Df flip_handle_2 = middle_handle_2 + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*-0.2, -20.f, 0.f) : 1.0f); Vec2Df flip_handle_2 = middle_handle_2 + (v1_position - v2_position).norm().orth() * (v1_v2_distance < 120.0f ? Clamp((120.0f - v1_v2_distance)*-0.2, -20.f, 0.f) : 1.0f);
// Dynamic opacity by distance to mouse // Dynamic opacity by distance to mouse
unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f); 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, 220.0f); unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 220.0f);
unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f); unsigned char opacity_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f);
if(!c->start->dragging && !c->end->dragging){ if(!c->start->dragging && !c->end->dragging){
// On/Off switches on both sides // On/Off switches on both sides
if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward; if(ViButton(mouse_pos, from_to_handle.x, from_to_handle.y, 8.0f, c->pass_forward ? (Color){120, 210, 115, opacity_from} : (Color){232, 50, 32, opacity_from} )) c->pass_forward = !c->pass_forward;
if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward; if(ViButton(mouse_pos, to_from_handle.x, to_from_handle.y, 8.0f, c->pass_backward ? (Color){120, 210, 115, opacity_to} : (Color){232, 50, 32, opacity_to} )) c->pass_backward = !c->pass_backward;
if(!c->pass_forward){ if(!c->pass_forward){
Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle; Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + from_to_handle;
Vec2Df block_point2 = (v1_position - v2_position).norm().orth()*-4.0f + from_to_handle; Vec2Df 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}); DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from});
}
if(!c->pass_backward){
Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle;
Vec2Df 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} )) removeEdge(c);
if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c);
} }
if(!c->pass_backward){
Vec2Df block_point1 = (v1_position - v2_position).norm().orth()*4.0f + to_from_handle;
Vec2Df 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} )) removeEdge(c);
if(ViButton(mouse_pos, flip_handle_2.x, flip_handle_2.y, 8.0f, (Color){232, 125, 62, opacity} )) splitEdge(c);
} }
} }
}
void drawNodes(Vec2Df mouse_pos, Camera2D camera){ void drawNodes(Vec2Df mouse_pos, Camera2D camera){
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->dragging && !n->selected;}) | std::ranges::to<VEC_SPTR_Node>(); auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->dragging && !n->selected;}) | std::ranges::to<VEC_SPTR_Node>();
float min_d = 480; float min_d = 480;
for(auto n : nodes){ for(auto n : nodes){
if(n->dragging){ if(n->dragging){
std::sort(neighbors.begin(), neighbors.end(), [n, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);}); std::sort(neighbors.begin(), neighbors.end(), [n, mouse_pos](SPTR_Node na, SPTR_Node nb){ return na->position.dist(mouse_pos) < nb->position.dist(mouse_pos);});
if(neighbors.size() > 1){ if(neighbors.size() > 1){
min_d = neighbors.front()->position.dist(mouse_pos); min_d = neighbors.front()->position.dist(mouse_pos);
n->neighbor = neighbors.front(); n->neighbor = neighbors.front();
DrawCircleLinesV(n->neighbor->position, 20.0f, VS_COLOR_PINK); DrawCircleLinesV(n->neighbor->position, 20.0f, VS_COLOR_PINK);
for(auto nb : neighbors){ for(auto nb : neighbors){
float d = nb->position.dist(mouse_pos); float d = nb->position.dist(mouse_pos);
float dn = nb->position.dist(n->position); float dn = nb->position.dist(n->position);
if(d > 0 && d < min_d*1.5 && d < 960) { if(d > 0 && d < min_d*1.5 && d < 960) {
std::string frac = MIDI::Fractions.at((int)Clamp(round(dn/60) - 1, 0, 31)); std::string frac = MIDI::Fractions.at((int)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 = 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); 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 = 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 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 = 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; 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(nb == neighbors.front() && 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);
}
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(nb == neighbors.front() && 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);
} }
} }
} }
n->draw(mouse_pos, camera);
} }
n->draw(mouse_pos, camera);
} }
}
}; };
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GraphManager, nodes, edges) inline void to_json(json& j, const GraphManager& G){
j = json{{"nodes", G.nodes}, {"edges", G.edges}};
}
inline void from_json(const json& j, GraphManager& G){
j.at("nodes").get_to(G.nodes); // = json{{"nodes", G.nodes}, {"edges", G.edges}};
}
}
+32 -16
View File
@@ -6,6 +6,10 @@
* *
*/ */
#include "graph-manager.h"
#include "vmath.h"
#include <filesystem>
#include <fstream>
#define RAYGUI_IMPLEMENTATION #define RAYGUI_IMPLEMENTATION
#include <raylib.h> #include <raylib.h>
#include <raymath.h> #include <raymath.h>
@@ -24,9 +28,11 @@
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include "state-io.h"
using json = nlohmann::json; using json = nlohmann::json;
AppState app; VISEQ::APP::AppState app;
Vec2Df mouse_pos; Vec2Df mouse_pos;
Vec2Df drag_start; Vec2Df drag_start;
@@ -72,19 +78,26 @@ int main(){
camera.rotation = 0.0f; camera.rotation = 0.0f;
camera.zoom = 1.0f; camera.zoom = 1.0f;
for(int i = 0; i < 4; ++i){ if(std::filesystem::exists("state.json")){
app.gm.addMidiNode(Vec2Df{(float)i * app.settings.grid*4 + 7 * app.settings.grid, 2 * app.settings.grid*4}); std::ifstream f("state.json");
} json graph = json::parse(f);
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 = graph["graphmanager"].template get<VISEQ::GRAPH::GraphManager>();
} 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});
}
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});
}
for(int i = 0; i < 8; ++i){
app.gm.addConnection(app.gm.nodes[i], app.gm.nodes[(i+1) % 8]);
}
app.sm.addSignalAtNode(app.gm.nodes.front());
} }
for(int i = 0; i < 8; ++i){
app.gm.addConnection(app.gm.nodes[i], app.gm.nodes[(i+1) % 8]);
}
app.sm.addSignalAtNode(app.gm.nodes.front());
app.sm.startSignalThread(); app.sm.startSignalThread();
enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE, DRAW_SELECTION, DELETE_SELECTED_NODES, COPY_SELECTED_NODES, PASTE_SELECTED_NODES}; enum InteractionState {HOVER_CANVAS, HOVER_NODE, HOVER_CONNECTOR, DRAG_NODE, CONNECT_NODE, DRAW_SELECTION, DELETE_SELECTED_NODES, COPY_SELECTED_NODES, PASTE_SELECTED_NODES};
@@ -223,7 +236,7 @@ int main(){
float pol_r = steps; float pol_r = steps;
float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbor->position)); float pol_theta = Vector2Angle(Vec2Df{1,0},(mouse_pos - dragNode->neighbor->position));
Vec2Df polar = VMath::polarToCartesian(pol_r, pol_theta); Vec2Df polar = VISEQ::VMATH::polarToCartesian(pol_r, pol_theta);
dragNode->position = dragNode->neighbor->position + polar; dragNode->position = dragNode->neighbor->position + polar;
} else { } else {
dragNode->position = mouse_pos + dragNode->drag_offset; dragNode->position = mouse_pos + dragNode->drag_offset;
@@ -330,8 +343,8 @@ int main(){
midiNode->midi_pitch_bend += wheel; midiNode->midi_pitch_bend += wheel;
break;} break;}
}; };
if(midiNode->midi_note < 0 ) midiNode->midi_note = app.sm.midi.midiNotes.size()-1; if(midiNode->midi_note < 0 ) midiNode->midi_note = VISEQ::MIDI::Notes.size()-1;
if(midiNode->midi_note >= app.sm.midi.midiNotes.size() ) midiNode->midi_note = 0; 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 < 0 ) midiNode->midi_cc = 127;
if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0; if(midiNode->midi_cc > 127 ) midiNode->midi_cc = 0;
@@ -390,7 +403,10 @@ int main(){
EndDrawing(); EndDrawing();
} }
std::cout << std::setw(4) << json(app.gm) << std::endl; std::ofstream file("state.json");
file << json(app);
std::cout << std::setw(4) << json(app) << std::endl;
CloseWindow(); CloseWindow();
+128 -129
View File
@@ -7,166 +7,165 @@
#define RADIUS 14.0f #define RADIUS 14.0f
struct NodeMidiOut{ namespace VISEQ::GRAPH::NODES {
int i;
std::string name;
bool enabled;
};
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(NodeMidiOut, i, name, enabled) class MidiNode : public Node{
class MidiNode : public Node{ public:
public: struct NodeMidiOut{
int i;
std::string name;
bool enabled;
};
MidiNode() {} MidiNode() {}
MidiNode(Vec2Df position) : Node(position) {} MidiNode(Vec2Df position) : Node(position) {}
NodeType nodeType = NodeType::MIDINODE; NodeType nodeType = NodeType::MIDINODE;
bool send_note = true; bool send_note = true;
bool send_cc = false; bool send_cc = false;
bool send_pc = false; bool send_pc = false;
bool send_pitch_bend = false; bool send_pitch_bend = false;
int midi_out = -1; int midi_out = -1;
std::map<int, NodeMidiOut> midi_outs; std::map<int, NodeMidiOut> midi_outs;
bool gui_midi_channel_edit = false; bool gui_midi_channel_edit = false;
bool gui_midi_note_edit = false; bool gui_midi_note_edit = false;
bool gui_midi_velocity_edit = false; bool gui_midi_velocity_edit = false;
bool gui_midi_gate_edit = false; bool gui_midi_gate_edit = false;
bool triggered = false; bool triggered = false;
int mode = 0; int mode = 0;
int midi_note_channel = 1; int midi_note_channel = 1;
int midi_note = 48; int midi_note = 48;
int midi_velocity = 60; int midi_velocity = 60;
int midi_gate = 25; // gate time in milliseconds int midi_gate = 25; // gate time in milliseconds
int midi_cc_channel = 0; int midi_cc_channel = 0;
int midi_cc = 0; int midi_cc = 0;
int midi_cc_value = 0; int midi_cc_value = 0;
int midi_pc_channel = 0; int midi_pc_channel = 0;
int midi_pc = 0; int midi_pc = 0;
int midi_pc_value = 0; int midi_pc_value = 0;
int midi_pitch_bend_channel = 0; int midi_pitch_bend_channel = 0;
int midi_pitch_bend = 8192; int midi_pitch_bend = 8192;
float start = 0; float start = 0;
float fade = 0; float fade = 0;
float time = 0; float time = 0;
float last = 0; float last = 0;
virtual NodeType getNodeType() override {return nodeType;} virtual NodeType getNodeType() const override {return nodeType;}
void setMidiPort(int midi_port, bool state){ void setMidiPort(int midi_port, bool state){
midi_outs[midi_port].enabled = state; midi_outs[midi_port].enabled = state;
}
void trigger() override {
triggered = true;
start = GetTime();
fade = midi_gate;
}
void SMode(int _mode) override{
mode = _mode;
}
int Ggate(){
return midi_gate;
}
void draw(Vec2Df mouse, Camera2D cam) override {
last = time;
time = GetTime();
if(time - start >= (float)midi_gate / 1000.0f){
triggered = false;
fade = 0;
} else {
fade -= (time - last)*1000.0f;
} }
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255}); void trigger() override {
DrawRing(position, radius, radius+4, 0.0f - 90.0f, (360.0f/127.0f) * midi_velocity - 90.0f, 24, (Color){158, 134, 200, 255}); triggered = true;
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255}); start = GetTime();
fade = midi_gate;
float midi_gate_map = round((255.f / midi_gate) * fade);
Color gate_color = VS_COLOR_GREEN;
switch(mode){
case 0:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 1:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
case 2:{
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
break;}
} }
if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, gate_color);
if(selected || dragging || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK); void SMode(int _mode) override{
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW); mode = _mode;
}
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255}); int Ggate(){
return midi_gate;
}
if(IsKeyDown(KEY_LEFT_ALT)){ void draw(Vec2Df mouse, Camera2D cam) override {
float value = 0; last = time;
int offset_gate = 0; time = GetTime();
switch(mode){ if(time - start >= (float)midi_gate / 1000.0f){
case 0: triggered = false;
value = (float)midi_gate/1000.0f; fade = 0;
offset_gate = MeasureText(std::format("{:.2f}", value).c_str(), 12); } else {
DrawText(std::format("{:.2f}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE ); fade -= (time - last)*1000.0f;
break;
case 1:
value = (float)midi_cc_value;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 2:
value = (float)midi_pitch_bend;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
} }
} else {
DrawRing(position, radius, radius+4, 0, 360, 24, (Color){60, 60, 60, 255});
DrawRing(position, radius, radius+4, 0.0f - 90.0f, (360.0f/127.0f) * midi_velocity - 90.0f, 24, (Color){158, 134, 200, 255});
DrawCircleLinesV(position, radius + 4, (Color){100, 100, 100, 255});
float midi_gate_map = round((255.f / midi_gate) * fade);
Color gate_color = VS_COLOR_GREEN;
switch(mode){ switch(mode){
case 0:{ case 0:{
int offset = MeasureText(MIDI::Notes[(int)midi_note].c_str(), 12); gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;} break;}
case 1:{ case 1:{
int offset = MeasureText(std::format("{}", midi_cc).c_str(), 12); gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
DrawText(std::format("{}", midi_cc).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;} break;}
case 2:{ case 2:{
int offset = MeasureText(std::format("{}", midi_pitch_bend).c_str(), 12); gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){232, 125, 62, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, WHITE);
DrawText(std::format("{}", midi_pitch_bend).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;} break;}
} }
if(!dragging && !selected && !in_rect && !hovering) DrawCircle(position.x, position.y, radius, gate_color);
if(selected || dragging || in_rect || hovering) DrawCircle(position.x, position.y, radius, (Color){108, 153, 187, 255});
if(distributionMode == Distribution::RANDOM) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_PINK);
if(distributionMode == Distribution::ROUNDROBIN) DrawCircle(position.x, position.y, radius/2.0f, VS_COLOR_YELLOW);
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
if(IsKeyDown(KEY_LEFT_ALT)){
float value = 0;
int offset_gate = 0;
switch(mode){
case 0:
value = (float)midi_gate/1000.0f;
offset_gate = MeasureText(std::format("{:.2f}", value).c_str(), 12);
DrawText(std::format("{:.2f}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 1:
value = (float)midi_cc_value;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
case 2:
value = (float)midi_pitch_bend;
offset_gate = MeasureText(std::format("{}", value).c_str(), 12);
DrawText(std::format("{}",value).c_str(), position.x-offset_gate/2.0f, position.y-5, 12, WHITE );
break;
}
} else {
switch(mode){
case 0:{
int offset = MeasureText(MIDI::Notes[(int)midi_note].c_str(), 12);
DrawText(MIDI::Notes[midi_note].c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 1:{
int offset = MeasureText(std::format("{}", midi_cc).c_str(), 12);
DrawText(std::format("{}", midi_cc).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
case 2:{
int offset = MeasureText(std::format("{}", midi_pitch_bend).c_str(), 12);
DrawText(std::format("{}", midi_pitch_bend).c_str(), position.x-offset/2.0f, position.y-5, 12, WHITE );
break;}
}
}
Vec2Df connector = position - ((position - mouse)).norm() * (radius + RADIUS);
float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f);
if(hover_connect){
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, 255});
} else {
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, (unsigned char)opacity});
}
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
} }
};
Vec2Df connector = position - ((position - mouse)).norm() * (radius + RADIUS); }
float opacity = 150.0f - Clamp(position.dist(mouse), 0.0f, 150.0f);
if(hover_connect){
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, 255});
} else {
DrawCircle(connector.x, connector.y, 8.0f, (Color){108, 153, 187, (unsigned char)opacity});
}
if(highlighted) DrawCircleLinesV(position, radius+2, (Color){255, 0, 0, 200});
}
};
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MidiNode, id, nodeType, send_note, send_cc, send_pc, send_pitch_bend, midi_outs, mode, midi_note_channel, midi_note, midi_velocity, midi_gate, midi_cc_channel, midi_cc, midi_cc_value, midi_pc_channel, midi_pc, midi_pc_value, midi_pitch_bend_channel, midi_pitch_bend)
+79 -96
View File
@@ -8,122 +8,105 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <memory> #include <memory>
#include <numbers>
#include "datatypes.h" #include "datatypes.h"
struct NoteTimer{ namespace VISEQ::MIDI {
int midi_out;
int channel;
int note;
int velocity;
float start;
int gate;
bool erase = false;
};
class Midi { struct NoteTimer{
int midi_out;
int channel;
int note;
int velocity;
float start;
int gate;
bool erase = false;
};
public: class Midi {
Midi() {
enumerate();
}
std::vector<std::string> midiNotes = { public:
"C-1", "C#-1", "D-1", "D#-1", "E-1", "F-1", "F#-1", "G-1", Midi() {
"G#-1", "A-1", "A#-1", "B-1", "C0", "C#0", "D0", "D#0", enumerate();
"E0", "F0", "F#0", "G0", "G#0", "A0", "A#0", "B0",
"C1", "C#1", "D1", "D#1", "E1", "F1", "F#1", "G1",
"G#1", "A1", "A#1", "B1", "C2", "C#2", "D2", "D#2",
"E2", "F2", "F#2", "G2", "G#2", "A2", "A#2", "B2",
"C3", "C#3", "D3", "D#3", "E3", "F3", "F#3", "G3",
"G#3", "A3", "A#3", "B3", "C4", "C#4", "D4", "D#4",
"E4", "F4", "F#4", "G4", "G#4", "A4", "A#4", "B4",
"C5", "C#5", "D5", "D#5", "E5", "F5", "F#5", "G5",
"G#5", "A5", "A#5", "B5", "C6", "C#6", "D6", "D#6",
"E6", "F6", "F#6", "G6", "G#6", "A6", "A#6", "B6",
"C7", "C#7", "D7", "D#7", "E7", "F7", "F#7", "G7",
"G#7", "A7", "A#7", "B7", "C8", "C#8", "D8", "D#8",
"E8", "F8", "F#8", "G8", "G#8", "A8", "A#8", "B8",
"C9", "C#9", "D9", "D#9", "E9", "F9", "F#9", "G9",
"G#9", "A9", "A#9", "B9", "C10"
};
libremidi::observer obs;
std::map<int, std::shared_ptr<libremidi::midi_out>> midi_outs;
std::vector<libremidi::input_port> midi_in_ports;
std::vector<MidiOutPort> midi_out_ports;
std::vector<NoteTimer> notes;
float timer;
void enumerate(){
std::cout << (int)obs.get_current_api() << std::endl;
int i = 0;
midi_out_ports.clear();
for(const libremidi::output_port& port : obs.get_output_ports()){
midi_out_ports.push_back({port, i, false});
i++;
} }
}
void open_midi_out(MidiOutPort *out_port){ libremidi::observer obs;
if(!midi_outs.contains(out_port->number)){
auto out = std::make_shared<libremidi::midi_out>();
out->open_port(out_port->port);
midi_outs[out_port->number] = out;
}
}
void close_midi_out(MidiOutPort *out_port){ std::map<int, std::shared_ptr<libremidi::midi_out>> midi_outs;
if(midi_outs.contains(out_port->number)){ std::vector<libremidi::input_port> midi_in_ports;
midi_outs[out_port->number]->close_port(); std::vector<MidiOutPort> midi_out_ports;
midi_outs.erase(out_port->number);
}
}
void note(int midi_out, int channel, int note, int velocity, int gate){ std::vector<NoteTimer> notes;
if(midi_outs.contains(midi_out)){
if(midi_outs[midi_out]->is_port_open()){ float timer;
midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate); void enumerate(){
std::cout << (int)obs.get_current_api() << std::endl;
int i = 0;
midi_out_ports.clear();
for(const libremidi::output_port& port : obs.get_output_ports()){
midi_out_ports.push_back({port, i, false});
i++;
} }
} }
}
void cc(int midi_out, int channel, int cc, int value){ void open_midi_out(MidiOutPort *out_port){
if(midi_outs.contains(midi_out)){ if(!midi_outs.contains(out_port->number)){
if(midi_outs[midi_out]->is_port_open()){ auto out = std::make_shared<libremidi::midi_out>();
midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value)); out->open_port(out_port->port);
midi_outs[out_port->number] = out;
} }
} }
}
void pitch_bend(int midi_out, int channel, int pitch_value){ void close_midi_out(MidiOutPort *out_port){
if(midi_outs.contains(midi_out)){ if(midi_outs.contains(out_port->number)){
if(midi_outs[midi_out]->is_port_open()){ midi_outs[out_port->number]->close_port();
midi_outs[midi_out]->send_message(libremidi::channel_events::pitch_bend(channel, pitch_value)); midi_outs.erase(out_port->number);
} }
} }
}
void update(){ void note(int midi_out, int channel, int note, int velocity, int gate){
float time = GetTime(); if(midi_outs.contains(midi_out)){
for(auto &n : notes){ if(midi_outs[midi_out]->is_port_open()){
if(time - n.start >= (float)n.gate / 1000.0f){ midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity)); notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate);
n.erase = true; }
} }
} }
std::erase_if(notes, [](const auto& item){return item.erase;});
for(auto& p : midi_out_ports){ void cc(int midi_out, int channel, int cc, int value){
if(p.enabled) { if(midi_outs.contains(midi_out)){
open_midi_out(&p); if(midi_outs[midi_out]->is_port_open()){
} else if(!p.enabled) { midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value));
close_midi_out(&p); }
} }
} }
}
}; void pitch_bend(int midi_out, int channel, int pitch_value){
if(midi_outs.contains(midi_out)){
if(midi_outs[midi_out]->is_port_open()){
midi_outs[midi_out]->send_message(libremidi::channel_events::pitch_bend(channel, pitch_value));
}
}
}
void update(){
float time = GetTime();
for(auto &n : notes){
if(time - n.start >= (float)n.gate / 1000.0f){
if(midi_outs.contains(n.midi_out) && midi_outs[n.midi_out]->is_port_open()) midi_outs[n.midi_out]->send_message(libremidi::channel_events::note_off(n.channel, n.note, n.velocity));
n.erase = true;
}
}
std::erase_if(notes, [](const auto& item){return item.erase;});
for(auto& p : midi_out_ports){
if(p.enabled) {
open_midi_out(&p);
} else if(!p.enabled) {
close_midi_out(&p);
}
}
}
};
}
+27 -30
View File
@@ -2,44 +2,41 @@
#include "datatypes.h" #include "datatypes.h"
namespace VISEQ::BASE {
class Movable{
class Movable{ public:
Movable() {}
virtual ~Movable() {}
public: Movable(Vec2Df p, Vec2Df d, float s) : speed(s), position(p), direction(d) {}
Movable() {} Movable(Vec2Df p, float s) : speed(s), position(p) {}
virtual ~Movable() {}
Movable(Vec2Df p, Vec2Df d, float s) : speed(s), position(p), direction(d) {} float speed = 1.0f;
Movable(Vec2Df p, float s) : speed(s), position(p) {}
float speed = 1.0f; Vec2Df position;
Vec2Df direction;
Vec2Df position; Vec2Df drag_offset;
Vec2Df direction;
Vec2Df drag_offset; bool in_rect = false;
bool in_rect = false; bool picked_up = false;
bool dragging = false;
bool hover_connect = false;
bool hovering = false;
bool connecting = false;
bool selected = false;
bool highlighted = false;
bool copied = false;
bool picked_up = false; bool erase = false;
bool dragging = false;
bool hover_connect = false;
bool hovering = false;
bool connecting = false;
bool selected = false;
bool highlighted = false;
bool copied = false;
bool erase = false; virtual void setPosition(Vec2Df _position) {position = _position;}
virtual Vec2Df getPosition() {return position;};
virtual void update(double frametime) {};
virtual void update() {};
virtual void setPosition(Vec2Df _position) {position = _position;} };
virtual Vec2Df getPosition() {return position;};
virtual void update(double frametime) {};
virtual void update() {};
/*virtual void to_json(json& j, const Movable& M){*/ }
/* j = json{{"position", M.position}, {"direction", M.direction}, {"speed", M.speed}};*/
/*}*/
};
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Movable, position, direction, speed)
+25 -32
View File
@@ -3,51 +3,44 @@
#include <memory> #include <memory>
#include <raylib.h> #include <raylib.h>
#include "nlohmann/json.hpp"
#include "datatypes.h" #include "datatypes.h"
#include "graph-item.h" #include "graph-item.h"
#include "moveable.h" #include "moveable.h"
class Edge; namespace VISEQ::GRAPH::EDGES{
class Edge;
}
typedef std::shared_ptr<Edge> SPTR_Edge; namespace VISEQ::GRAPH::NODES {
typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;
class Node : public Movable, public GraphItem{ class Node : public BASE::Movable, public GraphItem{
public: public:
Node() {} Node() {}
Node(float x, float y) : Movable(Vec2Df(x, y), 0.0f) {} Node(float x, float y) : Movable(Vec2Df(x, y), 0.0f) {}
Node(Vec2Df p) : Movable(p, 0.0f) {} Node(Vec2Df p) : Movable(p, 0.0f) {}
NodeType nodeType; NodeType nodeType;
std::shared_ptr<Node> neighbor; std::shared_ptr<Node> neighbor;
VEC_SPTR_Edge connections; std::vector<std::shared_ptr<VISEQ::GRAPH::EDGES::Edge>> connections;
Distribution distributionMode = Distribution::GEOMETRIC; Distribution distributionMode = Distribution::GEOMETRIC;
float radius = 15.0f; float radius = 15.0f;
bool config = false; bool config = false;
virtual NodeType getNodeType() {return nodeType;} virtual NodeType getNodeType() const {return nodeType;}
virtual void SMode(int mode) {}; virtual void SMode(int mode) {};
virtual int GGate() {return 0;}; virtual int GGate() {return 0;};
virtual int GActive() {return 0;}; virtual int GActive() {return 0;};
virtual void trigger() {}; virtual void trigger() {};
void draw(Vec2Df mouse_pos, Camera2D cam) override {} void draw(Vec2Df mouse_pos, Camera2D cam) override {}
}; };
typedef std::shared_ptr<Node> SPTR_Node; typedef std::shared_ptr<VISEQ::GRAPH::NODES::Node> SPTR_Node;
typedef std::vector<SPTR_Node> VEC_SPTR_Node; typedef std::vector<SPTR_Node> VEC_SPTR_Node;
}
/*inline void to_json(json& j, const std::shared_ptr<Node>& N){*/
/* j = json{{"id", N->id}, {"nodeType",N->nodeType}};*/
/*}*/
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Node, nodeType, neighbor, connections, distributionMode)
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Node, id, nodeType, distributionMode)
+89 -77
View File
@@ -10,107 +10,119 @@
#include <memory> #include <memory>
#include <thread> #include <thread>
class SignalManager{ using namespace VISEQ::GRAPH;
public: namespace VISEQ::SIGNAL {
SignalManager() {}
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
Midi midi; typedef std::shared_ptr<Signal> SPTR_Signal;
typedef std::vector<SPTR_Signal> VEC_SPTR_Signal;
std::mutex *mutex; class SignalManager{
GraphManager *gm;
VEC_SPTR_Signal signals; public:
SignalManager() {}
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
bool loop = true; VISEQ::MIDI::Midi midi;
bool play = false;
void pause(){ std::mutex *mutex;
play = false; GraphManager *gm;
}
void stop(){ VEC_SPTR_Signal signals;
play = false;
reset();
}
void start(){ bool loop = true;
play = true; bool play = false;
}
void reset(){ void pause(){
for(auto &s : signals){ play = false;
s->position = s->start->position + Vec2Df{0.1, 0.1};
s->next = s->start;
s->last = s->start;
} }
gm->reset();
}
void startSignalThread(){ void stop(){
std::thread t1(&SignalManager::updateSignals, this); play = false;
t1.detach(); reset();
} }
void stopSignalThread(){ void start(){
loop = false; play = true;
} }
void addSignalAtNode(SPTR_Node node){ void reset(){
Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200}; for(auto &s : signals){
signals.emplace_back(std::make_shared<Signal>(node->position + Vec2Df{0.5f, 0.5f}, 8.0f, 0, c)); // position + 1 or else it does not render. why? no idea! s->position = s->start->position + Vec2Df{0.1, 0.1};
signals.back()->next = node; s->next = s->start;
signals.back()->last = node; s->last = s->start;
signals.back()->start = node; }
} gm->reset();
}
void removeSignal(int n){ void startSignalThread(){
signals.erase(signals.begin() + n); std::thread t1(&SignalManager::updateSignals, this);
} t1.detach();
}
void updateSignals(){ void stopSignalThread(){
auto last = std::chrono::high_resolution_clock::now(); loop = false;
while(loop){ }
std::chrono::duration<double, std::micro> diff = std::chrono::high_resolution_clock::now() - last;
if((int)diff.count() >= 250){
mutex->lock();
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) < 0.6f) {
SPTR_Node next_node = gm->next(s->next, s->last);
s->next->trigger();
if(next_node != s->next && next_node != SPTR_Node(nullptr) && s->next->getNodeType() == NodeType::MIDINODE) { void addSignalAtNode(SPTR_Node node){
auto midinode = std::dynamic_pointer_cast<MidiNode>(s->next); Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200};
for(auto &m : midinode->midi_outs){ signals.emplace_back(std::make_shared<Signal>(node->position + Vec2Df{0.5f, 0.5f}, 8.0f, 0, c)); // position + 1 or else it does not render. why? no idea!
if(m.second.enabled) { signals.back()->next = node;
if(midinode->send_note) midi.note(m.second.i, midinode->midi_note_channel, midinode->midi_note, midinode->midi_velocity, midinode->midi_gate); signals.back()->last = node;
if(midinode->send_cc) midi.cc(m.second.i, midinode->midi_cc_channel, midinode->midi_cc, midinode->midi_cc_value); signals.back()->start = node;
if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->midi_pitch_bend_channel, midinode->midi_pitch_bend); }
void removeSignal(int n){
signals.erase(signals.begin() + n);
}
void updateSignals(){
auto last = std::chrono::high_resolution_clock::now();
while(loop){
std::chrono::duration<double, std::micro> diff = std::chrono::high_resolution_clock::now() - last;
if((int)diff.count() >= 250){
mutex->lock();
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) < 0.6f) {
SPTR_Node next_node = gm->next(s->next, s->last);
s->next->trigger();
if(next_node != s->next && next_node != SPTR_Node(nullptr) && s->next->getNodeType() == NodeType::MIDINODE) {
auto midinode = std::dynamic_pointer_cast<MidiNode>(s->next);
for(auto &m : midinode->midi_outs){
if(m.second.enabled) {
if(midinode->send_note) midi.note(m.second.i, midinode->midi_note_channel, midinode->midi_note, midinode->midi_velocity, midinode->midi_gate);
if(midinode->send_cc) midi.cc(m.second.i, midinode->midi_cc_channel, midinode->midi_cc, midinode->midi_cc_value);
if(midinode->send_pitch_bend) midi.pitch_bend(m.second.i, midinode->midi_pitch_bend_channel, midinode->midi_pitch_bend);
}
} }
s->last = s->next;
s->next = next_node;
} }
s->last = s->next;
s->next = next_node;
} }
} }
} }
midi.update();
mutex->unlock();
last = std::chrono::high_resolution_clock::now();
} }
midi.update();
mutex->unlock();
last = std::chrono::high_resolution_clock::now();
} }
} }
}
void drawSignals(Vec2Df mouse_pos, Camera2D camera){ void drawSignals(Vec2Df mouse_pos, Camera2D camera){
for(auto s : signals){ for(auto s : signals){
mutex->lock(); mutex->lock();
s->draw(mouse_pos, camera); s->draw(mouse_pos, camera);
mutex->unlock(); mutex->unlock();
}
} }
}
}; };
inline void to_json(json& j, const SignalManager& G){
j = json{{"signals", G.signals}};
}
}
+27 -23
View File
@@ -1,40 +1,44 @@
#pragma once #pragma once
#include <raylib.h> #include <raylib.h>
#include "datatypes.h"
#include "moveable.h" #include "moveable.h"
#include "node.h" #include "node.h"
#include "edge.h" #include "edge.h"
class Signal : public Movable{ using namespace VISEQ::GRAPH;
public:
Signal(); namespace VISEQ::SIGNAL {
Signal(Vec2Df position, float r, float _speed, Color color) : Movable(position, Vec2Df(0,0), _speed), radius(r), color(color) {} class Signal : public BASE::Movable{
public:
SPTR_Node next; Signal();
SPTR_Node last; Signal(Vec2Df position, float r, float _speed, Color color) : Movable(position, Vec2Df(0,0), _speed), radius(r), color(color) {}
SPTR_Edge edge;
SPTR_Node start; NODES::SPTR_Node next;
NODES::SPTR_Node last;
EDGES::SPTR_Edge edge;
bool at_target = false; NODES::SPTR_Node start;
float radius; bool at_target = false;
float multiplier = 1.0f; float radius;
Color color; float multiplier = 1.0f;
void update(double t_delta){ Color color;
// 120px / 16th = 1920px / whole note
// whole note = 2s at 120bpm
// 960px / s
position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
}
void draw(Vec2Df mouse, Camera2D cam) { void update(double t_delta){
DrawCircle(position.x, position.y, radius, color); // 120px / 16th = 1920px / whole note
} // whole note = 2s at 120bpm
}; // 960px / s
position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
}
void draw(Vec2Df mouse, Camera2D cam) {
DrawCircle(position.x, position.y, radius, color);
}
};
}
+151 -75
View File
@@ -4,94 +4,170 @@
#include "graph-item.h" #include "graph-item.h"
#include "node.h" #include "node.h"
#include "edge.h" #include "edge.h"
#include "graph-manager.h" //#include "graph-manager.h"
#include "datatypes.h" #include "datatypes.h"
#include "signal.h"
#include <iostream>
#include <memory> #include <memory>
/*namespace nlohmann { */ /*using namespace VISEQ::BASE;*/
/* template <typename T>*/ /*using namespace VISEQ::SIGNAL;*/
/* struct adl_serializer<std::shared_ptr<T>> {*/ /*using namespace VISEQ::GRAPH::NODES;*/
/* static void to_json(json& j, const std::shared_ptr<T>& opt){*/ /*using namespace VISEQ::GRAPH::EDGES;*/
/* if (opt)*/
/* {*/
/* j = *opt;*/
/* }*/
/* else*/
/* {*/
/* j = nullptr;*/
/* }*/
/* }*/
/**/
/* static void from_json(const json& j, std::shared_ptr<T>& opt){*/
/* if (j.is_null())*/
/* {*/
/* opt = nullptr;*/
/* }*/
/* else*/
/* {*/
/* opt.reset(new T(j.get<T>()));*/
/* //opt = j.get<T>();*/
/* }*/
/* }*/
/* };*/
/*}*/
typedef std::shared_ptr<VISEQ::GRAPH::GraphItem> SPTR_GraphItem;
typedef std::vector<SPTR_GraphItem> VEC_SPTR_GraphItem;
namespace VISEQ::GRAPH::EDGES {
using namespace VISEQ::GRAPH::NODES;
inline void to_json(json& j, const Edge& E){ inline void to_json(json& j, const VISEQ::GRAPH::EDGES::Edge& E){
j = json{{"id",E.id}, {"start", E.start->id}, {"end",E.end->id}, {"pass_forward",E.pass_forward}, {"pass_backward", E.pass_backward}}; j = json{{"id",E.id}, {"start", E.start->id}, {"end",E.end->id}, {"pass_forward",E.pass_forward}, {"pass_backward", E.pass_backward}};
} }
inline void to_json(json& j, const SPTR_Edge& E){ inline void from_json(const json& j, Edge& E){
j = *E; j.at("id").get_to(E.id);
} j.at("pass_forward").get_to(E.pass_forward);
j.at("pass_backward").get_to(E.pass_backward);
}
inline void to_json(json& j, const MidiNode& N){ inline void to_json(json& j, const VISEQ::GRAPH::EDGES::SPTR_Edge& E){
std::vector<int> conns; j = *E;
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](SPTR_Edge e){ return e->id;}); }
j = json{{"id", N.id}, {"position", N.position}, {"distributionMode", N.distributionMode},
{"midi_note_channel",N.midi_note_channel},
{"midi_velocity",N.midi_velocity},
{"midi_gatel",N.midi_gate},
{"midi_cc_channel",N.midi_cc_channel},
{"midi_cc",N.midi_cc},
{"midi_cc_value",N.midi_cc_value},
{"midi_pc_channel",N.midi_pc_channel},
{"midi_pc_value",N.midi_pc_value},
{"midi_pitch_bend_channel",N.midi_pitch_bend_channel},
{"midi_pitch_bend",N.midi_pitch_bend},
{"connections",conns}};
}
inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){ inline void from_json(const json& j, VISEQ::GRAPH::EDGES::SPTR_Edge& E){
j = *N; E.reset(new Edge(j.get<Edge>()));
}
inline void to_json(json& j, const GraphItem& G){
j = json{{"id", G.id}, {"type", G.graphItemType}};
}
inline void to_json(json& j, const SPTR_GraphItem& G){
j = *G;
}
inline void to_json(json& j, const MidiOutPort MO){
j = json{{"port_id", MO.number}, {"enabled", MO.enabled}};
}
inline void to_json(json& j, const NodeMidiOut MO){
j = json{{"port_id", MO.i}, {"enabled", MO.enabled}, {"name", MO.name}};
}
inline void to_json(json& j, const Node& N){
if(N.nodeType == NodeType::MIDINODE){
j = dynamic_cast<const MidiNode&>(N);
} }
} }
inline void to_json(json& j, const std::shared_ptr<Node>& N){ namespace VISEQ::GRAPH::NODES {
j = *N;
/* ------------------- MIDINODE ---------------------- */
inline void to_json(json& j, const MidiNode& N){
std::vector<int> conns;
std::ranges::transform(N.connections.begin(), N.connections.end(), std::back_inserter(conns), [](VISEQ::GRAPH::EDGES::SPTR_Edge e){ return e->id;});
j = json{
{"id", N.id},
{"position", N.position},
{"distributionMode", N.distributionMode},
{"nodeType", N.nodeType},
{"midi_note_channel",N.midi_note_channel},
{"midi_velocity",N.midi_velocity},
{"midi_gate",N.midi_gate},
{"midi_cc_channel",N.midi_cc_channel},
{"midi_cc",N.midi_cc},
{"midi_cc_value",N.midi_cc_value},
{"midi_pc_channel",N.midi_pc_channel},
{"midi_pc_value",N.midi_pc_value},
{"midi_pitch_bend_channel",N.midi_pitch_bend_channel},
{"midi_pitch_bend",N.midi_pitch_bend},
{"connections",conns}};
}
inline void from_json(const json& j, MidiNode& N){
j.at("id").get_to(N.id);
j.at("position").get_to(N.position);
j.at("distributionMode").get_to(N.distributionMode);
j.at("nodeType").get_to(N.nodeType);
j.at("midi_note_channel").get_to(N.midi_note_channel);
j.at("midi_velocity").get_to(N.midi_velocity);
j.at("midi_gate").get_to(N.midi_gate);
j.at("midi_cc_channel").get_to(N.midi_cc_channel);
j.at("midi_cc").get_to(N.midi_cc);
j.at("midi_cc_value").get_to(N.midi_cc_value);
j.at("midi_pc_channel").get_to(N.midi_pc_channel);
j.at("midi_pc_value").get_to(N.midi_pc_value);
j.at("midi_pitch_bend_channel").get_to(N.midi_pitch_bend_channel);
j.at("midi_pitch_bend").get_to(N.midi_pitch_bend);
//j.at("connections").get_to(N.connections);
}
inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){
j = *N;
}
inline void from_json(const json& j, std::shared_ptr<MidiNode>& N){
N.reset(new MidiNode(j.get<MidiNode>()));
}
/* ------------------- NODE ---------------------- */
inline void to_json(json& j, const Node& N){
if(N.getNodeType() == NodeType::MIDINODE){
j = dynamic_cast<const MidiNode&>(N);
}
}
inline void from_json(const json& j, Node& N){
if(j.at("nodeType") == NodeType::MIDINODE){
std::cout << j.at("nodeType") << std::endl;
N = j.template get<MidiNode>();
}
/*j.at("id").get_to(N.id);*/
/*j.at("position").get_to(N.position);*/
/*j.at("distributionMode").get_to(N.distributionMode);*/
/*j.at("nodeType").get_to(N.nodeType);*/
//j.at("connections").get_to(N.connections);
}
inline void to_json(json& j, const SPTR_Node& N){
j = *N;
}
inline void to_json(json& j, const MidiNode::NodeMidiOut MO){
j = json{{"port_id", MO.i}, {"enabled", MO.enabled}, {"name", MO.name}};
}
inline void from_json(const json& j, std::shared_ptr<Node>& N){
N.reset(new Node(j.get<Node>()));
}
} }
namespace VISEQ::GRAPH {
inline void to_json(json& j, const GraphItem& G){
j = json{{"id", G.id}, {"type", G.graphItemType}};
}
inline void to_json(json& j, const SPTR_GraphItem& G){
j = *G;
}
}
namespace VISEQ::BASE{
inline void to_json(json& j, const Movable& G){
j = json{{"position", G.position}};
}
inline void to_json(json& j, const std::shared_ptr<Movable>& G){
j = *G;
}
}
namespace VISEQ::SIGNAL{
inline void to_json(json& j, const Signal& G){
j = json{{"position", G.position},{"start",G.start->id},{"speed",G.speed}};
}
inline void to_json(json& j, const std::shared_ptr<Signal>& G){
j = *G;
}
}
namespace VISEQ::MIDI {
inline void to_json(json& j, const MidiOutPort MO){
j = json{{"port_id", MO.number}, {"enabled", MO.enabled}};
}
}
+107 -97
View File
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include "edge.h"
#include "datatypes.h" #include "datatypes.h"
#include "graph-manager.h" #include "graph-manager.h"
#include "midi-node.h" #include "midi-node.h"
@@ -13,134 +12,145 @@
#include <ranges> #include <ranges>
#include <vector> #include <vector>
class AppState{ namespace VISEQ::APP {
public:
AppState() : sm(&gm, &mutex), ui(&settings, &sm, &gm) {} class AppState{
public:
enum States {IDLE, CONFIG}; AppState() : sm(&gm, &mutex), ui(&settings, &sm, &gm) {}
AppSettings settings; enum States {IDLE, CONFIG};
States state; AppSettings settings;
std::mutex mutex; States state;
GraphManager gm; std::mutex mutex;
SignalManager sm;
AppUI ui; VISEQ::GRAPH::GraphManager gm;
VISEQ::SIGNAL::SignalManager sm;
void updateMidiPorts(){ UI::AppUI ui;
for(auto&n : gm.nodes | RANGE_FILTER([](SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;})){
auto nc = std::dynamic_pointer_cast<MidiNode>(n); void updateMidiPorts(){
for(auto& mo : sm.midi.midi_out_ports){ for(auto&n : gm.nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;})){
if(!nc->midi_outs.contains(mo.number) && mo.enabled){ auto nc = std::dynamic_pointer_cast<MidiNode>(n);
nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false}; for(auto& mo : sm.midi.midi_out_ports){
} if(!nc->midi_outs.contains(mo.number) && mo.enabled){
if(nc->midi_outs.contains(mo.number) && !mo.enabled){ nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false};
nc->midi_outs.erase(mo.number); }
if(nc->midi_outs.contains(mo.number) && !mo.enabled){
nc->midi_outs.erase(mo.number);
}
} }
} }
} }
}
void copyNodesAndConnections(VEC_SPTR_Node nodes, Vec2Df position){ void copyNodesAndConnections(VEC_SPTR_Node nodes, Vec2Df position){
VEC_SPTR_Edge copy_connections; VEC_SPTR_Edge copy_connections;
for(auto n : nodes){ for(auto n : nodes){
for(auto c : n->connections){ for(auto c : n->connections){
if(std::ranges::contains(nodes, c->start) && std::ranges::contains(nodes, c->end)) { if(std::ranges::contains(nodes, c->start) && std::ranges::contains(nodes, c->end)) {
copy_connections.push_back(std::make_shared<Edge>(*c)); copy_connections.push_back(std::make_shared<Edge>(*c));
}
} }
} }
} std::cout << copy_connections.size() << std::endl;
std::cout << copy_connections.size() << std::endl;
for(auto old_node : nodes){ for(auto old_node : nodes){
SPTR_Node new_node; SPTR_Node new_node;
if(old_node->getNodeType() == NodeType::MIDINODE) { if(old_node->getNodeType() == NodeType::MIDINODE) {
new_node = gm.copyNode(std::dynamic_pointer_cast<MidiNode>(old_node), position + old_node->drag_offset); new_node = gm.copyNode(std::dynamic_pointer_cast<MidiNode>(old_node), position + old_node->drag_offset);
} else { } else {
new_node = gm.copyNode(old_node, position + old_node->drag_offset); new_node = gm.copyNode(old_node, position + old_node->drag_offset);
}
new_node->copied = false;
new_node->selected = false;
for(auto c : copy_connections){
if(c->start == old_node) {
c->start = new_node;
c->start->connections.push_back(c);
} }
if(c->end == old_node) {
c->end = new_node; new_node->copied = false;
c->end->connections.push_back(c); new_node->selected = false;
for(auto c : copy_connections){
if(c->start == old_node) {
c->start = new_node;
c->start->connections.push_back(c);
}
if(c->end == old_node) {
c->end = new_node;
c->end->connections.push_back(c);
}
} }
} }
} std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(gm.edges));
std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(gm.edges));
}
void removeNodes(VEC_SPTR_Node nodes){
for(auto n : gm.nodes){
n->highlighted = false;
} }
auto erase_connections = gm.edges | RANGE_FILTER([nodes](SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);}); void removeNodes(VEC_SPTR_Node nodes){
VEC_SPTR_Node adjacent_nodes; for(auto n : gm.nodes){
n->highlighted = false;
}
for(auto c : erase_connections){ auto erase_connections = gm.edges | RANGE_FILTER([nodes](SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);});
if(!c->start->selected) adjacent_nodes.push_back(c->start);
if(!c->end->selected) adjacent_nodes.push_back(c->end);
}
auto reroute_signals = sm.signals | RANGE_FILTER([nodes](SPTR_Signal s){return std::ranges::contains(nodes, s->next);}); VEC_SPTR_Node adjacent_nodes;
for(auto c : erase_connections){
if(!c->start->selected) adjacent_nodes.push_back(c->start);
if(!c->end->selected) adjacent_nodes.push_back(c->end);
}
auto reroute_signals = sm.signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->next);});
for(auto s : reroute_signals){
float dist = 10000000;
SPTR_Node next_node;
for(auto n : adjacent_nodes){
if(n->position.dist(s->position) < dist) {
dist = n->position.dist(s->position);
next_node = n;
}
}
s->next = next_node;
s->last = next_node;
}
auto rebase_signals = sm.signals | RANGE_FILTER([nodes](SIGNAL::SPTR_Signal s){return std::ranges::contains(nodes, s->start);});
for(auto s : rebase_signals){
s->start = s->next;
}
for(auto s : reroute_signals){
float dist = 10000000;
SPTR_Node next_node;
for(auto n : adjacent_nodes){ for(auto n : adjacent_nodes){
if(n->position.dist(s->position) < dist) { std::erase_if(n->connections, [n, nodes](VISEQ::GRAPH::EDGES::SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);});
dist = n->position.dist(s->position);
next_node = n;
}
} }
s->next = next_node;
s->last = next_node; for(auto n : nodes){
std::erase_if(gm.edges, [n](SPTR_Edge c){ return c->start == n || c->end == n;});
}
std::erase_if(gm.nodes, [nodes](SPTR_Node n){ return std::ranges::contains(nodes, n);});
} }
auto rebase_signals = sm.signals | RANGE_FILTER([nodes](SPTR_Signal s){return std::ranges::contains(nodes, s->start);}); void draw(Vec2Df mouse_pos, Camera2D camera){
mutex.lock();
for(auto s : rebase_signals){ updateMidiPorts();
s->start = s->next; mutex.unlock();
gm.drawNodes(mouse_pos, camera);
gm.drawEdges(mouse_pos, camera);
sm.drawSignals(mouse_pos, camera);
} }
for(auto n : adjacent_nodes){ void drawUI(Vec2Df *mouse_pos, Camera2D *camera){
std::erase_if(n->connections, [n, nodes](SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);}); mutex.lock();
ui.draw(mouse_pos, camera);
mutex.unlock();
} }
};
for(auto n : nodes){ inline void to_json(json& j, const AppState& A){
std::erase_if(gm.edges, [n](SPTR_Edge c){ return c->start == n || c->end == n;}); j = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
} }
std::erase_if(gm.nodes, [nodes](SPTR_Node n){ return std::ranges::contains(nodes, n);});
}
void draw(Vec2Df mouse_pos, Camera2D camera){ inline void from_json(const json& j, AppState& A){
mutex.lock(); j.at("graphmanager").get_to(A.gm); // = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
updateMidiPorts(); }
mutex.unlock(); }
gm.drawNodes(mouse_pos, camera);
gm.drawEdges(mouse_pos, camera);
sm.drawSignals(mouse_pos, camera);
}
void drawUI(Vec2Df *mouse_pos, Camera2D *camera){
mutex.lock();
ui.draw(mouse_pos, camera);
mutex.unlock();
}
};
+164 -162
View File
@@ -12,176 +12,178 @@
#include <memory> #include <memory>
#include <ranges> #include <ranges>
class AppUI{ namespace VISEQ::APP::UI{
public: class AppUI{
public:
AppUI() {}
AppUI(AppSettings *settings, SIGNAL::SignalManager *sm, GraphManager *gm) : settings(settings), gm(gm), sm(sm) {}
AppUI(AppSettings *settings, SignalManager *sm, GraphManager *gm) : settings(settings), gm(gm), sm(sm) {} AppSettings *settings;
GraphManager *gm;
SIGNAL::SignalManager *sm;
AppSettings *settings; void init(){
GraphManager *gm; GuiLoadStyleDark();
SignalManager *sm;
void init(){
GuiLoadStyleDark();
}
void drawSideBar(){
GuiPanel((Rectangle){-1,-30,280,(float)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();
if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop();
if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
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());
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 + 75;
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate();
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.substr(0,32).c_str(), &mo.enabled);
i++;
} }
if(GuiButton((Rectangle){20, (float)s_midi + 45 + i*20.0f, 240, 20}, "ENABLE FOR ALL")) { void drawSideBar(){
for(auto &midis : sm->midi.midi_out_ports){
if(midis.enabled){ GuiPanel((Rectangle){-1,-30,280,(float)settings->height + 31}, "");
for(auto n : gm->nodes){ const int s_controls = 25;
if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast<MidiNode>(n)->setMidiPort(midis.number, true); //GuiSetStyle(BUTTON, STATE_FOCUSED, 0xFF0000FF);
if(GuiButton((Rectangle){20, (float)s_controls, 80, 30}, "PLAY")) sm->play ? sm->pause() : sm->start();
if(GuiButton((Rectangle){120, (float)s_controls, 60, 30}, "STOP")) sm->stop();
if(GuiButton((Rectangle){200, (float)s_controls, 60, 30}, "RESET")) sm->reset();
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());
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 + 75;
GuiLabel((Rectangle){20, (float)s_midi, 240, 20}, "MIDI OUT");
if(GuiButton((Rectangle){200, (float)s_midi, 60, 20}, "RELOAD")) sm->midi.enumerate();
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.substr(0,32).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 : gm->nodes){
if(n->getNodeType() == NodeType::MIDINODE) std::dynamic_pointer_cast<MidiNode>(n)->setMidiPort(midis.number, true);
}
}
}
}
int s_signal = s_midi + 100 + i*20;
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS");
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){
std::shared_ptr<Node> ni = gm->getRandomNode();
sm->addSignalAtNode(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, 10);
s->multiplier = s_multiplier;
s->speed = settings->speed;
signal_configs++;
}
int s_nodes = s_signal + 70 + 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")){
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);
}
void drawPerNodeConfig(Camera2D *camera){
for(auto& n : gm->nodes | std::views::filter([](std::shared_ptr<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->distributionMode;
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 73, 20 }, "GEO;RR;RAND", &option);
n->distributionMode = (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;
if(n->getNodeType() == NodeType::MIDINODE){
std::shared_ptr<MidiNode> mn = std::dynamic_pointer_cast<MidiNode>(n);
for(auto& mo : mn->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;
GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &mn->send_note);
GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &mn->send_cc);
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){
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);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE");
float v = mn->midi_note;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, MIDI::Notes[(int)round(v)].c_str());
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_note).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
mn->midi_note = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY");
v = mn->midi_velocity;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_velocity).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
mn->midi_velocity = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE");
v = mn->midi_gate;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)mn->midi_gate/1000.0f).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000);
mn->midi_gate = round(v);
break;}
case 1:
{
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC 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_cc_channel);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
float v = mn->midi_cc;
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_cc).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
mn->midi_cc = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE");
v = mn->midi_cc_value;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_cc_value).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
mn->midi_cc_value = round(v);
break;}
case 2:
{
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND 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_pitch_bend_channel);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
float v = mn->midi_pitch_bend;
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_pitch_bend).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383);
mn->midi_pitch_bend = round(v);
break;}
} }
} }
} }
} }
int s_signal = s_midi + 100 + i*20; void draw(Vec2Df *mouse_pos, Camera2D *camera){
GuiLabel((Rectangle){20, (float)s_signal, 240, 20}, "SIGNALS"); drawSideBar();
if(GuiButton((Rectangle){20, (float)s_signal + 35, 240, 20}, "ADD RANDOM SIGNAL")){ drawPerNodeConfig(camera);
std::shared_ptr<Node> ni = gm->getRandomNode();
sm->addSignalAtNode(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, 10);
s->multiplier = s_multiplier;
s->speed = settings->speed;
signal_configs++;
}
int s_nodes = s_signal + 70 + 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")){
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);
}
void drawPerNodeConfig(Camera2D *camera){
for(auto& n : gm->nodes | std::views::filter([](std::shared_ptr<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->distributionMode;
GuiToggleGroup((Rectangle){ np.x + 20, np.y + 40, 73, 20 }, "GEO;RR;RAND", &option);
n->distributionMode = (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;
if(n->getNodeType() == NodeType::MIDINODE){
std::shared_ptr<MidiNode> mn = std::dynamic_pointer_cast<MidiNode>(n);
for(auto& mo : mn->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;
GuiCheckBox((Rectangle){np.x + 20, np.y + y_channel, 20, 20}, "NOTE", &mn->send_note);
GuiCheckBox((Rectangle){np.x + 90, np.y + y_channel, 20, 20}, "CC", &mn->send_cc);
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){
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);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "NOTE");
float v = mn->midi_note;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note , 20, 20}, sm->midi.midiNotes[(int)round(v)].c_str());
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_note).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
mn->midi_note = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "VELOCITY");
v = mn->midi_velocity;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_velocity).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
mn->midi_velocity = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 120, 100, 20}, "GATE");
v = mn->midi_gate;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 120, 20, 20}, std::format("{}",(float)mn->midi_gate/1000.0f).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 150, 220, 20}, "", "", &v, 0, 30000);
mn->midi_gate = round(v);
break;}
case 1:
{
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "CC 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_cc_channel);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
float v = mn->midi_cc;
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_cc).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 127);
mn->midi_cc = round(v);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note + 60, 100, 20}, "CC VALUE");
v = mn->midi_cc_value;
GuiLabel((Rectangle){np.x + 210, np.y + y_cc_note + 60, 20, 20}, std::format("{}",mn->midi_cc_value).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 90, 220, 20}, "", "", &v, 0, 127);
mn->midi_cc_value = round(v);
break;}
case 2:
{
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note - 75, 220, 20}, "PITCH BEND 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_pitch_bend_channel);
GuiLabel((Rectangle){np.x + 20, np.y + y_cc_note , 100, 20}, "CC");
float v = mn->midi_pitch_bend;
GuiLabel((Rectangle){np.x + 160, np.y + y_cc_note , 40, 20}, std::format("{}",mn->midi_pitch_bend).c_str());
GuiSlider((Rectangle){np.x + 20, np.y + y_cc_note + 30, 220, 20}, "", "", &v, 0, 16383);
mn->midi_pitch_bend = round(v);
break;}
}
}
}
}
void draw(Vec2Df *mouse_pos, Camera2D *camera){
drawSideBar();
drawPerNodeConfig(camera);
}
};
+6 -2
View File
@@ -7,7 +7,7 @@
using json = nlohmann::json; using json = nlohmann::json;
namespace VMath{ namespace VISEQ::VMATH{
template<typename T> template<typename T>
class Vec2D{ class Vec2D{
@@ -158,4 +158,8 @@ namespace VMath{
} }
} }
using Vec2Df = VMath::Vec2D<float>; using Vec2Df = VISEQ::VMATH::Vec2D<float>;