~ubuntu-branches/ubuntu/utopic/ardour3/utopic

« back to all changes in this revision

Viewing changes to gtk2_ardour/step_editor.cc

  • Committer: Package Import Robot
  • Author(s): Felipe Sateler
  • Date: 2013-09-21 19:05:02 UTC
  • Revision ID: package-import@ubuntu.com-20130921190502-8gsftrku6jnzhd7v
Tags: upstream-3.4~dfsg
ImportĀ upstreamĀ versionĀ 3.4~dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    Copyright (C) 2012 Paul Davis 
 
3
 
 
4
    This program is free software; you can redistribute it and/or modify
 
5
    it under the terms of the GNU General Public License as published by
 
6
    the Free Software Foundation; either version 2 of the License, or
 
7
    (at your option) any later version.
 
8
 
 
9
    This program is distributed in the hope that it will be useful,
 
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
    GNU General Public License for more details.
 
13
 
 
14
    You should have received a copy of the GNU General Public License
 
15
    along with this program; if not, write to the Free Software
 
16
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
17
 
 
18
*/
 
19
 
 
20
#include "ardour/midi_track.h"
 
21
#include "ardour/midi_region.h"
 
22
#include "ardour/tempo.h"
 
23
#include "ardour/types.h"
 
24
 
 
25
#include "gui_thread.h"
 
26
#include "midi_region_view.h"
 
27
#include "public_editor.h"
 
28
#include "step_editor.h"
 
29
#include "step_entry.h"
 
30
 
 
31
using namespace ARDOUR;
 
32
using namespace Gtk;
 
33
using namespace std;
 
34
 
 
35
StepEditor::StepEditor (PublicEditor& e, boost::shared_ptr<MidiTrack> t, MidiTimeAxisView& mtv)
 
36
        : _editor (e)
 
37
        , _track (t)
 
38
        , step_editor (0)
 
39
        , _mtv (mtv)
 
40
{
 
41
        step_edit_insert_position = 0;
 
42
        _step_edit_triplet_countdown = 0;
 
43
        _step_edit_within_chord = 0;
 
44
        _step_edit_chord_duration = 0.0;
 
45
        step_edit_region_view = 0;
 
46
 
 
47
        _track->PlaylistChanged.connect (*this, invalidator (*this),
 
48
                                         boost::bind (&StepEditor::playlist_changed, this),
 
49
                                         gui_context());
 
50
        playlist_changed ();
 
51
}
 
52
 
 
53
StepEditor::~StepEditor()
 
54
{
 
55
        delete step_editor;
 
56
}
 
57
 
 
58
void
 
59
StepEditor::start_step_editing ()
 
60
{
 
61
        _step_edit_triplet_countdown = 0;
 
62
        _step_edit_within_chord = 0;
 
63
        _step_edit_chord_duration = 0.0;
 
64
        step_edit_region.reset ();
 
65
        step_edit_region_view = 0;
 
66
        last_added_pitch = -1;
 
67
        last_added_end = 0;
 
68
 
 
69
        resync_step_edit_position ();
 
70
        prepare_step_edit_region ();
 
71
        reset_step_edit_beat_pos ();
 
72
 
 
73
        assert (step_edit_region);
 
74
        assert (step_edit_region_view);
 
75
 
 
76
        if (step_editor == 0) {
 
77
                step_editor = new StepEntry (*this);
 
78
                step_editor->signal_delete_event().connect (sigc::mem_fun (*this, &StepEditor::step_editor_hidden));
 
79
                step_editor->signal_hide().connect (sigc::mem_fun (*this, &StepEditor::step_editor_hide));
 
80
        }
 
81
 
 
82
        step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
 
83
        step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
 
84
 
 
85
        step_editor->present ();
 
86
}
 
87
 
 
88
void
 
89
StepEditor::resync_step_edit_position ()
 
90
{
 
91
        step_edit_insert_position = _editor.get_preferred_edit_position ();
 
92
}
 
93
 
 
94
void
 
95
StepEditor::resync_step_edit_to_edit_point ()
 
96
{
 
97
        resync_step_edit_position ();
 
98
        if (step_edit_region) {
 
99
                reset_step_edit_beat_pos ();
 
100
        }
 
101
}
 
102
 
 
103
void
 
104
StepEditor::prepare_step_edit_region ()
 
