diff options
author | David Robillard <d@drobilla.net> | 2013-01-13 07:49:49 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2013-01-13 07:49:49 +0000 |
commit | 67a8adbc93991acfb688f378f52392995a272fac (patch) | |
tree | a4e629bd9c0d7da4cc5c7cb644b3352ca6f0dc78 /src/engine/Machine.cpp | |
parent | 33aa54fa98783d1da2a322ee136c17df7f9c98a5 (diff) | |
download | machina-67a8adbc93991acfb688f378f52392995a272fac.tar.gz machina-67a8adbc93991acfb688f378f52392995a272fac.tar.bz2 machina-67a8adbc93991acfb688f378f52392995a272fac.zip |
Change model to have a single initial node.
Merge multiple recording into branches off the same initial node.
Make transport state sane with 3 distinct states.
Handle announcing objects several times correctly.
Don't send useless zero coordinates for new nodes, position in visible area.
Rewrite and clean up Machine code.
Update help.
git-svn-id: http://svn.drobilla.net/lad/trunk/machina@4954 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/engine/Machine.cpp')
-rw-r--r-- | src/engine/Machine.cpp | 253 |
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 (© == 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) { |