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