105
{
 
106
        boost::shared_ptr<Region> r = _track->playlist()->top_region_at (step_edit_insert_position);
 
107
 
 
108
        if (r) {
 
109
                step_edit_region = boost::dynamic_pointer_cast<MidiRegion>(r);
 
110
        }
 
111
 
 
112
        if (step_edit_region) {
 
113
                RegionView* rv = _mtv.midi_view()->find_view (step_edit_region);
 
114
                step_edit_region_view = dynamic_cast<MidiRegionView*> (rv);
 
115
 
 
116
        } else {
 
117
 
 
118
                const Meter& m = _mtv.session()->tempo_map().meter_at (step_edit_insert_position);
 
119
                const Tempo& t = _mtv.session()->tempo_map().tempo_at (step_edit_insert_position);
 
120
 
 
121
                step_edit_region = _mtv.add_region (step_edit_insert_position, floor (m.frames_per_bar (t, _mtv.session()->frame_rate())), true);
 
122
 
 
123
                RegionView* rv = _mtv.midi_view()->find_view (step_edit_region);
 
124
                step_edit_region_view = dynamic_cast<MidiRegionView*>(rv);
 
125
        }
 
126
}
 
127
 
 
128
 
 
129
void
 
130
StepEditor::reset_step_edit_beat_pos ()
 
131
{
 
132
        assert (step_edit_region);
 
133
        assert (step_edit_region_view);
 
134
 
 
135
        framecnt_t frames_from_start = _editor.get_preferred_edit_position() - step_edit_region->position();
 
136
 
 
137
        if (frames_from_start < 0) {
 
138
                /* this can happen with snap enabled, and the edit point == Playhead. we snap the
 
139
                   position of the new region, and it can end up after the edit point.
 
140
                */
 
141
                frames_from_start = 0;
 
142
        }
 
143
 
 
144
        step_edit_beat_pos = step_edit_region_view->region_frames_to_region_beats (frames_from_start);
 
145
        step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
146
}
 
147
 
 
148
bool
 
149
StepEditor::step_editor_hidden (GdkEventAny*)
 
150
{
 
151
        step_editor_hide ();
 
152
        return true; // XXX remember position ?!
 
153
}
 
154
 
 
155
void
 
156
StepEditor::step_editor_hide ()
 
157
{
 
158
        /* everything else will follow the change in the model */
 
159
        _track->set_step_editing (false);
 
160
}
 
161
 
 
162
void
 
163
StepEditor::stop_step_editing ()
 
164
{
 
165
        if (step_editor) {
 
166
                step_editor->hide ();
 
167
        }
 
168
 
 
169
        if (step_edit_region_view) {
 
170
                step_edit_region_view->hide_step_edit_cursor();
 
171
        }
 
172
 
 
173
        step_edit_region.reset ();
 
174
}
 
175
 
 
176
void
 
177
StepEditor::check_step_edit ()
 
178
{
 
179
        MidiRingBuffer<framepos_t>& incoming (_track->step_edit_ring_buffer());
 
180
        uint8_t* buf;
 
181
        uint32_t bufsize = 32;
 
182
 
 
183
        buf = new uint8_t[bufsize];
 
184
 
 
185
        while (incoming.read_space()) {
 
186
                framepos_t time;
 
187
                Evoral::EventType type;
 
188
                uint32_t size;
 
189
 
 
190
                incoming.read_prefix (&time, &type, &size);
 
191
 
 
192
                if (size > bufsize) {
 
193
                        delete [] buf;
 
194
                        bufsize = size;
 
195
                        buf = new uint8_t[bufsize];
 
196
                }
 
197
 
 
198
                incoming.read_contents (size, buf);
 
199
 
 
200
                if ((buf[0] & 0xf0) == MIDI_CMD_NOTE_ON) {
 
201
                        step_add_note (buf[0] & 0xf, buf[1], buf[2], 0.0);
 
202
                }
 
203
        }
 
204
}
 
205
 
 
206
int
 
207
StepEditor::step_add_bank_change (uint8_t /*channel*/, uint8_t /*bank*/)
 
208
{
 
209
        return 0;
 
210
}
 
211
 
 
212
int
 
213
StepEditor::step_add_program_change (uint8_t /*channel*/, uint8_t /*program*/)
 
214
{
 
215
        return 0;
 
216
}
 
