json is loading, but does nothing yet
This commit is contained in:
@@ -3,3 +3,4 @@ build/**
|
||||
viseq
|
||||
viseq.exe
|
||||
compile_commands.json
|
||||
state.json
|
||||
|
||||
+33
-49
@@ -27,64 +27,48 @@ using json = nlohmann::json;
|
||||
#include <math.h>
|
||||
#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)
|
||||
|
||||
/*typedef std::shared_ptr<GraphItem> SPTR_GraphItem;*/
|
||||
/*typedef std::shared_ptr<Node> SPTR_Node;*/
|
||||
/*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;*/
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
enum Distribution {GEOMETRIC, ROUNDROBIN, RANDOM};
|
||||
}
|
||||
|
||||
// App Settings
|
||||
struct AppSettings{
|
||||
int mode = 0;
|
||||
int width;
|
||||
int height;
|
||||
float grid;
|
||||
float bpm;
|
||||
float fps;
|
||||
float speed = 16.0f;
|
||||
int gui_selected_midi_out;
|
||||
};
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
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};
|
||||
enum NodeType {OSCNODE, MIDINODE};
|
||||
struct MidiOut{
|
||||
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> Notes = {
|
||||
"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",
|
||||
|
||||
+26
-24
@@ -1,38 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "graph-item.h"
|
||||
#include <memory>
|
||||
#include "node.h"
|
||||
|
||||
class Edge : public GraphItem{
|
||||
using namespace VISEQ::GRAPH;
|
||||
|
||||
public:
|
||||
Edge() {}
|
||||
Edge(SPTR_Node start, SPTR_Node end) : start(start), end(end) {}
|
||||
namespace VISEQ::GRAPH::EDGES {
|
||||
class Edge : public GraphItem{
|
||||
|
||||
SPTR_Node start;
|
||||
SPTR_Node end;
|
||||
public:
|
||||
Edge() {}
|
||||
Edge(std::shared_ptr<NODES::Node> start, std::shared_ptr<NODES::Node> end) : start(start), end(end) {}
|
||||
|
||||
bool pass_forward = true;
|
||||
bool pass_backward = true;
|
||||
std::shared_ptr<NODES::Node> start;
|
||||
std::shared_ptr<NODES::Node> end;
|
||||
|
||||
bool active = false;
|
||||
bool config = false;
|
||||
bool split = false;
|
||||
bool pass_forward = true;
|
||||
bool pass_backward = true;
|
||||
|
||||
float connector_offset = 8.0f;
|
||||
float switches_offset = 25.0f;
|
||||
bool active = false;
|
||||
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){
|
||||
return start == origin ? end : start;
|
||||
}
|
||||
virtual bool GActive() {return active;}
|
||||
|
||||
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::vector<SPTR_Edge> VEC_SPTR_Edge;*/
|
||||
|
||||
//NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Edge, id, start, end, pass_forward, pass_backward)
|
||||
typedef std::shared_ptr<Edge> SPTR_Edge;
|
||||
typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;
|
||||
}
|
||||
|
||||
+12
-18
@@ -5,26 +5,20 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class GraphItem{
|
||||
namespace VISEQ::GRAPH {
|
||||
class GraphItem{
|
||||
|
||||
public:
|
||||
GraphItem() {static int _id; id=_id++;}
|
||||
std::vector<std::shared_ptr<GraphItem>> neighbors;
|
||||
int id = 0;
|
||||
GraphItemType graphItemType;
|
||||
public:
|
||||
GraphItem() {static int _id; id=_id++;}
|
||||
std::vector<std::shared_ptr<GraphItem>> neighbors;
|
||||
int id = 0;
|
||||
GraphItemType graphItemType;
|
||||
|
||||
virtual void draw(Vec2Df mouse, Camera2D cam) {};
|
||||
virtual GraphItemType getType() {return graphItemType;}
|
||||
virtual ~GraphItem() = default;
|
||||
virtual void draw(Vec2Df mouse, Camera2D cam) {};
|
||||
virtual GraphItemType getType() {return graphItemType;}
|
||||
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
@@ -8,9 +8,9 @@
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "button.h"
|
||||
#include "node.h"
|
||||
#include "midi-node.h"
|
||||
#include "edge.h"
|
||||
#include "node.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
@@ -26,271 +26,264 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
typedef std::shared_ptr<GraphItem> SPTR_GraphItem;
|
||||
typedef std::shared_ptr<Node> SPTR_Node;
|
||||
typedef std::shared_ptr<Edge> SPTR_Edge;
|
||||
using namespace VISEQ::GRAPH::NODES;
|
||||
using namespace VISEQ::GRAPH::EDGES;
|
||||
|
||||
typedef std::shared_ptr<Signal> SPTR_Signal;
|
||||
namespace VISEQ::GRAPH {
|
||||
|
||||
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;
|
||||
class GraphManager{
|
||||
|
||||
class GraphManager{
|
||||
public:
|
||||
|
||||
public:
|
||||
GraphManager() {}
|
||||
|
||||
GraphManager() {}
|
||||
NODES::VEC_SPTR_Node nodes;
|
||||
EDGES::VEC_SPTR_Edge edges;
|
||||
|
||||
VEC_SPTR_Node nodes;
|
||||
VEC_SPTR_Edge edges;
|
||||
EDGES::VEC_SPTR_Edge remove_edges;
|
||||
EDGES::VEC_SPTR_Edge split_edges;
|
||||
|
||||
VEC_SPTR_Edge remove_edges;
|
||||
VEC_SPTR_Edge split_edges;
|
||||
std::map<NODES::SPTR_Node, int> counter;
|
||||
|
||||
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){*/
|
||||
/* j = json{{"nodes", G.nodes}, {"edges", G.edges}};*/
|
||||
/*}*/
|
||||
/**/
|
||||
/*void from_json(const json& j, GraphManager& G){*/
|
||||
/**/
|
||||
/*}*/
|
||||
EDGES::SPTR_Edge getRandomEdge(){
|
||||
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());
|
||||
}
|
||||
|
||||
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<Node>(o.front());
|
||||
}
|
||||
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){
|
||||
for(auto n: _nodes){
|
||||
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
|
||||
nodes.back()->setPosition(position + nodes.back()->drag_offset);
|
||||
nodes.back()->selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
SPTR_Edge getRandomEdge(){
|
||||
VEC_SPTR_GraphItem o;
|
||||
std::ranges::sample(edges, std::back_inserter(o), 1, std::mt19937{std::random_device{}()});
|
||||
return std::dynamic_pointer_cast<Edge>(o.front());
|
||||
}
|
||||
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
|
||||
for(auto n: _nodes){
|
||||
nodes.insert(nodes.end(), std::make_shared<MidiNode>(*n));
|
||||
nodes.back()->setPosition(position + nodes.back()->drag_offset);
|
||||
nodes.back()->selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
void copyNodes(VEC_SPTR_Node _nodes, Vec2Df position){
|
||||
for(auto n: _nodes){
|
||||
SPTR_Node copyNode(SPTR_Node n, Vec2Df position){
|
||||
std::cout << "copy basenode" << std::endl;
|
||||
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
|
||||
nodes.back()->setPosition(position + nodes.back()->drag_offset);
|
||||
nodes.back()->selected = false;
|
||||
nodes.back()->setPosition(position);
|
||||
return nodes.back();
|
||||
}
|
||||
}
|
||||
|
||||
void copyNodes(std::vector<std::shared_ptr<MidiNode>> _nodes, Vec2Df position){
|
||||
for(auto n: _nodes){
|
||||
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 + nodes.back()->drag_offset);
|
||||
nodes.back()->selected = false;
|
||||
nodes.back()->setPosition(position);
|
||||
return nodes.back();
|
||||
}
|
||||
}
|
||||
|
||||
SPTR_Node copyNode(SPTR_Node n, Vec2Df position){
|
||||
std::cout << "copy basenode" << std::endl;
|
||||
nodes.insert(nodes.end(), std::make_shared<Node>(*n));
|
||||
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);
|
||||
SPTR_Node addMidiNode(Vec2Df position){
|
||||
nodes.emplace_back(std::make_shared<MidiNode>(position));
|
||||
return nodes.back();
|
||||
}
|
||||
}
|
||||
|
||||
void reset(){
|
||||
counter.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
void setNodeMode(int mode){
|
||||
for(auto n: nodes){
|
||||
n->SMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
void reset(){
|
||||
counter.clear();
|
||||
}
|
||||
|
||||
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){
|
||||
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){
|
||||
return_path = rand() % possible_connections.size();
|
||||
void splitEdge(SPTR_Edge edge){
|
||||
split_edges.push_back(edge);
|
||||
}
|
||||
|
||||
//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());
|
||||
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 splitEdge(SPTR_Edge edge){
|
||||
split_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);
|
||||
void removeEdge(SPTR_Edge edge){
|
||||
remove_edges.push_back(edge);
|
||||
}
|
||||
split_edges.clear();
|
||||
}
|
||||
|
||||
void removeEdge(SPTR_Edge edge){
|
||||
remove_edges.push_back(edge);
|
||||
}
|
||||
|
||||
void removeEdges(){
|
||||
for(auto edge : remove_edges){
|
||||
std::erase(edge->start->connections, edge);
|
||||
std::erase(edge->end->connections, edge);
|
||||
std::erase(edges, edge);
|
||||
void removeEdges(){
|
||||
for(auto edge : remove_edges){
|
||||
std::erase(edge->start->connections, edge);
|
||||
std::erase(edge->end->connections, edge);
|
||||
std::erase(edges, edge);
|
||||
}
|
||||
remove_edges.clear();
|
||||
}
|
||||
remove_edges.clear();
|
||||
}
|
||||
|
||||
void drawEdges(Vec2Df mouse_pos, Camera2D cam){
|
||||
if(!remove_edges.empty()) removeEdges();
|
||||
if(!split_edges.empty()) splitEdges();
|
||||
for(auto c : edges){
|
||||
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);
|
||||
float v1_v2_distance = v1_position.dist(&v2_position);
|
||||
void drawEdges(Vec2Df mouse_pos, Camera2D cam){
|
||||
if(!remove_edges.empty()) removeEdges();
|
||||
if(!split_edges.empty()) splitEdges();
|
||||
for(auto c : edges){
|
||||
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);
|
||||
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_v2 = Clamp((4.f / 1000.f) * c->end->GGate(), 1.f, 4.f);
|
||||
// Line with small connector circles
|
||||
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);
|
||||
// Line with small connector circles
|
||||
|
||||
Color col = {229, 181, 103, 255};
|
||||
if(c->GActive()) col = {108, 153, 187, 255};
|
||||
DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col);
|
||||
Color col = {229, 181, 103, 255};
|
||||
if(c->GActive()) col = {108, 153, 187, 255};
|
||||
DrawLine(v1_position.x, v1_position.y, v2_position.x, v2_position.y, col);
|
||||
|
||||
if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED;
|
||||
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col);
|
||||
if(midi_gate_v1 == 4.0f) col = VS_COLOR_RED;
|
||||
DrawCircle(v1_position.x, v1_position.y, midi_gate_v1, col);
|
||||
|
||||
col = {229, 181, 103, 255};
|
||||
if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED;
|
||||
DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col);
|
||||
col = {229, 181, 103, 255};
|
||||
if(midi_gate_v2 == 4.0f) col = VS_COLOR_RED;
|
||||
DrawCircle(v2_position.x, v2_position.y, midi_gate_v2, col);
|
||||
|
||||
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 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_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_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);
|
||||
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 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_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_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
|
||||
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_to = 250.0f - Clamp(to_from_handle.dist(mouse_pos), 0.0f, 220.0f);
|
||||
// Dynamic opacity by distance to mouse
|
||||
unsigned char opacity = 150.0f - Clamp(middle_handle.dist(mouse_pos), 0.0f, 150.0f);
|
||||
unsigned char opacity_from = 250.0f - Clamp(from_to_handle.dist(mouse_pos), 0.0f, 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){
|
||||
// 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, 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->start->dragging && !c->end->dragging){
|
||||
// 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, 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){
|
||||
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;
|
||||
DrawLineEx(block_point1, block_point2, 2.0f, (Color){232,232,232,opacity_from});
|
||||
if(!c->pass_forward){
|
||||
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;
|
||||
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){
|
||||
auto neighbors = nodes | RANGE_FILTER([](SPTR_Node n){ return !n->dragging && !n->selected;}) | std::ranges::to<VEC_SPTR_Node>();
|
||||
float min_d = 480;
|
||||
for(auto n : nodes){
|
||||
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);});
|
||||
if(neighbors.size() > 1){
|
||||
min_d = neighbors.front()->position.dist(mouse_pos);
|
||||
n->neighbor = neighbors.front();
|
||||
DrawCircleLinesV(n->neighbor->position, 20.0f, VS_COLOR_PINK);
|
||||
for(auto nb : neighbors){
|
||||
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((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_frac = MeasureTextEx(GetFontDefault(), frac.c_str(), 10.0f, 2.0f);
|
||||
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>();
|
||||
float min_d = 480;
|
||||
for(auto n : nodes){
|
||||
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);});
|
||||
if(neighbors.size() > 1){
|
||||
min_d = neighbors.front()->position.dist(mouse_pos);
|
||||
n->neighbor = neighbors.front();
|
||||
DrawCircleLinesV(n->neighbor->position, 20.0f, VS_COLOR_PINK);
|
||||
for(auto nb : neighbors){
|
||||
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((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_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;
|
||||
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(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
@@ -6,6 +6,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graph-manager.h"
|
||||
#include "vmath.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#define RAYGUI_IMPLEMENTATION
|
||||
#include <raylib.h>
|
||||
#include <raymath.h>
|
||||
@@ -24,9 +28,11 @@
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "state-io.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
AppState app;
|
||||
VISEQ::APP::AppState app;
|
||||
|
||||
Vec2Df mouse_pos;
|
||||
Vec2Df drag_start;
|
||||
@@ -72,19 +78,26 @@ int main(){
|
||||
camera.rotation = 0.0f;
|
||||
camera.zoom = 1.0f;
|
||||
|
||||
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});
|
||||
if(std::filesystem::exists("state.json")){
|
||||
std::ifstream f("state.json");
|
||||
json graph = json::parse(f);
|
||||
|
||||
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();
|
||||
|
||||
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_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;
|
||||
} else {
|
||||
dragNode->position = mouse_pos + dragNode->drag_offset;
|
||||
@@ -330,8 +343,8 @@ int main(){
|
||||
midiNode->midi_pitch_bend += wheel;
|
||||
break;}
|
||||
};
|
||||
if(midiNode->midi_note < 0 ) midiNode->midi_note = app.sm.midi.midiNotes.size()-1;
|
||||
if(midiNode->midi_note >= app.sm.midi.midiNotes.size() ) midiNode->midi_note = 0;
|
||||
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;
|
||||
@@ -390,7 +403,10 @@ int main(){
|
||||
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();
|
||||
|
||||
|
||||
+128
-129
@@ -7,166 +7,165 @@
|
||||
|
||||
#define RADIUS 14.0f
|
||||
|
||||
struct NodeMidiOut{
|
||||
int i;
|
||||
std::string name;
|
||||
bool enabled;
|
||||
};
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
//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(Vec2Df position) : Node(position) {}
|
||||
MidiNode() {}
|
||||
MidiNode(Vec2Df position) : Node(position) {}
|
||||
|
||||
NodeType nodeType = NodeType::MIDINODE;
|
||||
NodeType nodeType = NodeType::MIDINODE;
|
||||
|
||||
bool send_note = true;
|
||||
bool send_cc = false;
|
||||
bool send_pc = false;
|
||||
bool send_pitch_bend = false;
|
||||
bool send_note = true;
|
||||
bool send_cc = false;
|
||||
bool send_pc = 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_note_edit = false;
|
||||
bool gui_midi_velocity_edit = false;
|
||||
bool gui_midi_gate_edit = false;
|
||||
bool gui_midi_channel_edit = false;
|
||||
bool gui_midi_note_edit = false;
|
||||
bool gui_midi_velocity_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 = 48;
|
||||
int midi_velocity = 60;
|
||||
int midi_gate = 25; // gate time in milliseconds
|
||||
int midi_note_channel = 1;
|
||||
int midi_note = 48;
|
||||
int midi_velocity = 60;
|
||||
int midi_gate = 25; // gate time in milliseconds
|
||||
|
||||
int midi_cc_channel = 0;
|
||||
int midi_cc = 0;
|
||||
int midi_cc_value = 0;
|
||||
int midi_cc_channel = 0;
|
||||
int midi_cc = 0;
|
||||
int midi_cc_value = 0;
|
||||
|
||||
int midi_pc_channel = 0;
|
||||
int midi_pc = 0;
|
||||
int midi_pc_value = 0;
|
||||
int midi_pc_channel = 0;
|
||||
int midi_pc = 0;
|
||||
int midi_pc_value = 0;
|
||||
|
||||
int midi_pitch_bend_channel = 0;
|
||||
int midi_pitch_bend = 8192;
|
||||
int midi_pitch_bend_channel = 0;
|
||||
int midi_pitch_bend = 8192;
|
||||
|
||||
float start = 0;
|
||||
float fade = 0;
|
||||
float time = 0;
|
||||
float last = 0;
|
||||
float start = 0;
|
||||
float fade = 0;
|
||||
float time = 0;
|
||||
float last = 0;
|
||||
|
||||
virtual NodeType getNodeType() override {return nodeType;}
|
||||
virtual NodeType getNodeType() const override {return nodeType;}
|
||||
|
||||
void setMidiPort(int midi_port, bool 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;
|
||||
void setMidiPort(int midi_port, bool state){
|
||||
midi_outs[midi_port].enabled = state;
|
||||
}
|
||||
|
||||
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){
|
||||
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;}
|
||||
void trigger() override {
|
||||
triggered = true;
|
||||
start = GetTime();
|
||||
fade = midi_gate;
|
||||
}
|
||||
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);
|
||||
void SMode(int _mode) override{
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
if(selected) DrawCircleLinesV(position, radius+8, (Color){158, 255, 210, 255});
|
||||
int Ggate(){
|
||||
return midi_gate;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
} 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){
|
||||
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 );
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){180, 210, 115, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, 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 );
|
||||
gate_color = ColorAlphaBlend(VS_COLOR_RED, (Color){229, 181, 103, (unsigned char)Clamp(255 - midi_gate_map, 0, 255)}, 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 );
|
||||
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);
|
||||
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
@@ -8,122 +8,105 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <numbers>
|
||||
#include "datatypes.h"
|
||||
|
||||
struct NoteTimer{
|
||||
int midi_out;
|
||||
int channel;
|
||||
int note;
|
||||
int velocity;
|
||||
float start;
|
||||
int gate;
|
||||
bool erase = false;
|
||||
};
|
||||
namespace VISEQ::MIDI {
|
||||
|
||||
class Midi {
|
||||
struct NoteTimer{
|
||||
int midi_out;
|
||||
int channel;
|
||||
int note;
|
||||
int velocity;
|
||||
float start;
|
||||
int gate;
|
||||
bool erase = false;
|
||||
};
|
||||
|
||||
public:
|
||||
Midi() {
|
||||
enumerate();
|
||||
}
|
||||
class Midi {
|
||||
|
||||
std::vector<std::string> midiNotes = {
|
||||
"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",
|
||||
"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++;
|
||||
public:
|
||||
Midi() {
|
||||
enumerate();
|
||||
}
|
||||
}
|
||||
|
||||
void open_midi_out(MidiOutPort *out_port){
|
||||
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;
|
||||
}
|
||||
}
|
||||
libremidi::observer obs;
|
||||
|
||||
void close_midi_out(MidiOutPort *out_port){
|
||||
if(midi_outs.contains(out_port->number)){
|
||||
midi_outs[out_port->number]->close_port();
|
||||
midi_outs.erase(out_port->number);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
void note(int midi_out, int channel, int note, int velocity, int gate){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
|
||||
notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate);
|
||||
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 cc(int midi_out, int channel, int cc, int value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value));
|
||||
void open_midi_out(MidiOutPort *out_port){
|
||||
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 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 close_midi_out(MidiOutPort *out_port){
|
||||
if(midi_outs.contains(out_port->number)){
|
||||
midi_outs[out_port->number]->close_port();
|
||||
midi_outs.erase(out_port->number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
void note(int midi_out, int channel, int note, int velocity, int gate){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::note_on(channel, note, velocity));
|
||||
notes.emplace_back(midi_out, channel, note, velocity, GetTime(), gate);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
void cc(int midi_out, int channel, int cc, int value){
|
||||
if(midi_outs.contains(midi_out)){
|
||||
if(midi_outs[midi_out]->is_port_open()){
|
||||
midi_outs[midi_out]->send_message(libremidi::channel_events::control_change(channel, cc, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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
@@ -2,44 +2,41 @@
|
||||
|
||||
#include "datatypes.h"
|
||||
|
||||
namespace VISEQ::BASE {
|
||||
class Movable{
|
||||
|
||||
class Movable{
|
||||
public:
|
||||
Movable() {}
|
||||
virtual ~Movable() {}
|
||||
|
||||
public:
|
||||
Movable() {}
|
||||
virtual ~Movable() {}
|
||||
Movable(Vec2Df p, Vec2Df d, float s) : speed(s), position(p), direction(d) {}
|
||||
Movable(Vec2Df p, float s) : speed(s), position(p) {}
|
||||
|
||||
Movable(Vec2Df p, Vec2Df d, float s) : speed(s), position(p), direction(d) {}
|
||||
Movable(Vec2Df p, float s) : speed(s), position(p) {}
|
||||
float speed = 1.0f;
|
||||
|
||||
float speed = 1.0f;
|
||||
Vec2Df position;
|
||||
Vec2Df direction;
|
||||
|
||||
Vec2Df position;
|
||||
Vec2Df direction;
|
||||
Vec2Df drag_offset;
|
||||
|
||||
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 dragging = false;
|
||||
bool hover_connect = false;
|
||||
bool hovering = false;
|
||||
bool connecting = false;
|
||||
bool selected = false;
|
||||
bool highlighted = false;
|
||||
bool copied = false;
|
||||
bool erase = 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
@@ -3,51 +3,44 @@
|
||||
#include <memory>
|
||||
#include <raylib.h>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "graph-item.h"
|
||||
#include "moveable.h"
|
||||
|
||||
class Edge;
|
||||
namespace VISEQ::GRAPH::EDGES{
|
||||
class Edge;
|
||||
}
|
||||
|
||||
typedef std::shared_ptr<Edge> SPTR_Edge;
|
||||
typedef std::vector<SPTR_Edge> VEC_SPTR_Edge;
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
class Node : public Movable, public GraphItem{
|
||||
class Node : public BASE::Movable, public GraphItem{
|
||||
|
||||
public:
|
||||
Node() {}
|
||||
Node(float x, float y) : Movable(Vec2Df(x, y), 0.0f) {}
|
||||
Node(Vec2Df p) : Movable(p, 0.0f) {}
|
||||
public:
|
||||
Node() {}
|
||||
Node(float x, float y) : Movable(Vec2Df(x, y), 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;
|
||||
bool config = false;
|
||||
float radius = 15.0f;
|
||||
bool config = false;
|
||||
|
||||
virtual NodeType getNodeType() {return nodeType;}
|
||||
virtual NodeType getNodeType() const {return nodeType;}
|
||||
|
||||
virtual void SMode(int mode) {};
|
||||
virtual int GGate() {return 0;};
|
||||
virtual int GActive() {return 0;};
|
||||
virtual void trigger() {};
|
||||
void draw(Vec2Df mouse_pos, Camera2D cam) override {}
|
||||
};
|
||||
virtual void SMode(int mode) {};
|
||||
virtual int GGate() {return 0;};
|
||||
virtual int GActive() {return 0;};
|
||||
virtual void trigger() {};
|
||||
void draw(Vec2Df mouse_pos, Camera2D cam) override {}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Node> SPTR_Node;
|
||||
typedef std::vector<SPTR_Node> VEC_SPTR_Node;
|
||||
typedef std::shared_ptr<VISEQ::GRAPH::NODES::Node> 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
@@ -10,107 +10,119 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
class SignalManager{
|
||||
using namespace VISEQ::GRAPH;
|
||||
|
||||
public:
|
||||
SignalManager() {}
|
||||
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
|
||||
namespace VISEQ::SIGNAL {
|
||||
|
||||
Midi midi;
|
||||
typedef std::shared_ptr<Signal> SPTR_Signal;
|
||||
typedef std::vector<SPTR_Signal> VEC_SPTR_Signal;
|
||||
|
||||
std::mutex *mutex;
|
||||
GraphManager *gm;
|
||||
class SignalManager{
|
||||
|
||||
VEC_SPTR_Signal signals;
|
||||
public:
|
||||
SignalManager() {}
|
||||
SignalManager(GraphManager *gm, std::mutex *mutex) : mutex(mutex), gm(gm) {}
|
||||
|
||||
bool loop = true;
|
||||
bool play = false;
|
||||
VISEQ::MIDI::Midi midi;
|
||||
|
||||
void pause(){
|
||||
play = false;
|
||||
}
|
||||
std::mutex *mutex;
|
||||
GraphManager *gm;
|
||||
|
||||
void stop(){
|
||||
play = false;
|
||||
reset();
|
||||
}
|
||||
VEC_SPTR_Signal signals;
|
||||
|
||||
void start(){
|
||||
play = true;
|
||||
}
|
||||
bool loop = true;
|
||||
bool play = false;
|
||||
|
||||
void reset(){
|
||||
for(auto &s : signals){
|
||||
s->position = s->start->position + Vec2Df{0.1, 0.1};
|
||||
s->next = s->start;
|
||||
s->last = s->start;
|
||||
void pause(){
|
||||
play = false;
|
||||
}
|
||||
gm->reset();
|
||||
}
|
||||
|
||||
void startSignalThread(){
|
||||
std::thread t1(&SignalManager::updateSignals, this);
|
||||
t1.detach();
|
||||
}
|
||||
void stop(){
|
||||
play = false;
|
||||
reset();
|
||||
}
|
||||
|
||||
void stopSignalThread(){
|
||||
loop = false;
|
||||
}
|
||||
void start(){
|
||||
play = true;
|
||||
}
|
||||
|
||||
void addSignalAtNode(SPTR_Node node){
|
||||
Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200};
|
||||
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!
|
||||
signals.back()->next = node;
|
||||
signals.back()->last = node;
|
||||
signals.back()->start = node;
|
||||
}
|
||||
void reset(){
|
||||
for(auto &s : signals){
|
||||
s->position = s->start->position + Vec2Df{0.1, 0.1};
|
||||
s->next = s->start;
|
||||
s->last = s->start;
|
||||
}
|
||||
gm->reset();
|
||||
}
|
||||
|
||||
void removeSignal(int n){
|
||||
signals.erase(signals.begin() + n);
|
||||
}
|
||||
void startSignalThread(){
|
||||
std::thread t1(&SignalManager::updateSignals, this);
|
||||
t1.detach();
|
||||
}
|
||||
|
||||
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();
|
||||
void stopSignalThread(){
|
||||
loop = false;
|
||||
}
|
||||
|
||||
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);
|
||||
void addSignalAtNode(SPTR_Node node){
|
||||
Color c = {(unsigned char)(20 + (rand() % 200)), 100, (unsigned char)(20 + (int)(rand() % 200)), 200};
|
||||
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!
|
||||
signals.back()->next = node;
|
||||
signals.back()->last = node;
|
||||
signals.back()->start = node;
|
||||
}
|
||||
|
||||
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){
|
||||
for(auto s : signals){
|
||||
mutex->lock();
|
||||
s->draw(mouse_pos, camera);
|
||||
mutex->unlock();
|
||||
void drawSignals(Vec2Df mouse_pos, Camera2D camera){
|
||||
for(auto s : signals){
|
||||
mutex->lock();
|
||||
s->draw(mouse_pos, camera);
|
||||
mutex->unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
inline void to_json(json& j, const SignalManager& G){
|
||||
j = json{{"signals", G.signals}};
|
||||
}
|
||||
}
|
||||
|
||||
+27
-23
@@ -1,40 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <raylib.h>
|
||||
#include "datatypes.h"
|
||||
#include "moveable.h"
|
||||
|
||||
#include "node.h"
|
||||
#include "edge.h"
|
||||
|
||||
class Signal : public Movable{
|
||||
public:
|
||||
using namespace VISEQ::GRAPH;
|
||||
|
||||
Signal();
|
||||
Signal(Vec2Df position, float r, float _speed, Color color) : Movable(position, Vec2Df(0,0), _speed), radius(r), color(color) {}
|
||||
namespace VISEQ::SIGNAL {
|
||||
class Signal : public BASE::Movable{
|
||||
public:
|
||||
|
||||
SPTR_Node next;
|
||||
SPTR_Node last;
|
||||
SPTR_Edge edge;
|
||||
Signal();
|
||||
Signal(Vec2Df position, float r, float _speed, Color color) : Movable(position, Vec2Df(0,0), _speed), radius(r), color(color) {}
|
||||
|
||||
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){
|
||||
// 120px / 16th = 1920px / whole note
|
||||
// whole note = 2s at 120bpm
|
||||
// 960px / s
|
||||
position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
|
||||
}
|
||||
Color color;
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) {
|
||||
DrawCircle(position.x, position.y, radius, color);
|
||||
}
|
||||
};
|
||||
void update(double t_delta){
|
||||
// 120px / 16th = 1920px / whole note
|
||||
// whole note = 2s at 120bpm
|
||||
// 960px / s
|
||||
position += direction.norm() * speed * multiplier * (float)t_delta / 1000000.0f;
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse, Camera2D cam) {
|
||||
DrawCircle(position.x, position.y, radius, color);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+151
-75
@@ -4,94 +4,170 @@
|
||||
#include "graph-item.h"
|
||||
#include "node.h"
|
||||
#include "edge.h"
|
||||
#include "graph-manager.h"
|
||||
//#include "graph-manager.h"
|
||||
|
||||
#include "datatypes.h"
|
||||
#include "signal.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
/*namespace nlohmann { */
|
||||
/* template <typename T>*/
|
||||
/* struct adl_serializer<std::shared_ptr<T>> {*/
|
||||
/* static void to_json(json& j, const std::shared_ptr<T>& opt){*/
|
||||
/* 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>();*/
|
||||
/* }*/
|
||||
/* }*/
|
||||
/* };*/
|
||||
/*}*/
|
||||
/*using namespace VISEQ::BASE;*/
|
||||
/*using namespace VISEQ::SIGNAL;*/
|
||||
/*using namespace VISEQ::GRAPH::NODES;*/
|
||||
/*using namespace VISEQ::GRAPH::EDGES;*/
|
||||
|
||||
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){
|
||||
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 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}};
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const SPTR_Edge& E){
|
||||
j = *E;
|
||||
}
|
||||
inline void from_json(const json& j, Edge& 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){
|
||||
std::vector<int> conns;
|
||||
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 VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
j = *E;
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const std::shared_ptr<MidiNode>& N){
|
||||
j = *N;
|
||||
}
|
||||
|
||||
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 from_json(const json& j, VISEQ::GRAPH::EDGES::SPTR_Edge& E){
|
||||
E.reset(new Edge(j.get<Edge>()));
|
||||
}
|
||||
}
|
||||
|
||||
inline void to_json(json& j, const std::shared_ptr<Node>& N){
|
||||
j = *N;
|
||||
namespace VISEQ::GRAPH::NODES {
|
||||
|
||||
/* ------------------- 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
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "edge.h"
|
||||
#include "datatypes.h"
|
||||
#include "graph-manager.h"
|
||||
#include "midi-node.h"
|
||||
@@ -13,134 +12,145 @@
|
||||
#include <ranges>
|
||||
#include <vector>
|
||||
|
||||
class AppState{
|
||||
public:
|
||||
namespace VISEQ::APP {
|
||||
|
||||
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;
|
||||
SignalManager sm;
|
||||
std::mutex mutex;
|
||||
|
||||
AppUI ui;
|
||||
VISEQ::GRAPH::GraphManager gm;
|
||||
VISEQ::SIGNAL::SignalManager sm;
|
||||
|
||||
void updateMidiPorts(){
|
||||
for(auto&n : gm.nodes | RANGE_FILTER([](SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;})){
|
||||
auto nc = std::dynamic_pointer_cast<MidiNode>(n);
|
||||
for(auto& mo : sm.midi.midi_out_ports){
|
||||
if(!nc->midi_outs.contains(mo.number) && mo.enabled){
|
||||
nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false};
|
||||
}
|
||||
if(nc->midi_outs.contains(mo.number) && !mo.enabled){
|
||||
nc->midi_outs.erase(mo.number);
|
||||
UI::AppUI ui;
|
||||
|
||||
void updateMidiPorts(){
|
||||
for(auto&n : gm.nodes | RANGE_FILTER([](VISEQ::GRAPH::NODES::SPTR_Node n){return n->getNodeType() == NodeType::MIDINODE;})){
|
||||
auto nc = std::dynamic_pointer_cast<MidiNode>(n);
|
||||
for(auto& mo : sm.midi.midi_out_ports){
|
||||
if(!nc->midi_outs.contains(mo.number) && mo.enabled){
|
||||
nc->midi_outs[mo.number] = {mo.number, mo.port.port_name, false};
|
||||
}
|
||||
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;
|
||||
for(auto n : nodes){
|
||||
for(auto c : n->connections){
|
||||
if(std::ranges::contains(nodes, c->start) && std::ranges::contains(nodes, c->end)) {
|
||||
copy_connections.push_back(std::make_shared<Edge>(*c));
|
||||
VEC_SPTR_Edge copy_connections;
|
||||
for(auto n : nodes){
|
||||
for(auto c : n->connections){
|
||||
if(std::ranges::contains(nodes, c->start) && std::ranges::contains(nodes, c->end)) {
|
||||
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){
|
||||
SPTR_Node new_node;
|
||||
for(auto old_node : nodes){
|
||||
SPTR_Node new_node;
|
||||
|
||||
if(old_node->getNodeType() == NodeType::MIDINODE) {
|
||||
new_node = gm.copyNode(std::dynamic_pointer_cast<MidiNode>(old_node), position + old_node->drag_offset);
|
||||
} else {
|
||||
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(old_node->getNodeType() == NodeType::MIDINODE) {
|
||||
new_node = gm.copyNode(std::dynamic_pointer_cast<MidiNode>(old_node), position + old_node->drag_offset);
|
||||
} else {
|
||||
new_node = gm.copyNode(old_node, position + old_node->drag_offset);
|
||||
}
|
||||
if(c->end == old_node) {
|
||||
c->end = new_node;
|
||||
c->end->connections.push_back(c);
|
||||
|
||||
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;
|
||||
c->end->connections.push_back(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
std::ranges::copy(copy_connections.begin(), copy_connections.end(), std::back_inserter(gm.edges));
|
||||
}
|
||||
|
||||
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){
|
||||
if(!c->start->selected) adjacent_nodes.push_back(c->start);
|
||||
if(!c->end->selected) adjacent_nodes.push_back(c->end);
|
||||
}
|
||||
auto erase_connections = gm.edges | RANGE_FILTER([nodes](SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, 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){
|
||||
if(n->position.dist(s->position) < dist) {
|
||||
dist = n->position.dist(s->position);
|
||||
next_node = n;
|
||||
}
|
||||
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);});
|
||||
}
|
||||
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);});
|
||||
|
||||
for(auto s : rebase_signals){
|
||||
s->start = s->next;
|
||||
void draw(Vec2Df mouse_pos, Camera2D camera){
|
||||
mutex.lock();
|
||||
updateMidiPorts();
|
||||
mutex.unlock();
|
||||
gm.drawNodes(mouse_pos, camera);
|
||||
gm.drawEdges(mouse_pos, camera);
|
||||
sm.drawSignals(mouse_pos, camera);
|
||||
}
|
||||
|
||||
for(auto n : adjacent_nodes){
|
||||
std::erase_if(n->connections, [n, nodes](SPTR_Edge c){ return std::ranges::contains(nodes, c->start) || std::ranges::contains(nodes, c->end);});
|
||||
void drawUI(Vec2Df *mouse_pos, Camera2D *camera){
|
||||
mutex.lock();
|
||||
ui.draw(mouse_pos, camera);
|
||||
mutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
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);});
|
||||
}
|
||||
inline void to_json(json& j, const AppState& A){
|
||||
j = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
|
||||
}
|
||||
|
||||
void draw(Vec2Df mouse_pos, Camera2D camera){
|
||||
mutex.lock();
|
||||
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();
|
||||
}
|
||||
};
|
||||
inline void from_json(const json& j, AppState& A){
|
||||
j.at("graphmanager").get_to(A.gm); // = json{{"graphmanager", A.gm}, {"signalmanager", A.sm}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,176 +12,178 @@
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
class AppUI{
|
||||
public:
|
||||
namespace VISEQ::APP::UI{
|
||||
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;
|
||||
GraphManager *gm;
|
||||
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++;
|
||||
void init(){
|
||||
GuiLoadStyleDark();
|
||||
}
|
||||
|
||||
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);
|
||||
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")) {
|
||||
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;
|
||||
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);
|
||||
void draw(Vec2Df *mouse_pos, Camera2D *camera){
|
||||
drawSideBar();
|
||||
drawPerNodeConfig(camera);
|
||||
}
|
||||
|
||||
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
@@ -7,7 +7,7 @@
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace VMath{
|
||||
namespace VISEQ::VMATH{
|
||||
|
||||
template<typename T>
|
||||
class Vec2D{
|
||||
@@ -158,4 +158,8 @@ namespace VMath{
|
||||
}
|
||||
}
|
||||
|
||||
using Vec2Df = VMath::Vec2D<float>;
|
||||
using Vec2Df = VISEQ::VMATH::Vec2D<float>;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user