1 #include "ardour/midi_track.h"
2 #include "ardour/midi_region.h"
3 #include "ardour/tempo.h"
4 #include "ardour/types.h"
6 #include "gui_thread.h"
7 #include "midi_region_view.h"
8 #include "public_editor.h"
9 #include "step_editor.h"
10 #include "step_entry.h"
12 using namespace ARDOUR
;
16 StepEditor::StepEditor (PublicEditor
& e
, boost::shared_ptr
<MidiTrack
> t
, MidiTimeAxisView
& mtv
)
22 step_edit_insert_position
= 0;
23 _step_edit_triplet_countdown
= 0;
24 _step_edit_within_chord
= 0;
25 _step_edit_chord_duration
= 0.0;
26 step_edit_region_view
= 0;
28 _track
->PlaylistChanged
.connect (*this, invalidator (*this),
29 boost::bind (&StepEditor::playlist_changed
, this),
34 StepEditor::~StepEditor()
40 StepEditor::start_step_editing ()
42 _step_edit_triplet_countdown
= 0;
43 _step_edit_within_chord
= 0;
44 _step_edit_chord_duration
= 0.0;
45 step_edit_region
.reset ();
46 step_edit_region_view
= 0;
47 last_added_pitch
= -1;
50 resync_step_edit_position ();
51 prepare_step_edit_region ();
52 reset_step_edit_beat_pos ();
54 assert (step_edit_region
);
55 assert (step_edit_region_view
);
57 if (step_editor
== 0) {
58 step_editor
= new StepEntry (*this);
59 step_editor
->signal_delete_event().connect (sigc::mem_fun (*this, &StepEditor::step_editor_hidden
));
60 step_editor
->signal_hide().connect (sigc::mem_fun (*this, &StepEditor::step_editor_hide
));
63 step_edit_region_view
->show_step_edit_cursor (step_edit_beat_pos
);
64 step_edit_region_view
->set_step_edit_cursor_width (step_editor
->note_length());
66 step_editor
->set_position (WIN_POS_MOUSE
);
67 step_editor
->present ();
71 StepEditor::resync_step_edit_position ()
73 step_edit_insert_position
= _editor
.get_preferred_edit_position ();
77 StepEditor::resync_step_edit_to_edit_point ()
79 resync_step_edit_position ();
80 if (step_edit_region
) {
81 reset_step_edit_beat_pos ();
86 StepEditor::prepare_step_edit_region ()
88 boost::shared_ptr
<Region
> r
= _track
->playlist()->top_region_at (step_edit_insert_position
);
91 step_edit_region
= boost::dynamic_pointer_cast
<MidiRegion
>(r
);
94 if (step_edit_region
) {
95 RegionView
* rv
= _mtv
.midi_view()->find_view (step_edit_region
);
96 step_edit_region_view
= dynamic_cast<MidiRegionView
*> (rv
);
100 const Meter
& m
= _mtv
.session()->tempo_map().meter_at (step_edit_insert_position
);
101 const Tempo
& t
= _mtv
.session()->tempo_map().tempo_at (step_edit_insert_position
);
103 step_edit_region
= _mtv
.add_region (step_edit_insert_position
, floor (m
.frames_per_bar (t
, _mtv
.session()->frame_rate())), true);
105 RegionView
* rv
= _mtv
.midi_view()->find_view (step_edit_region
);
106 step_edit_region_view
= dynamic_cast<MidiRegionView
*>(rv
);
112 StepEditor::reset_step_edit_beat_pos ()
114 assert (step_edit_region
);
115 assert (step_edit_region_view
);
117 framecnt_t frames_from_start
= _editor
.get_preferred_edit_position() - step_edit_region
->position();
119 if (frames_from_start
< 0) {
120 /* this can happen with snap enabled, and the edit point == Playhead. we snap the
121 position of the new region, and it can end up after the edit point.
123 frames_from_start
= 0;
126 step_edit_beat_pos
= step_edit_region_view
->region_frames_to_region_beats (frames_from_start
);
127 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
131 StepEditor::step_editor_hidden (GdkEventAny
*)
138 StepEditor::step_editor_hide ()
140 /* everything else will follow the change in the model */
141 _track
->set_step_editing (false);
145 StepEditor::stop_step_editing ()
148 step_editor
->hide ();
151 if (step_edit_region_view
) {
152 step_edit_region_view
->hide_step_edit_cursor();
155 step_edit_region
.reset ();
159 StepEditor::check_step_edit ()
161 MidiRingBuffer
<framepos_t
>& incoming (_track
->step_edit_ring_buffer());
163 uint32_t bufsize
= 32;
165 buf
= new uint8_t[bufsize
];
167 while (incoming
.read_space()) {
169 Evoral::EventType type
;
172 incoming
.read_prefix (&time
, &type
, &size
);
174 if (size
> bufsize
) {
177 buf
= new uint8_t[bufsize
];
180 incoming
.read_contents (size
, buf
);
182 if ((buf
[0] & 0xf0) == MIDI_CMD_NOTE_ON
) {
183 step_add_note (buf
[0] & 0xf, buf
[1], buf
[2], 0.0);
189 StepEditor::step_add_bank_change (uint8_t /*channel*/, uint8_t /*bank*/)
195 StepEditor::step_add_program_change (uint8_t /*channel*/, uint8_t /*program*/)
201 StepEditor::step_edit_sustain (Evoral::MusicalTime beats
)
203 if (step_edit_region_view
) {
204 step_edit_region_view
->step_sustain (beats
);
209 StepEditor::move_step_edit_beat_pos (Evoral::MusicalTime beats
)
212 step_edit_beat_pos
= min (step_edit_beat_pos
+ beats
,
213 step_edit_region_view
->region_frames_to_region_beats (step_edit_region
->length()));
214 } else if (beats
< 0.0) {
215 if (-beats
< step_edit_beat_pos
) {
216 step_edit_beat_pos
+= beats
; // its negative, remember
218 step_edit_beat_pos
= 0;
221 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
225 StepEditor::step_add_note (uint8_t channel
, uint8_t pitch
, uint8_t velocity
, Evoral::MusicalTime beat_duration
)
227 /* do these things in case undo removed the step edit region
229 if (!step_edit_region
) {
230 resync_step_edit_position ();
231 prepare_step_edit_region ();
232 reset_step_edit_beat_pos ();
233 step_edit_region_view
->show_step_edit_cursor (step_edit_beat_pos
);
234 step_edit_region_view
->set_step_edit_cursor_width (step_editor
->note_length());
237 assert (step_edit_region
);
238 assert (step_edit_region_view
);
240 if (beat_duration
== 0.0) {
242 beat_duration
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
249 MidiStreamView
* msv
= _mtv
.midi_view();
251 /* make sure its visible on the vertical axis */
253 if (pitch
< msv
->lowest_note() || pitch
> msv
->highest_note()) {
254 msv
->update_note_range (pitch
);
255 msv
->set_note_range (MidiStreamView::ContentsRange
);
258 /* make sure its visible on the horizontal axis */
260 framepos_t fpos
= step_edit_region_view
->region_beats_to_absolute_frames (step_edit_beat_pos
+ beat_duration
);
262 if (fpos
>= (_editor
.leftmost_position() + _editor
.current_page_frames())) {
263 _editor
.reset_x_origin (fpos
- (_editor
.current_page_frames()/4));
266 Evoral::MusicalTime at
= step_edit_beat_pos
;
267 Evoral::MusicalTime len
= beat_duration
;
269 if ((last_added_pitch
>= 0) && (pitch
== last_added_pitch
) && (last_added_end
== step_edit_beat_pos
)) {
271 /* avoid any apparent note overlap - move the start of this note
272 up by 1 tick from where the last note ended
275 at
+= 1.0/Timecode::BBT_Time::ticks_per_beat
;
276 len
-= 1.0/Timecode::BBT_Time::ticks_per_beat
;
279 step_edit_region_view
->step_add_note (channel
, pitch
, velocity
, at
, len
);
281 last_added_pitch
= pitch
;
282 last_added_end
= at
+len
;
284 if (_step_edit_triplet_countdown
> 0) {
285 _step_edit_triplet_countdown
--;
287 if (_step_edit_triplet_countdown
== 0) {
288 _step_edit_triplet_countdown
= 3;
292 if (!_step_edit_within_chord
) {
293 step_edit_beat_pos
+= beat_duration
;
294 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
296 step_edit_beat_pos
+= 1.0/Timecode::BBT_Time::ticks_per_beat
; // tiny, but no longer overlapping
297 _step_edit_chord_duration
= max (_step_edit_chord_duration
, beat_duration
);
304 StepEditor::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
306 if (step_edit_region_view
) {
307 step_edit_region_view
->set_step_edit_cursor_width (beats
);
312 StepEditor::step_edit_within_triplet() const
314 return _step_edit_triplet_countdown
> 0;
318 StepEditor::step_edit_within_chord() const
320 return _step_edit_within_chord
;
324 StepEditor::step_edit_toggle_triplet ()
326 if (_step_edit_triplet_countdown
== 0) {
327 _step_edit_within_chord
= false;
328 _step_edit_triplet_countdown
= 3;
330 _step_edit_triplet_countdown
= 0;
335 StepEditor::step_edit_toggle_chord ()
337 if (_step_edit_within_chord
) {
338 _step_edit_within_chord
= false;
339 step_edit_beat_pos
+= _step_edit_chord_duration
;
340 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
342 _step_edit_triplet_countdown
= 0;
343 _step_edit_within_chord
= true;
348 StepEditor::step_edit_rest (Evoral::MusicalTime beats
)
353 beats
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
359 step_edit_beat_pos
+= beats
;
360 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
365 StepEditor::step_edit_beat_sync ()
367 step_edit_beat_pos
= ceil (step_edit_beat_pos
);
368 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
372 StepEditor::step_edit_bar_sync ()
374 Session
* _session
= _mtv
.session ();
376 if (!_session
|| !step_edit_region_view
|| !step_edit_region
) {
380 framepos_t fpos
= step_edit_region_view
->region_beats_to_absolute_frames (step_edit_beat_pos
);
381 fpos
= _session
->tempo_map().round_to_bar (fpos
, 1);
382 step_edit_beat_pos
= ceil (step_edit_region_view
->region_frames_to_region_beats (fpos
- step_edit_region
->position()));
383 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
387 StepEditor::playlist_changed ()
389 step_edit_region_connection
.disconnect ();
390 _track
->playlist()->RegionRemoved
.connect (step_edit_region_connection
, invalidator (*this),
391 ui_bind (&StepEditor::region_removed
, this, _1
),
396 StepEditor::region_removed (boost::weak_ptr
<Region
> wr
)
398 boost::shared_ptr
<Region
> r (wr
.lock());
404 if (step_edit_region
== r
) {
405 step_edit_region
.reset();
406 step_edit_region_view
= 0;
407 // force a recompute of the insert position
408 step_edit_beat_pos
= -1.0;
413 StepEditor::name() const
415 return _track
->name();