aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-02-05 06:23:05 +0000
committerDavid Robillard <d@drobilla.net>2007-02-05 06:23:05 +0000
commit4c8aad127d9504c7d355975180f877f5baa9f744 (patch)
treebdb70b9a849a290f32c9bbb45947da4b796dcd97
parent60647fedf17cdebfcf45c76d8fa9cee120006921 (diff)
downloadmachina-4c8aad127d9504c7d355975180f877f5baa9f744.tar.gz
machina-4c8aad127d9504c7d355975180f877f5baa9f744.tar.bz2
machina-4c8aad127d9504c7d355975180f877f5baa9f744.zip
It's aliiiiivee!
git-svn-id: http://svn.drobilla.net/lad/machina@278 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--data/test.ttl6
-rw-r--r--src/Driver.hpp41
-rw-r--r--src/Edge.hpp15
-rw-r--r--src/JackActions.cpp87
-rw-r--r--src/JackActions.hpp56
-rw-r--r--src/JackDriver.cpp101
-rw-r--r--src/JackDriver.hpp59
-rw-r--r--src/JackNodeFactory.cpp41
-rw-r--r--src/JackNodeFactory.hpp42
-rw-r--r--src/Loader.cpp149
-rw-r--r--src/Loader.hpp15
-rw-r--r--src/Machine.cpp127
-rw-r--r--src/Machine.hpp27
-rw-r--r--src/Makefile.am7
-rw-r--r--src/Node.cpp18
-rw-r--r--src/Node.hpp23
-rw-r--r--src/NodeFactory.hpp39
-rw-r--r--src/main.cpp51
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;
-}
-
+ */