aboutsummaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2013-01-13 07:49:49 +0000
committerDavid Robillard <d@drobilla.net>2013-01-13 07:49:49 +0000
commit67a8adbc93991acfb688f378f52392995a272fac (patch)
treea4e629bd9c0d7da4cc5c7cb644b3352ca6f0dc78 /src/engine
parent33aa54fa98783d1da2a322ee136c17df7f9c98a5 (diff)
downloadmachina-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')
-rw-r--r--src/engine/Controller.cpp105
-rw-r--r--src/engine/Edge.cpp5
-rw-r--r--src/engine/Engine.cpp4
-rw-r--r--src/engine/JackDriver.cpp77
-rw-r--r--src/engine/JackDriver.hpp13
-rw-r--r--src/engine/Loader.cpp4
-rw-r--r--src/engine/Machine.cpp253
-rw-r--r--src/engine/MachineBuilder.cpp11
-rw-r--r--src/engine/Node.cpp30
-rw-r--r--src/engine/Node.hpp3
-rw-r--r--src/engine/SMFDriver.hpp2
-rw-r--r--src/engine/machina/Driver.hpp24
-rw-r--r--src/engine/machina/Machine.hpp39
13 files changed, 279 insertions, 291 deletions
diff --git a/src/engine/Controller.cpp b/src/engine/Controller.cpp
index cc3c0de..c63d11e 100644
--- a/src/engine/Controller.cpp
+++ b/src/engine/Controller.cpp
@@ -40,9 +40,10 @@ Controller::Controller(SPtr<Engine> engine,
uint64_t
Controller::create(const client::ClientObject& properties)
{
- TimeDuration dur(_engine->machine()->time().unit(),
- properties.get(URIs::instance().machina_duration).get_float());
- SPtr<machina::Node> node(new machina::Node(dur));
+ TimeDuration dur(
+ _engine->machine()->time().unit(),
+ properties.get(URIs::instance().machina_duration).get_float());
+ SPtr<Node> node(new Node(dur));
SPtr<client::ClientObject> obj(
new client::ClientObject(properties, node->id()));
_objects.insert(node);
@@ -56,48 +57,56 @@ Controller::announce(SPtr<Machine> machine)
{
Raul::Forge& forge = _engine->forge();
- for (machina::Machine::Nodes::const_iterator n = machine->nodes().begin();
- n != machine->nodes().end(); ++n) {
- SPtr<machina::client::ClientObject> obj(
- new machina::client::ClientObject((*n)->id()));
- obj->set(URIs::instance().rdf_type,
- forge.make_urid(URIs::instance().machina_Node));
- obj->set(URIs::instance().machina_duration,
- forge.make(float((*n)->duration().to_double())));
- obj->set(URIs::instance().machina_canvas_x, forge.make(0.0f));
- obj->set(URIs::instance().machina_canvas_y, forge.make(0.0f));
-
- SPtr<MidiAction> midi_action = dynamic_ptr_cast<MidiAction>(
- (*n)->enter_action());
- if (midi_action) {
- SPtr<machina::client::ClientObject> action(
- new machina::client::ClientObject(midi_action->id()));
- action->set(URIs::instance().machina_note_number,
- forge.make((int32_t)midi_action->event()[1]));
- _client_model.new_object(action);
- obj->set(URIs::instance().machina_enter_action,
- forge.make(int32_t((*n)->enter_action()->id())));
+ for (const auto& n : machine->nodes()) {
+ // Find or create a new client object if necessary
+ SPtr<client::ClientObject> obj = _client_model.find(n->id());
+ if (!obj) {
+ obj = SPtr<client::ClientObject>(new client::ClientObject(n->id()));
+ obj->set(URIs::instance().rdf_type,
+ forge.make_urid(URIs::instance().machina_Node));
+ obj->set(URIs::instance().machina_duration,
+ forge.make(float(n->duration().to_double())));
+ if (n->is_initial()) {
+ obj->set(URIs::instance().machina_initial, forge.make(true));
+ }
+
+ SPtr<MidiAction> midi_action = dynamic_ptr_cast<MidiAction>(
+ n->enter_action());
+ if (midi_action) {
+ SPtr<client::ClientObject> action = _client_model.find(
+ midi_action->id());
+ if (!action) {
+ action = SPtr<client::ClientObject>(
+ new client::ClientObject(midi_action->id()));
+ action->set(URIs::instance().machina_note_number,
+ forge.make((int32_t)midi_action->event()[1]));
+ _client_model.new_object(action);
+ }
+ obj->set(URIs::instance().machina_enter_action,
+ forge.make(int32_t(n->enter_action()->id())));
+ }
}
- _objects.insert(*n);
+ _objects.insert(n);
_client_model.new_object(obj);
}
- for (machina::Machine::Nodes::const_iterator n = machine->nodes().begin();
- n != machine->nodes().end(); ++n) {
- for (machina::Node::Edges::const_iterator e = (*n)->edges().begin();
- e != (*n)->edges().end(); ++e) {
- _objects.insert(*e);
- SPtr<client::ClientObject> eobj(
- new client::ClientObject((*e)->id()));
- eobj->set(URIs::instance().rdf_type,
- forge.make_urid(URIs::instance().machina_Edge));
- eobj->set(URIs::instance().machina_probability,
- forge.make((*e)->probability()));
- eobj->set(URIs::instance().machina_tail_id,
- forge.make((int32_t)(*n)->id()));
- eobj->set(URIs::instance().machina_head_id,
- forge.make((int32_t)(*e)->head()->id()));
-
+ for (const auto& n : machine->nodes()) {
+ for (const auto& e : n->edges()) {
+ SPtr<client::ClientObject> eobj = _client_model.find(e->id());
+ if (!eobj) {
+ eobj = SPtr<client::ClientObject>(
+ new client::ClientObject(e->id()));
+ eobj->set(URIs::instance().rdf_type,
+ forge.make_urid(URIs::instance().machina_Edge));
+ eobj->set(URIs::instance().machina_probability,
+ forge.make(e->probability()));
+ eobj->set(URIs::instance().machina_tail_id,
+ forge.make((int32_t)n->id()));
+ eobj->set(URIs::instance().machina_head_id,
+ forge.make((int32_t)e->head()->id()));
+ }
+
+ _objects.insert(e);
_client_model.new_object(eobj);
}
}
@@ -117,7 +126,7 @@ Controller::find(uint64_t id)
void
Controller::learn(SPtr<Raul::Maid> maid, uint64_t node_id)
{
- SPtr<machina::Node> node = dynamic_ptr_cast<machina::Node>(find(node_id));
+ SPtr<Node> node = dynamic_ptr_cast<Node>(find(node_id));
if (node) {
_engine->machine()->learn(maid, node);
} else {
@@ -141,10 +150,10 @@ Controller::set_property(uint64_t object_id,
uint64_t
Controller::connect(uint64_t tail_id, uint64_t head_id)
{
- SPtr<machina::Node> tail = dynamic_ptr_cast<machina::Node>(find(tail_id));
- SPtr<machina::Node> head = dynamic_ptr_cast<machina::Node>(find(head_id));
+ SPtr<Node> tail = dynamic_ptr_cast<Node>(find(tail_id));
+ SPtr<Node> head = dynamic_ptr_cast<Node>(find(head_id));
- SPtr<machina::Edge> edge(new machina::Edge(tail, head));
+ SPtr<Edge> edge(new Edge(tail, head));
tail->add_edge(edge);
_objects.insert(edge);
@@ -165,8 +174,8 @@ Controller::connect(uint64_t tail_id, uint64_t head_id)
void
Controller::disconnect(uint64_t tail_id, uint64_t head_id)
{
- SPtr<machina::Node> tail = dynamic_ptr_cast<machina::Node>(find(tail_id));
- SPtr<machina::Node> head = dynamic_ptr_cast<machina::Node>(find(head_id));
+ SPtr<Node> tail = dynamic_ptr_cast<Node>(find(tail_id));
+ SPtr<Node> head = dynamic_ptr_cast<Node>(find(head_id));
SPtr<Edge> edge = tail->remove_edge_to(head);
if (edge) {
@@ -204,7 +213,7 @@ Controller::process_updates()
Raul::Atom value;
for (uint32_t i = 0; i < read_space; ) {
i += read_set(_updates, &subject, &key, &value);
- SPtr<machina::client::ClientObject> obj = _client_model.find(subject);
+ SPtr<client::ClientObject> obj = _client_model.find(subject);
if (obj) {
obj->set(key, value);
} else {
diff --git a/src/engine/Edge.cpp b/src/engine/Edge.cpp
index 0538c3c..85a4ab5 100644
--- a/src/engine/Edge.cpp
+++ b/src/engine/Edge.cpp
@@ -21,13 +21,16 @@
#include "Edge.hpp"
#include "Node.hpp"
+#include "machina/URIs.hpp"
namespace machina {
void
Edge::set(URIInt key, const Raul::Atom& value)
{
- std::cout << "EDGE SET " << key << std::endl;
+ if (key == URIs::instance().machina_probability) {
+ _probability.set(value.get_float());
+ }
}
void
diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp
index 12c351d..90aea9b 100644
--- a/src/engine/Engine.cpp
+++ b/src/engine/Engine.cpp
@@ -67,7 +67,6 @@ Engine::load_machine(const Glib::ustring& uri)
SPtr<Machine> old_machine;
if (machine) {
old_machine = _driver->machine(); // Keep a reference to old machine...
- machine->activate();
_driver->set_machine(machine); // Switch driver to new machine
}
@@ -89,7 +88,6 @@ Engine::load_machine_midi(const Glib::ustring& uri,
SPtr<Machine> old_machine;
if (machine) {
old_machine = _driver->machine(); // Keep a reference to old machine...
- machine->activate();
_driver->set_machine(machine); // Switch driver to new machine
}
@@ -101,7 +99,6 @@ Engine::load_machine_midi(const Glib::ustring& uri,
void
Engine::import_machine(SPtr<Machine> machine)
{
- machine->activate();
_driver->machine()->nodes().insert(machine->nodes().begin(),
machine->nodes().end());
// FIXME: thread safe?
@@ -118,7 +115,6 @@ Engine::export_midi(const Glib::ustring& filename, Raul::TimeDuration dur)
const bool activated = _driver->is_activated();
if (activated) {
_driver->deactivate(); // FIXME: disable instead
-
}
file_driver->writer()->start(filename, TimeStamp(dur.unit(), 0.0));
file_driver->run(machine, dur);
diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp
index 72b1cb1..0276cc5 100644
--- a/src/engine/JackDriver.cpp
+++ b/src/engine/JackDriver.cpp
@@ -16,13 +16,13 @@
*/
#include <iostream>
-#include <iomanip>
#include "machina/Context.hpp"
#include "machina/URIs.hpp"
#include "machina/Updates.hpp"
#include "machina_config.h"
+#include "Edge.hpp"
#include "JackDriver.hpp"
#include "LearnRequest.hpp"
#include "MidiAction.hpp"
@@ -46,7 +46,6 @@ JackDriver::JackDriver(Raul::Forge& forge, SPtr<Machine> machine)
, _stop(0)
, _stop_flag(false)
, _record_dur(_frames_unit) // = 0
- , _recording(false)
, _is_activated(false)
{
_context.set_sink(this);
@@ -108,16 +107,12 @@ JackDriver::attach(const std::string& client_name)
new Machine(TimeUnit::frames(
jack_get_sample_rate(jack_client()))));
}
-
- _machine->activate();
}
}
void
JackDriver::detach()
{
- _machine->deactivate();
-
if (_input_port) {
jack_port_unregister(jack_client(), _input_port);
_input_port = NULL;
@@ -339,13 +334,18 @@ JackDriver::on_process(jack_nframes_t nframes)
machine->reset(_context.sink(), _context.time().start_beats());
}
- if (_recording) {
- read_input_recording(machine, _context.time());
- } else {
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ break;
+ case PlayState::PLAYING:
read_input_playing(machine, _context.time());
+ break;
+ case PlayState::RECORDING:
+ read_input_recording(machine, _context.time());
+ break;
}
- if (machine->is_empty() || !machine->is_activated()) {
+ if (machine->is_empty()) {
goto end;
}
@@ -392,36 +392,61 @@ end:
}
void
-JackDriver::stop()
+JackDriver::set_play_state(PlayState state)
{
- if (recording()) {
- finish_record();
+ switch (state) {
+ case PlayState::STOPPED:
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ break;
+ case PlayState::RECORDING:
+ finish_record();
+ // nobreak
+ case PlayState::PLAYING:
+ _stop_flag = true;
+ _stop.wait();
+ }
+ break;
+ case PlayState::RECORDING:
+ start_record(false); // FIXME: step record?
+ break;
+ case PlayState::PLAYING:
+ if (_play_state == PlayState::RECORDING) {
+ finish_record();
+ }
}
-
- _stop_flag = true;
- _stop.wait();
- _machine->deactivate();
+ _play_state = state;
}
void
JackDriver::start_record(bool step)
{
- // FIXME: Choose an appropriate maximum ringbuffer size
- _recorder = SPtr<Recorder>(
- new Recorder(_forge, 1024, _beats_unit, _quantization.get(), step));
- _recorder->start();
- _record_dur = 0;
- _recording = true;
+ if (_play_state.load() != PlayState::RECORDING) {
+ // FIXME: Choose an appropriate maximum ringbuffer size
+ _recorder = SPtr<Recorder>(
+ new Recorder(_forge, 1024, _beats_unit, _quantization.get(), step));
+ _recorder->start();
+ _record_dur = 0;
+ _play_state = PlayState::RECORDING;
+ }
}
void
JackDriver::finish_record()
{
- _recording = false;
+ _play_state = PlayState::PLAYING;
SPtr<Machine> machine = _recorder->finish();
_recorder.reset();
- machine->activate();
- _machine->nodes().insert(machine->nodes().begin(), machine->nodes().end());
+ for (const auto& m : machine->nodes()) {
+ if (m->is_initial()) {
+ for (const auto& e : m->edges()) {
+ e->set_tail(_machine->initial_node());
+ _machine->initial_node()->edges().insert(e);
+ }
+ } else {
+ _machine->nodes().insert(m);
+ }
+ }
}
int
diff --git a/src/engine/JackDriver.hpp b/src/engine/JackDriver.hpp
index 3045165..14e84ec 100644
--- a/src/engine/JackDriver.hpp
+++ b/src/engine/JackDriver.hpp
@@ -43,8 +43,7 @@ class Node;
* "Ticks" are individual frames when running under this driver, and all code
* in the processing context must be realtime safe (non-blocking).
*/
-class JackDriver
- : public machina::Driver
+class JackDriver : public machina::Driver
{
public:
JackDriver(Raul::Forge& forge,
@@ -66,11 +65,7 @@ public:
void set_bpm(double bpm) { _bpm.set(bpm); }
void set_quantization(double q) { _quantization.set(q); }
- void stop();
-
- bool recording() { return _recording.load(); }
- void start_record(bool step);
- void finish_record();
+ void set_play_state(PlayState state);
void start_transport() { jack_transport_start(_client); }
void stop_transport() { jack_transport_stop(_client); }
@@ -100,6 +95,9 @@ private:
static int jack_process_cb(jack_nframes_t nframes, void* me);
static void jack_shutdown_cb(void* me);
+ void start_record(bool step);
+ void finish_record();
+
void on_process(jack_nframes_t nframes);
jack_client_t* _client;
@@ -122,7 +120,6 @@ private:
bool _stop_flag;
Raul::TimeDuration _record_dur;
- std::atomic<bool> _recording;
SPtr<Recorder> _recorder;
bool _is_activated;
};
diff --git a/src/engine/Loader.cpp b/src/engine/Loader.cpp
index 0ee5ba7..3c05401 100644
--- a/src/engine/Loader.cpp
+++ b/src/engine/Loader.cpp
@@ -82,7 +82,7 @@ Loader::load(const Glib::ustring& uri)
Sord::Node nil;
Sord::URI rdf_type(_rdf_world, NS_RDF "type");
- Sord::URI machina_SelectorNode(_rdf_world, NS_MACHINA "initialNode");
+ Sord::URI machina_SelectorNode(_rdf_world, NS_MACHINA "SelectorNode");
Sord::URI machina_node(_rdf_world, NS_MACHINA "node");
Sord::URI machina_initialNode(_rdf_world, NS_MACHINA "initialNode");
Sord::URI machina_duration(_rdf_world, NS_MACHINA "duration");
@@ -192,7 +192,7 @@ Loader::load(const Glib::ustring& uri)
}
- if (machine && machine->nodes().size() > 0) {
+ if (machine && !machine->nodes().empty()) {
machine->reset(NULL, machine->time());
return machine;
} else {
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)
{
diff --git a/src/engine/MachineBuilder.cpp b/src/engine/MachineBuilder.cpp
index d8fcff1..0a515a4 100644
--- a/src/engine/MachineBuilder.cpp
+++ b/src/engine/MachineBuilder.cpp
@@ -92,7 +92,7 @@ MachineBuilder::connect_nodes(SPtr<Machine> m,
SPtr<Node> delay_node;
- if (is_delay_node(tail) && tail->edges().size() == 0) {
+ if (is_delay_node(tail) && tail->edges().empty()) {
// Tail is a delay node, just accumulate the time difference into it
set_node_duration(tail,
tail->duration() + head_start_time - tail_end_time);
@@ -176,7 +176,7 @@ MachineBuilder::resolve_note(Raul::TimeStamp t,
for (PolyList::iterator j = _poly_nodes.begin();
j != _poly_nodes.end(); ++j) {
_machine->add_node(j->second);
- if (j->second->edges().size() == 0) {
+ if (j->second->edges().empty()) {
connect_nodes(_machine, j->second,
j->first + j->second->duration(),
_connect_node, t);
@@ -238,7 +238,8 @@ MachineBuilder::event(Raul::TimeStamp time_offset,
} else if ((buf[0] & 0xF0) == LV2_MIDI_MSG_NOTE_OFF) {
for (ActiveList::iterator i = _active_nodes.begin();
i != _active_nodes.end(); ++i) {
- SPtr<MidiAction> action = dynamic_ptr_cast<MidiAction>((*i)->enter_action());
+ SPtr<MidiAction> action = dynamic_ptr_cast<MidiAction>(
+ (*i)->enter_action());
if (!action) {
continue;
}
@@ -293,8 +294,8 @@ MachineBuilder::resolve()
_active_nodes.clear();
}
- // Add initial note if necessary
- if (_machine->nodes().size() > 0) {
+ // Add initial node if necessary
+ if (!_machine->nodes().empty()) {
_machine->add_node(_initial_node);
}
}
diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp
index 962a3c7..9aba95a 100644
--- a/src/engine/Node.cpp
+++ b/src/engine/Node.cpp
@@ -131,29 +131,29 @@ Node::set_exit_action(SPtr<Action> action)
void
Node::enter(MIDISink* sink, TimeStamp time)
{
- assert(!_is_active);
+ if (!_is_active) {
+ _changed = true;
+ _is_active = true;
+ _enter_time = time;
- _changed = true;
- _is_active = true;
- _enter_time = time;
-
- if (sink && _enter_action) {
- _enter_action->execute(sink, time);
+ if (sink && _enter_action) {
+ _enter_action->execute(sink, time);
+ }
}
}
void
Node::exit(MIDISink* sink, TimeStamp time)
{
- assert(_is_active);
+ if (_is_active) {
+ if (sink && _exit_action) {
+ _exit_action->execute(sink, time);
+ }
- if (sink && _exit_action) {
- _exit_action->execute(sink, time);
+ _changed = true;
+ _is_active = false;
+ _enter_time = 0;
}
-
- _changed = true;
- _is_active = false;
- _enter_time = 0;
}
SPtr<Edge>
@@ -208,7 +208,7 @@ void
Node::set(URIInt key, const Raul::Atom& value)
{
if (key == URIs::instance().machina_initial) {
- set_initial(value.get_bool());
+ std::cerr << "error: Attempt to change node initial state" << std::endl;
} else if (key == URIs::instance().machina_selector) {
set_selector(value.get_bool());
}
diff --git a/src/engine/Node.hpp b/src/engine/Node.hpp
index 575f39e..7baff9b 100644
--- a/src/engine/Node.hpp
+++ b/src/engine/Node.hpp
@@ -68,12 +68,11 @@ public:
void write_state(Sord::Model& model);
bool is_initial() const { return _is_initial; }
- void set_initial(bool i) { _is_initial = i; }
bool is_active() const { return _is_active; }
TimeStamp enter_time() const { return _enter_time; }
TimeStamp exit_time() const { return _enter_time + _duration; }
TimeDuration duration() const { return _duration; }
- void set_duration(TimeDuration d) { _duration = d; }
+ void set_duration(TimeDuration d) { _duration = d; }
bool is_selector() const { return _is_selector; }
void set_selector(bool i);
diff --git a/src/engine/SMFDriver.hpp b/src/engine/SMFDriver.hpp
index df726e8..83892d5 100644
--- a/src/engine/SMFDriver.hpp
+++ b/src/engine/SMFDriver.hpp
@@ -56,6 +56,8 @@ public:
void set_bpm(double /*bpm*/) {}
void set_quantization(double /*quantization*/) {}
+ void set_play_state(PlayState state) {}
+
SPtr<SMFWriter> writer() { return _writer; }
private:
diff --git a/src/engine/machina/Driver.hpp b/src/engine/machina/Driver.hpp
index 3e65c9d..84a6cfd 100644
--- a/src/engine/machina/Driver.hpp
+++ b/src/engine/machina/Driver.hpp
@@ -18,6 +18,8 @@
#ifndef MACHINA_DRIVER_HPP
#define MACHINA_DRIVER_HPP
+#include <atomic>
+
#include "raul/RingBuffer.hpp"
#include "machina/types.hpp"
@@ -28,15 +30,21 @@ namespace machina {
class Machine;
-class Driver
- : public MIDISink
+class Driver : public MIDISink
{
public:
Driver(Raul::Forge& forge, SPtr<Machine> machine)
: _forge(forge)
, _machine(machine)
+ , _play_state(PlayState::STOPPED)
{}
+ enum class PlayState {
+ STOPPED,
+ PLAYING,
+ RECORDING
+ };
+
virtual ~Driver() {}
SPtr<Machine> machine() { return _machine; }
@@ -51,23 +59,21 @@ public:
_updates = b;
}
- virtual void set_bpm(double bpm) = 0;
- virtual void set_quantization(double q) = 0;
+ virtual void set_bpm(double bpm) = 0;
+ virtual void set_quantization(double q) = 0;
+ virtual void set_play_state(PlayState state) = 0;
virtual bool is_activated() const { return false; }
virtual void activate() {}
virtual void deactivate() {}
- virtual void stop() {}
-
- virtual bool recording() { return false; }
- virtual void start_record(bool step) {}
- virtual void finish_record() {}
+ PlayState play_state() const { return _play_state.load(); }
protected:
Raul::Forge& _forge;
SPtr<Machine> _machine;
SPtr<Raul::RingBuffer> _updates;
+ std::atomic<PlayState> _play_state;
};
} // namespace machina
diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp
index e82f992..8a98e4c 100644
--- a/src/engine/machina/Machine.hpp
+++ b/src/engine/machina/Machine.hpp
@@ -43,20 +43,18 @@ class Machine
{
public:
Machine(TimeUnit unit);
+
+ /** Copy a Machine.
+ *
+ * Creates a deep copy which is the 'same' machine, but with fresh state,
+ * i.e. all nodes are inactive and time is at zero.
+ */
Machine(const Machine& copy);
Machine& operator=(const Machine& other);
- // Kluge to appease Eugene
- bool operator==(const Machine& other) { return false; }
-
- // Main context
- void activate() { _is_activated = true; }
- void deactivate() { _is_activated = false; }
-
- bool is_empty() { return _nodes.empty(); }
- bool is_finished() { return _is_finished; }
- bool is_activated() { return _is_activated; }
+ bool is_empty() { return _nodes.empty(); }
+ bool is_finished() { return _is_finished; }
void add_node(SPtr<Node> node);
void remove_node(SPtr<Node> node);
@@ -64,8 +62,19 @@ public:
void write_state(Sord::Model& model);
- // Audio context
- void reset(MIDISink* sink, Raul::TimeStamp time);
+ /** Exit all active nodes and reset time to 0.
+ */
+ void reset(MIDISink* sink, Raul::TimeStamp time);
+
+ /** Run the machine for a (real) time slice.
+ *
+ * Returns the duration of time the machine actually ran in frames.
+ *
+ * Caller can check is_finished() to determine if the machine still has any
+ * active nodes. 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 run(Context& context, SPtr<Raul::RingBuffer> updates);
// Any context
@@ -78,6 +87,8 @@ public:
Nodes& nodes() { return _nodes; }
const Nodes& nodes() const { return _nodes; }
+ SPtr<Node> initial_node() const { return _initial_node; }
+
SPtr<Node> random_node();
SPtr<Edge> random_edge();
@@ -85,6 +96,8 @@ private:
// Audio context
SPtr<Node> earliest_node() const;
+ void assign(const Machine& other);
+
bool enter_node(Context& context,
SPtr<Node> node,
SPtr<Raul::RingBuffer> updates);
@@ -95,12 +108,12 @@ private:
static const size_t MAX_ACTIVE_NODES = 128;
+ SPtr<Node> _initial_node;
std::vector< SPtr<Node> > _active_nodes;
SPtr<LearnRequest> _pending_learn;
Nodes _nodes;
Raul::TimeStamp _time;
- bool _is_activated;
bool _is_finished;
};