diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/ClientModel.cpp | 9 | ||||
-rw-r--r-- | src/client/ClientObject.cpp | 2 | ||||
-rw-r--r-- | src/client/ClientObject.hpp | 16 | ||||
-rw-r--r-- | src/engine/Controller.cpp | 105 | ||||
-rw-r--r-- | src/engine/Edge.cpp | 5 | ||||
-rw-r--r-- | src/engine/Engine.cpp | 4 | ||||
-rw-r--r-- | src/engine/JackDriver.cpp | 77 | ||||
-rw-r--r-- | src/engine/JackDriver.hpp | 13 | ||||
-rw-r--r-- | src/engine/Loader.cpp | 4 | ||||
-rw-r--r-- | src/engine/Machine.cpp | 253 | ||||
-rw-r--r-- | src/engine/MachineBuilder.cpp | 11 | ||||
-rw-r--r-- | src/engine/Node.cpp | 30 | ||||
-rw-r--r-- | src/engine/Node.hpp | 3 | ||||
-rw-r--r-- | src/engine/SMFDriver.hpp | 2 | ||||
-rw-r--r-- | src/engine/machina/Driver.hpp | 24 | ||||
-rw-r--r-- | src/engine/machina/Machine.hpp | 39 | ||||
-rw-r--r-- | src/gui/EdgeView.cpp | 11 | ||||
-rw-r--r-- | src/gui/EdgeView.hpp | 6 | ||||
-rw-r--r-- | src/gui/MachinaCanvas.cpp | 65 | ||||
-rw-r--r-- | src/gui/MachinaCanvas.hpp | 19 | ||||
-rw-r--r-- | src/gui/MachinaGUI.cpp | 74 | ||||
-rw-r--r-- | src/gui/MachinaGUI.hpp | 7 | ||||
-rw-r--r-- | src/gui/NodeView.cpp | 20 | ||||
-rw-r--r-- | src/gui/NodeView.hpp | 2 | ||||
-rw-r--r-- | src/gui/machina.ui | 66 |
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 (© == 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 <http://drobilla.net></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> |