diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/engine/JackDriver.cpp | 23 | ||||
-rw-r--r-- | src/engine/MachineBuilder.cpp | 72 | ||||
-rw-r--r-- | src/engine/MachineBuilder.hpp | 13 | ||||
-rw-r--r-- | src/engine/Recorder.cpp | 3 | ||||
-rw-r--r-- | src/engine/Recorder.hpp | 2 | ||||
-rw-r--r-- | src/engine/SMFDriver.cpp | 6 | ||||
-rw-r--r-- | src/engine/machina/Driver.hpp | 3 | ||||
-rw-r--r-- | src/gui/MachinaGUI.cpp | 26 | ||||
-rw-r--r-- | src/gui/MachinaGUI.hpp | 5 | ||||
-rw-r--r-- | src/gui/machina.ui | 49 |
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> |