aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2013-01-13 20:46:13 +0000
committerDavid Robillard <d@drobilla.net>2013-01-13 20:46:13 +0000
commitac0438352410a5af5d64d2268d3541b47e8a2669 (patch)
tree0fc9f575b80493fda4df0e8121a3b34b51a6cbc7
parentb73a80441f0ddd09654a6d4cc337d11f51d0e1c1 (diff)
downloadmachina-ac0438352410a5af5d64d2268d3541b47e8a2669.tar.gz
machina-ac0438352410a5af5d64d2268d3541b47e8a2669.tar.bz2
machina-ac0438352410a5af5d64d2268d3541b47e8a2669.zip
Fix step recording.
git-svn-id: http://svn.drobilla.net/lad/trunk/machina@4965 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--src/engine/JackDriver.cpp23
-rw-r--r--src/engine/MachineBuilder.cpp72
-rw-r--r--src/engine/MachineBuilder.hpp13
-rw-r--r--src/engine/Recorder.cpp3
-rw-r--r--src/engine/Recorder.hpp2
-rw-r--r--src/engine/SMFDriver.cpp6
-rw-r--r--src/engine/machina/Driver.hpp3
-rw-r--r--src/gui/MachinaGUI.cpp26
-rw-r--r--src/gui/MachinaGUI.hpp5
-rw-r--r--src/gui/machina.ui49
10 files changed, 122 insertions, 80 deletions
diff --git a/src/engine/JackDriver.cpp b/src/engine/JackDriver.cpp
index e5601d8..840573e 100644
--- a/src/engine/JackDriver.cpp
+++ b/src/engine/JackDriver.cpp
@@ -24,6 +24,7 @@
#include "Edge.hpp"
#include "JackDriver.hpp"
#include "LearnRequest.hpp"
+#include "MachineBuilder.hpp"
#include "MidiAction.hpp"
using namespace machina;
@@ -340,6 +341,7 @@ JackDriver::on_process(jack_nframes_t nframes)
read_input_playing(machine, _context.time());
break;
case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
read_input_recording(machine, _context.time());
break;
}
@@ -399,6 +401,7 @@ JackDriver::set_play_state(PlayState state)
case PlayState::STOPPED:
break;
case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
finish_record();
// nobreak
case PlayState::PLAYING:
@@ -407,10 +410,14 @@ JackDriver::set_play_state(PlayState state)
}
break;
case PlayState::RECORDING:
- start_record(false); // FIXME: step record?
+ start_record(false);
+ break;
+ case PlayState::STEP_RECORDING:
+ start_record(true);
break;
case PlayState::PLAYING:
- if (_play_state == PlayState::RECORDING) {
+ if (_play_state == PlayState::RECORDING ||
+ _play_state == PlayState::STEP_RECORDING) {
finish_record();
}
}
@@ -420,14 +427,20 @@ JackDriver::set_play_state(PlayState state)
void
JackDriver::start_record(bool step)
{
- if (_play_state.load() != PlayState::RECORDING) {
- // FIXME: Choose an appropriate maximum ringbuffer size
+ switch (_play_state) {
+ case PlayState::STOPPED:
+ case PlayState::PLAYING:
_recorder = SPtr<Recorder>(
new Recorder(_forge, 1024, _beats_unit, _quantization.get(), step));
_recorder->start();
_record_dur = 0;
- _play_state = PlayState::RECORDING;
+ break;
+ case PlayState::RECORDING:
+ case PlayState::STEP_RECORDING:
+ _recorder->builder()->set_step(true);
+ break;
}
+ _play_state = step ? PlayState::STEP_RECORDING : PlayState::RECORDING;
}
void
diff --git a/src/engine/MachineBuilder.cpp b/src/engine/MachineBuilder.cpp
index d94e2da..566944e 100644
--- a/src/engine/MachineBuilder.cpp
+++ b/src/engine/MachineBuilder.cpp
@@ -37,6 +37,7 @@ MachineBuilder::MachineBuilder(SPtr<Machine> machine, double q, bool step)
, _initial_node(machine->initial_node()) // duration 0
, _connect_node(_initial_node)
, _connect_node_end_time(_time) // = 0
+ , _step_duration(machine->time().unit(), 1, 0)
, _step(step)
{}
@@ -51,7 +52,9 @@ MachineBuilder::reset()
bool
MachineBuilder::is_delay_node(SPtr<Node> node) const
{
- return !node->enter_action() && !node->exit_action();
+ return node != _initial_node &&
+ !node->enter_action() &&
+ !node->exit_action();
}
/** Set the duration of a node, with quantization.
@@ -61,7 +64,7 @@ MachineBuilder::set_node_duration(SPtr<Node> node,
Raul::TimeDuration d) const
{
if (_step) {
- node->set_duration(TimeStamp(d.unit(), 1, 0));
+ node->set_duration(_step_duration);
return;
}
@@ -85,10 +88,10 @@ MachineBuilder::connect_nodes(SPtr<Machine> m,
SPtr<Node> head,
Raul::TimeStamp head_start_time)
{
- assert(tail != head);
- assert(head_start_time >= tail_end_time);
-
SPtr<Node> delay_node;
+ if (tail == head) {
+ return delay_node;
+ }
if (is_delay_node(tail) && tail->edges().empty()) {
// Tail is a delay node, just accumulate the time difference into it
@@ -112,9 +115,22 @@ MachineBuilder::connect_nodes(SPtr<Machine> m,
void
MachineBuilder::note_on(Raul::TimeStamp t, size_t ev_size, uint8_t* buf)
{
- SPtr<Node> node(new Node(TimeStamp(t.unit())));
+ SPtr<Node> node;
+ if (_step && _poly_nodes.empty() && is_delay_node(_connect_node)) {
+ /* Stepping and the connect node is the merge node after a polyphonic
+ group. Re-use it to avoid creating delay nodes in step mode. */
+ node = _connect_node;
+ node->set_duration(_step_duration);
+ } else {
+ node = SPtr<Node>(new Node(default_duration()));
+ }
+
node->set_enter_action(SPtr<Action>(new MidiAction(ev_size, buf)));
+ if (_step && _poly_nodes.empty()) {
+ t = _time = _time + _step_duration; // Advance time one step
+ }
+
SPtr<Node> this_connect_node;
Raul::TimeStamp this_connect_node_end_time(t.unit());
@@ -151,25 +167,27 @@ MachineBuilder::note_on(Raul::TimeStamp t, size_t ev_size, uint8_t* buf)
}
void
-MachineBuilder::resolve_note(Raul::TimeStamp t,
+MachineBuilder::resolve_note(Raul::TimeStamp time,
size_t ev_size,
uint8_t* buf,
SPtr<Node> resolved)
{
- resolved->set_exit_action(
- SPtr<Action>(new MidiAction(ev_size, buf)));
- set_node_duration(resolved, t - resolved->enter_time());
+ resolved->set_exit_action(SPtr<Action>(new MidiAction(ev_size, buf)));
if (_active_nodes.size() == 1) {
+ if (_step) {
+ time = _time = _time + _step_duration;
+ }
+
// Last active note
- _connect_node_end_time = t;
+ _connect_node_end_time = time;
if (!_poly_nodes.empty()) {
// Finish a polyphonic section
- _connect_node = SPtr<Node>(new Node(TimeStamp(t.unit())));
+ _connect_node = SPtr<Node>(new Node(TimeStamp(_time.unit(), 0, 0)));
_machine->add_node(_connect_node);
- connect_nodes(_machine, resolved, t, _connect_node, t);
+ connect_nodes(_machine, resolved, time, _connect_node, time);
for (PolyList::iterator j = _poly_nodes.begin();
j != _poly_nodes.end(); ++j) {
@@ -177,7 +195,7 @@ MachineBuilder::resolve_note(Raul::TimeStamp t,
if (j->second->edges().empty()) {
connect_nodes(_machine, j->second,
j->first + j->second->duration(),
- _connect_node, t);
+ _connect_node, time);
}
}
_poly_nodes.clear();
@@ -193,7 +211,6 @@ MachineBuilder::resolve_note(Raul::TimeStamp t,
// Trim useless delay node if possible (after poly sections)
_connect_node->edges().clear();
- assert(_connect_node->edges().empty());
_connect_node->set_enter_action(resolved->enter_action());
_connect_node->set_exit_action(resolved->exit_action());
resolved->set_enter_action(SPtr<Action>());
@@ -209,30 +226,32 @@ MachineBuilder::resolve_note(Raul::TimeStamp t,
}
} else {
- // Polyphonic, add this state to poly list
+ // Polyphonic, add this node to poly list
_poly_nodes.push_back(make_pair(resolved->enter_time(), resolved));
_connect_node = resolved;
- _connect_node_end_time = t;
+ _connect_node_end_time = _time;
}
if (resolved->is_active()) {
- resolved->exit(NULL, t);
+ resolved->exit(NULL, _time);
}
}
void
-MachineBuilder::event(Raul::TimeStamp time_offset,
+MachineBuilder::event(Raul::TimeStamp time,
size_t ev_size,
uint8_t* buf)
{
- Raul::TimeStamp t = _time + time_offset;
-
if (ev_size == 0) {
return;
}
+ if (!_step) {
+ _time = time;
+ }
+
if ((buf[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON) {
- note_on(t, ev_size, buf);
+ note_on(time, ev_size, buf);
} else if ((buf[0] & 0xF0) == LV2_MIDI_MSG_NOTE_OFF) {
for (ActiveList::iterator i = _active_nodes.begin();
i != _active_nodes.end(); ++i) {
@@ -249,7 +268,7 @@ MachineBuilder::event(Raul::TimeStamp time_offset,
(ev[0] & 0x0F) == (buf[0] & 0x0F) &&
ev[1] == buf[1]) {
// Same channel and note as on event
- resolve_note(t, ev_size, buf, *i);
+ resolve_note(time, ev_size, buf, *i);
_active_nodes.erase(i);
break;
}
@@ -277,11 +296,8 @@ MachineBuilder::resolve()
const size_t ev_size = action->event_size();
const uint8_t* ev = action->event();
if (ev_size == 3 && (ev[0] & 0xF0) == LV2_MIDI_MSG_NOTE_ON) {
- const uint8_t note_off[3] = {
- ((LV2_MIDI_MSG_NOTE_OFF & 0xF0) | (ev[0] & 0x0F)),
- ev[1],
- 0x40
- };
+ uint8_t st((LV2_MIDI_MSG_NOTE_OFF & 0xF0) | (ev[0] & 0x0F));
+ const uint8_t note_off[3] = { st, ev[1], 0x40 };
(*i)->set_exit_action(
SPtr<Action>(new MidiAction(3, note_off)));
set_node_duration((*i), _time - (*i)->enter_time());
diff --git a/src/engine/MachineBuilder.hpp b/src/engine/MachineBuilder.hpp
index c4bbe1b..83baf78 100644
--- a/src/engine/MachineBuilder.hpp
+++ b/src/engine/MachineBuilder.hpp
@@ -36,9 +36,9 @@ public:
double quantization,
bool step);
- void set_time(Raul::TimeStamp time) { _time = time; }
+ void event(Raul::TimeStamp time, size_t size, unsigned char* buf);
- void event(Raul::TimeStamp time_offset, size_t size, unsigned char* buf);
+ void set_step(bool step) { _step = step; }
void reset();
void resolve();
@@ -62,6 +62,10 @@ private:
SPtr<Node> head,
Raul::TimeStamp head_start_time);
+ Raul::TimeStamp default_duration() {
+ return _step ? _step_duration : Raul::TimeStamp(_time.unit(), 0, 0);
+ }
+
typedef std::list<SPtr<Node> > ActiveList;
ActiveList _active_nodes;
@@ -70,13 +74,12 @@ private:
double _quantization;
Raul::TimeStamp _time;
-
SPtr<Machine> _machine;
SPtr<Node> _initial_node;
SPtr<Node> _connect_node;
Raul::TimeStamp _connect_node_end_time;
-
- bool _step;
+ Raul::TimeStamp _step_duration;
+ bool _step;
};
} // namespace machina
diff --git a/src/engine/Recorder.cpp b/src/engine/Recorder.cpp
index e628b64..6636f1d 100644
--- a/src/engine/Recorder.cpp
+++ b/src/engine/Recorder.cpp
@@ -52,8 +52,7 @@ Recorder::_whipped()
success = _record_buffer.read(size, buf);
}
if (success) {
- _builder->set_time(t);
- _builder->event(TimeStamp(_unit), size, buf);
+ _builder->event(t, size, buf);
} else {
break;
}
diff --git a/src/engine/Recorder.hpp b/src/engine/Recorder.hpp
index 1c97219..529eec9 100644
--- a/src/engine/Recorder.hpp
+++ b/src/engine/Recorder.hpp
@@ -51,6 +51,8 @@ public:
}
}
+ SPtr<MachineBuilder> builder() { return _builder; }
+
SPtr<Machine> finish();
private:
diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp
index 1b7e727..58ff8a0 100644
--- a/src/engine/SMFDriver.cpp
+++ b/src/engine/SMFDriver.cpp
@@ -136,15 +136,13 @@ SMFDriver::learn_track(SPtr<MachineBuilder> builder,
const double frac = smf_ticks / (double)reader.ppqn();
const uint32_t ticks = frac * MACHINA_PPQN;
- // TODO: quantize
- builder->set_time(TimeStamp(unit, beats, ticks));
-
if (!max_duration.is_zero() && t > max_duration.to_double()) {
break;
}
if (ev_size > 0) {
- builder->event(TimeStamp(max_duration.unit(), 0, 0), ev_size, buf);
+ // TODO: quantize
+ builder->event(TimeStamp(unit, beats, ticks), ev_size, buf);
}
}
diff --git a/src/engine/machina/Driver.hpp b/src/engine/machina/Driver.hpp
index 83169ea..1fad9b3 100644
--- a/src/engine/machina/Driver.hpp
+++ b/src/engine/machina/Driver.hpp
@@ -41,7 +41,8 @@ public:
enum class PlayState {
STOPPED,
PLAYING,
- RECORDING
+ RECORDING,
+ STEP_RECORDING
};
virtual ~Driver() {}
diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp
index 1b2b1f5..1d4d272 100644
--- a/src/gui/MachinaGUI.cpp
+++ b/src/gui/MachinaGUI.cpp
@@ -71,13 +71,13 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
xml->get_widget("help_about_menuitem", _menu_help_about);
xml->get_widget("help_help_menuitem", _menu_help_help);
xml->get_widget("canvas_scrolledwindow", _canvas_scrolledwindow);
- xml->get_widget("step_record_checkbutton", _step_record_checkbutton);
xml->get_widget("bpm_spinbutton", _bpm_spinbutton);
xml->get_widget("quantize_checkbutton", _quantize_checkbutton);
xml->get_widget("quantize_spinbutton", _quantize_spinbutton);
- xml->get_widget("record_but", _record_button);
xml->get_widget("stop_but", _stop_button);
xml->get_widget("play_but", _play_button);
+ xml->get_widget("record_but", _record_button);
+ xml->get_widget("step_record_but", _step_record_button);
xml->get_widget("zoom_normal_but", _zoom_normal_button);
xml->get_widget("zoom_full_but", _zoom_full_button);
xml->get_widget("chain_but", _chain_button);
@@ -101,12 +101,14 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_canvas_scrolledwindow->property_hadjustment().get_value()->set_step_increment(10);
_canvas_scrolledwindow->property_vadjustment().get_value()->set_step_increment(10);
- _record_button->signal_toggled().connect(
- sigc::mem_fun(this, &MachinaGUI::record_toggled));
_stop_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::stop_toggled));
_play_button->signal_toggled().connect(
sigc::mem_fun(this, &MachinaGUI::play_toggled));
+ _record_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::record_toggled));
+ _step_record_button->signal_toggled().connect(
+ sigc::mem_fun(this, &MachinaGUI::step_record_toggled));
_zoom_normal_button->signal_clicked().connect(sigc::bind(
sigc::mem_fun(this, &MachinaGUI::zoom), 1.0));
@@ -179,7 +181,6 @@ MachinaGUI::MachinaGUI(SPtr<machina::Engine> engine)
_main_window->present();
- _step_record_checkbutton->show();
_quantize_checkbutton->set_active(false);
update_toolbar();
@@ -371,6 +372,7 @@ MachinaGUI::update_toolbar()
{
const Driver::PlayState state = _engine->driver()->play_state();
_record_button->set_active(state == Driver::PlayState::RECORDING);
+ _step_record_button->set_active(state == Driver::PlayState::STEP_RECORDING);
_play_button->set_active(state == Driver::PlayState::PLAYING);
_quantize_spinbutton->set_sensitive(_quantize_checkbutton->get_active());
}
@@ -666,7 +668,8 @@ MachinaGUI::stop_toggled()
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) {
+ if (old_state == Driver::PlayState::RECORDING ||
+ old_state == Driver::PlayState::STEP_RECORDING) {
rebuild_canvas();
}
}
@@ -678,7 +681,8 @@ MachinaGUI::play_toggled()
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) {
+ if (old_state == Driver::PlayState::RECORDING ||
+ old_state == Driver::PlayState::STEP_RECORDING) {
rebuild_canvas();
}
}
@@ -693,6 +697,14 @@ MachinaGUI::record_toggled()
}
void
+MachinaGUI::step_record_toggled()
+{
+ if (_step_record_button->get_active()) {
+ _engine->driver()->set_play_state(Driver::PlayState::STEP_RECORDING);
+ }
+}
+
+void
MachinaGUI::chain_toggled()
{
if (_chain_button->get_active()) {
diff --git a/src/gui/MachinaGUI.hpp b/src/gui/MachinaGUI.hpp
index 061cc8a..d794fbb 100644
--- a/src/gui/MachinaGUI.hpp
+++ b/src/gui/MachinaGUI.hpp
@@ -109,6 +109,7 @@ protected:
void stop_toggled();
void play_toggled();
void record_toggled();
+ void step_record_toggled();
void chain_toggled();
void fan_toggled();
@@ -152,13 +153,13 @@ protected:
Gtk::ScrolledWindow* _canvas_scrolledwindow;
Gtk::TextView* _status_text;
Gtk::Expander* _messages_expander;
- Gtk::ToggleToolButton* _step_record_checkbutton;
Gtk::SpinButton* _bpm_spinbutton;
Gtk::CheckButton* _quantize_checkbutton;
Gtk::SpinButton* _quantize_spinbutton;
- Gtk::ToggleToolButton* _record_button;
Gtk::ToggleToolButton* _stop_button;
Gtk::ToggleToolButton* _play_button;
+ Gtk::ToggleToolButton* _record_button;
+ Gtk::ToggleToolButton* _step_record_button;
Gtk::ToolButton* _zoom_normal_button;
Gtk::ToolButton* _zoom_full_button;
Gtk::RadioButton* _chain_button;
diff --git a/src/gui/machina.ui b/src/gui/machina.ui
index eeea2f4..cc66256 100644
--- a/src/gui/machina.ui
+++ b/src/gui/machina.ui
@@ -2,13 +2,6 @@
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy toplevel-contextual -->
- <object class="GtkAdjustment" id="bpm_adjustment">
- <property name="lower">1</property>
- <property name="upper">480</property>
- <property name="value">120</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
<object class="GtkRadioAction" id="record_action">
<property name="stock_id">gtk-media-record</property>
<property name="draw_as_radio">True</property>
@@ -18,6 +11,12 @@
<property name="draw_as_radio">True</property>
<property name="group">record_action</property>
</object>
+ <object class="GtkRadioAction" id="step_record_action">
+ <property name="label" translatable="yes">Step record</property>
+ <property name="stock_id">gtk-add</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>
@@ -66,6 +65,20 @@ along with Machina; if not, write to the Free Software Foundation, Inc.,
</object>
</child>
</object>
+ <object class="GtkAdjustment" id="bpm_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">480</property>
+ <property name="value">120</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="quantize_adjustment">
+ <property name="lower">1</property>
+ <property name="upper">256</property>
+ <property name="value">16</property>
+ <property name="step_increment">2</property>
+ <property name="page_increment">8</property>
+ </object>
<object class="GtkDialog" id="help_dialog">
<property name="can_focus">False</property>
<property name="border_width">8</property>
@@ -387,6 +400,7 @@ press stop or play and the new nodes will be added to the machine.
<property name="related_action">stop_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Stop</property>
<property name="use_underline">True</property>
</object>
<packing>
@@ -420,22 +434,12 @@ press stop or play and the new nodes will be added to the machine.
</packing>
</child>
<child>
- <object class="GtkSeparatorToolItem" id="separatortoolitem4">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleToolButton" id="step_record_checkbutton">
- <property name="use_action_appearance">False</property>
+ <object class="GtkToggleToolButton" id="step_record_but">
+ <property name="related_action">step_record_action</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="tooltip_text" translatable="yes">Step record</property>
- <property name="stock_id">gtk-add</property>
</object>
<packing>
<property name="expand">False</property>
@@ -1087,11 +1091,4 @@ press stop or play and the new nodes will be added to the machine.
<action-widget response="-5">node_properties_ok_button</action-widget>
</action-widgets>
</object>
- <object class="GtkAdjustment" id="quantize_adjustment">
- <property name="lower">1</property>
- <property name="upper">256</property>
- <property name="value">16</property>
- <property name="step_increment">2</property>
- <property name="page_increment">8</property>
- </object>
</interface>