aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/engine/Machine.cpp46
-rw-r--r--src/engine/MachineBuilder.cpp55
-rw-r--r--src/engine/Node.cpp16
-rw-r--r--src/engine/machina/MachineBuilder.hpp1
-rw-r--r--src/engine/machina/MidiAction.hpp1
-rw-r--r--src/engine/machina/Node.hpp4
-rw-r--r--src/gui/MachinaCanvas.cpp4
-rw-r--r--src/gui/MachinaGUI.cpp6
-rw-r--r--src/gui/NodeView.cpp45
-rw-r--r--src/gui/NodeView.hpp4
-rw-r--r--src/gui/machina.glade57
11 files changed, 162 insertions, 77 deletions
diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp
index c61bd4f..38e6e11 100644
--- a/src/engine/Machine.cpp
+++ b/src/engine/Machine.cpp
@@ -65,6 +65,9 @@ void
Machine::remove_node(SharedPtr<Node> node)
{
_nodes.erase(_nodes.find(node));
+
+ for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n)
+ (*n)->remove_outgoing_edges_to(node);
}
@@ -164,21 +167,44 @@ Machine::exit_node(SharedPtr<Raul::MIDISink> sink, const SharedPtr<Node> node)
}
}
- // Activate all successors to this node
+ // Activate successors to this node
// (that aren't aready active right now)
- for (Node::Edges::const_iterator s = node->outgoing_edges().begin();
- s != node->outgoing_edges().end(); ++s) {
-
- assert((*s)->head() != node); // no loops
+
+ if (node->is_selector()) {
const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
-
- if (rand_normal <= (*s)->probability()) {
- SharedPtr<Node> head = (*s)->head();
+ double range_min = 0;
+
+ for (Node::Edges::const_iterator s = node->outgoing_edges().begin();
+ s != node->outgoing_edges().end(); ++s) {
+
+ if (!(*s)->head()->is_active()
+ && rand_normal > range_min
+ && rand_normal < range_min + (*s)->probability()) {
+
+ enter_node(sink, (*s)->head());
+ break;
+
+ } else {
+ range_min += (*s)->probability();
+ }
+ }
+
+ } else {
+
+ for (Node::Edges::const_iterator s = node->outgoing_edges().begin();
+ s != node->outgoing_edges().end(); ++s) {
- if (!head->is_active())
- enter_node(sink, head);
+ const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
+
+ if (rand_normal <= (*s)->probability()) {
+ SharedPtr<Node> head = (*s)->head();
+
+ if ( ! head->is_active())
+ enter_node(sink, head);
+ }
}
+
}
}
diff --git a/src/engine/MachineBuilder.cpp b/src/engine/MachineBuilder.cpp
index 03d20b9..b62e90e 100644
--- a/src/engine/MachineBuilder.cpp
+++ b/src/engine/MachineBuilder.cpp
@@ -62,6 +62,21 @@ MachineBuilder::is_delay_node(SharedPtr<Node> node) const
}
+/** Set the duration of a node, with quantization.
+ */
+void
+MachineBuilder::set_node_duration(SharedPtr<Node> node, Raul::BeatTime d) const
+{
+ Raul::BeatTime q_dur = Quantizer::quantize(_quantization, d);
+
+ // Never quantize a note to duration 0
+ if (q_dur == 0 && ( node->enter_action() || node->exit_action() ))
+ q_dur = _quantization; // Round up
+
+ node->set_duration(q_dur);
+}
+
+
/** Connect two nodes, inserting a delay node between them if necessary.
*
* If a delay node is added to the machine, it is returned.
@@ -83,7 +98,7 @@ MachineBuilder::connect_nodes(SharedPtr<Machine> m,
if (is_delay_node(tail) && tail->outgoing_edges().size() == 0) {
// Tail is a delay node, just accumulate the time difference into it
//cerr << "Accumulating delay " << tail_end_time << " .. " << head_start_time << endl;
- tail->set_duration(tail->duration() + head_start_time - tail_end_time);
+ set_node_duration(tail, tail->duration() + head_start_time - tail_end_time);
tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, head)));
} else if (head_start_time == tail_end_time) {
// Connect directly
@@ -93,7 +108,7 @@ MachineBuilder::connect_nodes(SharedPtr<Machine> m,
// Need to actually create a delay node
//cerr << "Adding delay node for " << tail_end_time << " .. " << head_start_time << endl;
delay_node = SharedPtr<Node>(new Node());
- delay_node->set_duration(head_start_time - tail_end_time);
+ set_node_duration(delay_node, head_start_time - tail_end_time);
tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, delay_node)));
delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, head)));
m->add_node(delay_node);
@@ -181,7 +196,7 @@ MachineBuilder::event(Raul::BeatTime time_offset,
SharedPtr<Node> resolved = *i;
resolved->set_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
- resolved->set_duration(t - resolved->enter_time());
+ set_node_duration(resolved, t - resolved->enter_time());
// Last active note
if (_active_nodes.size() == 1) {
@@ -227,7 +242,7 @@ MachineBuilder::event(Raul::BeatTime time_offset,
_connect_node->set_exit_action(resolved->exit_action());
resolved->remove_enter_action();
resolved->remove_exit_action();
- _connect_node->set_duration(resolved->duration());
+ set_node_duration(_connect_node, resolved->duration());
resolved = _connect_node;
if (_machine->nodes().find(_connect_node) == _machine->nodes().end())
_machine->add_node(_connect_node);
@@ -281,19 +296,20 @@ MachineBuilder::resolve()
if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
unsigned char note_off[3] = { ((MIDI_CMD_NOTE_OFF & 0xF0) | (ev[0] & 0x0F)), ev[1], 0x40 };
(*i)->set_exit_action(SharedPtr<Action>(new MidiAction(3, note_off)));
- (*i)->set_duration(_time - (*i)->enter_time());
+ set_node_duration((*i), _time - (*i)->enter_time());
(*i)->exit(SharedPtr<Raul::MIDISink>(), _time);
_machine->add_node((*i));
}
}
_active_nodes.clear();
}
-
+
// Add initial note if necessary
if (_machine->nodes().size() > 0
&& _machine->nodes().find(_initial_node) == _machine->nodes().end())
_machine->add_node(_initial_node);
-
+
+#if 0
// Quantize
if (_quantization > 0) {
for (Machine::Nodes::iterator i = _machine->nodes().begin();
@@ -305,6 +321,31 @@ MachineBuilder::resolve()
(*i)->set_duration(q_dur);
}
}
+
+ // Remove any useless states
+ for (Machine::Nodes::iterator i = _machine->nodes().begin();
+ i != _machine->nodes().end() ; ) {
+
+ if (!(*i)->enter_action() && !(*i)->exit_action() && (*i)->duration() == 0
+ && (*i)->outgoing_edges().size() <= 1) {
+
+ // Connect any predecessors to the successor
+ if ((*i)->outgoing_edges().size() == 1) {
+ SharedPtr<Node> successor = *((*i)->outgoing_edges().begin())->head();
+
+ for (Machine::Nodes::iterator i = _machine->nodes().begin();
+ i != _machine->nodes().end() ; ) {
+
+
+ Machine::Nodes::iterator next = i;
+ ++next;
+
+ _machine->remove_node(*i);
+
+ i = next;
+ }
+ }
+#endif
}
diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp
index e51e33d..b55619d 100644
--- a/src/engine/Node.cpp
+++ b/src/engine/Node.cpp
@@ -34,6 +34,22 @@ Node::Node(BeatCount duration, bool initial)
void
+Node::set_selector(bool yn)
+{
+ _is_selector = yn;
+
+ if (yn) {
+ double prob_sum = 0;
+ for (Edges::iterator i = _outgoing_edges.begin(); i != _outgoing_edges.end(); ++i)
+ prob_sum += (*i)->probability();
+
+ for (Edges::iterator i = _outgoing_edges.begin(); i != _outgoing_edges.end(); ++i)
+ (*i)->set_probability((*i)->probability() / prob_sum);
+ }
+}
+
+
+void
Node::set_enter_action(SharedPtr<Action> action)
{
_enter_action = action;
diff --git a/src/engine/machina/MachineBuilder.hpp b/src/engine/machina/MachineBuilder.hpp
index ac446c9..25104e8 100644
--- a/src/engine/machina/MachineBuilder.hpp
+++ b/src/engine/machina/MachineBuilder.hpp
@@ -44,6 +44,7 @@ public:
private:
bool is_delay_node(SharedPtr<Node> node) const;
+ void set_node_duration(SharedPtr<Node> node, Raul::BeatTime d) const;
SharedPtr<Node>
connect_nodes(SharedPtr<Machine> m,
diff --git a/src/engine/machina/MidiAction.hpp b/src/engine/machina/MidiAction.hpp
index 60d9189..510c0d0 100644
--- a/src/engine/machina/MidiAction.hpp
+++ b/src/engine/machina/MidiAction.hpp
@@ -57,7 +57,6 @@ public:
private:
-
size_t _size;
const size_t _max_size;
Raul::AtomicPtr<byte> _event;
diff --git a/src/engine/machina/Node.hpp b/src/engine/machina/Node.hpp
index b4c72e2..66d1b2d 100644
--- a/src/engine/machina/Node.hpp
+++ b/src/engine/machina/Node.hpp
@@ -66,13 +66,13 @@ public:
bool is_initial() const { return _is_initial; }
void set_initial(bool i) { _is_initial = i; }
- bool is_selector() const { return _is_selector; }
- void set_selector(bool i) { _is_selector = i; }
bool is_active() const { return _is_active; }
BeatTime enter_time() const { assert(_is_active); return _enter_time; }
BeatTime exit_time() const { assert(_is_active); return _enter_time + _duration; }
BeatCount duration() { return _duration; }
void set_duration(BeatCount d) { _duration = d; }
+ bool is_selector() const { return _is_selector; }
+ void set_selector(bool i);
typedef Raul::List<SharedPtr<Edge> > Edges;
Edges& outgoing_edges() { return _outgoing_edges; }
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
index 134b885..716a40d 100644
--- a/src/gui/MachinaCanvas.cpp
+++ b/src/gui/MachinaCanvas.cpp
@@ -52,6 +52,9 @@ MachinaCanvas::node_clicked(WeakPtr<NodeView> item, GdkEventButton* event)
if (!node)
return;
+ if (event->state & GDK_CONTROL_MASK)
+ return;
+
// Middle click, learn
if (event->button == 2) {
_app->machine()->learn(Machina::LearnRequest::create(_app->maid(), node->node()));
@@ -192,6 +195,7 @@ void
MachinaCanvas::build(SharedPtr<Machina::Machine> machine)
{
destroy();
+ _last_clicked.reset();
if (!machine)
return;
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 3cf38bd..e5e6d90 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -608,6 +608,12 @@ void
MachinaGUI::show_labels_toggled()
{
const bool show = _menu_view_labels->get_active();
+
+ for (ItemList::iterator i = _canvas->items().begin(); i != _canvas->items().end(); ++i) {
+ const SharedPtr<NodeView> nv = PtrCast<NodeView>(*i);
+ if (nv)
+ nv->show_label(show);
+ }
for (ConnectionList::iterator c = _canvas->connections().begin();
c != _canvas->connections().end(); ++c) {
diff --git a/src/gui/NodeView.cpp b/src/gui/NodeView.cpp
index 6ff21ee..d38e228 100644
--- a/src/gui/NodeView.cpp
+++ b/src/gui/NodeView.cpp
@@ -15,6 +15,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include <machina/MidiAction.hpp>
#include "NodeView.hpp"
#include "NodePropertiesWindow.hpp"
@@ -29,7 +30,8 @@ NodeView::NodeView(Gtk::Window* window,
, _window(window)
, _node(node)
{
- //signal_double_clicked.connect(sigc::mem_fun(this, &NodeView::on_double_click));
+ signal_clicked.connect(sigc::mem_fun(this, &NodeView::handle_click));
+ update_state();
}
@@ -41,27 +43,50 @@ NodeView::on_double_click(GdkEventButton*)
void
-NodeView::on_click(GdkEventButton* event)
+NodeView::handle_click(GdkEventButton* event)
{
if (event->state & GDK_CONTROL_MASK) {
if (event->button == 1) {
bool is_initial = _node->is_initial();
_node->set_initial( ! is_initial );
- set_border_width(is_initial ? 1.0 : 6.0);
+ update_state();
} else if (event->button == 3) {
bool is_selector = _node->is_selector();
_node->set_selector( ! is_selector );
- set_border_width(is_selector ? 1.0 : 3.0);
+ update_state();
}
}
}
void
-NodeView::update_state()
+NodeView::show_label(bool show)
{
- static const uint32_t active_color = 0x80808AFF;
+ char str[4];
+
+ SharedPtr<Machina::MidiAction> action
+ = PtrCast<Machina::MidiAction>(_node->enter_action());
+
+ if (show) {
+ if (action && action->event_size() >= 2
+ && (action->event()[0] & 0xF0) == 0x90) {
+
+ unsigned char note = action->event()[1];
+ snprintf(str, 4, "%d", (int)note);
+
+ set_name(str);
+ }
+ } else {
+ set_name("");
+ }
+}
+
+void
+NodeView::update_state()
+{
+ static const uint32_t active_color = 0xA0A0AAFF;
+
if (_node->is_active()) {
if (_color != active_color) {
_old_color = _color;
@@ -70,5 +95,13 @@ NodeView::update_state()
} else if (_color == active_color) {
set_base_color(_old_color);
}
+
+ if (_node->is_selector())
+ if (_node->is_active())
+ set_base_color(0x00FF00FF);
+ else
+ set_base_color(0x00A000FF);
+
+ set_border_width(_node->is_initial() ? 4.0 : 1.0);
}
diff --git a/src/gui/NodeView.hpp b/src/gui/NodeView.hpp
index 4641787..41ff45f 100644
--- a/src/gui/NodeView.hpp
+++ b/src/gui/NodeView.hpp
@@ -32,11 +32,13 @@ public:
double y);
SharedPtr<Machina::Node> node() { return _node; }
+
+ void show_label(bool show);
void update_state();
private:
- void on_click(GdkEventButton* ev);
+ void handle_click(GdkEventButton* ev);
void on_double_click(GdkEventButton* ev);
Gtk::Window* _window;
diff --git a/src/gui/machina.glade b/src/gui/machina.glade
index b5349be..61096f1 100644
--- a/src/gui/machina.glade
+++ b/src/gui/machina.glade
@@ -317,51 +317,6 @@
</child>
<child>
- <widget class="GtkToolButton" id="add_button">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Add selected states</property>
- <property name="stock_id">gtk-add</property>
- <property name="visible_horizontal">True</property>
- <property name="visible_vertical">True</property>
- <property name="is_important">False</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkToolButton" id="multiply_button">
- <property name="visible">True</property>
- <property name="tooltip" translatable="yes">Multiply (merge) selected states</property>
- <property name="label" translatable="yes"></property>
- <property name="use_underline">True</property>
- <property name="stock_id">gtk-convert</property>
- <property name="visible_horizontal">True</property>
- <property name="visible_vertical">True</property>
- <property name="is_important">False</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
-
- <child>
- <widget class="GtkSeparatorToolItem" id="separatortoolitem5">
- <property name="visible">True</property>
- <property name="draw">True</property>
- <property name="visible_horizontal">True</property>
- <property name="visible_vertical">True</property>
- </widget>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">False</property>
- </packing>
- </child>
-
- <child>
<widget class="GtkToolItem" id="toolitem1">
<property name="visible">True</property>
<property name="visible_horizontal">True</property>
@@ -732,7 +687,8 @@
<property name="destroy_with_parent">True</property>
<property name="name" translatable="yes">Machina</property>
<property name="copyright" translatable="yes">© 2007 Dave Robillard &lt;http://drobilla.net&gt;</property>
- <property name="comments" translatable="yes">It's a machine</property>
+ <property name="comments" translatable="yes">A MIDI sequencer based on
+ probabilistic finite-state automata</property>
<property name="license" translatable="yes">Machina is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
@@ -808,14 +764,15 @@ along with Machina; if not, write to the Free Software Foundation, Inc.,
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
- <property name="label" translatable="yes">Interface is still a bit arbitrary:
+ <property name="label" translatable="yes">Initial nodes are shown with a thick border.
+Selector nodes are shown in green.
- Middle click canvas to create a new node
- Middle click nodes to learn a MIDI note for that node
- Right click two nodes in succession to connect nodes
-- Double click a node to make it an initial node
-
-Connect some nodes up and double click one. You'll get it.</property>
+- Double click a node to show its properties dialog
+- Ctrl+Left click a node to make it an initial node
+- Ctrl+Right click a node to make it a selector node</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>