217
 
 
218
void
 
219
StepEditor::step_edit_sustain (Evoral::MusicalTime beats)
 
220
{
 
221
        if (step_edit_region_view) {
 
222
                step_edit_region_view->step_sustain (beats);
 
223
        }
 
224
}
 
225
 
 
226
void
 
227
StepEditor::move_step_edit_beat_pos (Evoral::MusicalTime beats)
 
228
{
 
229
        if (beats > 0.0) {
 
230
                step_edit_beat_pos = min (step_edit_beat_pos + beats,
 
231
                                          step_edit_region_view->region_frames_to_region_beats (step_edit_region->length()));
 
232
        } else if (beats < 0.0) {
 
233
                if (-beats < step_edit_beat_pos) {
 
234
                        step_edit_beat_pos += beats; // its negative, remember
 
235
                } else {
 
236
                        step_edit_beat_pos = 0;
 
237
                }
 
238
        }
 
239
        step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
240
}
 
241
 
 
242
int
 
243
StepEditor::step_add_note (uint8_t channel, uint8_t pitch, uint8_t velocity, Evoral::MusicalTime beat_duration)
 
244
{
 
245
        /* do these things in case undo removed the step edit region
 
246
        */
 
247
        if (!step_edit_region) {
 
248
                resync_step_edit_position ();
 
249
                prepare_step_edit_region ();
 
250
                reset_step_edit_beat_pos ();
 
251
                step_edit_region_view->show_step_edit_cursor (step_edit_beat_pos);
 
252
                step_edit_region_view->set_step_edit_cursor_width (step_editor->note_length());
 
253
        }
 
254
 
 
255
        assert (step_edit_region);
 
256
        assert (step_edit_region_view);
 
257
 
 
258
        if (beat_duration == 0.0 && step_editor) {
 
259
                beat_duration = step_editor->note_length();
 
260
        } else if (beat_duration == 0.0) {
 
261
                bool success;
 
262
                beat_duration = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
 
263
 
 
264
                if (!success) {
 
265
                        return -1;
 
266
                }
 
267
        }
 
268
 
 
269
        MidiStreamView* msv = _mtv.midi_view();
 
270
 
 
271
        /* make sure its visible on the vertical axis */
 
272
 
 
273
        if (pitch < msv->lowest_note() || pitch > msv->highest_note()) {
 
274
                msv->update_note_range (pitch);
 
275
                msv->set_note_range (MidiStreamView::ContentsRange);
 
276
        }
 
277
 
 
278
        /* make sure its visible on the horizontal axis */
 
279
 
 
280
        framepos_t fpos = step_edit_region_view->region_beats_to_absolute_frames (step_edit_beat_pos + beat_duration);
 
281
 
 
282
        if (fpos >= (_editor.leftmost_position() + _editor.current_page_frames())) {
 
283
                _editor.reset_x_origin (fpos - (_editor.current_page_frames()/4));
 
284
        }
 
285
 
 
286
        Evoral::MusicalTime at = step_edit_beat_pos;
 
287
        Evoral::MusicalTime len = beat_duration;
 
288
 
 
289
        if ((last_added_pitch >= 0) && (pitch == last_added_pitch) && (last_added_end == step_edit_beat_pos)) {
 
290
 
 
291
                /* avoid any apparent note overlap - move the start of this note
 
292
                   up by 1 tick from where the last note ended
 
293
                */
 
294
 
 
295
                at += 1.0/Timecode::BBT_Time::ticks_per_beat;
 
296
                len -= 1.0/Timecode::BBT_Time::ticks_per_beat;
 
297
        }
 
298
 
 
299
        step_edit_region_view->step_add_note (channel, pitch, velocity, at, len);
 
300
 
 
301
        last_added_pitch = pitch;
 
302
        last_added_end = at+len;
 
303
 
 
304
        if (_step_edit_triplet_countdown > 0) {
 
305
                _step_edit_triplet_countdown--;
 
306
 
 
307
                if (_step_edit_triplet_countdown == 0) {
 
308
                        _step_edit_triplet_countdown = 3;
 
309
                }
 
310
        }
 
311
 
 
312
        if (!_step_edit_within_chord) {
 
313
                step_edit_beat_pos += beat_duration;
 
314
                step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
315
        } else {
 
316
                step_edit_beat_pos += 1.0/Timecode::BBT_Time::ticks_per_beat; // tiny, but no longer overlapping
 
317
                _step_edit_chord_duration = max (_step_edit_chord_duration, beat_duration);
 
318
        }
 
319
 
 
320
        return 0;
 
321
}
 
