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
->frames_to_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
->frames_to_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
->position() +
261 step_edit_region_view
->beats_to_frames (step_edit_beat_pos
+ beat_duration
);
263 if (fpos
>= (_editor
.leftmost_position() + _editor
.current_page_frames())) {
264 _editor
.reset_x_origin (fpos
- (_editor
.current_page_frames()/4));
267 Evoral::MusicalTime at
= step_edit_beat_pos
;
268 Evoral::MusicalTime len
= beat_duration
;
270 if ((last_added_pitch
>= 0) && (pitch
== last_added_pitch
) && (last_added_end
== step_edit_beat_pos
)) {
272 /* avoid any apparent note overlap - move the start of this note
273 up by 1 tick from where the last note ended
276 at
+= 1.0/Timecode::BBT_Time::ticks_per_beat
;
277 len
-= 1.0/Timecode::BBT_Time::ticks_per_beat
;
280 step_edit_region_view
->step_add_note (channel
, pitch
, velocity
, at
, len
);
282 last_added_pitch
= pitch
;
283 last_added_end
= at
+len
;
285 if (_step_edit_triplet_countdown
> 0) {
286 _step_edit_triplet_countdown
--;
288 if (_step_edit_triplet_countdown
== 0) {
289 _step_edit_triplet_countdown
= 3;
293 if (!_step_edit_within_chord
) {
294 step_edit_beat_pos
+= beat_duration
;
295 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
297 step_edit_beat_pos
+= 1.0/Timecode::BBT_Time::ticks_per_beat
; // tiny, but no longer overlapping
298 _step_edit_chord_duration
= max (_step_edit_chord_duration
, beat_duration
);
305 StepEditor::set_step_edit_cursor_width (Evoral::MusicalTime beats
)
307 if (step_edit_region_view
) {
308 step_edit_region_view
->set_step_edit_cursor_width (beats
);
313 StepEditor::step_edit_within_triplet() const
315 return _step_edit_triplet_countdown
> 0;
319 StepEditor::step_edit_within_chord() const
321 return _step_edit_within_chord
;
325 StepEditor::step_edit_toggle_triplet ()
327 if (_step_edit_triplet_countdown
== 0) {
328 _step_edit_within_chord
= false;
329 _step_edit_triplet_countdown
= 3;
331 _step_edit_triplet_countdown
= 0;
336 StepEditor::step_edit_toggle_chord ()
338 if (_step_edit_within_chord
) {
339 _step_edit_within_chord
= false;
340 step_edit_beat_pos
+= _step_edit_chord_duration
;
341 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
343 _step_edit_triplet_countdown
= 0;
344 _step_edit_within_chord
= true;
349 StepEditor::step_edit_rest (Evoral::MusicalTime beats
)
354 beats
= _editor
.get_grid_type_as_beats (success
, step_edit_insert_position
);
360 step_edit_beat_pos
+= beats
;
361 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
366 StepEditor::step_edit_beat_sync ()
368 step_edit_beat_pos
= ceil (step_edit_beat_pos
);
369 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
373 StepEditor::step_edit_bar_sync ()
375 Session
* _session
= _mtv
.session ();
377 if (!_session
|| !step_edit_region_view
|| !step_edit_region
) {
381 framepos_t fpos
= step_edit_region
->position() +
382 step_edit_region_view
->beats_to_frames (step_edit_beat_pos
);
383 fpos
= _session
->tempo_map().round_to_bar (fpos
, 1);
384 step_edit_beat_pos
= ceil (step_edit_region_view
->frames_to_beats (fpos
- step_edit_region
->position()));
385 step_edit_region_view
->move_step_edit_cursor (step_edit_beat_pos
);
389 StepEditor::playlist_changed ()
391 step_edit_region_connection
.disconnect ();
392 _track
->playlist()->RegionRemoved
.connect (step_edit_region_connection
, invalidator (*this),
393 ui_bind (&StepEditor::region_removed
, this, _1
),
398 StepEditor::region_removed (boost::weak_ptr
<Region
> wr
)
400 boost::shared_ptr
<Region
> r (wr
.lock());
406 if (step_edit_region
== r
) {
407 step_edit_region
.reset();
408 step_edit_region_view
= 0;
409 // force a recompute of the insert position
410 step_edit_beat_pos
= -1.0;
415 StepEditor::name() const
417 return _track
->name();