From 9d657891ea68ab979bb8c1877bfea656db0bdb1e Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 25 Feb 2007 04:11:07 +0000 Subject: More serialization work (loading, saving works from GUI). git-svn-id: http://svn.drobilla.net/lad/machina@338 a436a847-0d15-0410-975c-d299462d15a1 --- src/engine/Engine.cpp | 14 +++++++++ src/engine/JackDriver.cpp | 45 +++++++++++++++++--------- src/engine/Loader.cpp | 66 ++++++++++++++++++++++++--------------- src/engine/Machine.cpp | 6 ++-- src/engine/Node.cpp | 6 ++-- src/engine/machina/Engine.hpp | 4 ++- src/engine/machina/JackDriver.hpp | 4 ++- src/engine/machina/Machine.hpp | 4 ++- src/engine/machina/Node.hpp | 6 ++-- src/gui/MachinaCanvas.cpp | 63 ++++++++++++++++++++++++++++++++----- src/gui/MachinaCanvas.hpp | 4 ++- src/gui/MachinaGUI.cpp | 19 +++++++++-- 12 files changed, 177 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index d659626..31c98f2 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -15,12 +15,26 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include "machina/Loader.hpp" #include "machina/Engine.hpp" #include "machina/JackDriver.hpp" namespace Machina { +/** Load the machine at @a uri, and run it (replacing current machine). + * Safe to call while engine is processing. + */ +SharedPtr +Engine::load_machine(const Glib::ustring& uri) +{ + Loader l; // FIXME: namespaces? + SharedPtr m = l.load(uri); + _machine = m; + return m; +} + + void Engine::set_bpm(double bpm) { diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp index 404d972..1056820 100644 --- a/src/engine/JackDriver.cpp +++ b/src/engine/JackDriver.cpp @@ -80,7 +80,7 @@ JackDriver::detach() void -JackDriver::process_input(const TimeSlice& time) +JackDriver::process_input(SharedPtr machine, const TimeSlice& time) { // We only actually read Jack input at the beginning of a cycle assert(time.offset_ticks() == 0); @@ -98,25 +98,25 @@ JackDriver::process_input(const TimeSlice& time) if (ev.buffer[0] == 0x90) { - const SharedPtr learn = _machine->pending_learn(); + const SharedPtr learn = machine->pending_learn(); if (learn) { learn->enter_action()->set_event(ev.size, ev.buffer); cerr << "LEARN START\n"; learn->start(_quantization.get(), time.ticks_to_beats(jack_last_frame_time(_client) + ev.time)); - //LearnRecord learn = _machine->pop_learn(); + //LearnRecord learn = machine->pop_learn(); } } else if (ev.buffer[0] == 0x80) { - const SharedPtr learn = _machine->pending_learn(); + const SharedPtr learn = machine->pending_learn(); if (learn) { if (learn->started()) { learn->exit_action()->set_event(ev.size, ev.buffer); learn->finish( time.ticks_to_beats(jack_last_frame_time(_client) + ev.time)); - _machine->clear_pending_learn(); + machine->clear_pending_learn(); cerr << "LEARNED!\n"; } } @@ -158,33 +158,44 @@ JackDriver::write_event(Raul::BeatTime time, void JackDriver::on_process(jack_nframes_t nframes) { - using namespace std; + //using namespace std; //std::cerr << "> ======================================================\n"; - + _cycle_time.set_bpm(_bpm.get()); - // Start time set at end of previous cycle + // (start time set at end of previous cycle) _cycle_time.set_offset(0); _cycle_time.set_length(nframes); assert(_output_port); jack_midi_clear_buffer(jack_port_get_buffer(_output_port, nframes), nframes); + + /* Take a reference to machine here and use only it during the process + * cycle so _machine can be switched with set_machine during a cycle. */ + SharedPtr machine = _machine; + + // Machine was switched since last cycle, finalize old machine. + if (_last_machine && machine != _last_machine) { + _last_machine->reset(); // Exit all active states + assert(_last_machine.use_count() > 1); // Realtime, can't delete + _last_machine.reset(); // Cut our reference + } - process_input(_cycle_time); + process_input(machine, _cycle_time); - if (_machine->is_empty()) { + if (machine->is_empty()) { //cerr << "EMPTY\n"; return; } while (true) { - const BeatCount run_dur_beats = _machine->run(_cycle_time); + const BeatCount run_dur_beats = machine->run(_cycle_time); const TickCount run_dur_ticks = _cycle_time.beats_to_ticks(run_dur_beats); // Machine didn't run at all (empty, or no initial states) if (run_dur_ticks == 0) { - _machine->reset(); // Try again next cycle + machine->reset(); // Try again next cycle _cycle_time.set_start(0); return; @@ -193,7 +204,7 @@ JackDriver::on_process(jack_nframes_t nframes) const TickCount finish_offset = _cycle_time.offset_ticks() + run_dur_ticks; assert(finish_offset < nframes); - _machine->reset(); + machine->reset(); _cycle_time.set_start(0); _cycle_time.set_length(nframes - finish_offset); @@ -201,8 +212,8 @@ JackDriver::on_process(jack_nframes_t nframes) // Machine ran for entire cycle } else { - if (_machine->is_finished()) { - _machine->reset(); + if (machine->is_finished()) { + machine->reset(); _cycle_time.set_start(0); } else { _cycle_time.set_start( @@ -212,6 +223,10 @@ JackDriver::on_process(jack_nframes_t nframes) break; } } + + /* Remember the last machine run, in case a switch happens and + * we need to finalize it next cycle. */ + _last_machine = machine; //std::cerr << "< ======================================================\n"; } diff --git a/src/engine/Loader.cpp b/src/engine/Loader.cpp index ec884eb..078d783 100644 --- a/src/engine/Loader.cpp +++ b/src/engine/Loader.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -45,30 +46,33 @@ Loader::Loader(SharedPtr namespaces) /** Load (create) all objects from an RDF into the engine. * - * @param filename Filename to load objects from. - * @param parent Path of parent under which to load objects. - * @return whether or not load was successful. + * @param uri URI of machine (e.g. resolvable URI to an RDF document). + * @return Loaded Machine. */ SharedPtr -Loader::load(const Glib::ustring& filename) +Loader::load(const Glib::ustring& uri) { using Raul::RDFQuery; SharedPtr machine; rasqal_init(); - unsigned char* document_uri_str = raptor_uri_filename_to_uri_string(filename.c_str()); - assert(document_uri_str); - raptor_uri* document_raptor_uri = raptor_new_uri(document_uri_str); + //unsigned char* document_uri_str = raptor_uri_filename_to_uri_string(filename.c_str()); + //assert(document_uri_str); + //raptor_uri* document_raptor_uri = raptor_new_uri(document_uri_str); + raptor_uri* document_raptor_uri = raptor_new_uri((const unsigned char*)uri.c_str()); - if (!document_uri_str) - return machine; + if (!document_raptor_uri) + return machine; // NULL machine = SharedPtr(new Machine()); - Glib::ustring document_uri = (const char*)document_uri_str; + //Glib::ustring document_uri = (const char*)document_uri_str; + const Glib::ustring& document_uri = uri; - string machine_uri = "<> "; + //string machine_uri = "<> "; + //string machine_uri = string("<") + document_uri + "> "; + string machine_uri = "?foo "; cout << "[Loader] Loading " << machine_uri << " from " << document_uri << endl; @@ -79,18 +83,16 @@ Loader::load(const Glib::ustring& filename) /* Get initial nodes */ Raul::RDFQuery query = Raul::RDFQuery(*_namespaces, Glib::ustring( - "SELECT DISTINCT ?initialNode ?midiNote ?duration FROM <") + "SELECT DISTINCT ?initialNode ?duration FROM <") + document_uri + "> WHERE {\n" + machine_uri + " :initialNode ?initialNode .\n" - "?initialNode :midiNote ?midiNote ;\n" - " :duration ?duration .\n" + "?initialNode :duration ?duration .\n" "}\n"); RDFQuery::Results results = query.run(document_uri); for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) { const Glib::ustring& node_uri = (*i)["initialNode"]; - const Glib::ustring& midi_note = (*i)["midiNote"]; const Glib::ustring& duration = (*i)["duration"]; raptor_uri* node_raptor_uri @@ -99,7 +101,7 @@ Loader::load(const Glib::ustring& filename) char* node_name = (char*) raptor_uri_to_relative_uri_string(document_raptor_uri, node_raptor_uri); - //cout << "Initial: " << node_name << ": " << midi_note << " - " << duration << endl; + cout << "Initial: " << node_name << " - " << duration << endl; cerr << "FIXME: load\n"; /* @@ -114,6 +116,10 @@ Loader::load(const Glib::ustring& filename) created.insert(std::make_pair(node_uri.collate_key(), node)); */ + + SharedPtr node(new Node(strtod(duration.c_str(), NULL), true)); + machine->add_node(node); + created.insert(std::make_pair(node_uri.collate_key(), node)); raptor_free_uri(node_raptor_uri); free(node_name); @@ -123,18 +129,16 @@ Loader::load(const Glib::ustring& filename) /* Get remaining nodes */ query = Raul::RDFQuery(*_namespaces, Glib::ustring( - "SELECT DISTINCT ?node ?midiNote ?duration FROM <") + "SELECT DISTINCT ?node ?duration FROM <") + document_uri + "> WHERE {\n" + machine_uri + " :node ?node .\n" - "?node :midiNote ?midiNote ;\n" - " :duration ?duration .\n" + "?node :duration ?duration .\n" "}\n"); results = query.run(document_uri); for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) { const Glib::ustring& node_uri = (*i)["node"]; - const Glib::ustring& midi_note = (*i)["midiNote"]; const Glib::ustring& duration = (*i)["duration"]; raptor_uri* node_raptor_uri @@ -143,6 +147,7 @@ Loader::load(const Glib::ustring& filename) char* node_name = (char*) raptor_uri_to_relative_uri_string(document_raptor_uri, node_raptor_uri); + cout << "Node: " << node_name << " - " << duration << endl; cerr << "FIXME: load (2)\n"; /* @@ -159,6 +164,10 @@ Loader::load(const Glib::ustring& filename) created.insert(std::make_pair(node_uri.collate_key(), node)); } */ + SharedPtr node(new Node(strtod(duration.c_str(), NULL), true)); + machine->add_node(node); + created.insert(std::make_pair(node_uri.collate_key(), node)); + raptor_free_uri(node_raptor_uri); free(node_name); } @@ -167,16 +176,19 @@ Loader::load(const Glib::ustring& filename) /* Get edges */ query = Raul::RDFQuery(*_namespaces, Glib::ustring( - "SELECT DISTINCT ?src ?edge ?dst FROM <") + "SELECT DISTINCT ?edge ?src ?dst ?prob FROM <") + document_uri + "> WHERE {\n" + machine_uri + " :edge ?edge .\n" - "?edge :tail ?src ;\n" - " :head ?dst .\n }"); + "?edge :tail ?src ;\n" + " :head ?dst ;\n" + " :probability ?prob .\n }"); + results = query.run(document_uri); for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) { const Glib::ustring& src_uri = (*i)["src"]; - const Glib::ustring& dst_uri = (*i)["dst"]; + const Glib::ustring& dst_uri = (*i)["dst"]; + double prob = strtod((*i)["prob"].c_str(), NULL); Created::iterator src_i = created.find(src_uri.collate_key()); Created::iterator dst_i = created.find(dst_uri.collate_key()); @@ -185,7 +197,9 @@ Loader::load(const Glib::ustring& filename) const SharedPtr src = src_i->second; const SharedPtr dst = dst_i->second; - src->add_outgoing_edge(SharedPtr(new Edge(src, dst))); + SharedPtr edge(new Edge(src, dst)); + edge->set_probability(prob); + src->add_outgoing_edge(edge); } else { cerr << "[Loader] WARNING: Ignored edge between unknown nodes " @@ -194,7 +208,7 @@ Loader::load(const Glib::ustring& filename) } - free(document_uri_str); + //free(document_uri_str); raptor_free_uri(document_raptor_uri); return machine; diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp index 2d3358b..41335bc 100644 --- a/src/engine/Machine.cpp +++ b/src/engine/Machine.cpp @@ -92,7 +92,7 @@ Machine::exit_node(const SharedPtr node) // Activate all successors to this node // (that aren't aready active right now) - for (Node::EdgeList::const_iterator s = node->outgoing_edges().begin(); + for (Node::Edges::const_iterator s = node->outgoing_edges().begin(); s != node->outgoing_edges().end(); ++s) { const double rand_normal = rand() / (double)RAND_MAX; // [0, 1] @@ -211,7 +211,7 @@ Machine::write_state(Raul::RDFWriter& writer) { using Raul::RdfId; - writer.add_prefix("machina", "http://drobilla.net/ns/machina"); + writer.add_prefix("machina", "http://drobilla.net/ns/machina#"); writer.write(RdfId(RdfId::RESOURCE, ""), RdfId(RdfId::RESOURCE, "rdf:type"), @@ -225,7 +225,7 @@ Machine::write_state(Raul::RDFWriter& writer) RdfId(RdfId::RESOURCE, "machina:node"), (*n)->id()); - for (Node::EdgeList::const_iterator e = (*n)->outgoing_edges().begin(); + for (Node::Edges::const_iterator e = (*n)->outgoing_edges().begin(); e != (*n)->outgoing_edges().end(); ++e) { (*e)->write_state(writer); diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp index 1b4fdee..ecb7a22 100644 --- a/src/engine/Node.cpp +++ b/src/engine/Node.cpp @@ -103,8 +103,8 @@ Node::remove_outgoing_edge(SharedPtr edge) void Node::remove_outgoing_edges_to(SharedPtr node) { - for (EdgeList::iterator i = _outgoing_edges.begin(); i != _outgoing_edges.end() ; ) { - EdgeList::iterator next = i; + for (Edges::iterator i = _outgoing_edges.begin(); i != _outgoing_edges.end() ; ) { + Edges::iterator next = i; ++next; if ((*i)->dst() == node) @@ -131,7 +131,7 @@ Node::write_state(Raul::RDFWriter& writer) RdfId(RdfId::RESOURCE, "machina:duration"), Raul::Atom((float)_duration)); - for (Node::EdgeList::const_iterator e = _outgoing_edges.begin(); + for (Node::Edges::const_iterator e = _outgoing_edges.begin(); e != _outgoing_edges.end(); ++e) (*e)->write_state(writer); } diff --git a/src/engine/machina/Engine.hpp b/src/engine/machina/Engine.hpp index fcb75d3..cd4cf27 100644 --- a/src/engine/machina/Engine.hpp +++ b/src/engine/machina/Engine.hpp @@ -18,6 +18,7 @@ #ifndef MACHINA_ENGINE_HPP #define MACHINA_ENGINE_HPP +#include #include namespace Machina { @@ -36,8 +37,9 @@ public: SharedPtr driver() { return _driver; } SharedPtr machine() { return _machine; } + SharedPtr load_machine(const Glib::ustring& uri); + void set_bpm(double bpm); - void set_quantization(double beat_fraction); private: diff --git a/src/engine/machina/JackDriver.hpp b/src/engine/machina/JackDriver.hpp index 1f9b197..f78c1f1 100644 --- a/src/engine/machina/JackDriver.hpp +++ b/src/engine/machina/JackDriver.hpp @@ -55,10 +55,12 @@ public: void set_quantization(double quantization) { _quantization.set(quantization); } private: - void process_input(const Raul::TimeSlice& time); + void process_input(SharedPtr machine, + const Raul::TimeSlice& time); virtual void on_process(jack_nframes_t nframes); SharedPtr _machine; + SharedPtr _last_machine; jack_port_t* _input_port; jack_port_t* _output_port; diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp index a7b92c8..7b71d01 100644 --- a/src/engine/machina/Machine.hpp +++ b/src/engine/machina/Machine.hpp @@ -58,8 +58,10 @@ public: SharedPtr pending_learn() { return _pending_learn; } void clear_pending_learn() { _pending_learn.reset(); } -private: typedef Raul::List > Nodes; + const Nodes& nodes() { return _nodes; } + +private: // Audio context SharedPtr earliest_node() const; diff --git a/src/engine/machina/Node.hpp b/src/engine/machina/Node.hpp index 7573e39..569a54e 100644 --- a/src/engine/machina/Node.hpp +++ b/src/engine/machina/Node.hpp @@ -69,8 +69,8 @@ public: BeatCount duration() { return _duration; } void set_duration(BeatCount d) { _duration = d; } - typedef Raul::List > EdgeList; - const EdgeList& outgoing_edges() const { return _outgoing_edges; } + typedef Raul::List > Edges; + const Edges& outgoing_edges() const { return _outgoing_edges; } private: bool _is_initial; @@ -79,7 +79,7 @@ private: BeatCount _duration; SharedPtr _enter_action; SharedPtr _exit_action; - EdgeList _outgoing_edges; + Edges _outgoing_edges; }; diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp index 03a05bc..878e790 100644 --- a/src/gui/MachinaCanvas.cpp +++ b/src/gui/MachinaCanvas.cpp @@ -46,11 +46,9 @@ MachinaCanvas::status_message(const string& msg) void -MachinaCanvas::node_clicked(SharedPtr item, GdkEventButton* event) +MachinaCanvas::node_clicked(WeakPtr item, GdkEventButton* event) { - cerr << "CLICKED: " << item->name() << endl; - - SharedPtr node = PtrCast(item); + SharedPtr node = PtrCast(item.lock()); if (!node) return; @@ -101,10 +99,8 @@ MachinaCanvas::canvas_event(GdkEvent* event) SharedPtr view(new NodeView(node, shared_from_this(), name, x, y)); - //view->signal_clicked.connect(sigc::bind(sigc::mem_fun(this, - // &MachinaCanvas::node_clicked), view)); view->signal_clicked.connect(sigc::bind<0>(sigc::mem_fun(this, - &MachinaCanvas::node_clicked), view)); + &MachinaCanvas::node_clicked), WeakPtr(view))); add_item(view); view->resize(); view->raise_to_top(); @@ -173,3 +169,56 @@ MachinaCanvas::disconnect_node(boost::shared_ptr src, } +void +MachinaCanvas::build(SharedPtr machine) +{ + destroy(); + + for (Machina::Machine::Nodes::const_iterator i = machine->nodes().begin(); + i != machine->nodes().end(); ++i) { + + const double x = 1600 + rand() % 300; + const double y = 1200 + rand() % 300; + + SharedPtr view(new NodeView((*i), shared_from_this(), + "", x, y)); + + view->signal_clicked.connect(sigc::bind<0>(sigc::mem_fun(this, + &MachinaCanvas::node_clicked), WeakPtr(view))); + + add_item(view); + view->resize(); + } + + for (ItemMap::iterator n = _items.begin(); n != _items.end(); ++n) { + + SharedPtr src = PtrCast(n->second); + if (!src) + continue; + + for (Machina::Node::Edges::const_iterator e = src->node()->outgoing_edges().begin(); + e != src->node()->outgoing_edges().end(); ++e) { + + SharedPtr dst; + for (ItemMap::iterator m = _items.begin(); m != _items.end(); ++m) { + SharedPtr nv = PtrCast((*m).second); + if (nv && nv->node() == (*e)->dst()) { + dst = nv; + break; + } + } + + if (dst) { + boost::shared_ptr c(new EdgeView(shared_from_this(), + src, dst, (*e))); + src->add_connection(c); + dst->add_connection(c); + add_connection(c); + } + + } + } + +} + + diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp index 361e154..feead3e 100644 --- a/src/gui/MachinaCanvas.hpp +++ b/src/gui/MachinaCanvas.hpp @@ -42,10 +42,12 @@ public: void status_message(const string& msg); + void build(SharedPtr machine); + protected: bool canvas_event(GdkEvent* event); - void node_clicked(SharedPtr item, GdkEventButton* ev); + void node_clicked(WeakPtr item, GdkEventButton* ev); private: MachinaGUI* _app; diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp index 77dcbc5..3dd0c96 100644 --- a/src/gui/MachinaGUI.cpp +++ b/src/gui/MachinaGUI.cpp @@ -351,8 +351,21 @@ MachinaGUI::menu_file_quit() void MachinaGUI::menu_file_open() { - cerr << "open\n"; + Gtk::FileChooserDialog dialog(*_main_window, "Open Machine", Gtk::FILE_CHOOSER_ACTION_OPEN); + + dialog.set_local_only(false); + + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + const int result = dialog.run(); + + if (result == Gtk::RESPONSE_OK) { + SharedPtr new_machine = _engine->load_machine(dialog.get_uri()); + _canvas->destroy(); + _canvas->build(new_machine); + _save_filename = dialog.get_filename(); + } } @@ -387,8 +400,8 @@ MachinaGUI::menu_file_save_as() if (result == Gtk::RESPONSE_OK) { string filename = dialog.get_filename(); - if (filename.length() < 12 || filename.substr(filename.length()-12) != ".machina.ttl") - filename += ".machina.ttl"; + if (filename.length() < 8 || filename.substr(filename.length()-8) != ".machina") + filename += ".machina"; bool confirm = false; std::fstream fin; -- cgit v1.2.1