30de0090fd30cefea44034b8129fdc5569d5bdc1
[ardour.git] / gtk2_ardour / midi_streamview.cc
1 /*
2     Copyright (C) 2001-2007 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 #include <cmath>
20 #include <cassert>
21 #include <utility>
22
23 #include <gtkmm.h>
24
25 #include <gtkmm2ext/gtk_ui.h>
26
27 #include <ardour/midi_playlist.h>
28 #include <ardour/midi_region.h>
29 #include <ardour/midi_source.h>
30 #include <ardour/midi_diskstream.h>
31 #include <ardour/midi_track.h>
32 #include <ardour/midi_events.h>
33 #include <ardour/smf_source.h>
34 #include <ardour/region_factory.h>
35
36 #include "midi_streamview.h"
37 #include "region_view.h"
38 #include "midi_region_view.h"
39 #include "midi_time_axis.h"
40 #include "canvas-simplerect.h"
41 #include "region_selection.h"
42 #include "selection.h"
43 #include "public_editor.h"
44 #include "ardour_ui.h"
45 #include "rgb_macros.h"
46 #include "gui_thread.h"
47 #include "utils.h"
48 #include "simplerect.h"
49
50 using namespace std;
51 using namespace ARDOUR;
52 using namespace PBD;
53 using namespace Editing;
54
55 MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
56         : StreamView (tv)
57         , _range(ContentsRange)
58         , _lowest_note(60)
59         , _highest_note(60)
60 {
61         if (tv.is_track())
62                 stream_base_color = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
63         else
64                 stream_base_color = ARDOUR_UI::config()->canvasvar_MidiBusBase.get();
65         
66         canvas_rect->property_fill_color_rgba() = stream_base_color;
67         canvas_rect->property_outline_color_rgba() = RGBA_BLACK;
68
69         //use_rec_regions = tv.editor.show_waveforms_recording ();
70         use_rec_regions = true;
71 }
72
73 MidiStreamView::~MidiStreamView ()
74 {
75 }
76
77
78 RegionView*
79 MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wfd)
80 {
81         boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion> (r);
82
83         if (region == 0) {
84                 return NULL;
85         }
86
87         MidiRegionView *region_view;
88         list<RegionView *>::iterator i;
89
90         for (i = region_views.begin(); i != region_views.end(); ++i) {
91                 if ((*i)->region() == r) {
92                         
93                         /* great. we already have a MidiRegionView for this Region. use it again. */
94
95                         (*i)->set_valid (true);
96                         (*i)->enable_display(wfd);
97                         display_region(dynamic_cast<MidiRegionView*>(*i));
98
99                         return NULL;
100                 }
101         }
102         
103         region_view = new MidiRegionView (canvas_group, _trackview, region, 
104                         _samples_per_unit, region_color);
105                 
106         region_view->init (region_color, false);
107         region_views.push_front (region_view);
108         
109         /* follow global waveform setting */
110
111         if (wfd) {
112                 region_view->enable_display(true);
113                 region_view->midi_region()->midi_source(0)->load_model();
114         }
115
116         /* display events and find note range */
117         display_region(region_view);
118
119         /* always display at least 1 octave range */
120         _highest_note = max(_highest_note, static_cast<uint8_t>(_lowest_note + 11));
121
122         /* catch regionview going away */
123         region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_region_view), region));
124         
125         RegionViewAdded (region_view);
126
127         return region_view;
128 }
129
130 void
131 MidiStreamView::display_region(MidiRegionView* region_view)
132 {
133         if ( ! region_view)
134                 return;
135
136         boost::shared_ptr<MidiSource> source(region_view->midi_region()->midi_source(0));
137         source->load_model();
138
139         if (source->model()) {
140                 // Find our note range
141                 for (size_t i=0; i < source->model()->n_notes(); ++i) {
142                         const MidiModel::Note& note = source->model()->note_at(i);
143                         update_bounds(note.note());
144                 }
145         }
146         
147         // Display region contents
148         region_view->display_model(source->model());
149 }
150
151 // FIXME: code duplication with AudioStreamView
152 void
153 MidiStreamView::redisplay_diskstream ()
154 {
155         list<RegionView *>::iterator i, tmp;
156
157         for (i = region_views.begin(); i != region_views.end(); ++i) {
158                 (*i)->enable_display(true); // FIXME: double display
159                 (*i)->set_valid (false);
160                 
161                 /* FIXME: slow.  MidiRegionView needs a find_note_range method
162                  * that finds the range without wasting time drawing the events */
163
164                 // Load model if it isn't already, to get note range
165                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
166                 mrv->midi_region()->midi_source(0)->load_model();
167         }
168         
169         //_lowest_note = 60; // middle C
170         //_highest_note = _lowest_note + 11;
171
172         if (_trackview.is_midi_track()) {
173                 _trackview.get_diskstream()->playlist()->foreach_region (static_cast<StreamView*>(this), &StreamView::add_region_view);
174         }
175         
176         for (i = region_views.begin(); i != region_views.end(); ) {
177                 tmp = i;
178                 tmp++;
179
180                 if (!(*i)->is_valid()) {
181                         delete *i;
182                         region_views.erase (i);
183                 } else {
184                         (*i)->enable_display(true);
185                         (*i)->set_y_position_and_height(0, height); // apply note range
186                 }
187
188                 i = tmp;
189         }
190
191         /* now fix layering */
192
193         for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
194                 region_layered (*i);
195         }
196 }
197         
198 void 
199 MidiStreamView::update_bounds(uint8_t note_num)
200 {
201         _lowest_note = min(_lowest_note, note_num);
202         _highest_note = max(_highest_note, note_num);
203 }
204
205
206 void
207 MidiStreamView::setup_rec_box ()
208 {
209         // cerr << _trackview.name() << " streamview SRB\n";
210
211         if (_trackview.session().transport_rolling()) {
212
213                 if (!rec_active && 
214                     _trackview.session().record_status() == Session::Recording && 
215                     _trackview.get_diskstream()->record_enabled()) {
216
217                         if (use_rec_regions && rec_regions.size() == rec_rects.size()) {
218
219                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
220
221                                 MidiRegion::SourceList sources;
222                                 
223                                 for (list<sigc::connection>::iterator prc = rec_data_ready_connections.begin(); prc != rec_data_ready_connections.end(); ++prc) {
224                                         (*prc).disconnect();
225                                 }
226                                 rec_data_ready_connections.clear();
227
228                                 // FIXME
229                                 boost::shared_ptr<MidiDiskstream> mds = boost::dynamic_pointer_cast<MidiDiskstream>(_trackview.get_diskstream());
230                                 assert(mds);
231
232                                 sources.push_back(mds->write_source());
233                                 
234                                 rec_data_ready_connections.push_back (mds->write_source()->ViewDataRangeReady.connect (bind (mem_fun (*this, &MidiStreamView::rec_data_range_ready), boost::weak_ptr<Source>(mds->write_source())))); 
235
236                                 // handle multi
237                                 
238                                 jack_nframes_t start = 0;
239                                 if (rec_regions.size() > 0) {
240                                         start = rec_regions.back().first->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
241                                 }
242                                 
243                                 boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion>
244                                         (RegionFactory::create (sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false)));
245                                 assert(region);
246                                 region->set_position (_trackview.session().transport_frame(), this);
247                                 rec_regions.push_back (make_pair(region, (RegionView*)0));
248                                 
249                                 // rec regions are destroyed in setup_rec_box
250
251                                 /* we add the region later */
252                         }
253                         
254                         /* start a new rec box */
255
256                         boost::shared_ptr<MidiTrack> mt = _trackview.midi_track(); /* we know what it is already */
257                         boost::shared_ptr<MidiDiskstream> ds = mt->midi_diskstream();
258                         jack_nframes_t frame_pos = ds->current_capture_start ();
259                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
260                         gdouble xend;
261                         uint32_t fill_color;
262
263                         assert(_trackview.midi_track()->mode() == Normal);
264                         
265                         xend = xstart;
266                         fill_color = ARDOUR_UI::config()->canvasvar_RecordingRect.get();
267                         
268                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
269                         rec_rect->property_x1() = xstart;
270                         rec_rect->property_y1() = 1.0;
271                         rec_rect->property_x2() = xend;
272                         rec_rect->property_y2() = (double) _trackview.height - 1;
273                         rec_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_RecordingRect.get();
274                         rec_rect->property_fill_color_rgba() = fill_color;
275                         rec_rect->lower_to_bottom();
276                         
277                         RecBoxInfo recbox;
278                         recbox.rectangle = rec_rect;
279                         recbox.start = _trackview.session().transport_frame();
280                         recbox.length = 0;
281                         
282                         rec_rects.push_back (recbox);
283                         
284                         screen_update_connection.disconnect();
285                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &MidiStreamView::update_rec_box));    
286                         rec_updating = true;
287                         rec_active = true;
288
289                 } else if (rec_active &&
290                            (_trackview.session().record_status() != Session::Recording ||
291                             !_trackview.get_diskstream()->record_enabled())) {
292
293                         screen_update_connection.disconnect();
294                         rec_active = false;
295                         rec_updating = false;
296
297                 }
298                 
299         } else {
300
301                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
302
303                 if (!rec_rects.empty() || !rec_regions.empty()) {
304
305                         /* disconnect rapid update */
306                         screen_update_connection.disconnect();
307
308                         for (list<sigc::connection>::iterator prc = rec_data_ready_connections.begin(); prc != rec_data_ready_connections.end(); ++prc) {
309                                 (*prc).disconnect();
310                         }
311                         rec_data_ready_connections.clear();
312
313                         rec_updating = false;
314                         rec_active = false;
315                         
316                         /* remove temp regions */
317                         
318                         for (list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end();) {
319                                 list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator tmp;
320                                 
321                                 tmp = iter;
322                                 ++tmp;
323
324                                 (*iter).first->drop_references ();
325
326                                 iter = tmp;
327                         }
328                         
329                         rec_regions.clear();
330
331                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
332
333                         /* transport stopped, clear boxes */
334                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
335                                 RecBoxInfo &rect = (*iter);
336                                 delete rect.rectangle;
337                         }
338                         
339                         rec_rects.clear();
340                         
341                 }
342         }
343 }
344
345 void
346 MidiStreamView::update_rec_regions (boost::shared_ptr<MidiBuffer> data, nframes_t start, nframes_t dur)
347 {
348         ENSURE_GUI_THREAD (bind (mem_fun (*this, &MidiStreamView::update_rec_regions), data, start, dur));
349
350         if (use_rec_regions) {
351
352                 uint32_t n = 0;
353
354                 for (list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
355
356                         list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator tmp;
357
358                         tmp = iter;
359                         ++tmp;
360
361                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
362                                 /* rect already hidden, this region is done */
363                                 iter = tmp;
364                                 continue;
365                         }
366                         
367                         boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion>(iter->first);
368                         if (!region) {
369                                 continue;
370                         }
371
372                         nframes_t origlen = region->length();
373                         
374                         //cerr << "MIDI URR: " << start << " * " << dur
375                         //      << " (origlen " << origlen << ")" << endl;
376
377                         if (region == rec_regions.back().first && rec_active) {
378
379                                 if (start >= region->start()) {
380
381                                         nframes_t nlen = start + dur - region->start();
382
383                                         if (nlen != region->length()) {
384
385                                                 region->freeze ();
386                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
387                                                 region->set_length (nlen, this);
388                                                 region->thaw ("updated");
389
390                                                 if (origlen == 1) {
391                                                         /* our special initial length */
392                                                         iter->second = add_region_view_internal (region, false);
393                                                         ((MidiRegionView*)iter->second)->begin_write();
394                                                 }
395
396                                                 /* also update rect */
397                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
398                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
399                                                 rect->property_x2() = xend;
400
401                                                 /* draw events */
402                                                 MidiRegionView* mrv = (MidiRegionView*)iter->second;
403                                                 for (MidiBuffer::iterator i = data->begin(); i != data->end(); ++i) {
404                                                         const MidiEvent& ev = *i;
405                                                         mrv->add_event(ev);
406                                                         mrv->extend_active_notes();
407                                                 }
408
409                                         }
410                                 }
411
412                         } else {
413
414                                 nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
415
416                                 if (nlen != region->length()) {
417
418                                         if (region->source(0)->length() >= region->start() + nlen) {
419
420                                                 region->freeze ();
421                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
422                                                 region->set_length (nlen, this);
423                                                 region->thaw ("updated");
424                                                 
425                                                 if (origlen == 1) {
426                                                         /* our special initial length */
427                                                         iter->second = add_region_view_internal (region, false);
428                                                 }
429                                                 
430                                                 /* also hide rect */
431                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
432                                                 rect->hide();
433
434                                         }
435                                 }
436                         }
437
438                         iter = tmp;
439                 }
440         }
441 }
442
443 void
444 MidiStreamView::rec_data_range_ready (boost::shared_ptr<MidiBuffer> data, jack_nframes_t start, jack_nframes_t dur, boost::weak_ptr<Source> weak_src)
445 {
446         // this is called from the butler thread for now
447         
448         ENSURE_GUI_THREAD(bind (mem_fun (*this, &MidiStreamView::rec_data_range_ready), data, start, dur, weak_src));
449         
450         boost::shared_ptr<SMFSource> src (boost::dynamic_pointer_cast<SMFSource>(weak_src.lock()));
451         
452         //cerr << src.get() << " MIDI READY: " << start << " * " << dur
453         //      << " -- " << data->size() << " events!" << endl;
454         
455         this->update_rec_regions (data, start, dur);
456 }
457
458 void
459 MidiStreamView::color_handler ()
460 {
461
462         //case cMidiTrackBase:
463         if (_trackview.is_midi_track()) {
464                 canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiTrackBase.get();
465         } 
466
467         //case cMidiBusBase:
468         if (!_trackview.is_midi_track()) {
469                 canvas_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiBusBase.get();;
470         }
471 }
472