aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2007-03-12 21:36:32 +0000
committerDavid Robillard <d@drobilla.net>2007-03-12 21:36:32 +0000
commit1c4f8c131dec109677d0a9b2b008c39a66d08158 (patch)
treee3843ac691ef5009810921ac81617076dc9c4cae /src
parentcc78e514acdf2e3587d38bcdb42f091c0c7c4ebe (diff)
downloadmachina-1c4f8c131dec109677d0a9b2b008c39a66d08158.tar.gz
machina-1c4f8c131dec109677d0a9b2b008c39a66d08158.tar.bz2
machina-1c4f8c131dec109677d0a9b2b008c39a66d08158.zip
Export to DOT file.
Fixed loading of useless initial nodes. git-svn-id: http://svn.drobilla.net/lad/machina@358 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/engine/Engine.cpp2
-rw-r--r--src/engine/SMFDriver.cpp73
-rw-r--r--src/engine/machina/SMFDriver.hpp7
-rw-r--r--src/gui/MachinaCanvas.cpp8
-rw-r--r--src/gui/MachinaGUI.cpp61
-rw-r--r--src/gui/MachinaGUI.hpp2
-rw-r--r--src/gui/machina.glade32
-rw-r--r--src/gui/main.cpp38
8 files changed, 149 insertions, 74 deletions
diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp
index 289abd3..5991276 100644
--- a/src/engine/Engine.cpp
+++ b/src/engine/Engine.cpp
@@ -46,7 +46,7 @@ SharedPtr<Machine>
Engine::learn_midi(const Glib::ustring& uri)
{
SharedPtr<SMFDriver> file_driver(new SMFDriver());
- SharedPtr<Machine> m = file_driver->learn(uri);
+ SharedPtr<Machine> m = file_driver->learn(uri, 32.0);
m->activate();
_driver->set_machine(m);
return m;
diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp
index abd0fb2..d2fcabb 100644
--- a/src/engine/SMFDriver.cpp
+++ b/src/engine/SMFDriver.cpp
@@ -38,7 +38,7 @@ namespace Machina {
* @return the resulting machine.
*/
SharedPtr<Machine>
-SMFDriver::learn(const Glib::ustring& uri, unsigned track)
+SMFDriver::learn(const Glib::ustring& uri, unsigned track, Raul::BeatTime max_duration)
{
const string filename = Glib::filename_from_uri(uri);
@@ -50,7 +50,7 @@ SMFDriver::learn(const Glib::ustring& uri, unsigned track)
if (track > reader.num_tracks())
return SharedPtr<Machine>();
else
- learn_track(m, reader, track);
+ learn_track(m, reader, track, max_duration);
if (m->nodes().size() > 1)
return m;
@@ -64,7 +64,7 @@ SMFDriver::learn(const Glib::ustring& uri, unsigned track)
* This will result in a disjoint subgraph in the machine, one for each track.
*/
SharedPtr<Machine>
-SMFDriver::learn(const Glib::ustring& uri)
+SMFDriver::learn(const Glib::ustring& uri, Raul::BeatTime max_duration)
{
const string filename = Glib::filename_from_uri(uri);
@@ -74,7 +74,7 @@ SMFDriver::learn(const Glib::ustring& uri)
reader.open(filename);
for (unsigned t=1; t <= reader.num_tracks(); ++t) {
- learn_track(m, reader, t);
+ learn_track(m, reader, t, max_duration);
}
if (m->nodes().size() > 1)
@@ -87,7 +87,8 @@ SMFDriver::learn(const Glib::ustring& uri)
void
SMFDriver::learn_track(SharedPtr<Machine> m,
Raul::SMFReader& reader,
- unsigned track)
+ unsigned track,
+ Raul::BeatTime max_duration)
{
const bool found_track = reader.seek_to_track(track);
assert(found_track);
@@ -95,9 +96,13 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
list<SharedPtr<Node> > active_nodes;
SharedPtr<Node> initial_node(new Node());
- m->add_node(initial_node);
+ initial_node->set_initial(true);
+ //m->add_node(initial_node);
SharedPtr<Node> connect_node = initial_node;
+ Raul::BeatTime connect_node_end_time = 0;
+
+ unsigned added_nodes = 0;
Raul::BeatTime t = 0;
unsigned char buf[4];
@@ -105,13 +110,31 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
uint32_t ev_time;
while (reader.read_event(4, buf, &ev_size, &ev_time) >= 0) {
t += ev_time / (double)reader.ppqn();
+
+ if (t > max_duration)
+ break;
+
//cerr << "t = " << t << endl;
if (ev_size > 0) {
if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
- //cerr << "NOTE ON: " << (int)buf[1] << endl;
+ cerr << "NOTE ON: " << (int)buf[1] << ", channel = " << (int)(buf[0] & 0x0F) << endl;
SharedPtr<Node> node(new Node());
node->add_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
- connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, node)));
+ assert(connect_node_end_time <= t);
+
+ if (t == connect_node_end_time) {
+ connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, node)));
+ } else {
+ SharedPtr<Node> delay_node(new Node());
+ delay_node->set_duration(t - connect_node_end_time);
+ connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, delay_node)));
+ delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, node)));
+ m->add_node(delay_node);
+ ++added_nodes;
+ connect_node = delay_node;
+ connect_node_end_time = t;
+ }
+
node->enter(SharedPtr<Raul::MIDISink>(), t);
active_nodes.push_back(node);
} else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF) {
@@ -125,14 +148,19 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
const size_t ev_size = action->event_size();
const unsigned char* ev = action->event();
if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON
- && ev[1] == buf[1]) {
+ && (ev[0] & 0x0F) == (buf[0] & 0x0F) // same channel
+ && ev[1] == buf[1]) // same note
+ {
//cerr << "FOUND MATCHING NOTE ON!\n";
(*i)->add_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf)));
(*i)->set_duration(t - (*i)->enter_time());
(*i)->exit(SharedPtr<Raul::MIDISink>(), t);
m->add_node((*i));
- if (active_nodes.size() == 1)
+ ++added_nodes;
+ if (active_nodes.size() == 1) {
connect_node = (*i);
+ connect_node_end_time = t;
+ }
active_nodes.erase(i);
break;
}
@@ -141,7 +169,30 @@ SMFDriver::learn_track(SharedPtr<Machine> m,
}
}
- initial_node->set_initial(true);
+ // Resolve any stuck notes when the rest of the machine is finished
+ if ( ! active_nodes.empty()) {
+ for (list<SharedPtr<Node> >::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) {
+ cerr << "WARNING: Resolving stuck note from MIDI file." << endl;
+ SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action());
+ if (!action)
+ continue;
+
+ const size_t ev_size = action->event_size();
+ const unsigned char* ev = action->event();
+ if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
+ unsigned char note_off[3] = { ((MIDI_CMD_NOTE_OFF & 0xF0) | (ev[0] & 0x0F)), ev[1], 0x40 };
+ (*i)->add_exit_action(SharedPtr<Action>(new MidiAction(3, note_off)));
+ (*i)->set_duration(t - (*i)->enter_time());
+ (*i)->exit(SharedPtr<Raul::MIDISink>(), t);
+ m->add_node((*i));
+ ++added_nodes;
+ }
+ }
+ active_nodes.clear();
+ }
+
+ if (added_nodes > 0)
+ m->add_node(initial_node);
}
diff --git a/src/engine/machina/SMFDriver.hpp b/src/engine/machina/SMFDriver.hpp
index f9b266b..28d2277 100644
--- a/src/engine/machina/SMFDriver.hpp
+++ b/src/engine/machina/SMFDriver.hpp
@@ -35,15 +35,16 @@ class Machine;
class SMFDriver : public Raul::SMFWriter,
public boost::enable_shared_from_this<SMFDriver> {
public:
- SharedPtr<Machine> learn(const Glib::ustring& uri);
- SharedPtr<Machine> learn(const Glib::ustring& uri, unsigned track);
+ SharedPtr<Machine> learn(const Glib::ustring& uri, Raul::BeatTime max_duration);
+ SharedPtr<Machine> learn(const Glib::ustring& uri, unsigned track, Raul::BeatTime max_duration);
void run(SharedPtr<Machine> machine, Raul::BeatTime max_time);
private:
void learn_track(SharedPtr<Machine> machine,
Raul::SMFReader& reader,
- unsigned track);
+ unsigned track,
+ Raul::BeatTime max_duration);
};
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
index 3c2c1e8..c7d9843 100644
--- a/src/gui/MachinaCanvas.cpp
+++ b/src/gui/MachinaCanvas.cpp
@@ -176,6 +176,9 @@ MachinaCanvas::create_node_view(SharedPtr<Machina::Node> node)
SharedPtr<NodeView> view(new NodeView(node, shared_from_this(),
"", 10, 10));
+ 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), WeakPtr<NodeView>(view)));
@@ -209,7 +212,10 @@ MachinaCanvas::build(SharedPtr<Machina::Machine> machine)
e != view->node()->outgoing_edges().end(); ++e) {
SharedPtr<NodeView> dst_view = views[(*e)->dst()];
- assert(dst_view);
+ if (!dst_view) {
+ cerr << "WARNING: Edge to node with no view" << endl;
+ continue;
+ }
boost::shared_ptr<Connection> c(new EdgeView(shared_from_this(),
view, dst_view, (*e)));
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 592001f..0848f8b 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -28,46 +28,6 @@
#include "MachinaCanvas.hpp"
#include "NodeView.hpp"
-//#include "config.h"
-
-// FIXME: include to avoid undefined reference to boost SP debug hooks stuff
-#include <raul/SharedPtr.h>
-
-
-
-/* Gtk helpers (resize combo boxes) */
-
-#if 0
-static void
-gtkmm_get_ink_pixel_size (Glib::RefPtr<Pango::Layout> layout,
- int& width,
- int& height)
-{
- Pango::Rectangle ink_rect = layout->get_ink_extents ();
-
- width = (ink_rect.get_width() + PANGO_SCALE / 2) / PANGO_SCALE;
- height = (ink_rect.get_height() + PANGO_SCALE / 2) / PANGO_SCALE;
-}
-
-
-static void
-gtkmm_set_width_for_given_text (Gtk::Widget &w, const gchar *text,
- gint hpadding/*, gint vpadding*/)
-
-{
- int old_width, old_height;
- w.get_size_request(old_width, old_height);
-
- int width, height;
- w.ensure_style ();
-
- gtkmm_get_ink_pixel_size (w.create_pango_layout (text), width, height);
- w.set_size_request(width + hpadding, old_height);//height + vpadding);
-}
-#endif
-/* end Gtk helpers */
-
-
MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
: _pane_closed(false),
@@ -117,6 +77,7 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
xml->get_widget("quit_menuitem", _menu_file_quit);
xml->get_widget("import_midi_menuitem", _menu_import_midi);
xml->get_widget("export_midi_menuitem", _menu_export_midi);
+ xml->get_widget("export_graphviz_menuitem", _menu_export_graphviz);
xml->get_widget("view_toolbar_menuitem", _menu_view_toolbar);
//xml->get_widget("view_refresh_menuitem", _menu_view_refresh);
//xml->get_widget("view_messages_menuitem", _menu_view_messages);
@@ -160,6 +121,8 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
sigc::mem_fun(this, &MachinaGUI::menu_import_midi));
_menu_export_midi->signal_activate().connect(
sigc::mem_fun(this, &MachinaGUI::menu_export_midi));
+ _menu_export_graphviz->signal_activate().connect(
+ sigc::mem_fun(this, &MachinaGUI::menu_export_graphviz));
//_menu_view_refresh->signal_activate().connect(
// sigc::mem_fun(this, &MachinaGUI::menu_view_refresh));
_menu_view_toolbar->signal_toggled().connect(
@@ -216,6 +179,8 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
// Faster idle callback to update DSP load progress bar
//Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::update_load), 50);
+
+ _canvas->build(engine->machine());
}
@@ -462,7 +427,7 @@ MachinaGUI::menu_import_midi()
SharedPtr<Machina::SMFDriver> file_driver(new Machina::SMFDriver());
//SharedPtr<Machina::Machine> machine = file_driver->learn(dialog.get_uri(),
// track_sb->get_value_as_int());
- SharedPtr<Machina::Machine> machine = file_driver->learn(dialog.get_uri());
+ SharedPtr<Machina::Machine> machine = file_driver->learn(dialog.get_uri(), 16.0);
if (machine) {
dialog.hide();
@@ -503,6 +468,20 @@ MachinaGUI::menu_export_midi()
void
+MachinaGUI::menu_export_graphviz()
+{
+ Gtk::FileChooserDialog dialog(*_main_window, "Export to a GraphViz DOT file", Gtk::FILE_CHOOSER_ACTION_SAVE);
+ dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
+ dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
+
+ const int result = dialog.run();
+
+ if (result == Gtk::RESPONSE_OK)
+ _canvas->render_to_dot(dialog.get_filename());
+}
+
+
+void
MachinaGUI::on_pane_position_changed()
{
// avoid infinite recursion...
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
index 7774ff6..6677d24 100644
--- a/src/gui/MachinaGUI.hpp
+++ b/src/gui/MachinaGUI.hpp
@@ -61,6 +61,7 @@ protected:
void menu_file_save_as();
void menu_import_midi();
void menu_export_midi();
+ void menu_export_graphviz();
//void show_messages_toggled();
void show_toolbar_toggled();
//void menu_view_refresh();
@@ -102,6 +103,7 @@ protected:
Gtk::MenuItem* _menu_file_quit;
Gtk::MenuItem* _menu_import_midi;
Gtk::MenuItem* _menu_export_midi;
+ Gtk::MenuItem* _menu_export_graphviz;
Gtk::MenuItem* _menu_help_about;
Gtk::CheckMenuItem* _menu_view_toolbar;
//Gtk::CheckMenuItem* _menu_view_messages;
diff --git a/src/gui/machina.glade b/src/gui/machina.glade
index 216dfeb..b53398b 100644
--- a/src/gui/machina.glade
+++ b/src/gui/machina.glade
@@ -85,7 +85,7 @@
<accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image9">
+ <widget class="GtkImage" id="image16">
<property name="visible">True</property>
<property name="stock">gtk-media-record</property>
<property name="icon_size">1</property>
@@ -107,7 +107,35 @@
<accelerator key="E" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image10">
+ <widget class="GtkImage" id="image17">
+ <property name="visible">True</property>
+ <property name="stock">gtk-convert</property>
+ <property name="icon_size">1</property>
+ <property name="xalign">0.5</property>
+ <property name="yalign">0.5</property>
+ <property name="xpad">0</property>
+ <property name="ypad">0</property>
+ </widget>
+ </child>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkSeparatorMenuItem" id="separator7">
+ <property name="visible">True</property>
+ </widget>
+ </child>
+
+ <child>
+ <widget class="GtkImageMenuItem" id="export_graphviz_menuitem">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Export _Graphviz...</property>
+ <property name="use_underline">True</property>
+ <signal name="activate" handler="on_export_graphviz_menuitem_activate" last_modification_time="Mon, 12 Mar 2007 19:47:58 GMT"/>
+ <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+ <child internal-child="image">
+ <widget class="GtkImage" id="image18">
<property name="visible">True</property>
<property name="stock">gtk-convert</property>
<property name="icon_size">1</property>
diff --git a/src/gui/main.cpp b/src/gui/main.cpp
index 3e7f737..cb9b7e9 100644
--- a/src/gui/main.cpp
+++ b/src/gui/main.cpp
@@ -17,40 +17,48 @@
#include <signal.h>
#include <iostream>
+#include <string>
#include <libgnomecanvasmm.h>
-
#include "machina/Loader.hpp"
#include "machina/JackDriver.hpp"
-
+#include "machina/SMFDriver.hpp"
#include "MachinaGUI.hpp"
+using namespace std;
using namespace Machina;
int
main(int argc, char** argv)
{
- // Build engine
+ SharedPtr<Machina::Machine> machine;
- SharedPtr<JackDriver> driver(new JackDriver());
+ // Load machine, if given
+ if (argc == 2) {
+ string filename = argv[1];
+ cout << "Building machine from MIDI file " << filename << endl;
+ SharedPtr<Machina::SMFDriver> file_driver(new Machina::SMFDriver());
+ machine = file_driver->learn(string("file://") + filename, 16.0);
+ }
+ // Build engine
+ SharedPtr<JackDriver> driver(new JackDriver(machine));
driver->attach("machina");
-
SharedPtr<Engine> engine(new Engine(driver));
// Launch GUI
try {
-
- Gnome::Canvas::init();
- Gtk::Main app(argc, argv);
-
- driver->activate();
- MachinaGUI gui(engine);
- app.run(*gui.window());
-
- } catch (std::string msg) {
- std::cerr << "Caught exception, aborting. Error message was: " << msg << std::endl;
+ Gnome::Canvas::init();
+ Gtk::Main app(argc, argv);
+
+ driver->activate();
+ MachinaGUI gui(engine);
+
+ app.run(*gui.window());
+
+ } catch (string msg) {
+ cerr << "Caught exception, aborting. Error message was: " << msg << endl;
return 1;
}