aboutsummaryrefslogtreecommitdiffstats
path: root/src
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
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')
-rw-r--r--src/client/ClientModel.cpp9
-rw-r--r--src/client/ClientObject.cpp2
-rw-r--r--src/client/ClientObject.hpp16
-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
-rw-r--r--src/gui/EdgeView.cpp11
-rw-r--r--src/gui/EdgeView.hpp6
-rw-r--r--src/gui/MachinaCanvas.cpp65
-rw-r--r--src/gui/MachinaCanvas.hpp19
-rw-r--r--src/gui/MachinaGUI.cpp74
-rw-r--r--src/gui/MachinaGUI.hpp7
-rw-r--r--src/gui/NodeView.cpp20
-rw-r--r--src/gui/NodeView.hpp2
-rw-r--r--src/gui/machina.ui66
25 files changed, 439 insertions, 428 deletions
diff --git a/src/client/ClientModel.cpp b/src/client/ClientModel.cpp
index 099d997..e033509 100644
--- a/src/client/ClientModel.cpp
+++ b/src/client/ClientModel.cpp
@@ -35,8 +35,11 @@ ClientModel::find(uint64_t id)
void
ClientModel::new_object(SPtr<ClientObject> object)
{
- _objects.insert(object);
- signal_new_object.emit(object);
+ Objects::iterator i = _objects.find(object);
+ if (i == _objects.end()) {
+ _objects.insert(object);
+ signal_new_object.emit(object);
+ }
}
void
@@ -49,7 +52,7 @@ ClientModel::erase_object(uint64_t id)
}
signal_erase_object.emit(*i);
- (*i)->set_view(SPtr<ClientObject::View>());
+ (*i)->set_view(NULL);
_objects.erase(i);
}
diff --git a/src/client/ClientObject.cpp b/src/client/ClientObject.cpp
index 9b7c319..199cf90 100644
--- a/src/client/ClientObject.cpp
+++ b/src/client/ClientObject.cpp
@@ -24,10 +24,12 @@ namespace client {
ClientObject::ClientObject(uint64_t id)
: _id(id)
+ , _view(NULL)
{}
ClientObject::ClientObject(const ClientObject& copy, uint64_t id)
: _id(id)
+ , _view(NULL)
, _properties(copy._properties)
{}
diff --git a/src/client/ClientObject.hpp b/src/client/ClientObject.hpp
index c9469ed..e823a64 100644
--- a/src/client/ClientObject.hpp
+++ b/src/client/ClientObject.hpp
@@ -51,22 +51,20 @@ public:
typedef std::map<URIInt, Raul::Atom> Properties;
const Properties& properties() { return _properties; }
- SPtr<View> view() const { return _view; }
- void set_view(SPtr<View> view) { _view = view; }
+ View* view() const { return _view; }
+ void set_view(View* view) { _view = view; }
private:
- uint64_t _id;
- SPtr<View> _view;
+ uint64_t _id;
+ View* _view;
Properties _properties;
};
-class ClientObjectKey
- : public ClientObject
+/** Stub client object to use as a search key. */
+struct ClientObjectKey : public ClientObject
{
-public:
- ClientObjectKey(uint64_t id)
- : ClientObject(id) {}
+ ClientObjectKey(uint64_t id) : ClientObject(id) {}
};
}
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;
};
diff --git a/src/gui/EdgeView.cpp b/src/gui/EdgeView.cpp
index ec77a7b..da267e8 100644
--- a/src/gui/EdgeView.cpp
+++ b/src/gui/EdgeView.cpp
@@ -65,10 +65,10 @@ inline static uint32_t edge_color(float prob)
using namespace Ganv;
EdgeView::EdgeView(Canvas& canvas,
- SPtr<NodeView> src,
- SPtr<NodeView> dst,
+ NodeView* src,
+ NodeView* dst,
SPtr<machina::client::ClientObject> edge)
- : Ganv::Edge(canvas, src.get(), dst.get(), 0x9FA0A0F4, true, false)
+ : Ganv::Edge(canvas, src, dst, 0x9FA0A0F4, true, false)
, _edge(edge)
{
set_color(edge_color(probability()));
@@ -80,6 +80,11 @@ EdgeView::EdgeView(Canvas& canvas,
sigc::mem_fun(this, &EdgeView::on_event));
}
+EdgeView::~EdgeView()
+{
+ _edge->set_view(NULL);
+}
+
float
EdgeView::probability() const
{
diff --git a/src/gui/EdgeView.hpp b/src/gui/EdgeView.hpp
index 6d51427..dbf4476 100644
--- a/src/gui/EdgeView.hpp
+++ b/src/gui/EdgeView.hpp
@@ -35,10 +35,12 @@ class EdgeView
{
public:
EdgeView(Ganv::Canvas& canvas,
- SPtr<NodeView> src,
- SPtr<NodeView> dst,
+ NodeView* src,
+ NodeView* dst,
SPtr<machina::client::ClientObject> edge);
+ ~EdgeView();
+
void show_label(bool show);
virtual double length_hint() const;
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
index 627eb09..7a7efa9 100644
--- a/src/gui/MachinaCanvas.cpp
+++ b/src/gui/MachinaCanvas.cpp
@@ -39,6 +39,7 @@ namespace gui {
MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
: Canvas(width, height)
, _app(app)
+ , _last_clicked(NULL)
{
widget().grab_focus();
@@ -47,13 +48,8 @@ MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
}
bool
-MachinaCanvas::node_clicked(WPtr<NodeView> item, GdkEventButton* event)
+MachinaCanvas::node_clicked(NodeView* node, GdkEventButton* event)
{
- SPtr<NodeView> node = dynamic_ptr_cast<NodeView>(item.lock());
- if (!node) {
- return false;
- }
-
if (event->state & GDK_CONTROL_MASK) {
return false;
}
@@ -65,19 +61,17 @@ MachinaCanvas::node_clicked(WPtr<NodeView> item, GdkEventButton* event)
} else if (event->button == 1) {
// Left click: connect/disconnect
- SPtr<NodeView> last = _last_clicked.lock();
-
- if (last) {
- if (node != last) {
- if (get_edge(last.get(), node.get())) {
- action_disconnect(last, node);
+ if (_last_clicked) {
+ if (node != _last_clicked) {
+ if (get_edge(_last_clicked, node)) {
+ action_disconnect(_last_clicked, node);
} else {
- action_connect(last, node);
+ action_connect(_last_clicked, node);
}
}
- last->set_default_colors();
- _last_clicked.reset();
+ _last_clicked->set_default_colors();
+ _last_clicked = NULL;
} else {
_last_clicked = node;
@@ -112,17 +106,28 @@ MachinaCanvas::on_new_object(SPtr<client::ClientObject> object)
}
if (type.get<URIInt>() == uris.machina_Node) {
- SPtr<NodeView> view(
- new NodeView(_app->window(), *this, object,
- object->get(uris.machina_canvas_x).get_float(),
- object->get(uris.machina_canvas_y).get_float()));
+ const Raul::Atom& node_x = object->get(uris.machina_canvas_x);
+ const Raul::Atom& node_y = object->get(uris.machina_canvas_y);
+ float x, y;
+ if (node_x.type() == _app->forge().Float &&
+ node_y.type() == _app->forge().Float) {
+ x = node_x.get_float();
+ y = node_y.get_float();
+ } else {
+ int scroll_x, scroll_y;
+ get_scroll_offsets(scroll_x, scroll_y);
+ x = scroll_x + 64.0;
+ y = scroll_y + 64.0;
+ }
+
+ NodeView* view = new NodeView(_app->window(), *this, object, x, y);
//if ( ! node->enter_action() && ! node->exit_action() )
// view->set_base_color(0x101010FF);
view->signal_clicked().connect(
sigc::bind<0>(sigc::mem_fun(this, &MachinaCanvas::node_clicked),
- WPtr<NodeView>(view)));
+ view));
object->set_view(view);
@@ -141,12 +146,10 @@ MachinaCanvas::on_new_object(SPtr<client::ClientObject> object)
return;
}
- SPtr<NodeView> tail_view = dynamic_ptr_cast<NodeView>(tail->view());
- SPtr<NodeView> head_view = dynamic_ptr_cast<NodeView>(head->view());
+ NodeView* tail_view = dynamic_cast<NodeView*>(tail->view());
+ NodeView* head_view = dynamic_cast<NodeView*>(head->view());
- SPtr<EdgeView> view(new EdgeView(*this, tail_view, head_view, object));
-
- object->set_view(view);
+ object->set_view(new EdgeView(*this, tail_view, head_view, object));
} else {
std::cerr << "Unknown object type " << type.get<URIInt>() << std::endl;
@@ -160,7 +163,7 @@ MachinaCanvas::on_erase_object(SPtr<client::ClientObject> object)
if (type.get<URIInt>() == URIs::instance().machina_Node) {
// Destruction of the view will remove from the canvas
} else if (type.get<URIInt>() == URIs::instance().machina_Edge) {
- object->set_view(SPtr<client::ClientObject::View>());
+ object->set_view(NULL);
} else {
std::cerr << "Unknown object type" << std::endl;
}
@@ -179,17 +182,15 @@ MachinaCanvas::action_create_node(double x, double y)
}
void
-MachinaCanvas::action_connect(SPtr<NodeView> src,
- SPtr<NodeView> head)
+MachinaCanvas::action_connect(NodeView* tail, NodeView* head)
{
- _app->controller()->connect(src->node()->id(), head->node()->id());
+ _app->controller()->connect(tail->node()->id(), head->node()->id());
}
void
-MachinaCanvas::action_disconnect(SPtr<NodeView> src,
- SPtr<NodeView> head)
+MachinaCanvas::action_disconnect(NodeView* tail, NodeView* head)
{
- _app->controller()->disconnect(src->node()->id(), head->node()->id());
+ _app->controller()->disconnect(tail->node()->id(), head->node()->id());
}
} // namespace machina
diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp
index c396931..52e2d74 100644
--- a/src/gui/MachinaCanvas.hpp
+++ b/src/gui/MachinaCanvas.hpp
@@ -40,9 +40,6 @@ class MachinaCanvas
public:
MachinaCanvas(MachinaGUI* app, int width, int height);
- //void build(SPtr<const machina::Machine> machine, bool show_labels);
- //void update_edges();
-
void on_new_object(SPtr<machina::client::ClientObject> object);
void on_erase_object(SPtr<machina::client::ClientObject> object);
@@ -51,21 +48,15 @@ public:
protected:
bool on_event(GdkEvent* event);
- bool node_clicked(WPtr<NodeView> item, GdkEventButton* ev);
+ bool node_clicked(NodeView* node, GdkEventButton* ev);
private:
- //SPtr<NodeView> create_node_view(SPtr<machina::Node> node);
-
void action_create_node(double x, double y);
+ void action_connect(NodeView* tail, NodeView* head);
+ void action_disconnect(NodeView* tail, NodeView* head);
- void action_connect(SPtr<NodeView> port1,
- SPtr<NodeView> port2);
-
- void action_disconnect(SPtr<NodeView> port1,
- SPtr<NodeView> port2);
-
- MachinaGUI* _app;
- WPtr<NodeView> _last_clicked;
+ MachinaGUI* _app;
+ NodeView* _last_clicked;
};
} // namespace machina
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 63cbb76..f95eec2 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -102,8 +102,8 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_record_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::record_toggled));
- _stop_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::stop_clicked));
+ _stop_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::stop_toggled));
_play_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::play_toggled));
@@ -199,13 +199,12 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_evolve_toolbar->set_sensitive(false);
#endif
- _controller->announce(engine->machine());
- _canvas->arrange();
-
_client_model->signal_new_object.connect(
sigc::mem_fun(this, &MachinaGUI::on_new_object));
_client_model->signal_erase_object.connect(
sigc::mem_fun(this, &MachinaGUI::on_erase_object));
+
+ rebuild_canvas();
}
MachinaGUI::~MachinaGUI()
@@ -294,7 +293,7 @@ MachinaGUI::evolve_toggled()
_evolver = SPtr<Evolver>(
new Evolver(_unit, _target_filename, _engine->machine()));
_evolve = true;
- stop_clicked();
+ stop_toggled();
_engine->driver()->set_machine(SPtr<Machine>());
_evolver->start();
} else {
@@ -365,16 +364,24 @@ MachinaGUI::mutate(SPtr<Machine> machine, unsigned mutation)
void
MachinaGUI::update_toolbar()
{
- _record_button->set_active(_engine->driver()->recording());
- _play_button->set_active(_engine->machine()->is_activated());
+ const Driver::PlayState state = _engine->driver()->play_state();
+ _record_button->set_active(state == Driver::PlayState::RECORDING);
+ _play_button->set_active(state == Driver::PlayState::PLAYING);
_quantize_spinbutton->set_sensitive(_quantize_checkbutton->get_active());
}
void
+MachinaGUI::rebuild_canvas()
+{
+ _controller->announce(_engine->machine());
+ _canvas->arrange();
+}
+
+void
MachinaGUI::quantize_changed()
{
if (_quantize_checkbutton->get_active()) {
- _engine->set_quantization(1/(double)_quantize_spinbutton->get_value_as_int());
+ _engine->set_quantization(1.0 / _quantize_spinbutton->get_value());
} else {
_engine->set_quantization(0.0);
}
@@ -426,9 +433,7 @@ MachinaGUI::menu_file_open()
if (result == Gtk::RESPONSE_OK) {
SPtr<machina::Machine> new_machine = _engine->load_machine(dialog.get_uri());
if (new_machine) {
- _canvas->destroy();
- _controller->announce(new_machine);
- _canvas->arrange();
+ rebuild_canvas();
_save_uri = dialog.get_uri();
}
}
@@ -536,10 +541,9 @@ MachinaGUI::menu_import_midi()
if (machine) {
dialog.hide();
- machine->activate();
machine->reset(NULL, machine->time());
_engine->driver()->set_machine(machine);
- _controller->announce(machine);
+ rebuild_canvas();
} else {
Gtk::MessageDialog msg_dialog(dialog, "Error loading MIDI file",
false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
@@ -651,41 +655,35 @@ MachinaGUI::menu_help_help()
}
void
-MachinaGUI::record_toggled()
+MachinaGUI::stop_toggled()
{
- if (_record_button->get_active() && ! _engine->driver()->recording()) {
- _engine->driver()->start_record(_step_record_checkbutton->get_active());
- } else if (_engine->driver()->recording()) {
- _engine->driver()->finish_record();
- _controller->announce(_engine->machine());
- update_toolbar();
+ if (_stop_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::STOPPED);
+ if (old_state == Driver::PlayState::RECORDING) {
+ rebuild_canvas();
+ }
}
}
void
-MachinaGUI::stop_clicked()
+MachinaGUI::play_toggled()
{
- _play_button->set_active(false);
-
- if (_engine->driver()->recording()) {
- _engine->driver()->stop();
- _engine->machine()->deactivate();
- _controller->announce(_engine->machine());
- } else {
- _engine->driver()->stop();
- _engine->machine()->deactivate();
+ if (_play_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::PLAYING);
+ if (old_state == Driver::PlayState::RECORDING) {
+ rebuild_canvas();
+ }
}
-
- update_toolbar();
}
void
-MachinaGUI::play_toggled()
+MachinaGUI::record_toggled()
{
- if (_play_button->get_active())
- _engine->machine()->activate();
- else
- _engine->machine()->deactivate();
+ if (_record_button->get_active()) {
+ _engine->driver()->set_play_state(Driver::PlayState::RECORDING);
+ }
}
void
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
index 6c85dca..c06d6b3 100644
--- a/src/gui/MachinaGUI.hpp
+++ b/src/gui/MachinaGUI.hpp
@@ -95,6 +95,7 @@ protected:
void mutate(SPtr<machina::Machine> machine, unsigned mutation);
void zoom(double z);
void update_toolbar();
+ void rebuild_canvas();
bool scrolled_window_event(GdkEvent* ev);
bool idle_callback();
@@ -104,9 +105,9 @@ protected:
bool evolve_callback();
#endif
- void record_toggled();
- void stop_clicked();
+ void stop_toggled();
void play_toggled();
+ void record_toggled();
void quantize_changed();
void tempo_changed();
@@ -155,7 +156,7 @@ protected:
Gtk::CheckButton* _quantize_checkbutton;
Gtk::SpinButton* _quantize_spinbutton;
Gtk::ToggleToolButton* _record_button;
- Gtk::ToolButton* _stop_button;
+ Gtk::ToggleToolButton* _stop_button;
Gtk::ToggleToolButton* _play_button;
Gtk::ToolButton* _zoom_normal_button;
Gtk::ToolButton* _zoom_full_button;
diff --git a/src/gui/NodeView.cpp b/src/gui/NodeView.cpp
index a73b996..d1bd913 100644
--- a/src/gui/NodeView.cpp
+++ b/src/gui/NodeView.cpp
@@ -45,6 +45,12 @@ NodeView::NodeView(Gtk::Window* window,
signal_event().connect(
sigc::mem_fun(this, &NodeView::on_event));
+ MachinaCanvas* mcanvas = dynamic_cast<MachinaCanvas*>(&canvas);
+ if (is(mcanvas->app()->forge(), URIs::instance().machina_initial)) {
+ set_border_width(4.0);
+ set_label("init");
+ }
+
node->signal_property.connect(
sigc::mem_fun(this, &NodeView::on_property));
@@ -53,6 +59,11 @@ NodeView::NodeView(Gtk::Window* window,
}
}
+NodeView::~NodeView()
+{
+ _node->set_view(NULL);
+}
+
bool
NodeView::on_double_click(GdkEventButton*)
{
@@ -77,12 +88,6 @@ NodeView::on_event(GdkEvent* event)
if (event->button.button == 1) {
canvas->app()->controller()->set_property(
_node->id(),
- URIs::instance().machina_initial,
- forge.make(!is(forge, URIs::instance().machina_initial)));
- return true;
- } else if (event->button.button == 3) {
- canvas->app()->controller()->set_property(
- _node->id(),
URIs::instance().machina_selector,
forge.make(!is(forge, URIs::instance().machina_selector)));
return true;
@@ -133,7 +138,6 @@ NodeView::on_property(machina::URIInt key, const Raul::Atom& value)
static const uint32_t active_border_color = 0x00FF00FF;
if (key == URIs::instance().machina_selector) {
- //_node.property_dash() = value.get_bool() ? selector_dash() : 0;
if (value.get_bool()) {
set_dash_length(4.0);
} else {
@@ -162,8 +166,6 @@ NodeView::on_property(machina::URIInt key, const Raul::Atom& value)
on_action_property(i.first, i.second);
}
}
- } else {
- cout << "Unknown property " << key << endl;
}
}
diff --git a/src/gui/NodeView.hpp b/src/gui/NodeView.hpp
index 17f8c44..6ad68fb 100644
--- a/src/gui/NodeView.hpp
+++ b/src/gui/NodeView.hpp
@@ -38,6 +38,8 @@ public:
double x,
double y);
+ ~NodeView();
+
SPtr<machina::client::ClientObject> node() { return _node; }
void show_label(bool show);
diff --git a/src/gui/machina.ui b/src/gui/machina.ui
index b48df7d..bb1b711 100644
--- a/src/gui/machina.ui
+++ b/src/gui/machina.ui
@@ -2,15 +2,29 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkRadioAction" id="record_action">
+ <property name="stock_id">gtk-media-record</property>
+ <property name="draw_as_radio">True</property>
+ </object>
+ <object class="GtkRadioAction" id="play_action">
+ <property name="stock_id">gtk-media-play</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
+ <object class="GtkRadioAction" id="stop_action">
+ <property name="stock_id">gtk-media-stop</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
<object class="GtkAboutDialog" id="about_win">
<property name="can_focus">False</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">normal</property>
+ <property name="program_name">Machina</property>
<property name="copyright" translatable="yes">© 2013 David Robillard &lt;http://drobilla.net&gt;</property>
<property name="comments" translatable="yes">A MIDI sequencer based on
probabilistic finite-state automata</property>
<property name="website">http://drobilla.net/software/machina</property>
- <property name="website_label" translatable="yes">Website:</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
@@ -99,17 +113,24 @@ along with Machina; if not, write to the Free Software Foundation, Inc.,
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="label" translatable="yes">Initial nodes are shown with a thick border.
-Selector nodes are shown in green.
+ <property name="label" translatable="yes">Nodes represent notes, which have a pitch and duration. When a node is
+highlighted green, it is playing. After a node is finished playing, play
+travels along the outgoing arcs, depending on their probability.
+
+Nodes with dashed borders are selectors. Only one successor is played after a
+selector, i.e. only one outgoing arc is traversed.
+
+A machine can be constructed with the mouse or by recording MIDI input. To
+record, press the record button and play some MIDI notes. To finish recording,
+press stop or play and the new nodes will be added to the machine.
-- Right 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 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
-- Ctrl+Left click edge probabilities to decrease
-- Ctrl+Right click edge probabilities to increase</property>
+• Right click the canvas to create a new node
+• Middle click a node to learn a MIDI note
+• Click two nodes in succession to connect them
+• Double click a node to show its properties dialog
+• Ctrl+Left click a node to make it a selector
+• Ctrl+Left click an edge to decrease its probability
+• Ctrl+Right click an edge to increase its probability</property>
</object>
<packing>
<property name="expand">True</property>
@@ -353,12 +374,11 @@ Selector nodes are shown in green.
<property name="toolbar_style">icons</property>
<property name="show_arrow">False</property>
<child>
- <object class="GtkToggleToolButton" id="record_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="stop_but">
+ <property name="related_action">stop_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Record</property>
- <property name="stock_id">gtk-media-record</property>
+ <property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -366,12 +386,12 @@ Selector nodes are shown in green.
</packing>
</child>
<child>
- <object class="GtkToolButton" id="stop_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="play_but">
+ <property name="related_action">play_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Stop</property>
- <property name="stock_id">gtk-media-stop</property>
+ <property name="tooltip_text" translatable="yes">Play</property>
+ <property name="active">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -379,13 +399,11 @@ Selector nodes are shown in green.
</packing>
</child>
<child>
- <object class="GtkToggleToolButton" id="play_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="record_but">
+ <property name="related_action">record_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Play</property>
- <property name="stock_id">gtk-media-play</property>
- <property name="active">True</property>
+ <property name="tooltip_text" translatable="yes">Record</property>
</object>
<packing>
<property name="expand">False</property>