From 1c4f8c131dec109677d0a9b2b008c39a66d08158 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 12 Mar 2007 21:36:32 +0000 Subject: 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 --- src/engine/Engine.cpp | 2 +- src/engine/SMFDriver.cpp | 73 ++++++++++++++++++++++++++++++++++------ src/engine/machina/SMFDriver.hpp | 7 ++-- src/gui/MachinaCanvas.cpp | 8 ++++- src/gui/MachinaGUI.cpp | 61 +++++++++++---------------------- src/gui/MachinaGUI.hpp | 2 ++ src/gui/machina.glade | 32 ++++++++++++++++-- src/gui/main.cpp | 38 ++++++++++++--------- 8 files changed, 149 insertions(+), 74 deletions(-) (limited to 'src') 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 Engine::learn_midi(const Glib::ustring& uri) { SharedPtr file_driver(new SMFDriver()); - SharedPtr m = file_driver->learn(uri); + SharedPtr 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 -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(); 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 -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 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 m, list > active_nodes; SharedPtr initial_node(new Node()); - m->add_node(initial_node); + initial_node->set_initial(true); + //m->add_node(initial_node); SharedPtr 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 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(new Node()); node->add_enter_action(SharedPtr(new MidiAction(ev_size, buf))); - connect_node->add_outgoing_edge(SharedPtr(new Edge(connect_node, node))); + assert(connect_node_end_time <= t); + + if (t == connect_node_end_time) { + connect_node->add_outgoing_edge(SharedPtr(new Edge(connect_node, node))); + } else { + SharedPtr delay_node(new Node()); + delay_node->set_duration(t - connect_node_end_time); + connect_node->add_outgoing_edge(SharedPtr(new Edge(connect_node, delay_node))); + delay_node->add_outgoing_edge(SharedPtr(new Edge(delay_node, node))); + m->add_node(delay_node); + ++added_nodes; + connect_node = delay_node; + connect_node_end_time = t; + } + node->enter(SharedPtr(), t); active_nodes.push_back(node); } else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF) { @@ -125,14 +148,19 @@ SMFDriver::learn_track(SharedPtr 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(new MidiAction(ev_size, buf))); (*i)->set_duration(t - (*i)->enter_time()); (*i)->exit(SharedPtr(), 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 m, } } - initial_node->set_initial(true); + // Resolve any stuck notes when the rest of the machine is finished + if ( ! active_nodes.empty()) { + for (list >::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) { + cerr << "WARNING: Resolving stuck note from MIDI file." << endl; + SharedPtr action = PtrCast((*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(new MidiAction(3, note_off))); + (*i)->set_duration(t - (*i)->enter_time()); + (*i)->exit(SharedPtr(), 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 { public: - SharedPtr learn(const Glib::ustring& uri); - SharedPtr learn(const Glib::ustring& uri, unsigned track); + SharedPtr learn(const Glib::ustring& uri, Raul::BeatTime max_duration); + SharedPtr learn(const Glib::ustring& uri, unsigned track, Raul::BeatTime max_duration); void run(SharedPtr machine, Raul::BeatTime max_time); private: void learn_track(SharedPtr 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 node) SharedPtr 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(view))); @@ -209,7 +212,10 @@ MachinaCanvas::build(SharedPtr machine) e != view->node()->outgoing_edges().end(); ++e) { SharedPtr dst_view = views[(*e)->dst()]; - assert(dst_view); + if (!dst_view) { + cerr << "WARNING: Edge to node with no view" << endl; + continue; + } boost::shared_ptr 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 - - - -/* Gtk helpers (resize combo boxes) */ - -#if 0 -static void -gtkmm_get_ink_pixel_size (Glib::RefPtr 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 engine) : _pane_closed(false), @@ -117,6 +77,7 @@ MachinaGUI::MachinaGUI(SharedPtr 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 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 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 file_driver(new Machina::SMFDriver()); //SharedPtr machine = file_driver->learn(dialog.get_uri(), // track_sb->get_value_as_int()); - SharedPtr machine = file_driver->learn(dialog.get_uri()); + SharedPtr machine = file_driver->learn(dialog.get_uri(), 16.0); if (machine) { dialog.hide(); @@ -502,6 +467,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() { 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 @@ - + True gtk-media-record 1 @@ -107,7 +107,35 @@ - + + True + gtk-convert + 1 + 0.5 + 0.5 + 0 + 0 + + + + + + + + True + + + + + + True + Export _Graphviz... + True + + + + + True gtk-convert 1 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 #include +#include #include - #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 machine; - SharedPtr driver(new JackDriver()); + // Load machine, if given + if (argc == 2) { + string filename = argv[1]; + cout << "Building machine from MIDI file " << filename << endl; + SharedPtr file_driver(new Machina::SMFDriver()); + machine = file_driver->learn(string("file://") + filename, 16.0); + } + // Build engine + SharedPtr driver(new JackDriver(machine)); driver->attach("machina"); - SharedPtr 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; } -- cgit v1.2.1