aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/engine/ActionFactory.cpp10
-rw-r--r--src/engine/Machine.cpp19
-rw-r--r--src/engine/Makefile.am4
-rw-r--r--src/engine/Node.cpp18
-rw-r--r--src/engine/Problem.cpp112
-rw-r--r--src/engine/machina/ActionFactory.hpp1
-rw-r--r--src/engine/machina/Edge.hpp2
-rw-r--r--src/engine/machina/LearnRequest.hpp6
-rw-r--r--src/engine/machina/Machine.hpp4
-rw-r--r--src/engine/machina/MidiAction.hpp11
-rw-r--r--src/engine/machina/Node.hpp4
-rw-r--r--src/engine/machina/Problem.hpp56
-rw-r--r--src/gui/MachinaGUI.cpp156
-rw-r--r--src/gui/MachinaGUI.hpp14
-rw-r--r--src/gui/machina.glade22
15 files changed, 340 insertions, 99 deletions
diff --git a/src/engine/ActionFactory.cpp b/src/engine/ActionFactory.cpp
index 4ca1a3e..6632b3c 100644
--- a/src/engine/ActionFactory.cpp
+++ b/src/engine/ActionFactory.cpp
@@ -20,6 +20,16 @@
namespace Machina {
+SharedPtr<Action>
+ActionFactory::copy(SharedPtr<Action> copy)
+{
+ SharedPtr<MidiAction> ma = PtrCast<MidiAction>(copy);
+ if (ma)
+ return SharedPtr<Action>(new MidiAction(ma->event_size(), ma->event()));
+ else
+ return SharedPtr<Action>();
+}
+
SharedPtr<Action>
ActionFactory::note_on(unsigned char note)
diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp
index 44e8446..fbda9ad 100644
--- a/src/engine/Machine.cpp
+++ b/src/engine/Machine.cpp
@@ -38,6 +38,25 @@ Machine::Machine()
}
+/** Copy a Machine.
+ *
+ * Creates a deep copy which is the 'same' machine, but with
+ * fresh state (deactivated, rewound)
+ */
+Machine::Machine(const Machine& copy)
+ : Raul::Stateful() // don't copy RDF ID
+ , _is_activated(false)
+ , _is_finished(false)
+ , _time(0)
+ , _sink(copy._sink)
+{
+ for (Nodes::const_iterator i = copy._nodes.begin(); i != copy._nodes.end(); ++i) {
+ SharedPtr<Machina::Node> node(new Machina::Node(*i->get()));
+ _nodes.push_back(node);
+ }
+}
+
+
Machine::~Machine()
{
}
diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am
index 879c00d..c46ffed 100644
--- a/src/engine/Makefile.am
+++ b/src/engine/Makefile.am
@@ -2,8 +2,8 @@ SUBDIRS = machina
lib_LTLIBRARIES = libmachina.la
-libmachina_la_CXXFLAGS = @REDLANDMM_CFLAGS@ @RAUL_CFLAGS@ @JACK_CFLAGS@ @GLIBMM_CFLAGS@
-libmachina_la_LIBADD = @REDLANDMM_LIBS@ @RAUL_LIBS@ @JACK_LIBS@ @GLIBMM_LIBS@
+libmachina_la_CXXFLAGS = @EUGENE_CFLAGS@ @REDLANDMM_CFLAGS@ @RAUL_CFLAGS@ @JACK_CFLAGS@ @GLIBMM_CFLAGS@
+libmachina_la_LIBADD = @EUGENE_LIBS@ @RAUL_LIBS@ @JACK_LIBS@ @GLIBMM_LIBS@
libmachina_la_SOURCES = \
Action.cpp \
diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp
index 0eca0e6..f965aff 100644
--- a/src/engine/Node.cpp
+++ b/src/engine/Node.cpp
@@ -21,6 +21,7 @@
#include <redlandmm/Model.hpp>
#include <machina/Node.hpp>
#include <machina/Edge.hpp>
+#include <machina/ActionFactory.hpp>
namespace Machina {
@@ -35,6 +36,23 @@ Node::Node(BeatCount duration, bool initial)
}
+Node::Node(const Node& copy)
+ : Raul::Stateful() // don't copy RDF ID
+ , _is_initial(copy._is_initial)
+ , _is_selector(copy._is_selector)
+ , _is_active(false)
+ , _enter_time(0)
+ , _duration(copy._duration)
+ , _enter_action(ActionFactory::copy(copy._enter_action))
+ , _exit_action(ActionFactory::copy(copy._exit_action))
+{
+ for (Edges::const_iterator i = copy._edges.begin(); i != copy._edges.end(); ++i) {
+ SharedPtr<Edge> edge(new Edge(*i->get()));
+ _edges.push_back(edge);
+ }
+}
+
+
/** Always returns an edge, unless there are none */
SharedPtr<Edge>
Node::random_edge()
diff --git a/src/engine/Problem.cpp b/src/engine/Problem.cpp
new file mode 100644
index 0000000..f57aa15
--- /dev/null
+++ b/src/engine/Problem.cpp
@@ -0,0 +1,112 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * 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 <machina/Problem.hpp>
+#include <machina/Machine.hpp>
+#include <raul/SMFReader.hpp>
+#include <raul/midi_events.h>
+
+using namespace std;
+
+namespace Machina {
+
+
+Problem::Problem(const std::string& target_midi)
+ : _target(*this)
+{
+ Raul::SMFReader smf;
+ const bool opened = smf.open(target_midi);
+ assert(opened);
+
+ smf.seek_to_track(2); // FIXME: kluge
+
+ uint8_t buf[4];
+ uint32_t ev_size;
+ uint32_t delta_time;
+ while (smf.read_event(4, buf, &ev_size, &delta_time) >= 0) {
+ if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
+ const uint8_t note = buf[1];
+ ++_target._note_frequency[note];
+ ++_target._n_notes;
+ }
+ }
+
+ _target.compute();
+}
+
+
+float
+Problem::fitness(Machine& machine)
+{
+ SharedPtr<Evaluator> eval(new Evaluator(*this));
+
+ machine.reset(0.0f);
+ machine.set_sink(eval);
+
+ // FIXME: timing stuff here isn't right at all...
+
+ unsigned ppqn = 19200;
+ Raul::TimeSlice time(ppqn, 120);
+ time.set_start(0);
+ time.set_length(ppqn);
+
+ while (time.start_ticks() < _target._n_notes * ppqn) {
+ machine.run(time);
+ time.set_start(time.start_ticks() + ppqn);
+ }
+
+ eval->compute();
+
+ float f = 0;
+
+ // Punish for frequency differences.
+ // Optimal fitness = 0 (max)
+ for (uint8_t i=0; i < 128; ++i)
+ f -= fabs(_target._note_frequency[i] - eval->_note_frequency[i]);
+
+ return f;
+}
+
+
+void
+Problem::Evaluator::write_event(Raul::BeatTime time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error)
+{
+ if ((ev[0] & 0xF0) == MIDI_CMD_NOTE_ON) {
+ const uint8_t note = ev[1];
+ ++_note_frequency[note];
+ ++_n_notes;
+ }
+}
+
+
+void
+Problem::Evaluator::compute()
+{
+ for (uint8_t i=0; i < 128; ++i) {
+ if (_note_frequency[i] > 0) {
+ _note_frequency[i] /= (float)_n_notes;
+ //cout << (int)i << ":\t" << _note_frequency[i] << endl;
+ }
+ }
+}
+
+
+} // namespace Machina
+
diff --git a/src/engine/machina/ActionFactory.hpp b/src/engine/machina/ActionFactory.hpp
index b0aa1f9..f47a626 100644
--- a/src/engine/machina/ActionFactory.hpp
+++ b/src/engine/machina/ActionFactory.hpp
@@ -26,6 +26,7 @@ class Action;
namespace ActionFactory {
+ SharedPtr<Action> copy(SharedPtr<Action> copy);
SharedPtr<Action> note_on(unsigned char note);
SharedPtr<Action> note_off(unsigned char note);
}
diff --git a/src/engine/machina/Edge.hpp b/src/engine/machina/Edge.hpp
index 3f564ef..1b52dc7 100644
--- a/src/engine/machina/Edge.hpp
+++ b/src/engine/machina/Edge.hpp
@@ -31,7 +31,7 @@ namespace Machina {
class Node;
-class Edge : public Raul::Stateful, public boost::noncopyable {
+class Edge : public Raul::Stateful {
public:
Edge(WeakPtr<Node> tail, SharedPtr<Node> head)
diff --git a/src/engine/machina/LearnRequest.hpp b/src/engine/machina/LearnRequest.hpp
index 4ec22ff..f6bc60f 100644
--- a/src/engine/machina/LearnRequest.hpp
+++ b/src/engine/machina/LearnRequest.hpp
@@ -58,9 +58,11 @@ private:
LearnRequest(SharedPtr<Raul::Maid> maid, SharedPtr<Node> node)
: _started(false)
, _node(node)
- , _enter_action(MidiAction::create(maid, 4, NULL))
- , _exit_action(MidiAction::create(maid, 4, NULL))
+ , _enter_action(new MidiAction(4, NULL))
+ , _exit_action(new MidiAction(4, NULL))
{
+ maid->manage(_enter_action);
+ maid->manage(_exit_action);
}
bool _started;
diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp
index 889dc1a..4544090 100644
--- a/src/engine/machina/Machine.hpp
+++ b/src/engine/machina/Machine.hpp
@@ -20,6 +20,7 @@
#include <boost/utility.hpp>
#include <raul/SharedPtr.hpp>
+#include <raul/WeakPtr.hpp>
#include <raul/List.hpp>
#include <raul/TimeSlice.hpp>
#include <redlandmm/Model.hpp>
@@ -32,9 +33,10 @@ namespace Machina {
/** A (Finite State) Machine.
*/
-class Machine : public Raul::Stateful, public boost::noncopyable {
+class Machine : public Raul::Stateful {
public:
Machine();
+ Machine(const Machine& copy);
~Machine();
// Main context
diff --git a/src/engine/machina/MidiAction.hpp b/src/engine/machina/MidiAction.hpp
index 5639532..1a3f176 100644
--- a/src/engine/machina/MidiAction.hpp
+++ b/src/engine/machina/MidiAction.hpp
@@ -18,8 +18,6 @@
#ifndef MACHINA_MIDIACTION_HPP
#define MACHINA_MIDIACTION_HPP
-#include <raul/Maid.hpp>
-#include <raul/WeakPtr.hpp>
#include <raul/AtomicPtr.hpp>
#include <raul/TimeSlice.hpp>
#include "types.hpp"
@@ -37,15 +35,6 @@ public:
MidiAction(size_t size,
const unsigned char* event);
- static SharedPtr<MidiAction>
- create(SharedPtr<Raul::Maid> maid,
- size_t size, const unsigned char* event)
- {
- SharedPtr<MidiAction> ret(new MidiAction(size, event));
- maid->manage(ret);
- return ret;
- }
-
size_t event_size() { return _size; }
byte* event() { return _event.get(); }
diff --git a/src/engine/machina/Node.hpp b/src/engine/machina/Node.hpp
index 5926a50..1f0face 100644
--- a/src/engine/machina/Node.hpp
+++ b/src/engine/machina/Node.hpp
@@ -18,7 +18,6 @@
#ifndef MACHINA_NODE_HPP
#define MACHINA_NODE_HPP
-#include <boost/utility.hpp>
#include <raul/SharedPtr.hpp>
#include <raul/List.hpp>
#include <raul/Stateful.hpp>
@@ -41,11 +40,12 @@ using Raul::BeatTime;
* Initial nodes do not have enter actions (since they are entered at
* an undefined point in time <= 0).
*/
-class Node : public Raul::Stateful, public boost::noncopyable {
+class Node : public Raul::Stateful {
public:
typedef std::string ID;
Node(BeatCount duration=0, bool initial=false);
+ Node(const Node& copy);
void set_enter_action(SharedPtr<Action> action);
void set_exit_action(SharedPtr<Action> action);
diff --git a/src/engine/machina/Problem.hpp b/src/engine/machina/Problem.hpp
new file mode 100644
index 0000000..d221e5c
--- /dev/null
+++ b/src/engine/machina/Problem.hpp
@@ -0,0 +1,56 @@
+/* This file is part of Machina.
+ * Copyright (C) 2007 Dave Robillard <http://drobilla.net>
+ *
+ * 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_PROBLEM_HPP
+#define MACHINA_PROBLEM_HPP
+
+#include <raul/MIDISink.hpp>
+
+namespace Machina {
+
+class Machine;
+
+
+class Problem {
+public:
+ Problem(const std::string& target_midi);
+
+ float fitness(Machine& machine);
+
+private:
+ struct Evaluator : public Raul::MIDISink {
+ Evaluator(Problem& problem) : _problem(problem), _n_notes(0) {
+ for (uint8_t i=0; i < 128; ++i)
+ _note_frequency[i] = 0;
+ }
+ void write_event(Raul::BeatTime time,
+ size_t ev_size,
+ const uint8_t* ev) throw (std::logic_error);
+ void compute();
+ Problem& _problem;
+
+ float _note_frequency[128];
+ size_t _n_notes;
+ };
+
+ Evaluator _target;
+};
+
+
+} // namespace Machina
+
+#endif // MACHINA_PROBLEM_HPP
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 8e0b68a..42cb4ce 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -36,9 +36,10 @@ using namespace Machina;
MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
-: _refresh(false),
- _engine(engine),
- _maid(new Raul::Maid(32))
+ : _refresh(false)
+ , _evolve(false)
+ , _engine(engine)
+ , _maid(new Raul::Maid(32))
{
_canvas = boost::shared_ptr<MachinaCanvas>(new MachinaCanvas(this, 1600*2, 1200*2));
@@ -71,6 +72,7 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
xml->get_widget("zoom_normal_but", _zoom_normal_button);
xml->get_widget("zoom_full_but", _zoom_full_button);
xml->get_widget("arrange_but", _arrange_button);
+ xml->get_widget("evolve_but", _evolve_button);
xml->get_widget("mutate_but", _mutate_button);
xml->get_widget("compress_but", _compress_button);
xml->get_widget("add_node_but", _add_node_button);
@@ -128,22 +130,24 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
_quantize_spinbutton->signal_changed().connect(
sigc::mem_fun(this, &MachinaGUI::quantize_changed));
- _mutate_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::mutate));
- _compress_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::compress));
- _add_node_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::add_node));
- _remove_node_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::remove_node));
- _adjust_node_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::adjust_node));
- _add_edge_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::add_edge));
- _remove_edge_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::remove_edge));
- _adjust_edge_button->signal_clicked().connect(
- sigc::mem_fun(this, &MachinaGUI::adjust_edge));
+ _evolve_button->signal_clicked().connect(
+ sigc::mem_fun(this, &MachinaGUI::evolve));
+ _mutate_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::random_mutation), SharedPtr<Machine>()));
+ _compress_button->signal_clicked().connect(sigc::hide_return(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 0)));
+ _add_node_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 1));
+ _remove_node_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 2));
+ _adjust_node_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 3));
+ _add_edge_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 4));
+ _remove_edge_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 5));
+ _adjust_edge_button->signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(this, &MachinaGUI::mutate), SharedPtr<Machine>(), 6));
connect_widgets();
@@ -165,6 +169,9 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine)
// Idle callback to update node states
Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::idle_callback), 100);
+ // Periodic mutation callback (FIXME: temporary kludge, thread this)
+ Glib::signal_timeout().connect(sigc::mem_fun(this, &MachinaGUI::evolve_callback), 1000);
+
_canvas->build(engine->machine());
}
@@ -175,6 +182,20 @@ MachinaGUI::~MachinaGUI()
bool
+MachinaGUI::evolve_callback()
+{
+ if (_evolve) {
+ // Single greedy mutation
+ SharedPtr<Machine> copy(new Machine(*_engine->machine().get()));
+ random_mutation(copy);
+ _canvas->build(copy);
+ }
+
+ return true;
+}
+
+
+bool
MachinaGUI::idle_callback()
{
const bool show_labels = _menu_view_labels->get_active();
@@ -223,73 +244,64 @@ MachinaGUI::arrange()
void
-MachinaGUI::mutate()
+MachinaGUI::evolve()
{
- switch (rand() % 7) {
- case 0: compress(); break;
- case 1: add_node(); break;
- case 2: remove_node(); break;
- case 3: adjust_node(); break;
- case 4: add_edge(); break;
- case 5: remove_edge(); break;
- case 6: adjust_edge(); default: break;
- }
+ if (_evolve_button->get_active())
+ _evolve = true;
+ else
+ _evolve = false;
}
void
-MachinaGUI::compress()
+MachinaGUI::random_mutation(SharedPtr<Machine> machine)
{
- Mutation::Compress::mutate(*_engine->machine().get());
- _canvas->build(_engine->machine());
-}
-
+ if (!machine)
+ machine = _engine->machine();
-void
-MachinaGUI::add_node()
-{
- Mutation::AddNode::mutate(*_engine->machine().get());
- _canvas->build(_engine->machine());
+ mutate(machine, rand() % 7);
}
void
-MachinaGUI::remove_node()
+MachinaGUI::mutate(SharedPtr<Machine> machine, unsigned mutation)
{
- Mutation::RemoveNode::mutate(*_engine->machine().get());
- _canvas->build(_engine->machine());
-}
+ if (!machine)
+ machine = _engine->machine();
-
-void
-MachinaGUI::adjust_node()
-{
- Mutation::AdjustNode::mutate(*_engine->machine().get());
- idle_callback(); // update nodes
-}
+ using namespace Mutation;
-
-void
-MachinaGUI::add_edge()
-{
- Mutation::AddEdge::mutate(*_engine->machine().get());
- _canvas->build(_engine->machine());
-}
-
-
-void
-MachinaGUI::remove_edge()
-{
- Mutation::RemoveEdge::mutate(*_engine->machine().get());
- _canvas->build(_engine->machine());
-}
-
-
-void
-MachinaGUI::adjust_edge()
-{
- Mutation::AdjustEdge::mutate(*_engine->machine().get());
- _canvas->update_edges();
+ switch (mutation) {
+ case 0:
+ Compress::mutate(*machine.get());
+ _canvas->build(machine);
+ break;
+ case 1:
+ AddNode::mutate(*machine.get());
+ _canvas->build(machine);
+ break;
+ case 2:
+ RemoveNode::mutate(*machine.get());
+ _canvas->build(machine);
+ break;
+ case 3:
+ AdjustNode::mutate(*machine.get());
+ idle_callback(); // update nodes
+ break;
+ case 4:
+ AddEdge::mutate(*machine.get());
+ _canvas->build(machine);
+ break;
+ case 5:
+ RemoveEdge::mutate(*machine.get());
+ _canvas->build(machine);
+ break;
+ case 6:
+ AdjustEdge::mutate(*machine.get());
+ _canvas->update_edges();
+ break;
+ default: throw;
+ }
}
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
index c82cd01..bbba67f 100644
--- a/src/gui/MachinaGUI.hpp
+++ b/src/gui/MachinaGUI.hpp
@@ -63,16 +63,12 @@ protected:
void menu_help_about();
void menu_help_help();
void arrange();
- void mutate();
- void compress();
- void add_node();
- void remove_node();
- void adjust_node();
- void add_edge();
- void remove_edge();
- void adjust_edge();
+ void evolve();
+ void random_mutation(SharedPtr<Machina::Machine> machine);
+ void mutate(SharedPtr<Machina::Machine> machine, unsigned mutation);
void zoom(double z);
void update_toolbar();
+ bool evolve_callback();
bool idle_callback();
bool scrolled_window_event(GdkEvent* ev);
@@ -84,6 +80,7 @@ protected:
void tempo_changed();
bool _refresh;
+ bool _evolve;
string _save_uri;
@@ -123,6 +120,7 @@ protected:
Gtk::ToolButton* _zoom_normal_button;
Gtk::ToolButton* _zoom_full_button;
Gtk::ToolButton* _arrange_button;
+ Gtk::ToggleToolButton* _evolve_button;
Gtk::ToolButton* _mutate_button;
Gtk::ToolButton* _compress_button;
Gtk::ToolButton* _add_node_button;
diff --git a/src/gui/machina.glade b/src/gui/machina.glade
index 2153908..e91553d 100644
--- a/src/gui/machina.glade
+++ b/src/gui/machina.glade
@@ -404,6 +404,27 @@
<property name="icon_size">GTK_ICON_SIZE_SMALL_TOOLBAR</property>
<property name="icon_size_set">True</property>
<child>
+ <widget class="GtkSeparatorToolItem" id="toolbutton5">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToggleToolButton" id="evolve_but">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label" translatable="yes">Evolve machine (towards target MIDI)</property>
+ <property name="stock_id">gtk-execute</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
<widget class="GtkSeparatorToolItem" id="toolbutton4">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
@@ -432,6 +453,7 @@
</widget>
<packing>
<property name="expand">False</property>
+ <property name="homogeneous">False</property>
</packing>
</child>
<child>