aboutsummaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2013-01-13 07:49:49 +0000
committerDavid Robillard <d@drobilla.net>2013-01-13 07:49:49 +0000
commit67a8adbc93991acfb688f378f52392995a272fac (patch)
treea4e629bd9c0d7da4cc5c7cb644b3352ca6f0dc78 /src/gui
parent33aa54fa98783d1da2a322ee136c17df7f9c98a5 (diff)
downloadmachina-67a8adbc93991acfb688f378f52392995a272fac.tar.gz
machina-67a8adbc93991acfb688f378f52392995a272fac.tar.bz2
machina-67a8adbc93991acfb688f378f52392995a272fac.zip
Change model to have a single initial node.
Merge multiple recording into branches off the same initial node. Make transport state sane with 3 distinct states. Handle announcing objects several times correctly. Don't send useless zero coordinates for new nodes, position in visible area. Rewrite and clean up Machine code. Update help. git-svn-id: http://svn.drobilla.net/lad/trunk/machina@4954 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/EdgeView.cpp11
-rw-r--r--src/gui/EdgeView.hpp6
-rw-r--r--src/gui/MachinaCanvas.cpp65
-rw-r--r--src/gui/MachinaCanvas.hpp19
-rw-r--r--src/gui/MachinaGUI.cpp74
-rw-r--r--src/gui/MachinaGUI.hpp7
-rw-r--r--src/gui/NodeView.cpp20
-rw-r--r--src/gui/NodeView.hpp2
-rw-r--r--src/gui/machina.ui66
9 files changed, 145 insertions, 125 deletions
diff --git a/src/gui/EdgeView.cpp b/src/gui/EdgeView.cpp
index ec77a7b..da267e8 100644
--- a/src/gui/EdgeView.cpp
+++ b/src/gui/EdgeView.cpp
@@ -65,10 +65,10 @@ inline static uint32_t edge_color(float prob)
using namespace Ganv;
EdgeView::EdgeView(Canvas& canvas,
- SPtr<NodeView> src,
- SPtr<NodeView> dst,
+ NodeView* src,
+ NodeView* dst,
SPtr<machina::client::ClientObject> edge)
- : Ganv::Edge(canvas, src.get(), dst.get(), 0x9FA0A0F4, true, false)
+ : Ganv::Edge(canvas, src, dst, 0x9FA0A0F4, true, false)
, _edge(edge)
{
set_color(edge_color(probability()));
@@ -80,6 +80,11 @@ EdgeView::EdgeView(Canvas& canvas,
sigc::mem_fun(this, &EdgeView::on_event));
}
+EdgeView::~EdgeView()
+{
+ _edge->set_view(NULL);
+}
+
float
EdgeView::probability() const
{
diff --git a/src/gui/EdgeView.hpp b/src/gui/EdgeView.hpp
index 6d51427..dbf4476 100644
--- a/src/gui/EdgeView.hpp
+++ b/src/gui/EdgeView.hpp
@@ -35,10 +35,12 @@ class EdgeView
{
public:
EdgeView(Ganv::Canvas& canvas,
- SPtr<NodeView> src,
- SPtr<NodeView> dst,
+ NodeView* src,
+ NodeView* dst,
SPtr<machina::client::ClientObject> edge);
+ ~EdgeView();
+
void show_label(bool show);
virtual double length_hint() const;
diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp
index 627eb09..7a7efa9 100644
--- a/src/gui/MachinaCanvas.cpp
+++ b/src/gui/MachinaCanvas.cpp
@@ -39,6 +39,7 @@ namespace gui {
MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
: Canvas(width, height)
, _app(app)
+ , _last_clicked(NULL)
{
widget().grab_focus();
@@ -47,13 +48,8 @@ MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height)
}
bool
-MachinaCanvas::node_clicked(WPtr<NodeView> item, GdkEventButton* event)
+MachinaCanvas::node_clicked(NodeView* node, GdkEventButton* event)
{
- SPtr<NodeView> node = dynamic_ptr_cast<NodeView>(item.lock());
- if (!node) {
- return false;
- }
-
if (event->state & GDK_CONTROL_MASK) {
return false;
}
@@ -65,19 +61,17 @@ MachinaCanvas::node_clicked(WPtr<NodeView> item, GdkEventButton* event)
} else if (event->button == 1) {
// Left click: connect/disconnect
- SPtr<NodeView> last = _last_clicked.lock();
-
- if (last) {
- if (node != last) {
- if (get_edge(last.get(), node.get())) {
- action_disconnect(last, node);
+ if (_last_clicked) {
+ if (node != _last_clicked) {
+ if (get_edge(_last_clicked, node)) {
+ action_disconnect(_last_clicked, node);
} else {
- action_connect(last, node);
+ action_connect(_last_clicked, node);
}
}
- last->set_default_colors();
- _last_clicked.reset();
+ _last_clicked->set_default_colors();
+ _last_clicked = NULL;
} else {
_last_clicked = node;
@@ -112,17 +106,28 @@ MachinaCanvas::on_new_object(SPtr<client::ClientObject> object)
}
if (type.get<URIInt>() == uris.machina_Node) {
- SPtr<NodeView> view(
- new NodeView(_app->window(), *this, object,
- object->get(uris.machina_canvas_x).get_float(),
- object->get(uris.machina_canvas_y).get_float()));
+ const Raul::Atom& node_x = object->get(uris.machina_canvas_x);
+ const Raul::Atom& node_y = object->get(uris.machina_canvas_y);
+ float x, y;
+ if (node_x.type() == _app->forge().Float &&
+ node_y.type() == _app->forge().Float) {
+ x = node_x.get_float();
+ y = node_y.get_float();
+ } else {
+ int scroll_x, scroll_y;
+ get_scroll_offsets(scroll_x, scroll_y);
+ x = scroll_x + 64.0;
+ y = scroll_y + 64.0;
+ }
+
+ NodeView* view = new NodeView(_app->window(), *this, object, x, y);
//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),
- WPtr<NodeView>(view)));
+ view));
object->set_view(view);
@@ -141,12 +146,10 @@ MachinaCanvas::on_new_object(SPtr<client::ClientObject> object)
return;
}
- SPtr<NodeView> tail_view = dynamic_ptr_cast<NodeView>(tail->view());
- SPtr<NodeView> head_view = dynamic_ptr_cast<NodeView>(head->view());
+ NodeView* tail_view = dynamic_cast<NodeView*>(tail->view());
+ NodeView* head_view = dynamic_cast<NodeView*>(head->view());
- SPtr<EdgeView> view(new EdgeView(*this, tail_view, head_view, object));
-
- object->set_view(view);
+ object->set_view(new EdgeView(*this, tail_view, head_view, object));
} else {
std::cerr << "Unknown object type " << type.get<URIInt>() << std::endl;
@@ -160,7 +163,7 @@ MachinaCanvas::on_erase_object(SPtr<client::ClientObject> object)
if (type.get<URIInt>() == URIs::instance().machina_Node) {
// Destruction of the view will remove from the canvas
} else if (type.get<URIInt>() == URIs::instance().machina_Edge) {
- object->set_view(SPtr<client::ClientObject::View>());
+ object->set_view(NULL);
} else {
std::cerr << "Unknown object type" << std::endl;
}
@@ -179,17 +182,15 @@ MachinaCanvas::action_create_node(double x, double y)
}
void
-MachinaCanvas::action_connect(SPtr<NodeView> src,
- SPtr<NodeView> head)
+MachinaCanvas::action_connect(NodeView* tail, NodeView* head)
{
- _app->controller()->connect(src->node()->id(), head->node()->id());
+ _app->controller()->connect(tail->node()->id(), head->node()->id());
}
void
-MachinaCanvas::action_disconnect(SPtr<NodeView> src,
- SPtr<NodeView> head)
+MachinaCanvas::action_disconnect(NodeView* tail, NodeView* head)
{
- _app->controller()->disconnect(src->node()->id(), head->node()->id());
+ _app->controller()->disconnect(tail->node()->id(), head->node()->id());
}
} // namespace machina
diff --git a/src/gui/MachinaCanvas.hpp b/src/gui/MachinaCanvas.hpp
index c396931..52e2d74 100644
--- a/src/gui/MachinaCanvas.hpp
+++ b/src/gui/MachinaCanvas.hpp
@@ -40,9 +40,6 @@ class MachinaCanvas
public:
MachinaCanvas(MachinaGUI* app, int width, int height);
- //void build(SPtr<const machina::Machine> machine, bool show_labels);
- //void update_edges();
-
void on_new_object(SPtr<machina::client::ClientObject> object);
void on_erase_object(SPtr<machina::client::ClientObject> object);
@@ -51,21 +48,15 @@ public:
protected:
bool on_event(GdkEvent* event);
- bool node_clicked(WPtr<NodeView> item, GdkEventButton* ev);
+ bool node_clicked(NodeView* node, GdkEventButton* ev);
private:
- //SPtr<NodeView> create_node_view(SPtr<machina::Node> node);
-
void action_create_node(double x, double y);
+ void action_connect(NodeView* tail, NodeView* head);
+ void action_disconnect(NodeView* tail, NodeView* head);
- void action_connect(SPtr<NodeView> port1,
- SPtr<NodeView> port2);
-
- void action_disconnect(SPtr<NodeView> port1,
- SPtr<NodeView> port2);
-
- MachinaGUI* _app;
- WPtr<NodeView> _last_clicked;
+ MachinaGUI* _app;
+ NodeView* _last_clicked;
};
} // namespace machina
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 63cbb76..f95eec2 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -102,8 +102,8 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_record_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::record_toggled));
- _stop_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::stop_clicked));
+ _stop_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::stop_toggled));
_play_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::play_toggled));
@@ -199,13 +199,12 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_evolve_toolbar->set_sensitive(false);
#endif
- _controller->announce(engine->machine());
- _canvas->arrange();
-
_client_model->signal_new_object.connect(
sigc::mem_fun(this, &MachinaGUI::on_new_object));
_client_model->signal_erase_object.connect(
sigc::mem_fun(this, &MachinaGUI::on_erase_object));
+
+ rebuild_canvas();
}
MachinaGUI::~MachinaGUI()
@@ -294,7 +293,7 @@ MachinaGUI::evolve_toggled()
_evolver = SPtr<Evolver>(
new Evolver(_unit, _target_filename, _engine->machine()));
_evolve = true;
- stop_clicked();
+ stop_toggled();
_engine->driver()->set_machine(SPtr<Machine>());
_evolver->start();
} else {
@@ -365,16 +364,24 @@ MachinaGUI::mutate(SPtr<Machine> machine, unsigned mutation)
void
MachinaGUI::update_toolbar()
{
- _record_button->set_active(_engine->driver()->recording());
- _play_button->set_active(_engine->machine()->is_activated());
+ const Driver::PlayState state = _engine->driver()->play_state();
+ _record_button->set_active(state == Driver::PlayState::RECORDING);
+ _play_button->set_active(state == Driver::PlayState::PLAYING);
_quantize_spinbutton->set_sensitive(_quantize_checkbutton->get_active());
}
void
+MachinaGUI::rebuild_canvas()
+{
+ _controller->announce(_engine->machine());
+ _canvas->arrange();
+}
+
+void
MachinaGUI::quantize_changed()
{
if (_quantize_checkbutton->get_active()) {
- _engine->set_quantization(1/(double)_quantize_spinbutton->get_value_as_int());
+ _engine->set_quantization(1.0 / _quantize_spinbutton->get_value());
} else {
_engine->set_quantization(0.0);
}
@@ -426,9 +433,7 @@ MachinaGUI::menu_file_open()
if (result == Gtk::RESPONSE_OK) {
SPtr<machina::Machine> new_machine = _engine->load_machine(dialog.get_uri());
if (new_machine) {
- _canvas->destroy();
- _controller->announce(new_machine);
- _canvas->arrange();
+ rebuild_canvas();
_save_uri = dialog.get_uri();
}
}
@@ -536,10 +541,9 @@ MachinaGUI::menu_import_midi()
if (machine) {
dialog.hide();
- machine->activate();
machine->reset(NULL, machine->time());
_engine->driver()->set_machine(machine);
- _controller->announce(machine);
+ rebuild_canvas();
} else {
Gtk::MessageDialog msg_dialog(dialog, "Error loading MIDI file",
false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
@@ -651,41 +655,35 @@ MachinaGUI::menu_help_help()
}
void
-MachinaGUI::record_toggled()
+MachinaGUI::stop_toggled()
{
- if (_record_button->get_active() && ! _engine->driver()->recording()) {
- _engine->driver()->start_record(_step_record_checkbutton->get_active());
- } else if (_engine->driver()->recording()) {
- _engine->driver()->finish_record();
- _controller->announce(_engine->machine());
- update_toolbar();
+ if (_stop_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::STOPPED);
+ if (old_state == Driver::PlayState::RECORDING) {
+ rebuild_canvas();
+ }
}
}
void
-MachinaGUI::stop_clicked()
+MachinaGUI::play_toggled()
{
- _play_button->set_active(false);
-
- if (_engine->driver()->recording()) {
- _engine->driver()->stop();
- _engine->machine()->deactivate();
- _controller->announce(_engine->machine());
- } else {
- _engine->driver()->stop();
- _engine->machine()->deactivate();
+ if (_play_button->get_active()) {
+ const Driver::PlayState old_state = _engine->driver()->play_state();
+ _engine->driver()->set_play_state(Driver::PlayState::PLAYING);
+ if (old_state == Driver::PlayState::RECORDING) {
+ rebuild_canvas();
+ }
}
-
- update_toolbar();
}
void
-MachinaGUI::play_toggled()
+MachinaGUI::record_toggled()
{
- if (_play_button->get_active())
- _engine->machine()->activate();
- else
- _engine->machine()->deactivate();
+ if (_record_button->get_active()) {
+ _engine->driver()->set_play_state(Driver::PlayState::RECORDING);
+ }
}
void
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
index 6c85dca..c06d6b3 100644
--- a/src/gui/MachinaGUI.hpp
+++ b/src/gui/MachinaGUI.hpp
@@ -95,6 +95,7 @@ protected:
void mutate(SPtr<machina::Machine> machine, unsigned mutation);
void zoom(double z);
void update_toolbar();
+ void rebuild_canvas();
bool scrolled_window_event(GdkEvent* ev);
bool idle_callback();
@@ -104,9 +105,9 @@ protected:
bool evolve_callback();
#endif
- void record_toggled();
- void stop_clicked();
+ void stop_toggled();
void play_toggled();
+ void record_toggled();
void quantize_changed();
void tempo_changed();
@@ -155,7 +156,7 @@ protected:
Gtk::CheckButton* _quantize_checkbutton;
Gtk::SpinButton* _quantize_spinbutton;
Gtk::ToggleToolButton* _record_button;
- Gtk::ToolButton* _stop_button;
+ Gtk::ToggleToolButton* _stop_button;
Gtk::ToggleToolButton* _play_button;
Gtk::ToolButton* _zoom_normal_button;
Gtk::ToolButton* _zoom_full_button;
diff --git a/src/gui/NodeView.cpp b/src/gui/NodeView.cpp
index a73b996..d1bd913 100644
--- a/src/gui/NodeView.cpp
+++ b/src/gui/NodeView.cpp
@@ -45,6 +45,12 @@ NodeView::NodeView(Gtk::Window* window,
signal_event().connect(
sigc::mem_fun(this, &NodeView::on_event));
+ MachinaCanvas* mcanvas = dynamic_cast<MachinaCanvas*>(&canvas);
+ if (is(mcanvas->app()->forge(), URIs::instance().machina_initial)) {
+ set_border_width(4.0);
+ set_label("init");
+ }
+
node->signal_property.connect(
sigc::mem_fun(this, &NodeView::on_property));
@@ -53,6 +59,11 @@ NodeView::NodeView(Gtk::Window* window,
}
}
+NodeView::~NodeView()
+{
+ _node->set_view(NULL);
+}
+
bool
NodeView::on_double_click(GdkEventButton*)
{
@@ -77,12 +88,6 @@ NodeView::on_event(GdkEvent* event)
if (event->button.button == 1) {
canvas->app()->controller()->set_property(
_node->id(),
- URIs::instance().machina_initial,
- forge.make(!is(forge, URIs::instance().machina_initial)));
- return true;
- } else if (event->button.button == 3) {
- canvas->app()->controller()->set_property(
- _node->id(),
URIs::instance().machina_selector,
forge.make(!is(forge, URIs::instance().machina_selector)));
return true;
@@ -133,7 +138,6 @@ NodeView::on_property(machina::URIInt key, const Raul::Atom& value)
static const uint32_t active_border_color = 0x00FF00FF;
if (key == URIs::instance().machina_selector) {
- //_node.property_dash() = value.get_bool() ? selector_dash() : 0;
if (value.get_bool()) {
set_dash_length(4.0);
} else {
@@ -162,8 +166,6 @@ NodeView::on_property(machina::URIInt key, const Raul::Atom& value)
on_action_property(i.first, i.second);
}
}
- } else {
- cout << "Unknown property " << key << endl;
}
}
diff --git a/src/gui/NodeView.hpp b/src/gui/NodeView.hpp
index 17f8c44..6ad68fb 100644
--- a/src/gui/NodeView.hpp
+++ b/src/gui/NodeView.hpp
@@ -38,6 +38,8 @@ public:
double x,
double y);
+ ~NodeView();
+
SPtr<machina::client::ClientObject> node() { return _node; }
void show_label(bool show);
diff --git a/src/gui/machina.ui b/src/gui/machina.ui
index b48df7d..bb1b711 100644
--- a/src/gui/machina.ui
+++ b/src/gui/machina.ui
@@ -2,15 +2,29 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
+ <object class="GtkRadioAction" id="record_action">
+ <property name="stock_id">gtk-media-record</property>
+ <property name="draw_as_radio">True</property>
+ </object>
+ <object class="GtkRadioAction" id="play_action">
+ <property name="stock_id">gtk-media-play</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
+ <object class="GtkRadioAction" id="stop_action">
+ <property name="stock_id">gtk-media-stop</property>
+ <property name="draw_as_radio">True</property>
+ <property name="group">record_action</property>
+ </object>
<object class="GtkAboutDialog" id="about_win">
<property name="can_focus">False</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">normal</property>
+ <property name="program_name">Machina</property>
<property name="copyright" translatable="yes">© 2013 David Robillard &lt;http://drobilla.net&gt;</property>
<property name="comments" translatable="yes">A MIDI sequencer based on
probabilistic finite-state automata</property>
<property name="website">http://drobilla.net/software/machina</property>
- <property name="website_label" translatable="yes">Website:</property>
<property name="license" translatable="yes">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
@@ -99,17 +113,24 @@ along with Machina; if not, write to the Free Software Foundation, Inc.,
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
- <property name="label" translatable="yes">Initial nodes are shown with a thick border.
-Selector nodes are shown in green.
+ <property name="label" translatable="yes">Nodes represent notes, which have a pitch and duration. When a node is
+highlighted green, it is playing. After a node is finished playing, play
+travels along the outgoing arcs, depending on their probability.
+
+Nodes with dashed borders are selectors. Only one successor is played after a
+selector, i.e. only one outgoing arc is traversed.
+
+A machine can be constructed with the mouse or by recording MIDI input. To
+record, press the record button and play some MIDI notes. To finish recording,
+press stop or play and the new nodes will be added to the machine.
-- Right click canvas to create a new node
-- Middle click nodes to learn a MIDI note for that node
-- Right click two nodes in succession to connect nodes
-- Double click a node to show its properties dialog
-- Ctrl+Left click a node to make it an initial node
-- Ctrl+Right click a node to make it a selector node
-- Ctrl+Left click edge probabilities to decrease
-- Ctrl+Right click edge probabilities to increase</property>
+• Right click the canvas to create a new node
+• Middle click a node to learn a MIDI note
+• Click two nodes in succession to connect them
+• Double click a node to show its properties dialog
+• Ctrl+Left click a node to make it a selector
+• Ctrl+Left click an edge to decrease its probability
+• Ctrl+Right click an edge to increase its probability</property>
</object>
<packing>
<property name="expand">True</property>
@@ -353,12 +374,11 @@ Selector nodes are shown in green.
<property name="toolbar_style">icons</property>
<property name="show_arrow">False</property>
<child>
- <object class="GtkToggleToolButton" id="record_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="stop_but">
+ <property name="related_action">stop_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Record</property>
- <property name="stock_id">gtk-media-record</property>
+ <property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -366,12 +386,12 @@ Selector nodes are shown in green.
</packing>
</child>
<child>
- <object class="GtkToolButton" id="stop_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="play_but">
+ <property name="related_action">play_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Stop</property>
- <property name="stock_id">gtk-media-stop</property>
+ <property name="tooltip_text" translatable="yes">Play</property>
+ <property name="active">True</property>
</object>
<packing>
<property name="expand">False</property>
@@ -379,13 +399,11 @@ Selector nodes are shown in green.
</packing>
</child>
<child>
- <object class="GtkToggleToolButton" id="play_but">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="record_but">
+ <property name="related_action">record_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Play</property>
- <property name="stock_id">gtk-media-play</property>
- <property name="active">True</property>
+ <property name="tooltip_text" translatable="yes">Record</property>
</object>
<packing>
<property name="expand">False</property>