Remove attempt to retrieve invalid Meterbridge properties
[ardour.git] / gtk2_ardour / audio_region_view.cc
1 /*
2     Copyright (C) 2001-2006 Paul Davis
3
4     This program is free software; you can r>edistribute 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 <algorithm>
22 #include <vector>
23
24 #include <boost/scoped_array.hpp>
25
26 #include <gtkmm.h>
27
28 #include <gtkmm2ext/gtk_ui.h>
29
30 #include "ardour/playlist.h"
31 #include "ardour/audioregion.h"
32 #include "ardour/audiosource.h"
33 #include "ardour/profile.h"
34 #include "ardour/session.h"
35
36 #include "pbd/memento_command.h"
37 #include "pbd/stacktrace.h"
38
39 #include "evoral/Curve.hpp"
40
41 #include "canvas/rectangle.h"
42 #include "canvas/polygon.h"
43 #include "canvas/poly_line.h"
44 #include "canvas/line.h"
45 #include "canvas/text.h"
46 #include "canvas/xfade_curve.h"
47 #include "canvas/debug.h"
48 #include "canvas/utils.h"
49 #include "canvas/colors.h"
50
51 #include "streamview.h"
52 #include "audio_region_view.h"
53 #include "audio_time_axis.h"
54 #include "enums_convert.h"
55 #include "public_editor.h"
56 #include "audio_region_editor.h"
57 #include "audio_streamview.h"
58 #include "region_gain_line.h"
59 #include "control_point.h"
60 #include "ghostregion.h"
61 #include "audio_time_axis.h"
62 #include "rgb_macros.h"
63 #include "gui_thread.h"
64 #include "ui_config.h"
65
66 #include "pbd/i18n.h"
67
68 #define MUTED_ALPHA 48
69
70 using namespace std;
71 using namespace ARDOUR;
72 using namespace PBD;
73 using namespace Editing;
74 using namespace ArdourCanvas;
75
76 static double const handle_size = 10; /* height of fade handles */
77
78 Cairo::RefPtr<Cairo::Pattern> AudioRegionView::pending_peak_pattern;
79
80 static Cairo::RefPtr<Cairo::Pattern> create_pending_peak_pattern() {
81         cairo_surface_t * is = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 8, 8);
82
83         // create checker pattern
84         unsigned char *img = cairo_image_surface_get_data (is);
85         cairo_surface_flush (is);
86         const int stride = cairo_image_surface_get_stride (is);
87
88         for (int y = 0; y < 8; ++y) {
89                 for (int x = 0; x < 8; ++x) {
90                         const int off = (y * stride + x * 4);
91                         uint32_t *pixel = (uint32_t*) &img[off];
92                         if ((x < 4) ^ (y < 4)) {
93                                 *pixel = 0xa0000000;
94                         } else {
95                                 *pixel = 0x40000000;
96                         }
97                 }
98         }
99         cairo_surface_mark_dirty (is);
100
101         cairo_pattern_t* pat = cairo_pattern_create_for_surface (is);
102         cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
103         Cairo::RefPtr<Cairo::Pattern> p (new Cairo::Pattern (pat, false));
104         cairo_surface_destroy (is);
105         return p;
106 }
107
108 AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
109                                   uint32_t basic_color)
110         : RegionView (parent, tv, r, spu, basic_color)
111         , sync_mark(0)
112         , fade_in_handle(0)
113         , fade_out_handle(0)
114         , fade_in_trim_handle(0)
115         , fade_out_trim_handle(0)
116         , pending_peak_data(0)
117         , start_xfade_curve (0)
118         , start_xfade_rect (0)
119         , _start_xfade_visible (false)
120         , end_xfade_curve (0)
121         , end_xfade_rect (0)
122         , _end_xfade_visible (false)
123         , _amplitude_above_axis(1.0)
124         , trim_fade_in_drag_active(false)
125         , trim_fade_out_drag_active(false)
126 {
127         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &AudioRegionView::parameter_changed));
128 }
129
130 AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
131                                   uint32_t basic_color, bool recording, TimeAxisViewItem::Visibility visibility)
132         : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
133         , sync_mark(0)
134         , fade_in_handle(0)
135         , fade_out_handle(0)
136         , fade_in_trim_handle(0)
137         , fade_out_trim_handle(0)
138         , pending_peak_data(0)
139         , start_xfade_curve (0)
140         , start_xfade_rect (0)
141         , _start_xfade_visible (false)
142         , end_xfade_curve (0)
143         , end_xfade_rect (0)
144         , _end_xfade_visible (false)
145         , _amplitude_above_axis(1.0)
146         , trim_fade_in_drag_active(false)
147         , trim_fade_out_drag_active(false)
148 {
149         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &AudioRegionView::parameter_changed));
150 }
151
152 AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_ptr<AudioRegion> other_region)
153         : RegionView (other, boost::shared_ptr<Region> (other_region))
154         , fade_in_handle(0)
155         , fade_out_handle(0)
156         , fade_in_trim_handle(0)
157         , fade_out_trim_handle(0)
158         , pending_peak_data(0)
159         , start_xfade_curve (0)
160         , start_xfade_rect (0)
161         , _start_xfade_visible (false)
162         , end_xfade_curve (0)
163         , end_xfade_rect (0)
164         , _end_xfade_visible (false)
165         , _amplitude_above_axis (other._amplitude_above_axis)
166         , trim_fade_in_drag_active(false)
167         , trim_fade_out_drag_active(false)
168 {
169         init (true);
170
171         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &AudioRegionView::parameter_changed));
172 }
173
174 void
175 AudioRegionView::init (bool wfd)
176 {
177         // FIXME: Some redundancy here with RegionView::init.  Need to figure out
178         // where order is important and where it isn't...
179
180         if (!pending_peak_pattern) {
181                 pending_peak_pattern = create_pending_peak_pattern();
182         }
183
184         // needs to be created first, RegionView::init() calls set_height()
185         pending_peak_data = new ArdourCanvas::Rectangle (group);
186         CANVAS_DEBUG_NAME (pending_peak_data, string_compose ("pending peak rectangle for %1", region()->name()));
187         pending_peak_data->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 0.0));
188         pending_peak_data->set_pattern (pending_peak_pattern);
189         pending_peak_data->set_data ("regionview", this);
190         pending_peak_data->hide ();
191
192         RegionView::init (wfd);
193
194         _amplitude_above_axis = 1.0;
195
196         create_waves ();
197
198         if (!_recregion) {
199                 fade_in_handle = new ArdourCanvas::Rectangle (group);
200                 CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in handle for %1", region()->name()));
201                 fade_in_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
202                 fade_in_handle->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
203                 fade_in_handle->set_data ("regionview", this);
204                 fade_in_handle->hide ();
205
206                 fade_out_handle = new ArdourCanvas::Rectangle (group);
207                 CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out handle for %1", region()->name()));
208                 fade_out_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
209                 fade_out_handle->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
210                 fade_out_handle->set_data ("regionview", this);
211                 fade_out_handle->hide ();
212
213                 fade_in_trim_handle = new ArdourCanvas::Rectangle (group);
214                 CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in trim handle for %1", region()->name()));
215                 fade_in_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
216                 fade_in_trim_handle->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
217                 fade_in_trim_handle->set_data ("regionview", this);
218                 fade_in_trim_handle->hide ();
219
220                 fade_out_trim_handle = new ArdourCanvas::Rectangle (group);
221                 CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out trim handle for %1", region()->name()));
222                 fade_out_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
223                 fade_out_trim_handle->set_fill_color (UIConfiguration::instance().color ("inactive fade handle"));
224                 fade_out_trim_handle->set_data ("regionview", this);
225                 fade_out_trim_handle->hide ();
226         }
227
228         setup_fade_handle_positions ();
229
230         if (!trackview.session()->config.get_show_region_fades()) {
231                 set_fade_visibility (false);
232         }
233
234         const string line_name = _region->name() + ":gain";
235
236         gain_line.reset (new AudioRegionGainLine (line_name, *this, *group, audio_region()->envelope()));
237
238         update_envelope_visibility ();
239         gain_line->reset ();
240
241         /* streamview will call set_height() */
242         //set_height (trackview.current_height()); // XXX not correct for Layered mode, but set_height() will fix later.
243
244         region_muted ();
245         region_sync_changed ();
246
247         region_resized (ARDOUR::bounds_change);
248         /* region_resized sets ghost region duration */
249
250         /* region_locked is a synonym for region_renamed () which is called in region_muted() above */
251         //region_locked ();
252
253         envelope_active_changed ();
254         fade_in_active_changed ();
255         fade_out_active_changed ();
256
257         reset_width_dependent_items (_pixel_width);
258
259         if (fade_in_handle) {
260                 fade_in_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this, false));
261         }
262
263         if (fade_out_handle) {
264                 fade_out_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this, false));
265         }
266
267         if (fade_in_trim_handle) {
268                 fade_in_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_trim_handle, this, true));
269         }
270
271         if (fade_out_trim_handle) {
272                 fade_out_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_trim_handle, this, true));
273         }
274
275         set_colors ();
276
277         setup_waveform_visibility ();
278
279         /* XXX sync mark drag? */
280 }
281
282 AudioRegionView::~AudioRegionView ()
283 {
284         in_destructor = true;
285
286         RegionViewGoingAway (this); /* EMIT_SIGNAL */
287
288         for (vector<ScopedConnection*>::iterator i = _data_ready_connections.begin(); i != _data_ready_connections.end(); ++i) {
289                 delete *i;
290         }
291         _data_ready_connections.clear ();
292
293         for (list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator i = feature_lines.begin(); i != feature_lines.end(); ++i) {
294                 delete ((*i).second);
295         }
296
297         /* all waveviews etc will be destroyed when the group is destroyed */
298 }
299
300 boost::shared_ptr<ARDOUR::AudioRegion>
301 AudioRegionView::audio_region() const
302 {
303         // "Guaranteed" to succeed...
304         return boost::dynamic_pointer_cast<AudioRegion>(_region);
305 }
306
307 void
308 AudioRegionView::region_changed (const PropertyChange& what_changed)
309 {
310         ENSURE_GUI_THREAD (*this, &AudioRegionView::region_changed, what_changed);
311
312         RegionView::region_changed (what_changed);
313
314         if (what_changed.contains (ARDOUR::Properties::scale_amplitude)) {
315                 region_scale_amplitude_changed ();
316         }
317         if (what_changed.contains (ARDOUR::Properties::fade_in)) {
318                 fade_in_changed ();
319         }
320         if (what_changed.contains (ARDOUR::Properties::fade_out)) {
321                 fade_out_changed ();
322         }
323         if (what_changed.contains (ARDOUR::Properties::fade_in_active)) {
324                 fade_in_active_changed ();
325         }
326         if (what_changed.contains (ARDOUR::Properties::fade_out_active)) {
327                 fade_out_active_changed ();
328         }
329         if (what_changed.contains (ARDOUR::Properties::envelope_active)) {
330                 envelope_active_changed ();
331         }
332         if (what_changed.contains (ARDOUR::Properties::valid_transients)) {
333                 transients_changed ();
334         }
335 }
336
337 void
338 AudioRegionView::fade_in_changed ()
339 {
340         reset_fade_in_shape ();
341 }
342
343 void
344 AudioRegionView::fade_out_changed ()
345 {
346         reset_fade_out_shape ();
347 }
348
349 void
350 AudioRegionView::fade_in_active_changed ()
351 {
352         if (start_xfade_rect) {
353                 if (audio_region()->fade_in_active()) {
354                         start_xfade_rect->set_fill (false);
355                 } else {
356                         start_xfade_rect->set_fill_color (UIConfiguration::instance().color_mod ("inactive crossfade", "inactive crossfade"));
357                         start_xfade_rect->set_fill (true);
358                 }
359         }
360 }
361
362 void
363 AudioRegionView::fade_out_active_changed ()
364 {
365         if (end_xfade_rect) {
366                 if (audio_region()->fade_out_active()) {
367                         end_xfade_rect->set_fill (false);
368                 } else {
369                         end_xfade_rect->set_fill_color (UIConfiguration::instance().color_mod ("inactive crossfade", "inactive crossfade"));
370                         end_xfade_rect->set_fill (true);
371                 }
372         }
373 }
374
375
376 void
377 AudioRegionView::region_scale_amplitude_changed ()
378 {
379         for (uint32_t n = 0; n < waves.size(); ++n) {
380                 waves[n]->gain_changed ();
381         }
382         region_renamed ();
383 }
384
385 void
386 AudioRegionView::region_renamed ()
387 {
388         std::string str = RegionView::make_name ();
389
390         if (audio_region()->speed_mismatch (trackview.session()->frame_rate())) {
391                 str = string ("*") + str;
392         }
393
394         if (_region->muted()) {
395                 str = string ("!") + str;
396         }
397
398
399         boost::shared_ptr<AudioRegion> ar (audio_region());
400         if (ar->scale_amplitude() != 1.0) {
401                 char tmp[32];
402                 snprintf (tmp, 32, " (%.1fdB)", accurate_coefficient_to_dB (ar->scale_amplitude()));
403                 str += tmp;
404         }
405
406         set_item_name (str, this);
407         set_name_text (str);
408 }
409
410 void
411 AudioRegionView::region_resized (const PropertyChange& what_changed)
412 {
413         AudioGhostRegion* agr;
414
415         RegionView::region_resized(what_changed);
416         PropertyChange interesting_stuff;
417
418         interesting_stuff.add (ARDOUR::Properties::start);
419         interesting_stuff.add (ARDOUR::Properties::length);
420
421         if (what_changed.contains (interesting_stuff)) {
422
423                 for (uint32_t n = 0; n < waves.size(); ++n) {
424                         waves[n]->region_resized ();
425                 }
426
427                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
428                         if ((agr = dynamic_cast<AudioGhostRegion*>(*i)) != 0) {
429
430                                 for (vector<WaveView*>::iterator w = agr->waves.begin(); w != agr->waves.end(); ++w) {
431                                         (*w)->region_resized ();
432                                 }
433                         }
434                 }
435
436                 /* hide transient lines that extend beyond the region */
437                 list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
438                 framepos_t first = _region->first_frame();
439                 framepos_t last = _region->last_frame();
440
441                 for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
442                         if (l->first < first || l->first >= last) {
443                                 l->second->hide();
444                         } else {
445                                 l->second->show();
446                         }
447                 }
448         }
449 }
450
451 void
452 AudioRegionView::reset_width_dependent_items (double pixel_width)
453 {
454         if (pixel_width == _width) {
455                 return;
456         }
457
458         RegionView::reset_width_dependent_items(pixel_width);
459         assert(_pixel_width == pixel_width);
460
461         pending_peak_data->set_x1(pixel_width);
462
463         if (pixel_width <= 20.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) {
464                 if (fade_in_handle)       { fade_in_handle->hide(); }
465                 if (fade_out_handle)      { fade_out_handle->hide(); }
466                 if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
467                 if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
468                 if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
469                 if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
470         }
471
472         reset_fade_shapes ();
473
474         /* Update feature lines */
475         AnalysisFeatureList analysis_features;
476         _region->transients (analysis_features);
477
478         if (feature_lines.size () != analysis_features.size ()) {
479                 cerr << "postponed freature line update.\n"; // XXX
480                 // AudioRegionView::transients_changed () will pick up on this
481                 return;
482         }
483
484         framepos_t position = _region->position();
485
486         AnalysisFeatureList::const_iterator i;
487         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
488         double y1;
489         if (_height >= NAME_HIGHLIGHT_THRESH) {
490                 y1 = _height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 1;
491         } else {
492                 y1 = _height - 1;
493         }
494         for (i = analysis_features.begin(), l = feature_lines.begin(); i != analysis_features.end() && l != feature_lines.end(); ++i, ++l) {
495                 float x_pos = trackview.editor().sample_to_pixel ((*i) - position);
496                 (*l).first = *i;
497                 (*l).second->set (ArdourCanvas::Duple (x_pos, 2.0),
498                                   ArdourCanvas::Duple (x_pos, y1));
499         }
500 }
501
502 void
503 AudioRegionView::region_muted ()
504 {
505         RegionView::region_muted();
506         set_waveform_colors ();
507 }
508
509 void
510 AudioRegionView::setup_fade_handle_positions()
511 {
512         /* position of fade handle offset from the top of the region view */
513         double const handle_pos = 0.0;
514
515         if (fade_in_handle) {
516                 fade_in_handle->set_y0 (handle_pos);
517                 fade_in_handle->set_y1 (handle_pos + handle_size);
518         }
519
520         if (fade_out_handle) {
521                 fade_out_handle->set_y0 (handle_pos);
522                 fade_out_handle->set_y1 (handle_pos + handle_size);
523         }
524
525         if (fade_in_trim_handle) {
526                 fade_in_trim_handle->set_y0 (_height - handle_size);
527                 fade_in_trim_handle->set_y1 (_height);
528         }
529
530         if (fade_out_trim_handle) {
531                 fade_out_trim_handle->set_y0 (_height - handle_size );
532                 fade_out_trim_handle->set_y1 (_height);
533         }
534 }
535
536 void
537 AudioRegionView::set_height (gdouble height)
538 {
539         if (height == _height) {
540                 return;
541         }
542
543         RegionView::set_height (height);
544         pending_peak_data->set_y1 (height);
545
546         uint32_t wcnt = waves.size();
547
548         if (wcnt > 0) {
549
550                 gdouble ht;
551
552                 if (!UIConfiguration::instance().get_show_name_highlight() || (height < NAME_HIGHLIGHT_THRESH)) {
553                         ht = height / (double) wcnt;
554                 } else {
555                         ht = (height - NAME_HIGHLIGHT_SIZE) / (double) wcnt;
556                 }
557
558                 for (uint32_t n = 0; n < wcnt; ++n) {
559
560                         gdouble yoff = floor (ht * n);
561
562                         waves[n]->set_height (ht);
563                         waves[n]->set_y_position (yoff);
564                 }
565         }
566
567         if (gain_line) {
568
569                 if ((height/wcnt) < NAME_HIGHLIGHT_THRESH) {
570                         gain_line->hide ();
571                 } else {
572                         update_envelope_visibility ();
573                 }
574
575                 gain_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE) - 2);
576         }
577
578         reset_fade_shapes ();
579
580         /* Update heights for any feature lines */
581         framepos_t position = _region->position();
582         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
583         double y1;
584         if (_height >= NAME_HIGHLIGHT_THRESH) {
585                 y1 = _height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 1;
586         } else {
587                 y1 = _height - 1;
588         }
589         for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
590                 float pos_x = trackview.editor().sample_to_pixel((*l).first - position);
591                 (*l).second->set (ArdourCanvas::Duple (pos_x, 2.0),
592                                 ArdourCanvas::Duple (pos_x, y1));
593         }
594
595         if (name_text) {
596                 name_text->raise_to_top();
597         }
598
599         setup_fade_handle_positions();
600 }
601
602 void
603 AudioRegionView::reset_fade_shapes ()
604 {
605         if (!trim_fade_in_drag_active) { reset_fade_in_shape (); }
606         if (!trim_fade_out_drag_active) { reset_fade_out_shape (); }
607 }
608
609 void
610 AudioRegionView::reset_fade_in_shape ()
611 {
612         reset_fade_in_shape_width (audio_region(), (framecnt_t) audio_region()->fade_in()->back()->when);
613 }
614
615 void
616 AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
617 {
618         trim_fade_in_drag_active = drag_active;
619         if (fade_in_handle == 0) {
620                 return;
621         }
622
623         /* smallest size for a fade is 64 frames */
624
625         width = std::max ((framecnt_t) 64, width);
626
627         /* round here to prevent little visual glitches with sub-pixel placement */
628         double const pwidth = floor (width / samples_per_pixel);
629         double const handle_left = pwidth;
630
631         /* Put the fade in handle so that its left side is at the end-of-fade line */
632         fade_in_handle->set_x0 (handle_left);
633         fade_in_handle->set_x1 (handle_left + handle_size);
634
635         if (fade_in_trim_handle) {
636                 fade_in_trim_handle->set_x0 (0);
637                 fade_in_trim_handle->set_x1 (handle_size);
638         }
639
640         if (fade_in_handle->visible()) {
641                 //see comment for drag_start
642                 entered();
643         }
644
645         if (pwidth < 5) {
646                 hide_start_xfade();
647                 return;
648         }
649
650         if (!trackview.session()->config.get_show_region_fades()) {
651                 hide_start_xfade ();
652                 return;
653         }
654
655         double effective_height;
656
657         if (_height >= NAME_HIGHLIGHT_THRESH) {
658                 effective_height = _height - NAME_HIGHLIGHT_SIZE;
659         } else {
660                 effective_height = _height;
661         }
662
663         /* points *MUST* be in anti-clockwise order */
664
665         Points points;
666         Points::size_type pi;
667         boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_in());
668         Evoral::ControlList::const_iterator x;
669         double length = list->length();
670
671         points.assign (list->size(), Duple());
672
673         for (x = list->begin(), pi = 0; x != list->end(); ++x, ++pi) {
674                 points[pi].x = (pwidth * ((*x)->when/length));
675                 points[pi].y = effective_height - ((*x)->value * (effective_height - 1.));
676         }
677
678         /* draw the line */
679
680         redraw_start_xfade_to (ar, width, points, effective_height, handle_left);
681
682         /* ensure trim handle stays on top */
683         if (frame_handle_start) {
684                 frame_handle_start->raise_to_top();
685         }
686 }
687
688 void
689 AudioRegionView::reset_fade_out_shape ()
690 {
691         reset_fade_out_shape_width (audio_region(), (framecnt_t) audio_region()->fade_out()->back()->when);
692 }
693
694 void
695 AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
696 {
697         trim_fade_out_drag_active = drag_active;
698         if (fade_out_handle == 0) {
699                 return;
700         }
701
702         /* smallest size for a fade is 64 frames */
703
704         width = std::max ((framecnt_t) 64, width);
705
706
707         double const pwidth = floor(trackview.editor().sample_to_pixel (width));
708
709         /* the right edge should be right on the region frame is the pixel
710          * width is zero. Hence the additional + 1.0 at the end.
711          */
712
713         double const handle_right = rint(trackview.editor().sample_to_pixel (_region->length()) - pwidth);
714         double const trim_handle_right = rint(trackview.editor().sample_to_pixel (_region->length()));
715
716         /* Put the fade out handle so that its right side is at the end-of-fade line;
717          */
718         fade_out_handle->set_x0 (handle_right - handle_size);
719         fade_out_handle->set_x1 (handle_right);
720         if (fade_out_trim_handle) {
721                 fade_out_trim_handle->set_x0 (1 + trim_handle_right - handle_size);
722                 fade_out_trim_handle->set_x1 (1 + trim_handle_right);
723         }
724
725         if (fade_out_handle->visible()) {
726                 //see comment for drag_start
727                 entered();
728         }
729         /* don't show shape if its too small */
730
731         if (pwidth < 5) {
732                 hide_end_xfade();
733                 return;
734         }
735
736         if (!trackview.session()->config.get_show_region_fades()) {
737                 hide_end_xfade();
738                 return;
739         }
740
741         double effective_height;
742
743         effective_height = _height;
744
745         if (UIConfiguration::instance().get_show_name_highlight() && effective_height >= NAME_HIGHLIGHT_THRESH) {
746                 effective_height -= NAME_HIGHLIGHT_SIZE;
747         }
748
749         /* points *MUST* be in anti-clockwise order */
750
751         Points points;
752         Points::size_type pi;
753         boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_out());
754         Evoral::ControlList::const_iterator x;
755         double length = list->length();
756
757         points.assign (list->size(), Duple());
758
759         for (x = list->begin(), pi = 0; x != list->end(); ++x, ++pi) {
760                 points[pi].x = _pixel_width - pwidth + (pwidth * ((*x)->when/length));
761                 points[pi].y = effective_height - ((*x)->value * (effective_height - 1.));
762         }
763
764         /* draw the line */
765
766         redraw_end_xfade_to (ar, width, points, effective_height, handle_right, pwidth);
767
768         /* ensure trim handle stays on top */
769         if (frame_handle_end) {
770                 frame_handle_end->raise_to_top();
771         }
772 }
773
774 framepos_t
775 AudioRegionView::get_fade_in_shape_width ()
776 {
777         return audio_region()->fade_in()->back()->when;
778 }
779
780 framepos_t
781 AudioRegionView::get_fade_out_shape_width ()
782 {
783         return audio_region()->fade_out()->back()->when;
784 }
785
786
787 void
788 AudioRegionView::redraw_start_xfade ()
789 {
790         boost::shared_ptr<AudioRegion> ar (audio_region());
791
792         if (!ar->fade_in() || ar->fade_in()->empty()) {
793                 return;
794         }
795
796         show_start_xfade();
797         reset_fade_in_shape_width (ar, ar->fade_in()->back()->when);
798 }
799
800 void
801 AudioRegionView::redraw_start_xfade_to (boost::shared_ptr<AudioRegion> ar, framecnt_t /*width*/, Points& points, double effective_height,
802                                         double rect_width)
803 {
804         if (points.size() < 2) {
805                 return;
806         }
807
808         if (!start_xfade_curve) {
809                 start_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::Start);
810                 CANVAS_DEBUG_NAME (start_xfade_curve, string_compose ("xfade start out line for %1", region()->name()));
811                 start_xfade_curve->set_fill_color (UIConfiguration::instance().color_mod ("active crossfade", "crossfade alpha"));
812                 start_xfade_curve->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
813                 start_xfade_curve->set_ignore_events (true);
814         }
815         if (!start_xfade_rect) {
816                 start_xfade_rect = new ArdourCanvas::Rectangle (group);
817                 CANVAS_DEBUG_NAME (start_xfade_rect, string_compose ("xfade start rect for %1", region()->name()));
818                 start_xfade_rect->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
819                 start_xfade_rect->set_fill (false);
820                 start_xfade_rect->set_outline (false);
821                 start_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_start_xfade_event), start_xfade_rect, this));
822                 start_xfade_rect->set_data ("regionview", this);
823         }
824
825         start_xfade_rect->set (ArdourCanvas::Rect (0.0, 0.0, rect_width, effective_height));
826
827         /* fade out line */
828
829         boost::shared_ptr<AutomationList> inverse = ar->inverse_fade_in ();
830         Points ipoints;
831         Points::size_type npoints;
832
833         if (!inverse) {
834
835                 /* there is no explicit inverse fade in curve, so take the
836                  * regular fade in curve given to use as "points" (already a
837                  * set of coordinates), and convert to the inverse shape.
838                  */
839
840                 npoints = points.size();
841                 ipoints.assign (npoints, Duple());
842
843                 for (Points::size_type i = 0, pci = 0; i < npoints; ++i, ++pci) {
844                         ArdourCanvas::Duple &p (ipoints[pci]);
845                         /* leave x-axis alone but invert with respect to y-axis */
846                         p.y = effective_height - points[pci].y;
847                 }
848
849         } else {
850
851                 /* there is an explicit inverse fade in curve. Grab the points
852                    and convert them into coordinates for the inverse fade in
853                    line.
854                 */
855
856                 npoints = inverse->size();
857                 ipoints.assign (npoints, Duple());
858
859                 Evoral::ControlList::const_iterator x;
860                 Points::size_type pi;
861                 double length = inverse->length();
862
863                 for (x = inverse->begin(), pi = 0; x != inverse->end(); ++x, ++pi) {
864                         ArdourCanvas::Duple& p (ipoints[pi]);
865                         p.x = (rect_width * ((*x)->when/length));
866                         p.y = effective_height - ((*x)->value * (effective_height));
867                 }
868         }
869
870         start_xfade_curve->set_inout (points, ipoints);
871
872         show_start_xfade();
873 }
874
875 void
876 AudioRegionView::redraw_end_xfade ()
877 {
878         boost::shared_ptr<AudioRegion> ar (audio_region());
879
880         if (!ar->fade_out() || ar->fade_out()->empty()) {
881                 return;
882         }
883
884         show_end_xfade();
885
886         reset_fade_out_shape_width (ar, ar->fade_out()->back()->when);
887 }
888
889 void
890 AudioRegionView::redraw_end_xfade_to (boost::shared_ptr<AudioRegion> ar, framecnt_t width, Points& points, double effective_height,
891                                       double rect_edge, double rect_width)
892 {
893         if (points.size() < 2) {
894                 return;
895         }
896
897         if (!end_xfade_curve) {
898                 end_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::End);
899                 CANVAS_DEBUG_NAME (end_xfade_curve, string_compose ("xfade end out line for %1", region()->name()));
900                 end_xfade_curve->set_fill_color (UIConfiguration::instance().color_mod ("active crossfade", "crossfade alpha"));
901                 end_xfade_curve->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
902                 end_xfade_curve->set_ignore_events (true);
903         }
904
905         if (!end_xfade_rect) {
906                 end_xfade_rect = new ArdourCanvas::Rectangle (group);
907                 CANVAS_DEBUG_NAME (end_xfade_rect, string_compose ("xfade end rect for %1", region()->name()));
908                 end_xfade_rect->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
909                 end_xfade_rect->set_fill (false);
910                 end_xfade_rect->set_outline (false);
911                 end_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_end_xfade_event), end_xfade_rect, this));
912                 end_xfade_rect->set_data ("regionview", this);
913         }
914
915         end_xfade_rect->set (ArdourCanvas::Rect (rect_edge, 0.0, rect_edge + rect_width, effective_height));
916
917         /* fade in line */
918
919         boost::shared_ptr<AutomationList> inverse = ar->inverse_fade_out ();
920         Points ipoints;
921         Points::size_type npoints;
922
923         if (!inverse) {
924
925                 /* there is no explicit inverse fade out curve, so take the
926                  * regular fade out curve given to use as "points" (already a
927                  * set of coordinates), and convert to the inverse shape.
928                  */
929
930                 npoints = points.size();
931                 ipoints.assign (npoints, Duple());
932
933                 Points::size_type pci;
934
935                 for (pci = 0; pci < npoints; ++pci) {
936                         ArdourCanvas::Duple &p (ipoints[pci]);
937                         p.y = effective_height - points[pci].y;
938                 }
939
940         } else {
941
942                 /* there is an explicit inverse fade out curve. Grab the points
943                    and convert them into coordinates for the inverse fade out
944                    line.
945                 */
946
947                 npoints = inverse->size();
948                 ipoints.assign (npoints, Duple());
949
950                 const double rend = trackview.editor().sample_to_pixel (_region->length() - width);
951
952                 Evoral::ControlList::const_iterator x;
953                 Points::size_type pi;
954                 double length = inverse->length();
955
956                 for (x = inverse->begin(), pi = 0; x != inverse->end(); ++x, ++pi) {
957                         ArdourCanvas::Duple& p (ipoints[pi]);
958                         p.x = (rect_width * ((*x)->when/length)) + rend;
959                         p.y = effective_height - ((*x)->value * (effective_height));
960                 }
961         }
962
963         end_xfade_curve->set_inout (ipoints, points);
964
965         show_end_xfade();
966 }
967
968 void
969 AudioRegionView::hide_xfades ()
970 {
971         hide_start_xfade ();
972         hide_end_xfade ();
973 }
974
975 void
976 AudioRegionView::hide_start_xfade ()
977 {
978         if (start_xfade_curve) {
979                 start_xfade_curve->hide();
980         }
981         if (start_xfade_rect) {
982                 start_xfade_rect->hide ();
983         }
984
985         _start_xfade_visible = false;
986 }
987
988 void
989 AudioRegionView::hide_end_xfade ()
990 {
991         if (end_xfade_curve) {
992                 end_xfade_curve->hide();
993         }
994         if (end_xfade_rect) {
995                 end_xfade_rect->hide ();
996         }
997
998         _end_xfade_visible = false;
999 }
1000
1001 void
1002 AudioRegionView::show_start_xfade ()
1003 {
1004         if (start_xfade_curve) {
1005                 start_xfade_curve->show();
1006         }
1007         if (start_xfade_rect) {
1008                 start_xfade_rect->show ();
1009         }
1010
1011         _start_xfade_visible = true;
1012 }
1013
1014 void
1015 AudioRegionView::show_end_xfade ()
1016 {
1017         if (end_xfade_curve) {
1018                 end_xfade_curve->show();
1019         }
1020         if (end_xfade_rect) {
1021                 end_xfade_rect->show ();
1022         }
1023
1024         _end_xfade_visible = true;
1025 }
1026
1027 void
1028 AudioRegionView::set_samples_per_pixel (gdouble fpp)
1029 {
1030         RegionView::set_samples_per_pixel (fpp);
1031
1032         if (UIConfiguration::instance().get_show_waveforms ()) {
1033                 for (uint32_t n = 0; n < waves.size(); ++n) {
1034                         waves[n]->set_samples_per_pixel (fpp);
1035                 }
1036         }
1037
1038         if (gain_line) {
1039                 gain_line->reset ();
1040         }
1041
1042         reset_fade_shapes ();
1043 }
1044
1045 void
1046 AudioRegionView::set_amplitude_above_axis (gdouble a)
1047 {
1048         for (uint32_t n=0; n < waves.size(); ++n) {
1049                 waves[n]->set_amplitude_above_axis (a);
1050         }
1051 }
1052
1053 void
1054 AudioRegionView::set_colors ()
1055 {
1056         RegionView::set_colors();
1057
1058         if (gain_line) {
1059                 gain_line->set_line_color (audio_region()->envelope_active() ?
1060                                            UIConfiguration::instance().color ("gain line") :
1061                                            UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
1062         }
1063
1064         set_waveform_colors ();
1065
1066         if (start_xfade_curve) {
1067                 start_xfade_curve->set_fill_color (UIConfiguration::instance().color_mod ("active crossfade", "crossfade alpha"));
1068                 start_xfade_curve->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
1069         }
1070         if (end_xfade_curve) {
1071                 end_xfade_curve->set_fill_color (UIConfiguration::instance().color_mod ("active crossfade", "crossfade alpha"));
1072                 end_xfade_curve->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
1073         }
1074
1075         if (start_xfade_rect) {
1076                 start_xfade_rect->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
1077         }
1078         if (end_xfade_rect) {
1079                 end_xfade_rect->set_outline_color (UIConfiguration::instance().color ("crossfade line"));
1080         }
1081 }
1082
1083 void
1084 AudioRegionView::setup_waveform_visibility ()
1085 {
1086         if (UIConfiguration::instance().get_show_waveforms ()) {
1087                 for (uint32_t n = 0; n < waves.size(); ++n) {
1088                         /* make sure the zoom level is correct, since we don't update
1089                            this when waveforms are hidden.
1090                         */
1091                         // CAIROCANVAS
1092                         // waves[n]->set_samples_per_pixel (_samples_per_pixel);
1093                         waves[n]->show();
1094                 }
1095         } else {
1096                 for (uint32_t n = 0; n < waves.size(); ++n) {
1097                         waves[n]->hide();
1098                 }
1099         }
1100 }
1101
1102 void
1103 AudioRegionView::temporarily_hide_envelope ()
1104 {
1105         if (gain_line) {
1106                 gain_line->hide ();
1107         }
1108 }
1109
1110 void
1111 AudioRegionView::unhide_envelope ()
1112 {
1113         update_envelope_visibility ();
1114 }
1115
1116 void
1117 AudioRegionView::update_envelope_visibility ()
1118 {
1119         if (!gain_line) {
1120                 return;
1121         }
1122
1123         if (trackview.editor().current_mouse_mode() == Editing::MouseDraw || trackview.editor().current_mouse_mode() == Editing::MouseContent ) {
1124                 gain_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::ControlPoints|AutomationLine::Line));
1125                 gain_line->canvas_group().raise_to_top ();
1126         } else if (UIConfiguration::instance().get_show_region_gain() || trackview.editor().current_mouse_mode() == Editing::MouseRange ) {
1127                 gain_line->set_visibility (AutomationLine::VisibleAspects(AutomationLine::Line));
1128                 gain_line->canvas_group().raise_to_top ();
1129         } else {
1130                 gain_line->set_visibility (AutomationLine::VisibleAspects(0));
1131         }
1132 }
1133
1134 void
1135 AudioRegionView::delete_waves ()
1136 {
1137         for (vector<ScopedConnection*>::iterator i = _data_ready_connections.begin(); i != _data_ready_connections.end(); ++i) {
1138                 delete *i;
1139         }
1140         _data_ready_connections.clear ();
1141
1142         for (vector<WaveView*>::iterator w = waves.begin(); w != waves.end(); ++w) {
1143                 group->remove(*w);
1144         }
1145         waves.clear();
1146         tmp_waves.clear();
1147         pending_peak_data->show ();
1148 }
1149
1150 void
1151 AudioRegionView::create_waves ()
1152 {
1153         // cerr << "AudioRegionView::create_waves() called on " << this << endl;//DEBUG
1154         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
1155
1156         if (!atv.track()) {
1157                 return;
1158         }
1159
1160         ChanCount nchans = atv.track()->n_channels();
1161
1162         // cerr << "creating waves for " << _region->name() << " with wfd = " << wait_for_data
1163         //              << " and channels = " << nchans.n_audio() << endl;
1164
1165         /* in tmp_waves, set up null pointers for each channel so the vector is allocated */
1166         for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
1167                 tmp_waves.push_back (0);
1168         }
1169
1170         for (vector<ScopedConnection*>::iterator i = _data_ready_connections.begin(); i != _data_ready_connections.end(); ++i) {
1171                 delete *i;
1172         }
1173
1174         _data_ready_connections.clear ();
1175
1176         for (uint32_t i = 0; i < nchans.n_audio(); ++i) {
1177                 _data_ready_connections.push_back (0);
1178         }
1179
1180         for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
1181
1182                 if (n >= audio_region()->n_channels()) {
1183                         break;
1184                 }
1185
1186                 // cerr << "\tchannel " << n << endl;
1187
1188                 if (wait_for_data) {
1189                         if (audio_region()->audio_source(n)->peaks_ready (boost::bind (&AudioRegionView::peaks_ready_handler, this, n), &_data_ready_connections[n], gui_context())) {
1190                                 // cerr << "\tData is ready\n";
1191                                 create_one_wave (n, true);
1192                         } else {
1193                                 // cerr << "\tdata is not ready\n";
1194                                 // we'll get a PeaksReady signal from the source in the future
1195                                 // and will call create_one_wave(n) then.
1196                                 pending_peak_data->show ();
1197                         }
1198
1199                 } else {
1200                         // cerr << "\tdon't delay, display today!\n";
1201                         create_one_wave (n, true);
1202                 }
1203
1204         }
1205 }
1206
1207 void
1208 AudioRegionView::create_one_wave (uint32_t which, bool /*direct*/)
1209 {
1210         //cerr << "AudioRegionView::create_one_wave() called which: " << which << " this: " << this << endl;//DEBUG
1211         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
1212         if (!trackview.session() || trackview.session()->deletion_in_progress () || !atv.track()) {
1213                 /* peaks_ready_handler() may be called from peak_thread_work() while
1214                  * session deletion is in progress.
1215                  * Since session-unload happens in the GUI thread, we need to test
1216                  * in this context.
1217                  */
1218                 return;
1219         }
1220         uint32_t nchans = atv.track()->n_channels().n_audio();
1221         uint32_t n;
1222         uint32_t nwaves = std::min (nchans, audio_region()->n_channels());
1223         gdouble ht;
1224
1225         /* compare to set_height(), use _height as set by streamview (child_height),
1226          * not trackview.current_height() to take stacked layering into acconnt
1227          */
1228         if (!UIConfiguration::instance().get_show_name_highlight() || (_height < NAME_HIGHLIGHT_THRESH)) {
1229                 ht = _height / (double) nchans;
1230         } else {
1231                 ht = (_height - NAME_HIGHLIGHT_SIZE) / (double) nchans;
1232         }
1233
1234         /* first waveview starts at 1.0, not 0.0 since that will overlap the
1235          * frame
1236          */
1237
1238         gdouble yoff = which * ht;
1239
1240         WaveView *wave = new WaveView (group, audio_region ());
1241         CANVAS_DEBUG_NAME (wave, string_compose ("wave view for chn %1 of %2", which, get_item_name()));
1242
1243         wave->set_channel (which);
1244         wave->set_y_position (yoff);
1245         wave->set_height (ht);
1246         wave->set_samples_per_pixel (samples_per_pixel);
1247         wave->set_show_zero_line (true);
1248         wave->set_clip_level (UIConfiguration::instance().get_waveform_clip_level ());
1249         wave->set_start_shift (1.0);
1250
1251         wave->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_wave_view_event), wave, this));
1252
1253         switch (UIConfiguration::instance().get_waveform_shape()) {
1254         case Rectified:
1255                 wave->set_shape (WaveView::Rectified);
1256                 break;
1257         default:
1258                 wave->set_shape (WaveView::Normal);
1259         }
1260
1261         wave->set_logscaled (UIConfiguration::instance().get_waveform_scale() == Logarithmic);
1262
1263         vector<ArdourCanvas::WaveView*> v;
1264         v.push_back (wave);
1265         set_some_waveform_colors (v);
1266
1267         if (!UIConfiguration::instance().get_show_waveforms ()) {
1268                 wave->hide();
1269         }
1270
1271         /* note: calling this function is serialized by the lock
1272            held in the peak building thread that signals that
1273            peaks are ready for use *or* by the fact that it is
1274            called one by one from the GUI thread.
1275         */
1276
1277         if (which < nchans) {
1278                 tmp_waves[which] = wave;
1279         } else {
1280                 /* n-channel track, >n-channel source */
1281         }
1282
1283         /* see if we're all ready */
1284
1285         for (n = 0; n < nchans; ++n) {
1286                 if (tmp_waves[n] == 0) {
1287                         break;
1288                 }
1289         }
1290
1291         if (n == nwaves && waves.empty()) {
1292                 /* all waves are ready */
1293                 tmp_waves.resize(nwaves);
1294
1295                 waves = tmp_waves;
1296                 tmp_waves.clear ();
1297
1298                 /* indicate peak-completed */
1299                 pending_peak_data->hide ();
1300
1301                 /* Restore stacked coverage */
1302                 LayerDisplay layer_display;
1303                 if (trackview.get_gui_property ("layer-display", layer_display)) {
1304                         update_coverage_frames (layer_display);
1305           }
1306         }
1307
1308         /* channel wave created, don't hook into peaks ready anymore */
1309         delete _data_ready_connections[which];
1310         _data_ready_connections[which] = 0;
1311 }
1312
1313 void
1314 AudioRegionView::peaks_ready_handler (uint32_t which)
1315 {
1316         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&AudioRegionView::create_one_wave, this, which, false));
1317         // cerr << "AudioRegionView::peaks_ready_handler() called on " << which << " this: " << this << endl;
1318 }
1319
1320 void
1321 AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev, bool with_guard_points)
1322 {
1323         if (!gain_line) {
1324                 return;
1325         }
1326
1327         uint32_t before_p, after_p;
1328         double mx = ev->button.x;
1329         double my = ev->button.y;
1330
1331         item->canvas_to_item (mx, my);
1332
1333         framecnt_t const frame_within_region = (framecnt_t) floor (mx * samples_per_pixel);
1334
1335         if (!gain_line->control_points_adjacent (frame_within_region, before_p, after_p)) {
1336                 /* no adjacent points */
1337                 return;
1338         }
1339
1340         /*y is in item frame */
1341         double const bx = gain_line->nth (before_p)->get_x();
1342         double const ax = gain_line->nth (after_p)->get_x();
1343         double const click_ratio = (ax - mx) / (ax - bx);
1344
1345         double y = ((gain_line->nth (before_p)->get_y() * click_ratio) + (gain_line->nth (after_p)->get_y() * (1 - click_ratio)));
1346
1347         /* don't create points that can't be seen */
1348
1349         update_envelope_visibility ();
1350         framepos_t rpos = region ()->position ();
1351         MusicFrame snap_pos (trackview.editor().pixel_to_sample (mx) + rpos, 0);
1352         trackview.editor ().snap_to_with_modifier (snap_pos, ev);
1353         framepos_t fx = snap_pos.frame - rpos;
1354
1355         if (fx > _region->length()) {
1356                 return;
1357         }
1358
1359         /* compute vertical fractional position */
1360
1361         y = 1.0 - (y / (gain_line->height()));
1362
1363         /* map using gain line */
1364
1365         gain_line->view_to_model_coord (mx, y);
1366
1367         /* XXX STATEFUL: can't convert to stateful diff until we
1368            can represent automation data with it.
1369         */
1370
1371         XMLNode &before = audio_region()->envelope()->get_state();
1372         MementoCommand<AudioRegion>* region_memento = 0;
1373
1374         if (!audio_region()->envelope_active()) {
1375                 XMLNode &region_before = audio_region()->get_state();
1376                 audio_region()->set_envelope_active(true);
1377                 XMLNode &region_after = audio_region()->get_state();
1378                 region_memento = new MementoCommand<AudioRegion>(*(audio_region().get()), &region_before, &region_after);
1379         }
1380
1381         if (audio_region()->envelope()->editor_add (fx, y, with_guard_points)) {
1382                 XMLNode &after = audio_region()->envelope()->get_state();
1383                 std::list<Selectable*> results;
1384
1385                 trackview.editor().begin_reversible_command (_("add gain control point"));
1386
1387                 if (region_memento) {
1388                         trackview.session()->add_command (region_memento);
1389                 }
1390
1391                 trackview.session()->add_command (new MementoCommand<AutomationList>(*audio_region()->envelope().get(), &before, &after));
1392
1393                 gain_line->get_selectables (fx + region ()->position (), fx + region ()->position (), 0.0, 1.0, results);
1394                 trackview.editor ().get_selection ().set (results);
1395
1396                 trackview.editor ().commit_reversible_command ();
1397                 trackview.session ()->set_dirty ();
1398         }
1399 }
1400
1401 void
1402 AudioRegionView::remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent* /*ev*/)
1403 {
1404         ControlPoint *cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1405         audio_region()->envelope()->erase (cp->model());
1406 }
1407
1408 GhostRegion*
1409 AudioRegionView::add_ghost (TimeAxisView& tv)
1410 {
1411         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
1412
1413         if (!rtv) {
1414                 return 0;
1415         }
1416
1417         double unit_position = _region->position () / samples_per_pixel;
1418         AudioGhostRegion* ghost = new AudioGhostRegion (*this, tv, trackview, unit_position);
1419         uint32_t nchans;
1420
1421         nchans = rtv->track()->n_channels().n_audio();
1422
1423         for (uint32_t n = 0; n < nchans; ++n) {
1424
1425                 if (n >= audio_region()->n_channels()) {
1426                         break;
1427                 }
1428
1429                 WaveView *wave = new WaveView (ghost->group, audio_region());
1430                 CANVAS_DEBUG_NAME (wave, string_compose ("ghost wave for %1", get_item_name()));
1431
1432                 wave->set_channel (n);
1433                 wave->set_samples_per_pixel (samples_per_pixel);
1434                 wave->set_amplitude_above_axis (_amplitude_above_axis);
1435
1436                 ghost->waves.push_back(wave);
1437         }
1438
1439         ghost->set_height ();
1440         ghost->set_duration (_region->length() / samples_per_pixel);
1441         ghost->set_colors();
1442         ghosts.push_back (ghost);
1443
1444         return ghost;
1445 }
1446
1447 void
1448 AudioRegionView::entered ()
1449 {
1450         trackview.editor().set_current_trimmable (_region);
1451         trackview.editor().set_current_movable (_region);
1452
1453         update_envelope_visibility();
1454
1455         if ((trackview.editor().current_mouse_mode() == Editing::MouseObject)) {
1456                 if (start_xfade_rect) {
1457                         start_xfade_rect->set_outline (true);
1458                 }
1459                 if (end_xfade_rect) {
1460                         end_xfade_rect->set_outline (true);
1461                 }
1462                 if (fade_in_handle) {
1463                         fade_in_handle->show ();
1464                         fade_in_handle->raise_to_top ();
1465                 }
1466                 if (fade_out_handle) {
1467                         fade_out_handle->show ();
1468                         fade_out_handle->raise_to_top ();
1469                 }
1470                 if (fade_in_trim_handle) {
1471                         boost::shared_ptr<AudioRegion> ar (audio_region());
1472                         if (!ar->locked() && (ar->fade_in()->back()->when > 64 || (ar->can_trim() & Trimmable::FrontTrimEarlier))) {
1473                                 fade_in_trim_handle->show ();
1474                                 fade_in_trim_handle->raise_to_top ();
1475                         } else {
1476                                 fade_in_trim_handle->hide ();
1477                         }
1478                 }
1479                 if (fade_out_trim_handle) {
1480                         boost::shared_ptr<AudioRegion> ar (audio_region());
1481                         if (!ar->locked() && (ar->fade_out()->back()->when > 64 || (ar->can_trim() & Trimmable::EndTrimLater))) {
1482                                 fade_out_trim_handle->show ();
1483                                 fade_out_trim_handle->raise_to_top ();
1484                         } else {
1485                                 fade_out_trim_handle->hide ();
1486                         }
1487                 }
1488         } else {  //this happens when we switch tools; if we switch away from Grab mode,  hide all the fade handles
1489                 if (fade_in_handle)       { fade_in_handle->hide(); }
1490                 if (fade_out_handle)      { fade_out_handle->hide(); }
1491                 if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
1492                 if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
1493                 if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
1494                 if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
1495         }
1496 }
1497
1498 void
1499 AudioRegionView::exited ()
1500 {
1501         trackview.editor().set_current_trimmable (boost::shared_ptr<Trimmable>());
1502         trackview.editor().set_current_movable (boost::shared_ptr<Movable>());
1503
1504 //      if (gain_line) {
1505 //              gain_line->remove_visibility (AutomationLine::ControlPoints);
1506 //      }
1507
1508         if (fade_in_handle)       { fade_in_handle->hide(); }
1509         if (fade_out_handle)      { fade_out_handle->hide(); }
1510         if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
1511         if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
1512         if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
1513         if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
1514 }
1515
1516 void
1517 AudioRegionView::envelope_active_changed ()
1518 {
1519         if (gain_line) {
1520                 gain_line->set_line_color (audio_region()->envelope_active() ?
1521                                            UIConfiguration::instance().color ("gain line") :
1522                                            UIConfiguration::instance().color_mod ("gain line inactive", "gain line inactive"));
1523                 update_envelope_visibility ();
1524         }
1525 }
1526
1527 void
1528 AudioRegionView::color_handler ()
1529 {
1530         //case cMutedWaveForm:
1531         //case cWaveForm:
1532         //case cWaveFormClip:
1533         //case cZeroLine:
1534         set_colors ();
1535
1536         //case cGainLineInactive:
1537         //case cGainLine:
1538         envelope_active_changed();
1539
1540 }
1541
1542 void
1543 AudioRegionView::set_waveform_colors ()
1544 {
1545         set_some_waveform_colors (waves);
1546 }
1547
1548 void
1549 AudioRegionView::set_some_waveform_colors (vector<ArdourCanvas::WaveView*>& waves_to_color)
1550 {
1551         ArdourCanvas::Color fill;
1552         ArdourCanvas::Color outline;
1553         ArdourCanvas::Color clip = UIConfiguration::instance().color ("clipped waveform");
1554         ArdourCanvas::Color zero = UIConfiguration::instance().color ("zero line");
1555
1556         if (_selected) {
1557                 if (_region->muted()) {
1558                         /* hide outline with zero alpha */
1559                         outline = UINT_RGBA_CHANGE_A(UIConfiguration::instance().color ("selected waveform outline"), 0);
1560                         fill = UINT_RGBA_CHANGE_A(UIConfiguration::instance().color ("selected waveform fill"), MUTED_ALPHA);
1561                 } else {
1562                         outline = UIConfiguration::instance().color ("selected waveform outline");
1563                         fill = UIConfiguration::instance().color ("selected waveform fill");
1564                 }
1565         } else {
1566                 if (_recregion) {
1567                         outline = UIConfiguration::instance().color ("recording waveform outline");
1568                         fill = UIConfiguration::instance().color ("recording waveform fill");
1569                 } else {
1570                         if (_region->muted()) {
1571                                 /* hide outline with zero alpha */
1572                                 outline = UINT_RGBA_CHANGE_A(UIConfiguration::instance().color ("waveform outline"), 0);
1573                                 fill = UINT_RGBA_CHANGE_A(UIConfiguration::instance().color ("waveform fill"), MUTED_ALPHA);
1574                         } else {
1575                                 outline = UIConfiguration::instance().color ("waveform outline");
1576                                 fill = UIConfiguration::instance().color ("waveform fill");
1577                         }
1578                 }
1579         }
1580
1581         for (vector<ArdourCanvas::WaveView*>::iterator w = waves_to_color.begin(); w != waves_to_color.end(); ++w) {
1582                 (*w)->set_fill_color (fill);
1583                 (*w)->set_outline_color (outline);
1584                 (*w)->set_clip_color (clip);
1585                 (*w)->set_zero_color (zero);
1586         }
1587 }
1588
1589 void
1590 AudioRegionView::set_frame_color ()
1591 {
1592         if (!frame) {
1593                 return;
1594         }
1595
1596         RegionView::set_frame_color ();
1597
1598         set_waveform_colors ();
1599 }
1600
1601 void
1602 AudioRegionView::set_fade_visibility (bool yn)
1603 {
1604         if (yn) {
1605                 if (start_xfade_curve)    { start_xfade_curve->show (); }
1606                 if (end_xfade_curve)      { end_xfade_curve->show (); }
1607                 if (start_xfade_rect)     { start_xfade_rect->show (); }
1608                 if (end_xfade_rect)       { end_xfade_rect->show (); }
1609                 } else {
1610                 if (start_xfade_curve)    { start_xfade_curve->hide(); }
1611                 if (end_xfade_curve)      { end_xfade_curve->hide(); }
1612                 if (fade_in_handle)       { fade_in_handle->hide(); }
1613                 if (fade_out_handle)      { fade_out_handle->hide(); }
1614                 if (fade_in_trim_handle)  { fade_in_trim_handle->hide(); }
1615                 if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
1616                 if (start_xfade_rect)     { start_xfade_rect->hide (); }
1617                 if (end_xfade_rect)       { end_xfade_rect->hide (); }
1618                 if (start_xfade_rect)     { start_xfade_rect->set_outline (false); }
1619                 if (end_xfade_rect)       { end_xfade_rect->set_outline (false); }
1620         }
1621 }
1622
1623 void
1624 AudioRegionView::update_coverage_frames (LayerDisplay d)
1625 {
1626         RegionView::update_coverage_frames (d);
1627
1628         if (d == Stacked) {
1629                 if (fade_in_handle)       { fade_in_handle->raise_to_top (); }
1630                 if (fade_out_handle)      { fade_out_handle->raise_to_top (); }
1631                 if (fade_in_trim_handle)  { fade_in_trim_handle->raise_to_top (); }
1632                 if (fade_out_trim_handle) { fade_out_trim_handle->raise_to_top (); }
1633         }
1634 }
1635
1636 void
1637 AudioRegionView::show_region_editor ()
1638 {
1639         if (editor == 0) {
1640                 editor = new AudioRegionEditor (trackview.session(), audio_region());
1641         }
1642
1643         editor->present ();
1644         editor->show_all();
1645 }
1646
1647 void
1648 AudioRegionView::transients_changed ()
1649 {
1650         AnalysisFeatureList analysis_features;
1651         _region->transients (analysis_features);
1652         framepos_t position = _region->position();
1653         framepos_t first = _region->first_frame();
1654         framepos_t last = _region->last_frame();
1655
1656         double y1;
1657         if (_height >= NAME_HIGHLIGHT_THRESH) {
1658                 y1 = _height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 1;
1659         } else {
1660                 y1 = _height - 1;
1661         }
1662
1663         while (feature_lines.size() < analysis_features.size()) {
1664                 ArdourCanvas::Line* canvas_item = new ArdourCanvas::Line(group);
1665                 CANVAS_DEBUG_NAME (canvas_item, string_compose ("transient group for %1", region()->name()));
1666                 canvas_item->set_outline_color (UIConfiguration::instance().color ("zero line")); // also in Editor::leave_handler()
1667
1668                 canvas_item->set (ArdourCanvas::Duple (-1.0, 2.0),
1669                                   ArdourCanvas::Duple (1.0, y1));
1670
1671                 canvas_item->raise_to_top ();
1672                 canvas_item->show ();
1673
1674                 canvas_item->set_data ("regionview", this);
1675                 canvas_item->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_feature_line_event), canvas_item, this));
1676
1677                 feature_lines.push_back (make_pair(0, canvas_item));
1678         }
1679
1680         while (feature_lines.size() > analysis_features.size()) {
1681                 ArdourCanvas::Line* line = feature_lines.back().second;
1682                 feature_lines.pop_back ();
1683                 delete line;
1684         }
1685
1686         AnalysisFeatureList::const_iterator i;
1687         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1688
1689         for (i = analysis_features.begin(), l = feature_lines.begin(); i != analysis_features.end() && l != feature_lines.end(); ++i, ++l) {
1690
1691                 float *pos = new float;
1692                 *pos = trackview.editor().sample_to_pixel (*i - position);
1693
1694                 (*l).second->set (
1695                         ArdourCanvas::Duple (*pos, 2.0),
1696                         ArdourCanvas::Duple (*pos, y1)
1697                         );
1698
1699                 (*l).second->set_data ("position", pos); // is this *modified* (drag?), if not use *i
1700                 (*l).first = *i;
1701
1702                 if (l->first < first || l->first >= last) {
1703                         l->second->hide();
1704                 } else {
1705                         l->second->show();
1706                 }
1707         }
1708 }
1709
1710 void
1711 AudioRegionView::update_transient(float /*old_pos*/, float new_pos)
1712 {
1713         /* Find frame at old pos, calulate new frame then update region transients*/
1714         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1715
1716         for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
1717
1718                 /* Line has been updated in drag so we compare to new_pos */
1719
1720                 float* pos = (float*) (*l).second->get_data ("position");
1721
1722                 if (rint(new_pos) == rint(*pos)) {
1723                                 framepos_t position = _region->position();
1724                     framepos_t old_frame = (*l).first;
1725                     framepos_t new_frame = trackview.editor().pixel_to_sample (new_pos) + position;
1726                     _region->update_transient (old_frame, new_frame);
1727                     break;
1728                 }
1729         }
1730 }
1731
1732 void
1733 AudioRegionView::remove_transient (float pos)
1734 {
1735         /* this is called from Editor::remove_transient () with pos == get_data ("position")
1736          * which is the item's x-coordinate inside the ARV.
1737          *
1738          * Find frame at old pos, calulate new frame then update region transients
1739          */
1740         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1741
1742         for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
1743                 float *line_pos = (float*) (*l).second->get_data ("position");
1744                 if (rint(pos) == rint(*line_pos)) {
1745                     _region->remove_transient ((*l).first);
1746                     break;
1747                 }
1748         }
1749 }
1750
1751 void
1752 AudioRegionView::thaw_after_trim ()
1753 {
1754         RegionView::thaw_after_trim ();
1755         unhide_envelope ();
1756         drag_end ();
1757 }
1758
1759
1760 void
1761 AudioRegionView::show_xfades ()
1762 {
1763         show_start_xfade ();
1764         show_end_xfade ();
1765 }
1766
1767 void
1768 AudioRegionView::drag_start ()
1769 {
1770         TimeAxisViewItem::drag_start ();
1771
1772         //we used to hide xfades here.  I don't see the point with the new model, but we can re-implement if needed
1773 }
1774
1775 void
1776 AudioRegionView::drag_end ()
1777 {
1778         TimeAxisViewItem::drag_end ();
1779         //see comment for drag_start
1780
1781         if (fade_in_handle && fade_in_handle->visible()) {
1782                 // lenght of region or fade changed, re-check
1783                 // if fade_in_trim_handle or fade_out_trim_handle should
1784                 // be visible. -- If the fade_in_handle is visible
1785                 // we have focus and are not in internal edit mode.
1786                 entered();
1787         }
1788 }
1789
1790 void
1791 AudioRegionView::parameter_changed (string const & p)
1792 {
1793         if (p == "show-waveforms") {
1794                 setup_waveform_visibility ();
1795         }
1796 }