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