322
 
 
323
void
 
324
StepEditor::set_step_edit_cursor_width (Evoral::MusicalTime beats)
 
325
{
 
326
        if (step_edit_region_view) {
 
327
                step_edit_region_view->set_step_edit_cursor_width (beats);
 
328
        }
 
329
}
 
330
 
 
331
bool
 
332
StepEditor::step_edit_within_triplet() const
 
333
{
 
334
        return _step_edit_triplet_countdown > 0;
 
335
}
 
336
 
 
337
bool
 
338
StepEditor::step_edit_within_chord() const
 
339
{
 
340
        return _step_edit_within_chord;
 
341
}
 
342
 
 
343
void
 
344
StepEditor::step_edit_toggle_triplet ()
 
345
{
 
346
        if (_step_edit_triplet_countdown == 0) {
 
347
                _step_edit_within_chord = false;
 
348
                _step_edit_triplet_countdown = 3;
 
349
        } else {
 
350
                _step_edit_triplet_countdown = 0;
 
351
        }
 
352
}
 
353
 
 
354
void
 
355
StepEditor::step_edit_toggle_chord ()
 
356
{
 
357
        if (_step_edit_within_chord) {
 
358
                _step_edit_within_chord = false;
 
359
                step_edit_beat_pos += _step_edit_chord_duration;
 
360
                step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
361
        } else {
 
362
                _step_edit_triplet_countdown = 0;
 
363
                _step_edit_within_chord = true;
 
364
        }
 
365
}
 
366
 
 
367
void
 
368
StepEditor::step_edit_rest (Evoral::MusicalTime beats)
 
369
{
 
370
        bool success;
 
371
 
 
372
        if (beats == 0.0) {
 
373
                beats = _editor.get_grid_type_as_beats (success, step_edit_insert_position);
 
374
        } else {
 
375
                success = true;
 
376
        }
 
377
 
 
378
        if (success) {
 
379
                step_edit_beat_pos += beats;
 
380
                step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
381
        }
 
382
}
 
383
 
 
384
void
 
385
StepEditor::step_edit_beat_sync ()
 
386
{
 
387
        step_edit_beat_pos = ceil (step_edit_beat_pos);
 
388
        step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
389
}
 
390
 
 
391
void
 
392
StepEditor::step_edit_bar_sync ()
 
393
{
 
394
        Session* _session = _mtv.session ();
 
395
 
 
396
        if (!_session || !step_edit_region_view || !step_edit_region) {
 
397
                return;
 
398
        }
 
399
 
 
400
        framepos_t fpos = step_edit_region_view->region_beats_to_absolute_frames (step_edit_beat_pos);
 
401
        fpos = _session->tempo_map().round_to_bar (fpos, 1);
 
402
        step_edit_beat_pos = ceil (step_edit_region_view->region_frames_to_region_beats (fpos - step_edit_region->position()));
 
403
        step_edit_region_view->move_step_edit_cursor (step_edit_beat_pos);
 
404
}
 
405
 
 
406
void
 
407
StepEditor::playlist_changed ()
 
408
{
 
409
        step_edit_region_connection.disconnect ();
 
410
        _track->playlist()->RegionRemoved.connect (step_edit_region_connection, invalidator (*this),
 
411
                                                   boost::bind (&StepEditor::region_removed, this, _1),
 
412
                                                   gui_context());
 
413
}
 
414
 
 
415
void
 
416
StepEditor::region_removed (boost::weak_ptr<Region> wr)
 
417
{
 
418
        boost::shared_ptr<Region> r (wr.lock());
 
419
 
 
420
        if (!r) {
 
421
                return;
 
422
        }
 
423
 
 
424
        if (step_edit_region == r) {
 
425
                step_edit_region.reset();
 
426
                step_edit_region_view = 0;
 
427
                // force a recompute of the insert position
 
428
                step_edit_beat_pos = -1.0;
 
429
        }
 
430
}
 
431
 
 
432
string
 
433
StepEditor::name() const
 
434
{
 
435
        return _track->name();
 
436
}