Merged with trunk R708
[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
22 #include <gtkmm.h>
23
24 #include <gtkmm2ext/gtk_ui.h>
25
26 #include <ardour/midi_playlist.h>
27 #include <ardour/midi_region.h>
28 #include <ardour/midi_source.h>
29 #include <ardour/midi_diskstream.h>
30 #include <ardour/midi_track.h>
31 //#include <ardour/playlist_templates.h>
32 #include <ardour/source.h>
33
34 #include "midi_streamview.h"
35 #include "regionview.h"
36 //#include "midi_regionview.h"
37 #include "midi_time_axis.h"
38 #include "canvas-simplerect.h"
39 #include "region_selection.h"
40 #include "selection.h"
41 #include "public_editor.h"
42 #include "ardour_ui.h"
43 #include "rgb_macros.h"
44 #include "gui_thread.h"
45 #include "utils.h"
46 #include "color.h"
47
48 using namespace ARDOUR;
49 using namespace PBD;
50 using namespace Editing;
51
52 MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
53         : StreamView (tv)
54 {
55         region_color = _trackview.color();
56
57         if (tv.is_midi_track())
58                 stream_base_color = color_map[cMidiTrackBase];
59         else
60                 stream_base_color = color_map[cMidiBusBase];
61
62         /* set_position() will position the group */
63
64         canvas_group = new ArdourCanvas::Group(*_trackview.canvas_display);
65
66         canvas_rect = new ArdourCanvas::SimpleRect (*canvas_group);
67         canvas_rect->property_x1() = 0.0;
68         canvas_rect->property_y1() = 0.0;
69         canvas_rect->property_x2() = 1000000.0;
70         canvas_rect->property_y2() = (double) tv.height;
71         canvas_rect->property_outline_color_rgba() = color_map[cMidiTrackOutline];
72         canvas_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);  // outline ends and bottom 
73         canvas_rect->property_fill_color_rgba() = stream_base_color;
74
75         canvas_rect->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), canvas_rect, &_trackview));
76
77         _samples_per_unit = _trackview.editor.get_current_zoom();
78
79         if (_trackview.is_midi_track()) {
80                 _trackview.midi_track()->DiskstreamChanged.connect (mem_fun (*this, &MidiStreamView::diskstream_changed));
81                 _trackview.session().TransportStateChange.connect (mem_fun (*this, &MidiStreamView::transport_changed));
82                 _trackview.get_diskstream()->RecordEnableChanged.connect (mem_fun (*this, &MidiStreamView::rec_enable_changed));
83                 _trackview.session().RecordStateChanged.connect (mem_fun (*this, &MidiStreamView::sess_rec_enable_changed));
84         } 
85
86         rec_updating = false;
87         rec_active = false;
88         use_rec_regions = tv.editor.show_waveforms_recording ();
89
90         ColorChanged.connect (mem_fun (*this, &MidiStreamView::color_handler));
91 }
92
93 MidiStreamView::~MidiStreamView ()
94 {
95         undisplay_diskstream ();
96         delete canvas_group;
97 }
98
99
100 void
101 MidiStreamView::add_region_view_internal (Region *r, bool wait_for_waves)
102 {
103 #if 0
104         ENSURE_GUI_THREAD (bind (mem_fun (*this, &MidiStreamView::add_region_view), r));
105
106         MidiRegion* region = dynamic_cast<MidiRegion*> (r);
107
108         if (region == 0) {
109                 return;
110         }
111
112         MidiRegionView *region_view;
113         list<RegionView *>::iterator i;
114
115         for (i = region_views.begin(); i != region_views.end(); ++i) {
116                 if (&(*i)->region() == r) {
117                         
118                         /* great. we already have a MidiRegionView for this Region. use it again. */
119
120                         (*i)->set_valid (true);
121                         return;
122                 }
123         }
124         
125         /* FIXME 
126         switch (_trackview.midi_track()->mode()) {
127         case Normal:
128                 region_view = new MidiRegionView (canvas_group, _trackview, *region, 
129                                                    _samples_per_unit, region_color);
130                 break;
131         case Destructive:
132                 region_view = new TapeMidiRegionView (canvas_group, _trackview, *region, 
133                                                        _samples_per_unit, region_color);
134                 break;
135         }
136         */
137         region_view = new MidiRegionView (canvas_group, _trackview, *region, 
138                                            _samples_per_unit, region_color);
139
140         region_view->init (region_color, wait_for_waves);
141         region_views.push_front (region_view);
142         
143         /* catch regionview going away */
144
145         region->GoingAway.connect (mem_fun (*this, &MidiStreamView::remove_region_view));
146         
147         RegionViewAdded (region_view);
148 #endif
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)->set_valid (false);
159         }
160
161         if (_trackview.is_midi_track()) {
162                 _trackview.get_diskstream()->playlist()->foreach_region (static_cast<StreamView*>(this), &StreamView::add_region_view);
163         }
164
165         for (i = region_views.begin(); i != region_views.end(); ) {
166                 tmp = i;
167                 tmp++;
168
169                 if (!(*i)->is_valid()) {
170                         delete *i;
171                         region_views.erase (i);
172                 } 
173
174                 i = tmp;
175         }
176
177         /* now fix layering */
178
179         playlist_modified ();
180 }
181
182
183 void
184 MidiStreamView::setup_rec_box ()
185 {
186 #if 0
187         // cerr << _trackview.name() << " streamview SRB\n";
188
189         if (_trackview.session().transport_rolling()) {
190
191                 // cerr << "\trolling\n";
192
193                 if (!rec_active && 
194                     _trackview.session().record_status() == Session::Recording && 
195                     _trackview.get_diskstream()->record_enabled()) {
196
197                         if (_trackview.midi_track()->mode() == Normal && use_rec_regions && rec_regions.size() == rec_rects.size()) {
198
199                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
200
201                                 MidiRegion::SourceList sources;
202
203                                 // FIXME
204                                 MidiDiskstream* ads = dynamic_cast<MidiDiskstream*>(_trackview.get_diskstream());
205                                 assert(ads);
206
207                                 // handle multi
208                                 
209                                 jack_nframes_t start = 0;
210                                 if (rec_regions.size() > 0) {
211                                         start = rec_regions.back()->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
212                                 }
213                                 
214                                 MidiRegion * region = new MidiRegion(sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false);
215                                 region->set_position (_trackview.session().transport_frame(), this);
216                                 rec_regions.push_back (region);
217                                 /* catch it if it goes away */
218                                 region->GoingAway.connect (mem_fun (*this, &MidiStreamView::remove_rec_region));
219
220                                 /* we add the region later */
221                         }
222                         
223                         /* start a new rec box */
224
225                         MidiTrack* at;
226
227                         at = _trackview.midi_track(); /* we know what it is already */
228                         MidiDiskstream& ds = at->midi_diskstream();
229                         jack_nframes_t frame_pos = ds.current_capture_start ();
230                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
231                         gdouble xend;
232                         uint32_t fill_color;
233
234                         switch (_trackview.midi_track()->mode()) {
235                         case Normal:
236                                 xend = xstart;
237                                 fill_color = color_map[cRecordingRectFill];
238                                 break;
239
240                         case Destructive:
241                                 xend = xstart + 2;
242                                 fill_color = color_map[cRecordingRectFill];
243                                 /* make the recording rect translucent to allow
244                                    the user to see the peak data coming in, etc.
245                                 */
246                                 fill_color = UINT_RGBA_CHANGE_A (fill_color, 120);
247                                 break;
248                         }
249                         
250                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
251                         rec_rect->property_x1() = xstart;
252                         rec_rect->property_y1() = 1.0;
253                         rec_rect->property_x2() = xend;
254                         rec_rect->property_y2() = (double) _trackview.height - 1;
255                         rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
256                         rec_rect->property_fill_color_rgba() = fill_color;
257                         
258                         RecBoxInfo recbox;
259                         recbox.rectangle = rec_rect;
260                         recbox.start = _trackview.session().transport_frame();
261                         recbox.length = 0;
262                         
263                         rec_rects.push_back (recbox);
264                         
265                         screen_update_connection.disconnect();
266                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &MidiStreamView::update_rec_box));    
267                         rec_updating = true;
268                         rec_active = true;
269
270                 } else if (rec_active &&
271                            (_trackview.session().record_status() != Session::Recording ||
272                             !_trackview.get_diskstream()->record_enabled())) {
273
274                         screen_update_connection.disconnect();
275                         rec_active = false;
276                         rec_updating = false;
277
278                 }
279                 
280         } else {
281
282                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
283
284                 if (!rec_rects.empty() || !rec_regions.empty()) {
285
286                         /* disconnect rapid update */
287                         screen_update_connection.disconnect();
288
289                         for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
290                                 (*prc).disconnect();
291                         }
292                         peak_ready_connections.clear();
293
294                         rec_updating = false;
295                         rec_active = false;
296                         last_rec_peak_frame = 0;
297                         
298                         /* remove temp regions */
299                         for (list<Region*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
300                         {
301                                 list<Region*>::iterator tmp;
302
303                                 tmp = iter;
304                                 ++tmp;
305
306                                 /* this will trigger the remove_region_view */
307                                 delete *iter;
308
309                                 iter = tmp;
310                         }
311                         
312                         rec_regions.clear();
313
314                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
315                 
316
317                         /* transport stopped, clear boxes */
318                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
319                                 RecBoxInfo &rect = (*iter);
320                                 delete rect.rectangle;
321                         }
322                         
323                         rec_rects.clear();
324                         
325                 }
326         }
327 #endif
328 }
329
330 void
331 MidiStreamView::update_rec_regions ()
332 {
333 #if 0
334         if (use_rec_regions) {
335
336                 uint32_t n = 0;
337
338                 for (list<Region*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
339
340                         list<Region*>::iterator tmp;
341
342                         tmp = iter;
343                         ++tmp;
344
345                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
346                                 /* rect already hidden, this region is done */
347                                 iter = tmp;
348                                 continue;
349                         }
350                         
351                         // FIXME
352                         MidiRegion * region = dynamic_cast<MidiRegion*>(*iter);
353                         assert(region);
354
355                         jack_nframes_t origlen = region->length();
356
357                         if (region == rec_regions.back() && rec_active) {
358
359                                 if (last_rec_peak_frame > region->start()) {
360
361                                         jack_nframes_t nlen = last_rec_peak_frame - region->start();
362
363                                         if (nlen != region->length()) {
364
365                                                 region->freeze ();
366                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
367                                                 region->set_length (nlen, this);
368                                                 region->thaw ("updated");
369
370                                                 if (origlen == 1) {
371                                                         /* our special initial length */
372                                                         add_region_view_internal (region, false);
373                                                 }
374
375                                                 /* also update rect */
376                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
377                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
378                                                 rect->property_x2() = xend;
379                                         }
380                                 }
381
382                         } else {
383
384                                 jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
385
386                                 if (nlen != region->length()) {
387
388                                         if (region->source(0).length() >= region->start() + nlen) {
389
390                                                 region->freeze ();
391                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
392                                                 region->set_length (nlen, this);
393                                                 region->thaw ("updated");
394                                                 
395                                                 if (origlen == 1) {
396                                                         /* our special initial length */
397                                                         add_region_view_internal (region, false);
398                                                 }
399                                                 
400                                                 /* also hide rect */
401                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
402                                                 rect->hide();
403
404                                         }
405                                 }
406                         }
407
408                         iter = tmp;
409                 }
410         }
411 #endif
412 }
413
414 void
415 MidiStreamView::color_handler (ColorID id, uint32_t val)
416 {
417         switch (id) {
418         case cMidiTrackBase:
419                 if (_trackview.is_midi_track()) {
420                         canvas_rect->property_fill_color_rgba() = val;
421                 } 
422                 break;
423         case cMidiBusBase:
424                 if (!_trackview.is_midi_track()) {
425                         canvas_rect->property_fill_color_rgba() = val;
426                 }
427                 break;
428         case cMidiTrackOutline:
429                 canvas_rect->property_outline_color_rgba() = val;
430                 break;
431
432         default:
433                 break;
434         }
435 }