diff options
-rw-r--r-- | data/test.ttl | 6 | ||||
-rw-r--r-- | src/Driver.hpp | 41 | ||||
-rw-r--r-- | src/Edge.hpp | 15 | ||||
-rw-r--r-- | src/JackActions.cpp | 87 | ||||
-rw-r--r-- | src/JackActions.hpp | 56 | ||||
-rw-r--r-- | src/JackDriver.cpp | 101 | ||||
-rw-r--r-- | src/JackDriver.hpp | 59 | ||||
-rw-r--r-- | src/JackNodeFactory.cpp | 41 | ||||
-rw-r--r-- | src/JackNodeFactory.hpp | 42 | ||||
-rw-r--r-- | src/Loader.cpp | 149 | ||||
-rw-r--r-- | src/Loader.hpp | 15 | ||||
-rw-r--r-- | src/Machine.cpp | 127 | ||||
-rw-r--r-- | src/Machine.hpp | 27 | ||||
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/Node.cpp | 18 | ||||
-rw-r--r-- | src/Node.hpp | 23 | ||||
-rw-r--r-- | src/NodeFactory.hpp | 39 | ||||
-rw-r--r-- | src/main.cpp | 51 |
18 files changed, 803 insertions, 101 deletions
diff --git a/data/test.ttl b/data/test.ttl index 741d9cb..e5431fc 100644 --- a/data/test.ttl +++ b/data/test.ttl @@ -15,10 +15,10 @@ <#n1> a :Node ; :midiNote 60 ; - :duration 600 . + :duration 20000 . <#n2> a :Node ; - :midiNote 70 ; - :duration 700 . + :midiNote 72 ; + :duration 20000 . diff --git a/src/Driver.hpp b/src/Driver.hpp new file mode 100644 index 0000000..e1afe07 --- /dev/null +++ b/src/Driver.hpp @@ -0,0 +1,41 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MACHINA_JACKDRIVER_HPP +#define MACHINA_JACKDRIVER_HPP + +#include <raul/JackDriver.h> + +namespace Machine { + + +class JackDriver : public Raul::JackDriver { +public: + JackDriver(SharedPtr<Machine> machine); + + virtual void set_machine(SharedPtr<Machine> machine); + +protected: + virtual void on_process(jack_nframes_t nframes); + +private: + SharedPtr<Machine> _machine; +}; + + +} // namespace Machina + +#endif // MACHINA_JACKDRIVER_HPP diff --git a/src/Edge.hpp b/src/Edge.hpp index eb2715d..3805f81 100644 --- a/src/Edge.hpp +++ b/src/Edge.hpp @@ -19,6 +19,8 @@ #include <list> #include <boost/utility.hpp> +#include <raul/WeakPtr.h> +#include <raul/SharedPtr.h> #include "types.hpp" #include "Action.hpp" @@ -29,16 +31,17 @@ class Node; class Edge : boost::noncopyable { public: - Edge(Node* dst) : _src(NULL) , _dst(dst) {} + Edge(WeakPtr<Node> src, SharedPtr<Node> dst) : _src(src) , _dst(dst) {} - Node* src() { return _src; } - Node* dst() { return _dst; } + WeakPtr<Node> src() { return _src; } + SharedPtr<Node> dst() { return _dst; } - void set_src(Node* src) { _src = src; } + void set_src(WeakPtr<Node> src) { _src = src; } + void set_dst(SharedPtr<Node> dst) { _dst = dst; } private: - Node* _src; - Node* _dst; + WeakPtr<Node> _src; + SharedPtr<Node> _dst; }; diff --git a/src/JackActions.cpp b/src/JackActions.cpp new file mode 100644 index 0000000..50f7e23 --- /dev/null +++ b/src/JackActions.cpp @@ -0,0 +1,87 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include "JackActions.hpp" +#include "JackDriver.hpp" + +namespace Machina { + + +/* NOTE ON */ + +JackNoteOnAction::JackNoteOnAction(WeakPtr<JackDriver> driver, + unsigned char note_num) + : _driver(driver) + , _note_num(note_num) +{ +} + + +void +JackNoteOnAction::execute(Timestamp time) +{ + SharedPtr<JackDriver> driver = _driver.lock(); + if (!driver) + return; + + const FrameCount nframes = driver->current_cycle_nframes(); + const FrameCount offset = driver->stamp_to_offset(time); + + //std::cerr << offset << " \tNOTE ON:\t" << (int)_note_num << "\t@ " << time << std::endl; + + jack_midi_data_t ev[] = { 0x80, _note_num, 0x40 }; + + jack_midi_event_write( + jack_port_get_buffer(driver->output_port(), nframes), + offset, ev, 3, nframes); +} + + + +/* NOTE OFF */ + +JackNoteOffAction::JackNoteOffAction(WeakPtr<JackDriver> driver, + unsigned char note_num) + : _driver(driver) + , _note_num(note_num) +{ +} + + +void +JackNoteOffAction::execute(Timestamp time) +{ + SharedPtr<JackDriver> driver = _driver.lock(); + if (!driver) + return; + + const FrameCount nframes = driver->current_cycle_nframes(); + const FrameCount offset = driver->stamp_to_offset(time); + + //std::cerr << offset << " \tNOTE OFF:\t" << (int)_note_num << "\t@ " << time << std::endl; + + jack_midi_data_t ev[] = { 0x90, _note_num, 0x40 }; + + jack_midi_event_write( + jack_port_get_buffer(driver->output_port(), nframes), + offset, ev, 3, nframes); +} + + +} // namespace Machina + + diff --git a/src/JackActions.hpp b/src/JackActions.hpp new file mode 100644 index 0000000..e55d863 --- /dev/null +++ b/src/JackActions.hpp @@ -0,0 +1,56 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MACHINA_JACKACTIONS_HPP +#define MACHINA_JACKACTIONS_HPP + +#include <raul/WeakPtr.h> +#include "types.hpp" +#include "Action.hpp" + +namespace Machina { + +class Node; +class JackDriver; + + +class JackNoteOnAction : public Action { +public: + JackNoteOnAction(WeakPtr<JackDriver> driver, unsigned char note_num); + + void execute(Timestamp time); + +private: + WeakPtr<JackDriver> _driver; + unsigned char _note_num; +}; + + +class JackNoteOffAction : public Action { +public: + JackNoteOffAction(WeakPtr<JackDriver> driver, unsigned char note_num); + + void execute(Timestamp time); + +private: + WeakPtr<JackDriver> _driver; + unsigned char _note_num; +}; + + +} // namespace Machina + +#endif // MACHINA_JACKACTIONS_HPP diff --git a/src/JackDriver.cpp b/src/JackDriver.cpp new file mode 100644 index 0000000..cc9a52a --- /dev/null +++ b/src/JackDriver.cpp @@ -0,0 +1,101 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "JackDriver.hpp" + +#include <iostream> + +namespace Machina { + + +JackDriver::JackDriver() + : _output_port(NULL) + , _current_cycle_start(0) + , _current_cycle_nframes(0) +{ +} + + +void +JackDriver::attach(const std::string& client_name) +{ + Raul::JackDriver::attach(client_name); + + if (jack_client()) { + _output_port = jack_port_register(jack_client(), + "out", + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, + 0); + } +} + + +void +JackDriver::detach() +{ + jack_port_unregister(jack_client(), _output_port); + _output_port = NULL; + + Raul::JackDriver::detach(); +} + + +Timestamp +JackDriver::stamp_to_offset(Timestamp stamp) +{ + Timestamp ret = stamp - _current_cycle_start + _current_cycle_offset; + assert(ret < _current_cycle_offset + _current_cycle_nframes); + return ret; +} + + +void +JackDriver::on_process(jack_nframes_t nframes) +{ + //std::cerr << "======================================================\n"; + + _current_cycle_offset = 0; + _current_cycle_nframes = nframes; + + jack_midi_clear_buffer(jack_port_get_buffer(_output_port, nframes), nframes); + + while (true) { + + bool machine_done = ! _machine->run(_current_cycle_nframes); + + if (!machine_done) { + _current_cycle_start += _current_cycle_nframes; + break; + + } else { + const Timestamp finish_time = _machine->time(); + const FrameCount finish_offset = stamp_to_offset(finish_time); + + if (finish_offset >= _current_cycle_nframes) + break; + + _current_cycle_offset = stamp_to_offset(finish_time); + _current_cycle_nframes -= _current_cycle_offset; + _current_cycle_start = 0; + _machine->reset(); + } + } + + //std::cerr << "======================================================\n"; +} + + +} // namespace Machina diff --git a/src/JackDriver.hpp b/src/JackDriver.hpp new file mode 100644 index 0000000..716bb06 --- /dev/null +++ b/src/JackDriver.hpp @@ -0,0 +1,59 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MACHINA_JACKDRIVER_HPP +#define MACHINA_JACKDRIVER_HPP + +#include <raul/JackDriver.h> +#include <raul/SharedPtr.h> +#include <jack/midiport.h> +#include "Machine.hpp" + +namespace Machina { + + +class JackDriver : public Raul::JackDriver { +public: + JackDriver(); + + void attach(const std::string& client_name); + void detach(); + + void set_machine(SharedPtr<Machine> machine) { _machine = machine; } + + // Audio context + Timestamp stamp_to_offset(Timestamp stamp); + jack_port_t* output_port() { return _output_port; } + //Timestamp current_cycle_start() { return _current_cycle_start; } + //Timestamp current_cycle_offset() { return _current_cycle_offset; } + FrameCount current_cycle_nframes() { return _current_cycle_nframes; } + + +protected: + virtual void on_process(jack_nframes_t nframes); + +private: + SharedPtr<Machine> _machine; + jack_port_t* _output_port; + Timestamp _current_cycle_start; + Timestamp _current_cycle_offset; ///< for split cycles + FrameCount _current_cycle_nframes; +}; + + +} // namespace Machina + +#endif // MACHINA_JACKDRIVER_HPP diff --git a/src/JackNodeFactory.cpp b/src/JackNodeFactory.cpp new file mode 100644 index 0000000..be0f6d3 --- /dev/null +++ b/src/JackNodeFactory.cpp @@ -0,0 +1,41 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "JackNodeFactory.hpp" +#include "JackActions.hpp" +#include "Node.hpp" + +namespace Machina { + + +SharedPtr<Node> +JackNodeFactory::create_node(Node::ID, unsigned char note, FrameCount duration) +{ + // FIXME: leaks like a sieve, obviously + + Node* n = new Node(duration); + JackNoteOnAction* a_enter = new JackNoteOnAction(_driver, note); + JackNoteOffAction* a_exit = new JackNoteOffAction(_driver, note); + + n->add_enter_action(a_enter); + n->add_exit_action(a_exit); + + return SharedPtr<Node>(n); +} + + +} // namespace Machina + diff --git a/src/JackNodeFactory.hpp b/src/JackNodeFactory.hpp new file mode 100644 index 0000000..cd8735d --- /dev/null +++ b/src/JackNodeFactory.hpp @@ -0,0 +1,42 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MACHINA_JACKNODEFACTORY_HPP +#define MACHINA_JACKNODEFACTORY_HPP + +#include <raul/WeakPtr.h> +#include "NodeFactory.hpp" + +namespace Machina { + +class JackDriver; + + +class JackNodeFactory : public NodeFactory { +public: + JackNodeFactory(WeakPtr<JackDriver> driver) : _driver(driver) {} + + SharedPtr<Node> create_node(Node::ID id, + unsigned char note, + FrameCount duration); +private: + WeakPtr<JackDriver> _driver; +}; + + +} // namespace Machina + +#endif // MACHINA_JACKNODEFACTORY_HPP diff --git a/src/Loader.cpp b/src/Loader.cpp index dfac552..7f61e05 100644 --- a/src/Loader.cpp +++ b/src/Loader.cpp @@ -1,4 +1,4 @@ -/* This file is part of Machina. Copyright (C) 2006 Dave Robillard. +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. * * 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 @@ -15,12 +15,16 @@ */ #include <iostream> +#include <map> #include <raptor.h> #include <rasqal.h> +#include <glibmm/ustring.h> #include "raul/RDFQuery.h" #include "Loader.hpp" #include "Node.hpp" +#include "Edge.hpp" #include "Machine.hpp" +#include "NodeFactory.hpp" using namespace Raul; using std::cerr; using std::cout; using std::endl; @@ -28,14 +32,15 @@ using std::cerr; using std::cout; using std::endl; namespace Machina { +/* // FIXME: remove Node* create_debug_node(const Node::ID& id, FrameCount duration) { // leaks like a sieve, obviously Node* n = new Node(duration); - PrintAction* a_enter = new PrintAction(string("> ") + id); - PrintAction* a_exit = new PrintAction(string("< ")/* + name*/); + PrintAction* a_enter = new PrintAction(string("\t> ") + id); + PrintAction* a_exit = new PrintAction(string("\t< ") + id); n->add_enter_action(a_enter); n->add_exit_action(a_exit); @@ -44,10 +49,12 @@ Node* create_debug_node(const Node::ID& id, FrameCount duration) return n; } - +*/ -Loader::Loader(SharedPtr<Namespaces> namespaces) - : _namespaces(namespaces) +Loader::Loader(SharedPtr<NodeFactory> node_factory, + SharedPtr<Namespaces> namespaces) + : _node_factory(node_factory) + , _namespaces(namespaces) { if (!_namespaces) _namespaces = SharedPtr<Namespaces>(new Namespaces()); @@ -66,6 +73,7 @@ Loader::Loader(SharedPtr<Namespaces> namespaces) SharedPtr<Machine> Loader::load(const Glib::ustring& filename) { + using Raul::RDFQuery; SharedPtr<Machine> machine; rasqal_init(); @@ -77,45 +85,130 @@ Loader::load(const Glib::ustring& filename) if (!document_uri_str) return machine; - machine = SharedPtr<Machine>(new Machine(1)); + machine = SharedPtr<Machine>(new Machine()); Glib::ustring document_uri = (const char*)document_uri_str; string machine_uri = "<> "; - cerr << "[Loader] Loading " << machine_uri << " from " << document_uri << endl; + cout << "[Loader] Loading " << machine_uri << " from " << document_uri << endl; - /* Load nodes */ - - RDFQuery query = RDFQuery(*_namespaces, Glib::ustring( - "SELECT DISTINCT ?node ?midiNote ?duration FROM <") + document_uri + "> WHERE {\n" + - machine_uri + " :node ?node .\n" - "?node :midiNote ?midiNote ;\n" - " :duration ?duration .\n" - //" FILTER ( datatype(?midiNote) = xsd:decimal )\n" - //" FILTER ( datatype(?duration) = xsd:decimal )\n" - "}"); + typedef std::map<string, SharedPtr<Node> > Created; + Created created; + + + /* Get initial nodes */ + Raul::RDFQuery query = Raul::RDFQuery(*_namespaces, Glib::ustring( + "SELECT DISTINCT ?initialNode ?midiNote ?duration FROM <") + + document_uri + "> WHERE {\n" + + machine_uri + " :initialNode ?initialNode .\n" + "?initialNode :midiNote ?midiNote ;\n" + " :duration ?duration .\n" + "}\n"); + RDFQuery::Results results = query.run(document_uri); for (RDFQuery::Results::iterator i = results.begin(); i != results.end(); ++i) { - raptor_uri* node_uri = raptor_new_uri((const unsigned char*)(*i)["node"].c_str()); - unsigned char* node_name - = raptor_uri_to_relative_uri_string(document_raptor_uri, node_uri); + 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 + = raptor_new_uri((const unsigned char*)node_uri.c_str()); + + char* node_name = (char*) + raptor_uri_to_relative_uri_string(document_raptor_uri, node_raptor_uri); - const Glib::ustring& note = (*i)["midiNote"]; - const Glib::ustring& duration = (*i)["duration"]; + //cout << "Initial: " << node_name << ": " << midi_note << " - " << duration << endl; - cout << "NODE: " << node_name << ": " << note << " - " << duration << endl; - SharedPtr<Node> node = SharedPtr<Node>( - create_debug_node((const char*)node_name, strtol(duration.c_str(), NULL, 10))); + SharedPtr<Node> node = SharedPtr<Node>(_node_factory->create_node( + node_name, + strtol(midi_note.c_str(), NULL, 10), + strtol(duration.c_str(), NULL, 10))); - machine->add_node(string((const char*)node_name).substr(1), node); // (chop leading "#") + node->set_initial(true); + //machine->add_node(string(node_name).substr(1), node); // (chop leading "#") + machine->add_node(node); + + created.insert(std::make_pair(node_uri.collate_key(), node)); - raptor_free_uri(node_uri); + raptor_free_uri(node_raptor_uri); free(node_name); } + + /* Get remaining nodes */ + + query = Raul::RDFQuery(*_namespaces, Glib::ustring( + "SELECT DISTINCT ?node ?midiNote ?duration FROM <") + + document_uri + "> WHERE {\n" + + machine_uri + " :node ?node .\n" + "?node :midiNote ?midiNote ;\n" + " :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 + = raptor_new_uri((const unsigned char*)node_uri.c_str()); + + char* node_name = (char*) + raptor_uri_to_relative_uri_string(document_raptor_uri, node_raptor_uri); + + + SharedPtr<Node> node = SharedPtr<Node>(_node_factory->create_node( + node_name, + strtol(midi_note.c_str(), NULL, 10), + strtol(duration.c_str(), NULL, 10))); + + if (created.find(node_uri) == created.end()) { + //cout << "Node: " << node_name << ": " << midi_note << " - " << duration << endl; + //machine->add_node(string(node_name).substr(1), node); // (chop leading "#") + machine->add_node(node); + created.insert(std::make_pair(node_uri.collate_key(), node)); + } + + raptor_free_uri(node_raptor_uri); + free(node_name); + } + + + /* Get edges */ + + query = Raul::RDFQuery(*_namespaces, Glib::ustring( + "SELECT DISTINCT ?src ?edge ?dst FROM <") + + document_uri + "> WHERE {\n" + + machine_uri + " :edge ?edge .\n" + "?edge :tail ?src ;\n" + " :head ?dst .\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"]; + + Created::iterator src_i = created.find(src_uri.collate_key()); + Created::iterator dst_i = created.find(dst_uri.collate_key()); + + if (src_i != created.end() && dst_i != created.end()) { + const SharedPtr<Node> src = src_i->second; + const SharedPtr<Node> dst = dst_i->second; + + src->add_outgoing_edge(SharedPtr<Edge>(new Edge(src, dst))); + + } else { + cerr << "[Loader] WARNING: Ignored edge between unknown nodes " + << src_uri << " -> " << dst_uri << endl; + } + + } + free(document_uri_str); raptor_free_uri(document_raptor_uri); diff --git a/src/Loader.hpp b/src/Loader.hpp index 35ac5f0..0149584 100644 --- a/src/Loader.hpp +++ b/src/Loader.hpp @@ -1,4 +1,4 @@ -/* This file is part of Machina. Copyright (C) 2006 Dave Robillard. +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. * * 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 @@ -14,8 +14,8 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef LOADER_HPP -#define LOADER_HPP +#ifndef MACHINA_LOADER_HPP +#define MACHINA_LOADER_HPP #include <glibmm/ustring.h> #include "raul/SharedPtr.h" @@ -27,19 +27,22 @@ using Raul::Namespaces; namespace Machina { class Machine; +class NodeFactory; class Loader { public: - Loader(SharedPtr<Namespaces> = SharedPtr<Namespaces>()); + Loader(SharedPtr<NodeFactory> node_factory, + SharedPtr<Namespaces> = SharedPtr<Namespaces>()); SharedPtr<Machine> load(const Glib::ustring& filename); private: - SharedPtr<Namespaces> _namespaces; + SharedPtr<NodeFactory> _node_factory; + SharedPtr<Namespaces> _namespaces; }; } // namespace Machina -#endif // LOADER_HPP +#endif // MACHINA_LOADER_HPP diff --git a/src/Machine.cpp b/src/Machine.cpp index 246db73..a6e63d3 100644 --- a/src/Machine.cpp +++ b/src/Machine.cpp @@ -22,15 +22,11 @@ namespace Machina { -Machine::Machine(size_t poly) - : _activated(false) - , _initial_node(new Node()) - , _voices(poly, NULL)//_initial_node) +Machine::Machine() + : _is_activated(false) + , _is_finished(false) , _time(0) { - /* reserve poly spaces in _voices, so accessing it - * with operator[] should be realtime safe. - */ } @@ -40,39 +36,122 @@ Machine::~Machine() void +Machine::add_node(SharedPtr<Node> node) +{ + assert(!_is_activated); + + _nodes.push_back(node); +} + + +/** Exit all active states and reset time to 0. + */ +void Machine::reset() { - assert(!_activated); + if (!_is_finished) { + for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) { + const SharedPtr<Node> node = (*n); + + if (node->is_active()) + node->exit(_time); + } + } + + _time = 0; + _is_finished = false; +} - for (std::vector<Node*>::iterator i = _voices.begin(); - i != _voices.end(); ++i) { - *i = NULL; + +/** Return the active Node with the earliest exit time. + */ +SharedPtr<Node> +Machine::earliest_node() const +{ + SharedPtr<Node> earliest; + + for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) { + const SharedPtr<Node> node = (*n); + + if (node->is_active()) + if (!earliest || node->exit_time() < earliest->exit_time()) + earliest = node; } + + return earliest; } +/** Exit an active node at the current _time. + */ void -Machine::add_node(const Node::ID& id, SharedPtr<Node> node) +Machine::exit_node(const SharedPtr<Node> node) { - assert(!_activated); - _nodes[id] = node; + node->exit(_time); + + // Activate all successors to this node + // (that aren't aready active right now) + for (Node::EdgeList::const_iterator s = node->outgoing_edges().begin(); + s != node->outgoing_edges().end(); ++s) { + SharedPtr<Node> dst = (*s)->dst(); + + if (!dst->is_active()) + dst->enter(_time); + + } } -void -Machine::process(FrameCount nframes) +/** Run the machine for @a nframes frames. + * + * Returns false when the machine has finished running (i.e. there are + * no currently active states). + * + * If this returns false, time() will return the exact time stamp the + * machine actually finished on (so it can be restarted immediately + * with sample accuracy if necessary). + */ +bool +Machine::run(FrameCount nframes) { - const FrameCount cycle_end = _time + nframes; - bool done = false; + if (_is_finished) + return false; - assert(_activated); + const FrameCount cycle_end = _time + nframes; - FrameCount latest_event = _time; + assert(_is_activated); - std::cerr << "--------- " << _time << " - " << _time + nframes << std::endl; + //std::cerr << "--------- " << _time << " - " << _time + nframes << std::endl; - // FIXME: way too much iteration + // Initial run, enter all initial states + if (_time == 0) + for (Nodes::const_iterator n = _nodes.begin(); n != _nodes.end(); ++n) + if ((*n)->is_initial()) + (*n)->enter(0); + while (true) { + + SharedPtr<Node> earliest = earliest_node(); + + // No more active states, machine is finished + if (!earliest) { + _is_finished = true; + return false; + + // Earliest active state ends this cycle + } else if (earliest->exit_time() < cycle_end) { + _time = earliest->exit_time(); + exit_node(earliest); + + // Earliest active state ends in the future, done this cycle + } else { + _time = cycle_end; + return true; + } + + } + +#if 0 while (!done) { done = true; @@ -130,8 +209,10 @@ Machine::process(FrameCount nframes) } } } - _time += nframes; + + return false; +#endif } diff --git a/src/Machine.hpp b/src/Machine.hpp index a2360bb..8b96583 100644 --- a/src/Machine.hpp +++ b/src/Machine.hpp @@ -28,28 +28,33 @@ namespace Machina { class Machine { public: - Machine(size_t poly); + Machine(); ~Machine(); // Main context - void activate() { _activated = true; } - void deactivate() { _activated = false; } - void add_node(const Node::ID& id, SharedPtr<Node> node); + void activate() { _is_activated = true; } + void deactivate() { _is_activated = false; } + + void add_node(SharedPtr<Node> node); // Audio context - void reset(); - void process(FrameCount nframes); + void reset(); + bool run(FrameCount nframes); - SharedPtr<Node> initial_node() { return _initial_node; } + // Any context + FrameCount time() { return _time; } private: - bool _activated; - SharedPtr<Node> _initial_node; - std::vector<Node*> _voices; + typedef std::vector<SharedPtr<Node> > Nodes; - std::map<Node::ID, SharedPtr<Node> > _nodes; + // Audio context + SharedPtr<Node> earliest_node() const; + void exit_node(const SharedPtr<Node>); + bool _is_activated; + bool _is_finished; FrameCount _time; + Nodes _nodes; }; diff --git a/src/Makefile.am b/src/Makefile.am index 7d720d6..928262c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,4 +17,11 @@ machina_SOURCES = \ Machine.cpp \ Loader.h \ Loader.cpp \ + JackDriver.h \ + JackDriver.cpp \ + JackActions.hpp \ + JackActions.cpp \ + NodeFactory.hpp \ + JackNodeFactory.hpp \ + JackNodeFactory.cpp \ main.cpp diff --git a/src/Node.cpp b/src/Node.cpp index 841b728..2e45d51 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -21,9 +21,10 @@ namespace Machina { -Node::Node(FrameCount duration) - : _is_active(false) - , _start_time(0) +Node::Node(FrameCount duration, bool initial) + : _is_initial(initial) + , _is_active(false) + , _enter_time(0) , _duration(duration) , _enter_action(NULL) , _exit_action(NULL) @@ -66,7 +67,7 @@ void Node::enter(Timestamp time) { _is_active = true; - _start_time = time; + _enter_time = time; if (_enter_action) _enter_action->execute(time); } @@ -78,20 +79,21 @@ Node::exit(Timestamp time) if (_exit_action) _exit_action->execute(time); _is_active = false; - _start_time = 0; + _enter_time = 0; } void -Node::add_outgoing_edge(Edge* edge) +Node::add_outgoing_edge(SharedPtr<Edge> edge) { - edge->set_src(this); + assert(edge->src().lock().get() == this); + _outgoing_edges.push_back(edge); } void -Node::remove_outgoing_edge(Edge* edge) +Node::remove_outgoing_edge(SharedPtr<Edge> edge) { _outgoing_edges.remove(edge); } diff --git a/src/Node.hpp b/src/Node.hpp index 40fc6f3..e5a3dbe 100644 --- a/src/Node.hpp +++ b/src/Node.hpp @@ -19,6 +19,7 @@ #include <list> #include <boost/utility.hpp> +#include <raul/SharedPtr.h> #include "types.hpp" #include "Action.hpp" @@ -31,12 +32,15 @@ class Edge; * * It contains a action, as well as a duration and pointers to it's * successors (states/nodes that (may) follow it). + * + * Initial nodes do not have enter actions (since they are entered at + * an undefined point in time <= 0). */ class Node : public boost::noncopyable { public: typedef std::string ID; - Node(FrameCount duration=0); + Node(FrameCount duration=0, bool initial=false); void add_enter_action(Action* action); void remove_enter_action(Action* action); @@ -47,23 +51,24 @@ public: void enter(Timestamp time); void exit(Timestamp time); - void add_outgoing_edge(Edge* edge); - void remove_outgoing_edge(Edge* edge); - - Timestamp process(Timestamp time, FrameCount nframes); + void add_outgoing_edge(SharedPtr<Edge> edge); + void remove_outgoing_edge(SharedPtr<Edge> edge); + bool is_initial() const { return _is_initial; } + void set_initial(bool i) { _is_initial = i; } bool is_active() const { return _is_active; } - Timestamp start_time() const { return _start_time; } - Timestamp end_time() const { return _start_time + _duration; } + Timestamp enter_time() const { return _enter_time; } + Timestamp exit_time() const { return _enter_time + _duration; } FrameCount duration() { return _duration; } void set_duration(FrameCount d) { _duration = d; } - typedef std::list<Edge*> EdgeList; + typedef std::list<SharedPtr<Edge> > EdgeList; const EdgeList& outgoing_edges() const { return _outgoing_edges; } private: + bool _is_initial; bool _is_active; - Timestamp _start_time; ///< valid iff _is_active + Timestamp _enter_time; ///< valid iff _is_active FrameCount _duration; Action* _enter_action; Action* _exit_action; diff --git a/src/NodeFactory.hpp b/src/NodeFactory.hpp new file mode 100644 index 0000000..4098ac2 --- /dev/null +++ b/src/NodeFactory.hpp @@ -0,0 +1,39 @@ +/* This file is part of Machina. Copyright (C) 2007 Dave Robillard. + * + * 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 (at your option) any later + * version. + * + * Machina is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MACHINA_NODEFACTORY_HPP +#define MACHINA_NODEFACTORY_HPP + +#include <raul/SharedPtr.h> +#include "types.hpp" +#include "Node.hpp" + +namespace Machina { + + +class NodeFactory { +public: + virtual ~NodeFactory() {} + + virtual SharedPtr<Node> create_node(Node::ID id, + unsigned char note, + FrameCount duration) = 0; +}; + + +} // namespace Machina + +#endif // MACHINA_NODEFACTORY_HPP diff --git a/src/main.cpp b/src/main.cpp index 79809e0..325757b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,27 +15,66 @@ */ #include <iostream> +#include <signal.h> #include "Machine.hpp" #include "Node.hpp" #include "Action.hpp" #include "Edge.hpp" #include "Loader.hpp" +#include "JackDriver.hpp" +#include "JackNodeFactory.hpp" using namespace std; using namespace Machina; +bool quit = false; + + +void +catch_int(int) +{ + signal(SIGINT, catch_int); + signal(SIGTERM, catch_int); + + std::cout << "Interrupted" << std::endl; + + quit = true; +} + + int main(int argc, char** argv) { - if (argc != 2) + if (argc != 2) { + cout << "Usage: " << argv[0] << " FILE" << endl; return -1; + } + + SharedPtr<JackDriver> driver(new JackDriver()); + SharedPtr<NodeFactory> factory(new JackNodeFactory(driver)); + + Loader l(factory); - Loader l; SharedPtr<Machine> m = l.load(argv[1]); m->activate(); + driver->set_machine(m); + driver->attach("machina"); + + signal(SIGINT, catch_int); + signal(SIGTERM, catch_int); + + while (!quit) + sleep(1); + + driver->detach(); + + return 0; +} + + /* Machine m(1); @@ -47,14 +86,12 @@ main(int argc, char** argv) n2->add_outgoing_edge(new Edge(m.initial_node())); */ + /* Timestamp t = 0; while (t < 4000) { - m->process(1000); + m->run(1000); t += 1000; } - - return 0; -} - + */ |