aboutsummaryrefslogtreecommitdiffstats
path: root/src/engine/Machine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/Machine.cpp')
-rw-r--r--src/engine/Machine.cpp253
1 files changed, 95 insertions, 158 deletions
diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp
index fc026f9..a6abcff 100644
--- a/src/engine/Machine.cpp
+++ b/src/engine/Machine.cpp
@@ -1,5 +1,5 @@
/* This file is part of Machina.
- * Copyright 2007-2011 David Robillard <http://drobilla.net>
+ * Copyright 2007-2013 David Robillard <http://drobilla.net>
*
* Machina is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,71 +39,63 @@ using namespace Raul;
namespace machina {
Machine::Machine(TimeUnit unit)
- : _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
+ : _initial_node(new Node(TimeStamp(unit, 0, 0), true))
+ , _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
, _time(unit, 0, 0)
- , _is_activated(false)
, _is_finished(false)
-{}
+{
+ _nodes.insert(_initial_node);
+}
-/** Copy a Machine.
- *
- * Creates a deep copy which is the 'same' machine, but with
- * fresh state (deactivated, rewound)
- */
-Machine::Machine(const Machine& copy)
- : Stateful() // don't copy RDF ID
- , _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
- , _time(copy.time())
- , _is_activated(false)
- , _is_finished(false)
+void
+Machine::assign(const Machine& copy)
{
std::map< SPtr<Node>, SPtr<Node> > replacements;
- for (Nodes::const_iterator n = copy._nodes.begin(); n != copy._nodes.end();
- ++n) {
- SPtr<machina::Node> node(new machina::Node(*n->get()));
- _nodes.insert(node);
- replacements[*n] = node;
+ replacements[copy.initial_node()] = _initial_node;
+
+ for (const auto& n : copy._nodes) {
+ if (!n->is_initial()) {
+ SPtr<machina::Node> node(new machina::Node(*n.get()));
+ _nodes.insert(node);
+ replacements[n] = node;
+ }
}
- for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
- for (Node::Edges::const_iterator e = (*n)->edges().begin();
- e != (*n)->edges().end(); ++e) {
- (*e)->set_tail(*n);
- (*e)->set_head(replacements[(*e)->head()]);
- assert((*e)->head());
+ for (const auto& n : _nodes) {
+ for (const auto& e : n->edges()) {
+ e->set_tail(n);
+ e->set_head(replacements[e->head()]);
}
}
}
+Machine::Machine(const Machine& copy)
+ : Stateful() // don't copy RDF ID
+ , _initial_node(new Node(TimeStamp(copy.time().unit(), 0, 0), true))
+ , _active_nodes(MAX_ACTIVE_NODES, SPtr<Node>())
+ , _time(copy.time())
+ , _is_finished(false)
+{
+ _nodes.insert(_initial_node);
+ assign(copy);
+}
+
Machine&
-Machine::operator=(const Machine& other)
+Machine::operator=(const Machine& copy)
{
- _active_nodes = std::vector< SPtr<Node> >(MAX_ACTIVE_NODES,
- SPtr<Node>());
- _is_activated = false;
+ if (&copy == this) {
+ return *this;
+ }
+
+ _active_nodes = std::vector< SPtr<Node> >(MAX_ACTIVE_NODES, SPtr<Node>());
_is_finished = false;
- _time = other._time;
+ _time = copy._time;
_pending_learn = SPtr<LearnRequest>();
- _nodes.clear();
- map< SPtr<Node>, SPtr<Node> > replacements;
-
- for (Nodes::const_iterator n = other._nodes.begin(); n != other._nodes.end();
- ++n) {
- SPtr<machina::Node> node(new machina::Node(*n->get()));
- _nodes.insert(node);
- replacements[*n] = node;
- }
-
- for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
- for (Node::Edges::const_iterator e = (*n)->edges().begin();
- e != (*n)->edges().end(); ++e) {
- (*e)->set_tail(*n);
- (*e)->set_head(replacements[(*e)->head()]);
- assert((*e)->head());
- }
- }
+ _nodes.clear();
+ _nodes.insert(_initial_node);
+ assign(copy);
return *this;
}
@@ -158,24 +150,15 @@ Machine::remove_node(SPtr<Node> node)
}
}
-/** Exit all active states and reset time to 0.
- */
void
Machine::reset(MIDISink* sink, Raul::TimeStamp time)
{
if (!_is_finished) {
- for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) {
- SPtr<Node> node = (*n);
-
- if (node->is_active()) {
- node->exit(sink, time);
+ for (auto& n : _active_nodes) {
+ if (n) {
+ n->exit(sink, time);
+ n.reset();
}
-
- assert(!node->is_active());
- }
-
- for (size_t i = 0; i < MAX_ACTIVE_NODES; ++i) {
- _active_nodes.at(i).reset();
}
}
@@ -190,26 +173,19 @@ Machine::earliest_node() const
{
SPtr<Node> earliest;
- for (size_t i = 0; i < MAX_ACTIVE_NODES; ++i) {
- SPtr<Node> node = _active_nodes.at(i);
-
- if (node) {
- if (!node->is_active()) {
- std::cerr << "Inactive node in active node list" << std::endl;
- continue;
- }
- if (!earliest || (node->exit_time() < earliest->exit_time()) ) {
- earliest = node;
- }
+ for (const auto& n : _active_nodes) {
+ if (n && (!earliest || n->exit_time() < earliest->exit_time())) {
+ earliest = n;
}
}
return earliest;
}
-/** Enter a state at the current _time.
+/** Enter a node at the current _time.
*
- * Returns true if node was entered, or false if the maximum active nodes has been reached.
+ * Returns true if node was entered, or false if the maximum active nodes has
+ * been reached.
*/
bool
Machine::enter_node(Context& context,
@@ -217,15 +193,15 @@ Machine::enter_node(Context& context,
SPtr<Raul::RingBuffer> updates)
{
assert(!node->is_active());
+ assert(_active_nodes.size() == MAX_ACTIVE_NODES);
/* FIXME: Would be best to use the MIDI note here as a hash key, at least
* while all actions are still MIDI notes... */
size_t index = (rand() % MAX_ACTIVE_NODES);
for (size_t i = 0; i < MAX_ACTIVE_NODES; ++i) {
- if (_active_nodes.at(index) == NULL) {
+ if (!_active_nodes[index]) {
node->enter(context.sink(), _time);
- assert(node->is_active());
- _active_nodes.at(index) = node;
+ _active_nodes[index] = node;
write_set(updates,
node->id(),
@@ -240,78 +216,58 @@ Machine::enter_node(Context& context,
return false;
}
-/** Exit an active node at the current _time.
- */
void
Machine::exit_node(Context& context,
SPtr<Node> node,
SPtr<Raul::RingBuffer> updates)
{
+ // Exit node
node->exit(context.sink(), _time);
+
+ // Notify UI
write_set(updates,
node->id(),
URIs::instance().machina_active,
context.forge().make(false));
- assert(!node->is_active());
-
- for (size_t i = 0; i < MAX_ACTIVE_NODES; ++i) {
- if (_active_nodes.at(i) == node) {
- _active_nodes.at(i).reset();
+ // Remove node from _active_nodes
+ for (auto& n : _active_nodes) {
+ if (n == node) {
+ n.reset();
}
}
- // Activate successors to this node
- // (that aren't aready active right now)
-
+ // Activate successors
if (node->is_selector()) {
-
const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
double range_min = 0;
- for (Node::Edges::const_iterator s = node->edges().begin();
- s != node->edges().end(); ++s) {
-
- if (!(*s)->head()->is_active()
+ for (const auto& e : node->edges()) {
+ if (!e->head()->is_active()
&& rand_normal > range_min
- && rand_normal < range_min + (*s)->probability()) {
+ && rand_normal < range_min + e->probability()) {
- enter_node(context, (*s)->head(), updates);
+ enter_node(context, e->head(), updates);
break;
} else {
- range_min += (*s)->probability();
+ range_min += e->probability();
}
}
-
} else {
-
- for (Node::Edges::const_iterator e = node->edges().begin();
- e != node->edges().end(); ++e) {
-
+ for (const auto& e : node->edges()) {
const double rand_normal = rand() / (double)RAND_MAX; // [0, 1]
-
- if (rand_normal <= (*e)->probability()) {
- SPtr<Node> head = (*e)->head();
+ if (rand_normal <= e->probability()) {
+ SPtr<Node> head = e->head();
if (!head->is_active()) {
enter_node(context, head, updates);
}
}
}
-
}
}
-/** Run the machine for a (real) time slice.
- *
- * Returns the duration of time the machine actually ran.
- *
- * Caller can check is_finished() to determine if the machine still has any
- * active states. If not, time() will return the exact time stamp the
- * machine actually finished on (so it can be restarted immediately
- * with sample accuracy if necessary).
- */
uint32_t
Machine::run(Context& context, SPtr<Raul::RingBuffer> updates)
{
@@ -319,77 +275,58 @@ Machine::run(Context& context, SPtr<Raul::RingBuffer> updates)
return 0;
}
- const TimeStamp cycle_end_frames = context.time().start_ticks()
- + context.time().length_ticks();
- const TimeStamp cycle_end = context.time().ticks_to_beats(
- cycle_end_frames);
-
- assert(_is_activated);
-
- // Initial run, enter all initial states
- if (_time.is_zero()) {
- bool entered = false;
- if (!_nodes.empty()) {
- for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end();
- ++n) {
- if ((*n)->is_active()) {
- (*n)->exit(context.sink(), _time);
- write_set(updates,
- (*n)->id(),
- URIs::instance().machina_active,
- context.forge().make(false));
- }
+ const Raul::TimeSlice& time = context.time();
- if ((*n)->is_initial()) {
- if (enter_node(context, (*n), updates)) {
- entered = true;
- }
- }
+ const TimeStamp end_frames = (time.start_ticks() + time.length_ticks());
+ const TimeStamp end_beats = time.ticks_to_beats(end_frames);
+
+ if (_time.is_zero()) { // Initial run
+ // Exit any active nodes
+ for (auto& n : _active_nodes) {
+ if (n && n->is_active()) {
+ n->exit(context.sink(), _time);
+ write_set(updates,
+ n->id(),
+ URIs::instance().machina_active,
+ context.forge().make(false));
}
+ n.reset();
}
- if (!entered) {
+
+ // Enter initial node
+ enter_node(context, _initial_node, updates);
+
+ if (_initial_node->edges().empty()) { // Nowhere to go, exit
_is_finished = true;
return 0;
}
}
while (true) {
-
SPtr<Node> earliest = earliest_node();
-
if (!earliest) {
// No more active states, machine is finished
-#ifndef NDEBUG
- for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end();
- ++n) {
- assert(!(*n)->is_active());
- }
-#endif
_is_finished = true;
break;
+ }
- } else if (context.time().beats_to_ticks(earliest->exit_time()) <
- cycle_end_frames) {
- // Earliest active state ends this cycle
+ const TimeStamp exit_time = earliest->exit_time();
+ if (time.beats_to_ticks(exit_time) < end_frames) {
+ // Earliest active state ends this cycle, exit it
_time = earliest->exit_time();
exit_node(context, earliest, updates);
} else {
// Earliest active state ends in the future, done this cycle
- _time = cycle_end;
+ _time = end_beats;
break;
}
}
- return context.time().beats_to_ticks(_time).ticks()
- - context.time().start_ticks().ticks();
+ return time.beats_to_ticks(_time).ticks() - time.start_ticks().ticks();
}
-/** Push a node onto the learn stack.
- *
- * NOT realtime (actions are allocated here).
- */
void
Machine::learn(SPtr<Raul::Maid> maid, SPtr<Node> node)
{