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