d9ad504aeed6f8eceb28015e652062df7b8d5cb9
[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                 cerr << "Fade in changed, reset xfade\n";
564                 if (fade_in_handle) {
565                         fade_in_handle->hide ();
566                         fade_in_shape->hide ();
567                 }
568                 redraw_start_xfade ();
569                 return;
570         } else {
571                 if (start_xfade_in) {
572                         start_xfade_in->hide ();
573                         start_xfade_out->hide ();
574                         start_xfade_rect->hide ();
575                 }
576         }
577
578         if (fade_in_handle == 0) {
579                 return;
580         }
581
582         fade_in_handle->show ();
583
584         /* smallest size for a fade is 64 frames */
585
586         width = std::max ((framecnt_t) 64, width);
587
588         Points* points;
589
590         /* round here to prevent little visual glitches with sub-pixel placement */
591         double const pwidth = rint (width / samples_per_unit);
592         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
593         double h;
594
595         if (_height < 5) {
596                 fade_in_shape->hide();
597                 fade_in_handle->hide();
598                 return;
599         }
600
601         double const handle_center = pwidth;
602
603         /* Put the fade in handle so that its left side is at the end-of-fade line */
604         fade_in_handle->property_x1() = handle_center;
605         fade_in_handle->property_x2() = handle_center + 6;
606
607         if (pwidth < 5) {
608                 fade_in_shape->hide();
609                 return;
610         }
611
612         if (trackview.session()->config.get_show_region_fades()) {
613                 fade_in_shape->show();
614         }
615
616         float curve[npoints];
617         audio_region()->fade_in()->curve().get_vector (0, audio_region()->fade_in()->back()->when, curve, npoints);
618
619         points = get_canvas_points ("fade in shape", npoints + 3);
620
621         if (_height >= NAME_HIGHLIGHT_THRESH) {
622                 h = _height - NAME_HIGHLIGHT_SIZE - 2;
623         } else {
624                 h = _height;
625         }
626
627         /* points *MUST* be in anti-clockwise order */
628
629         uint32_t pi, pc;
630         double xdelta = pwidth/npoints;
631
632         for (pi = 0, pc = 0; pc < npoints; ++pc) {
633                 (*points)[pi].set_x(1 + (pc * xdelta));
634                 (*points)[pi++].set_y(2 + (h - (curve[pc] * h)));
635         }
636
637         /* fold back */
638
639         (*points)[pi].set_x(pwidth);
640         (*points)[pi++].set_y(2);
641
642         (*points)[pi].set_x(1);
643         (*points)[pi++].set_y(2);
644
645         /* connect the dots ... */
646
647         (*points)[pi] = (*points)[0];
648
649         fade_in_shape->property_points() = *points;
650         delete points;
651
652         /* ensure trim handle stays on top */
653         if (frame_handle_start) {
654                 frame_handle_start->raise_to_top();
655         }
656 }
657
658 void
659 AudioRegionView::reset_fade_out_shape ()
660 {
661         reset_fade_out_shape_width ((framecnt_t) audio_region()->fade_out()->back()->when);
662 }
663
664 void
665 AudioRegionView::reset_fade_out_shape_width (framecnt_t width)
666 {
667         if (dragging()) {
668                 return;
669         }
670
671         if (audio_region()->fade_out_is_xfade()) {
672                 cerr << "Fade out changed, reset xfade\n";
673                 if (fade_out_handle) {
674                         fade_out_handle->hide ();
675                         fade_out_shape->hide ();
676                 }
677                 redraw_end_xfade ();
678                 return;
679         } else {
680                 if (end_xfade_in) {
681                         end_xfade_in->hide ();
682                         end_xfade_out->hide ();
683                         end_xfade_rect->hide ();
684                 }
685         }
686
687         if (fade_out_handle == 0) {
688                 return;
689         }
690
691         fade_out_handle->show ();
692
693         /* smallest size for a fade is 64 frames */
694
695         width = std::max ((framecnt_t) 64, width);
696
697         Points* points;
698
699         /* round here to prevent little visual glitches with sub-pixel placement */
700         double const pwidth = rint (width / samples_per_unit);
701         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
702         double h;
703
704         if (_height < 5) {
705                 fade_out_shape->hide();
706                 fade_out_handle->hide();
707                 return;
708         }
709
710         double const handle_center = (_region->length() - width) / samples_per_unit;
711
712         /* Put the fade out handle so that its right side is at the end-of-fade line;
713          * it's `one out' for precise pixel accuracy.
714          */
715         fade_out_handle->property_x1() = handle_center - 5;
716         fade_out_handle->property_x2() = handle_center + 1;
717
718         /* don't show shape if its too small */
719
720         if (pwidth < 5) {
721                 fade_out_shape->hide();
722                 return;
723         }
724
725         if (trackview.session()->config.get_show_region_fades()) {
726                 fade_out_shape->show();
727         }
728
729         float curve[npoints];
730         audio_region()->fade_out()->curve().get_vector (0, audio_region()->fade_out()->back()->when, curve, npoints);
731
732         if (_height >= NAME_HIGHLIGHT_THRESH) {
733                 h = _height - NAME_HIGHLIGHT_SIZE - 2;
734         } else {
735                 h = _height;
736         }
737
738         /* points *MUST* be in anti-clockwise order */
739
740         points = get_canvas_points ("fade out shape", npoints + 3);
741
742         uint32_t pi, pc;
743         double xdelta = pwidth/npoints;
744
745         for (pi = 0, pc = 0; pc < npoints; ++pc) {
746                 (*points)[pi].set_x(_pixel_width - pwidth + (pc * xdelta));
747                 (*points)[pi++].set_y(2 + (h - (curve[pc] * h)));
748         }
749
750         /* fold back */
751
752         (*points)[pi].set_x(_pixel_width);
753         (*points)[pi++].set_y(h);
754
755         (*points)[pi].set_x(_pixel_width);
756         (*points)[pi++].set_y(2);
757
758         /* connect the dots ... */
759
760         (*points)[pi] = (*points)[0];
761
762         fade_out_shape->property_points() = *points;
763         delete points;
764
765         /* ensure trim handle stays on top */
766         if (frame_handle_end) {
767                 frame_handle_end->raise_to_top();
768         }
769 }
770
771 void
772 AudioRegionView::set_samples_per_unit (gdouble spu)
773 {
774         RegionView::set_samples_per_unit (spu);
775
776         if (_flags & WaveformVisible) {
777                 for (uint32_t n=0; n < waves.size(); ++n) {
778                         waves[n]->property_samples_per_unit() = spu;
779                 }
780         }
781
782         if (gain_line) {
783                 gain_line->reset ();
784         }
785
786         reset_fade_shapes ();
787 }
788
789 void
790 AudioRegionView::set_amplitude_above_axis (gdouble spp)
791 {
792         for (uint32_t n=0; n < waves.size(); ++n) {
793                 waves[n]->property_amplitude_above_axis() = spp;
794         }
795 }
796
797 void
798 AudioRegionView::compute_colors (Gdk::Color const & basic_color)
799 {
800         RegionView::compute_colors (basic_color);
801
802         /* gain color computed in envelope_active_changed() */
803
804         fade_color = UINT_RGBA_CHANGE_A (fill_color, 120);
805 }
806
807 void
808 AudioRegionView::set_colors ()
809 {
810         RegionView::set_colors();
811
812         if (gain_line) {
813                 gain_line->set_line_color (audio_region()->envelope_active() ? ARDOUR_UI::config()->canvasvar_GainLine.get() : ARDOUR_UI::config()->canvasvar_GainLineInactive.get());
814         }
815
816         for (uint32_t n=0; n < waves.size(); ++n) {
817                 if (_region->muted()) {
818                         waves[n]->property_wave_color() = UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_WaveForm.get(), MUTED_ALPHA);
819                 } else {
820                         waves[n]->property_wave_color() = ARDOUR_UI::config()->canvasvar_WaveForm.get();
821                 }
822
823                 waves[n]->property_clip_color() = ARDOUR_UI::config()->canvasvar_WaveFormClip.get();
824                 waves[n]->property_zero_color() = ARDOUR_UI::config()->canvasvar_ZeroLine.get();
825         }
826 }
827
828 void
829 AudioRegionView::set_waveform_visible (bool yn)
830 {
831         if (((_flags & WaveformVisible) != yn)) {
832                 if (yn) {
833                         for (uint32_t n=0; n < waves.size(); ++n) {
834                                 /* make sure the zoom level is correct, since we don't update
835                                    this when waveforms are hidden.
836                                 */
837                                 waves[n]->property_samples_per_unit() = samples_per_unit;
838                                 waves[n]->show();
839                         }
840                         _flags |= WaveformVisible;
841                 } else {
842                         for (uint32_t n=0; n < waves.size(); ++n) {
843                                 waves[n]->hide();
844                         }
845                         _flags &= ~WaveformVisible;
846                 }
847                 store_flags ();
848         }
849 }
850
851 void
852 AudioRegionView::temporarily_hide_envelope ()
853 {
854         if (gain_line) {
855                 gain_line->hide ();
856         }
857 }
858
859 void
860 AudioRegionView::unhide_envelope ()
861 {
862         if (gain_line) {
863                 gain_line->show ();
864         }
865 }
866
867 void
868 AudioRegionView::set_envelope_visible (bool yn)
869 {
870         if (gain_line) {
871                 if (yn) {
872                         gain_line->show ();
873                 } else {
874                         gain_line->hide ();
875                 }
876         }
877 }
878
879 void
880 AudioRegionView::create_waves ()
881 {
882         // cerr << "AudioRegionView::create_waves() called on " << this << endl;//DEBUG
883         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
884
885         if (!atv.track()) {
886                 return;
887         }
888
889         ChanCount nchans = atv.track()->n_channels();
890
891         // cerr << "creating waves for " << _region->name() << " with wfd = " << wait_for_data
892         //              << " and channels = " << nchans.n_audio() << endl;
893
894         /* in tmp_waves, set up null pointers for each channel so the vector is allocated */
895         for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
896                 tmp_waves.push_back (0);
897         }
898
899         for (vector<ScopedConnection*>::iterator i = _data_ready_connections.begin(); i != _data_ready_connections.end(); ++i) {
900                 delete *i;
901         }
902
903         _data_ready_connections.clear ();
904
905         for (uint32_t i = 0; i < nchans.n_audio(); ++i) {
906                 _data_ready_connections.push_back (0);
907         }
908
909         for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
910
911                 if (n >= audio_region()->n_channels()) {
912                         break;
913                 }
914
915                 wave_caches.push_back (WaveView::create_cache ());
916
917                 // cerr << "\tchannel " << n << endl;
918
919                 if (wait_for_data) {
920                         if (audio_region()->audio_source(n)->peaks_ready (boost::bind (&AudioRegionView::peaks_ready_handler, this, n), &_data_ready_connections[n], gui_context())) {
921                                 // cerr << "\tData is ready\n";
922                                 create_one_wave (n, true);
923                         } else {
924                                 // cerr << "\tdata is not ready\n";
925                                 // we'll get a PeaksReady signal from the source in the future
926                                 // and will call create_one_wave(n) then.
927                         }
928
929                 } else {
930                         // cerr << "\tdon't delay, display today!\n";
931                         create_one_wave (n, true);
932                 }
933
934         }
935 }
936
937 void
938 AudioRegionView::create_one_wave (uint32_t which, bool /*direct*/)
939 {
940         //cerr << "AudioRegionView::create_one_wave() called which: " << which << " this: " << this << endl;//DEBUG
941         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
942         uint32_t nchans = atv.track()->n_channels().n_audio();
943         uint32_t n;
944         uint32_t nwaves = std::min (nchans, audio_region()->n_channels());
945         gdouble ht;
946
947         if (trackview.current_height() < NAME_HIGHLIGHT_THRESH) {
948                 ht = ((trackview.current_height()) / (double) nchans);
949         } else {
950                 ht = ((trackview.current_height() - NAME_HIGHLIGHT_SIZE) / (double) nchans);
951         }
952
953         gdouble yoff = which * ht;
954
955         WaveView *wave = new WaveView(*group);
956
957         wave->property_data_src() = (gpointer) _region.get();
958         wave->property_cache() =  wave_caches[which];
959         wave->property_cache_updater() = true;
960         wave->property_channel() =  which;
961         wave->property_length_function() = (gpointer) region_length_from_c;
962         wave->property_sourcefile_length_function() = (gpointer) sourcefile_length_from_c;
963         wave->property_peak_function() =  (gpointer) region_read_peaks_from_c;
964         wave->property_x() =  0.0;
965         wave->property_y() =  yoff;
966         wave->property_height() =  (double) ht;
967         wave->property_samples_per_unit() =  samples_per_unit;
968         wave->property_amplitude_above_axis() =  _amplitude_above_axis;
969
970         if (_recregion) {
971                 wave->property_wave_color() = _region->muted() ? UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_RecWaveForm.get(), MUTED_ALPHA) : ARDOUR_UI::config()->canvasvar_RecWaveForm.get();
972                 wave->property_fill_color() = ARDOUR_UI::config()->canvasvar_RecWaveFormFill.get();
973         } else {
974                 wave->property_wave_color() = _region->muted() ? UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_WaveForm.get(), MUTED_ALPHA) : ARDOUR_UI::config()->canvasvar_WaveForm.get();
975                 wave->property_fill_color() = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
976         }
977
978         wave->property_clip_color() = ARDOUR_UI::config()->canvasvar_WaveFormClip.get();
979         wave->property_zero_color() = ARDOUR_UI::config()->canvasvar_ZeroLine.get();
980         wave->property_zero_line() = true;
981         wave->property_region_start() = _region->start();
982         wave->property_rectified() = (bool) (_flags & WaveformRectified);
983         wave->property_logscaled() = (bool) (_flags & WaveformLogScaled);
984
985         if (!(_flags & WaveformVisible)) {
986                 wave->hide();
987         }
988
989         /* note: calling this function is serialized by the lock
990            held in the peak building thread that signals that
991            peaks are ready for use *or* by the fact that it is
992            called one by one from the GUI thread.
993         */
994
995         if (which < nchans) {
996                 tmp_waves[which] = wave;
997         } else {
998                 /* n-channel track, >n-channel source */
999         }
1000
1001         /* see if we're all ready */
1002
1003         for (n = 0; n < nchans; ++n) {
1004                 if (tmp_waves[n] == 0) {
1005                         break;
1006                 }
1007         }
1008
1009         if (n == nwaves && waves.empty()) {
1010                 /* all waves are ready */
1011                 tmp_waves.resize(nwaves);
1012
1013                 waves = tmp_waves;
1014                 tmp_waves.clear ();
1015
1016                 /* all waves created, don't hook into peaks ready anymore */
1017                 delete _data_ready_connections[which];
1018                 _data_ready_connections[which] = 0;
1019         }
1020 }
1021
1022 void
1023 AudioRegionView::peaks_ready_handler (uint32_t which)
1024 {
1025         Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&AudioRegionView::create_one_wave, this, which, false));
1026         // cerr << "AudioRegionView::peaks_ready_handler() called on " << which << " this: " << this << endl;
1027 }
1028
1029 void
1030 AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
1031 {
1032         if (!gain_line) {
1033                 return;
1034         }
1035
1036         double x, y;
1037
1038         /* don't create points that can't be seen */
1039
1040         set_envelope_visible (true);
1041
1042         x = ev->button.x;
1043         y = ev->button.y;
1044
1045         item->w2i (x, y);
1046
1047         framepos_t fx = trackview.editor().pixel_to_frame (x);
1048
1049         if (fx > _region->length()) {
1050                 return;
1051         }
1052
1053         /* compute vertical fractional position */
1054
1055         y = 1.0 - (y / (_height - NAME_HIGHLIGHT_SIZE));
1056
1057         /* map using gain line */
1058
1059         gain_line->view_to_model_coord (x, y);
1060
1061         /* XXX STATEFUL: can't convert to stateful diff until we
1062            can represent automation data with it.
1063         */
1064
1065         trackview.session()->begin_reversible_command (_("add gain control point"));
1066         XMLNode &before = audio_region()->envelope()->get_state();
1067
1068         if (!audio_region()->envelope_active()) {
1069                 XMLNode &region_before = audio_region()->get_state();
1070                 audio_region()->set_envelope_active(true);
1071                 XMLNode &region_after = audio_region()->get_state();
1072                 trackview.session()->add_command (new MementoCommand<AudioRegion>(*(audio_region().get()), &region_before, &region_after));
1073         }
1074
1075         audio_region()->envelope()->add (fx, y);
1076
1077         XMLNode &after = audio_region()->envelope()->get_state();
1078         trackview.session()->add_command (new MementoCommand<AutomationList>(*audio_region()->envelope().get(), &before, &after));
1079         trackview.session()->commit_reversible_command ();
1080 }
1081
1082 void
1083 AudioRegionView::remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent */*ev*/)
1084 {
1085         ControlPoint *cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1086         audio_region()->envelope()->erase (cp->model());
1087 }
1088
1089 void
1090 AudioRegionView::store_flags()
1091 {
1092         XMLNode *node = new XMLNode ("GUI");
1093
1094         node->add_property ("waveform-visible", (_flags & WaveformVisible) ? "yes" : "no");
1095         node->add_property ("waveform-rectified", (_flags & WaveformRectified) ? "yes" : "no");
1096         node->add_property ("waveform-logscaled", (_flags & WaveformLogScaled) ? "yes" : "no");
1097
1098         _region->add_extra_xml (*node);
1099 }
1100
1101 void
1102 AudioRegionView::set_flags (XMLNode* node)
1103 {
1104         XMLProperty *prop;
1105
1106         if ((prop = node->property ("waveform-visible")) != 0) {
1107                 if (string_is_affirmative (prop->value())) {
1108                         _flags |= WaveformVisible;
1109                 }
1110         }
1111
1112         if ((prop = node->property ("waveform-rectified")) != 0) {
1113                 if (string_is_affirmative (prop->value())) {
1114                         _flags |= WaveformRectified;
1115                 }
1116         }
1117
1118         if ((prop = node->property ("waveform-logscaled")) != 0) {
1119                 if (string_is_affirmative (prop->value())) {
1120                         _flags |= WaveformLogScaled;
1121                 }
1122         }
1123 }
1124
1125 void
1126 AudioRegionView::set_waveform_shape (WaveformShape shape)
1127 {
1128         bool yn;
1129
1130         /* this slightly odd approach is to leave the door open to
1131            other "shapes" such as spectral displays, etc.
1132         */
1133
1134         switch (shape) {
1135         case Rectified:
1136                 yn = true;
1137                 break;
1138
1139         default:
1140                 yn = false;
1141                 break;
1142         }
1143
1144         if (yn != (bool) (_flags & WaveformRectified)) {
1145                 for (vector<WaveView *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
1146                         (*wave)->property_rectified() = yn;
1147                 }
1148
1149                 if (yn) {
1150                         _flags |= WaveformRectified;
1151                 } else {
1152                         _flags &= ~WaveformRectified;
1153                 }
1154                 store_flags ();
1155         }
1156 }
1157
1158 void
1159 AudioRegionView::set_waveform_scale (WaveformScale scale)
1160 {
1161         bool yn = (scale == Logarithmic);
1162
1163         if (yn != (bool) (_flags & WaveformLogScaled)) {
1164                 for (vector<WaveView *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
1165                         (*wave)->property_logscaled() = yn;
1166                 }
1167
1168                 if (yn) {
1169                         _flags |= WaveformLogScaled;
1170                 } else {
1171                         _flags &= ~WaveformLogScaled;
1172                 }
1173                 store_flags ();
1174         }
1175 }
1176
1177
1178 GhostRegion*
1179 AudioRegionView::add_ghost (TimeAxisView& tv)
1180 {
1181         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&trackview);
1182         assert(rtv);
1183
1184         double unit_position = _region->position () / samples_per_unit;
1185         AudioGhostRegion* ghost = new AudioGhostRegion (tv, trackview, unit_position);
1186         uint32_t nchans;
1187
1188         nchans = rtv->track()->n_channels().n_audio();
1189
1190         for (uint32_t n = 0; n < nchans; ++n) {
1191
1192                 if (n >= audio_region()->n_channels()) {
1193                         break;
1194                 }
1195
1196                 WaveView *wave = new WaveView(*ghost->group);
1197
1198                 wave->property_data_src() = _region.get();
1199                 wave->property_cache() =  wave_caches[n];
1200                 wave->property_cache_updater() = false;
1201                 wave->property_channel() = n;
1202                 wave->property_length_function() = (gpointer)region_length_from_c;
1203                 wave->property_sourcefile_length_function() = (gpointer) sourcefile_length_from_c;
1204                 wave->property_peak_function() =  (gpointer) region_read_peaks_from_c;
1205                 wave->property_x() =  0.0;
1206                 wave->property_samples_per_unit() =  samples_per_unit;
1207                 wave->property_amplitude_above_axis() =  _amplitude_above_axis;
1208
1209                 wave->property_region_start() = _region->start();
1210
1211                 ghost->waves.push_back(wave);
1212         }
1213
1214         ghost->set_height ();
1215         ghost->set_duration (_region->length() / samples_per_unit);
1216         ghost->set_colors();
1217         ghosts.push_back (ghost);
1218
1219         return ghost;
1220 }
1221
1222 void
1223 AudioRegionView::entered (bool internal_editing)
1224 {
1225         trackview.editor().set_current_trimmable (_region);
1226         trackview.editor().set_current_movable (_region);
1227
1228         if (gain_line && Config->get_show_region_gain ()) {
1229                 gain_line->show_all_control_points ();
1230         }
1231
1232         if (fade_in_handle && !internal_editing) {
1233                 fade_in_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 255);
1234                 fade_out_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 255);
1235         }
1236 }
1237
1238 void
1239 AudioRegionView::exited ()
1240 {
1241         trackview.editor().set_current_trimmable (boost::shared_ptr<Trimmable>());
1242         trackview.editor().set_current_movable (boost::shared_ptr<Movable>());
1243
1244         if (gain_line) {
1245                 gain_line->hide_all_but_selected_control_points ();
1246         }
1247
1248         if (fade_in_handle) {
1249                 fade_in_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 0);
1250                 fade_out_handle->property_fill_color_rgba() = UINT_RGBA_CHANGE_A (fade_color, 0);
1251         }
1252 }
1253
1254 void
1255 AudioRegionView::envelope_active_changed ()
1256 {
1257         if (gain_line) {
1258                 gain_line->set_line_color (audio_region()->envelope_active() ? ARDOUR_UI::config()->canvasvar_GainLine.get() : ARDOUR_UI::config()->canvasvar_GainLineInactive.get());
1259         }
1260 }
1261
1262 void
1263 AudioRegionView::set_waveview_data_src()
1264 {
1265         AudioGhostRegion* agr;
1266         double unit_length= _region->length() / samples_per_unit;
1267
1268         for (uint32_t n = 0; n < waves.size(); ++n) {
1269                 // TODO: something else to let it know the channel
1270                 waves[n]->property_data_src() = _region.get();
1271         }
1272
1273         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1274
1275                 (*i)->set_duration (unit_length);
1276
1277                 if((agr = dynamic_cast<AudioGhostRegion*>(*i)) != 0) {
1278                         for (vector<WaveView*>::iterator w = agr->waves.begin(); w != agr->waves.end(); ++w) {
1279                                 (*w)->property_data_src() = _region.get();
1280                         }
1281                 }
1282         }
1283
1284 }
1285
1286 void
1287 AudioRegionView::color_handler ()
1288 {
1289         //case cMutedWaveForm:
1290         //case cWaveForm:
1291         //case cWaveFormClip:
1292         //case cZeroLine:
1293         set_colors ();
1294
1295         //case cGainLineInactive:
1296         //case cGainLine:
1297         envelope_active_changed();
1298
1299 }
1300
1301 void
1302 AudioRegionView::set_frame_color ()
1303 {
1304         if (!frame) {
1305                 return;
1306         }
1307
1308         if (_region->opaque()) {
1309                 fill_opacity = 130;
1310         } else {
1311                 fill_opacity = 0;
1312         }
1313
1314         TimeAxisViewItem::set_frame_color ();
1315
1316         uint32_t wc;
1317         uint32_t fc;
1318
1319         if (_selected) {
1320                 if (_region->muted()) {
1321                         wc = UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_SelectedWaveForm.get(), MUTED_ALPHA);
1322                 } else {
1323                         wc = ARDOUR_UI::config()->canvasvar_SelectedWaveForm.get();
1324                 }
1325                 fc = ARDOUR_UI::config()->canvasvar_SelectedWaveFormFill.get();
1326         } else {
1327                 if (_recregion) {
1328                         if (_region->muted()) {
1329                                 wc = UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_RecWaveForm.get(), MUTED_ALPHA);
1330                         } else {
1331                                 wc = ARDOUR_UI::config()->canvasvar_RecWaveForm.get();
1332                         }
1333                         fc = ARDOUR_UI::config()->canvasvar_RecWaveFormFill.get();
1334                 } else {
1335                         if (_region->muted()) {
1336                                 wc = UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_WaveForm.get(), MUTED_ALPHA);
1337                         } else {
1338                                 wc = ARDOUR_UI::config()->canvasvar_WaveForm.get();
1339                         }
1340                         fc = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
1341                 }
1342         }
1343
1344         for (vector<ArdourCanvas::WaveView*>::iterator w = waves.begin(); w != waves.end(); ++w) {
1345                 if (_region->muted()) {
1346                         (*w)->property_wave_color() = wc;
1347                 } else {
1348                         (*w)->property_wave_color() = wc;
1349                         (*w)->property_fill_color() = fc;
1350                 }
1351         }
1352 }
1353
1354 void
1355 AudioRegionView::set_fade_visibility (bool yn)
1356 {
1357         if (yn) {
1358                 if (fade_in_shape) {
1359                         fade_in_shape->show();
1360                 }
1361                 if (fade_out_shape) {
1362                         fade_out_shape->show ();
1363                 }
1364                 if (fade_in_handle) {
1365                         fade_in_handle->show ();
1366                 }
1367                 if (fade_out_handle) {
1368                         fade_out_handle->show ();
1369                 }
1370         } else {
1371                 if (fade_in_shape) {
1372                         fade_in_shape->hide();
1373                 }
1374                 if (fade_out_shape) {
1375                         fade_out_shape->hide ();
1376                 }
1377                 if (fade_in_handle) {
1378                         fade_in_handle->hide ();
1379                 }
1380                 if (fade_out_handle) {
1381                         fade_out_handle->hide ();
1382                 }
1383         }
1384 }
1385
1386 void
1387 AudioRegionView::update_coverage_frames (LayerDisplay d)
1388 {
1389         RegionView::update_coverage_frames (d);
1390
1391         if (fade_in_handle) {
1392                 fade_in_handle->raise_to_top ();
1393                 fade_out_handle->raise_to_top ();
1394         }
1395 }
1396
1397 void
1398 AudioRegionView::show_region_editor ()
1399 {
1400         if (editor == 0) {
1401                 editor = new AudioRegionEditor (trackview.session(), audio_region());
1402         }
1403
1404         editor->present ();
1405         editor->set_position (Gtk::WIN_POS_MOUSE);
1406         editor->show_all();
1407 }
1408
1409
1410 void
1411 AudioRegionView::show_fade_line (framepos_t pos)
1412 {
1413         fade_position_line->property_x1() = trackview.editor().frame_to_pixel (pos);
1414         fade_position_line->property_x2() = trackview.editor().frame_to_pixel (pos);
1415         fade_position_line->show ();
1416         fade_position_line->raise_to_top ();
1417 }
1418
1419 void
1420 AudioRegionView::hide_fade_line ()
1421 {
1422         fade_position_line->hide ();
1423 }
1424
1425
1426 void
1427 AudioRegionView::transients_changed ()
1428 {
1429         AnalysisFeatureList analysis_features = _region->transients();
1430
1431         while (feature_lines.size() < analysis_features.size()) {
1432
1433                 ArdourCanvas::Line* canvas_item = new ArdourCanvas::Line(*group);
1434
1435                 ArdourCanvas::Points points;
1436
1437                 points.push_back(Gnome::Art::Point(-1.0, 2.0)); // first x-coord needs to be a non-normal value
1438                 points.push_back(Gnome::Art::Point(1.0, _height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 1));
1439
1440                 canvas_item->property_points() = points;
1441                 canvas_item->property_width_pixels() = 1;
1442                 canvas_item->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_ZeroLine.get();
1443                 canvas_item->property_first_arrowhead() = TRUE;
1444                 canvas_item->property_last_arrowhead() = TRUE;
1445                 canvas_item->property_arrow_shape_a() = 11.0;
1446                 canvas_item->property_arrow_shape_b() = 0.0;
1447                 canvas_item->property_arrow_shape_c() = 4.0;
1448
1449                 canvas_item->raise_to_top ();
1450                 canvas_item->show ();
1451
1452                 canvas_item->set_data ("regionview", this);
1453                 canvas_item->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_feature_line_event), canvas_item, this));
1454
1455                 feature_lines.push_back (make_pair(0, canvas_item));
1456         }
1457
1458         while (feature_lines.size() > analysis_features.size()) {
1459                 ArdourCanvas::Line* line = feature_lines.back().second;
1460                 feature_lines.pop_back ();
1461                 delete line;
1462         }
1463
1464         AnalysisFeatureList::const_iterator i;
1465         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1466
1467         for (i = analysis_features.begin(), l = feature_lines.begin(); i != analysis_features.end() && l != feature_lines.end(); ++i, ++l) {
1468
1469                 ArdourCanvas::Points points;
1470
1471                 float *pos = new float;
1472                 *pos = trackview.editor().frame_to_pixel (*i);
1473
1474                 points.push_back(Gnome::Art::Point(*pos, 2.0)); // first x-coord needs to be a non-normal value
1475                 points.push_back(Gnome::Art::Point(*pos, _height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 1));
1476
1477                 (*l).second->property_points() = points;
1478                 (*l).second->set_data ("position", pos);
1479
1480                 (*l).first = *i;
1481         }
1482 }
1483
1484 void
1485 AudioRegionView::update_transient(float /*old_pos*/, float new_pos)
1486 {
1487         /* Find frame at old pos, calulate new frame then update region transients*/
1488         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1489
1490         for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
1491
1492                 /* Line has been updated in drag so we compare to new_pos */
1493
1494                 float* pos = (float*) (*l).second->get_data ("position");
1495
1496                 if (rint(new_pos) == rint(*pos)) {
1497
1498                     framepos_t old_frame = (*l).first;
1499                     framepos_t new_frame = trackview.editor().pixel_to_frame (new_pos);
1500
1501                     _region->update_transient (old_frame, new_frame);
1502
1503                     break;
1504                 }
1505         }
1506 }
1507
1508 void
1509 AudioRegionView::remove_transient(float pos)
1510 {
1511         /* Find frame at old pos, calulate new frame then update region transients*/
1512         list<std::pair<framepos_t, ArdourCanvas::Line*> >::iterator l;
1513
1514         for (l = feature_lines.begin(); l != feature_lines.end(); ++l) {
1515
1516                 /* Line has been updated in drag so we compare to new_pos */
1517                 float *line_pos = (float*) (*l).second->get_data ("position");
1518
1519                 if (rint(pos) == rint(*line_pos)) {
1520                     _region->remove_transient ((*l).first);
1521                     break;
1522                 }
1523         }
1524 }
1525
1526 void
1527 AudioRegionView::thaw_after_trim ()
1528 {
1529         RegionView::thaw_after_trim ();
1530         unhide_envelope ();
1531         drag_end ();
1532 }
1533
1534 void
1535 AudioRegionView::redraw_start_xfade ()
1536 {
1537         boost::shared_ptr<AudioRegion> ar (audio_region());
1538
1539         if (!ar->fade_in() || ar->fade_in()->empty()) {
1540                 return;
1541         }
1542
1543         if (!ar->fade_in_is_xfade()) {
1544                 if (start_xfade_in) {
1545                         start_xfade_in->hide ();
1546                         start_xfade_out->hide ();
1547                         start_xfade_rect->hide ();
1548                 }
1549                 return;
1550         }
1551
1552         redraw_start_xfade_to (ar, ar->fade_in()->back()->when);
1553 }
1554
1555 void
1556 AudioRegionView::redraw_start_xfade_to (boost::shared_ptr<AudioRegion> ar, framecnt_t len)
1557 {
1558         int32_t const npoints = trackview.editor().frame_to_pixel (len);
1559
1560         if (npoints < 3) {
1561                 return;
1562         }
1563
1564         if (!start_xfade_in) {
1565                 start_xfade_in = new ArdourCanvas::Line (*group);
1566                 start_xfade_in->property_width_pixels() = 1;
1567                 start_xfade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get();
1568         }
1569
1570         if (!start_xfade_out) {
1571                 start_xfade_out = new ArdourCanvas::Line (*group);
1572                 start_xfade_out->property_width_pixels() = 1;
1573                 start_xfade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get();
1574         }
1575
1576         if (!start_xfade_rect) {
1577                 start_xfade_rect = new ArdourCanvas::SimpleRect (*group);
1578                 start_xfade_rect->property_draw() = true;
1579                 start_xfade_rect->property_fill() = true;;
1580                 start_xfade_rect->property_fill_color_rgba() =  ARDOUR_UI::config()->canvasvar_ActiveCrossfade.get();
1581                 start_xfade_rect->property_outline_pixels() = 0;
1582                 start_xfade_rect->signal_event().connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_start_xfade_event), start_xfade_rect, this));
1583                 start_xfade_rect->set_data ("regionview", this);
1584         }
1585
1586         Points* points = get_canvas_points ("xfade edit redraw", npoints);
1587         boost::scoped_ptr<float> vec (new float[npoints]);
1588         double effective_height = _height - NAME_HIGHLIGHT_SIZE - 1.0;
1589
1590         ar->fade_in()->curve().get_vector (0, ar->fade_in()->back()->when, vec.get(), npoints);
1591
1592         for (int i = 0, pci = 0; i < npoints; ++i) {
1593                 Gnome::Art::Point &p ((*points)[pci++]);
1594                 p.set_x (i);
1595                 p.set_y (1.0 + effective_height - (effective_height * vec.get()[i]));
1596         }
1597
1598         start_xfade_rect->property_x1() = ((*points)[0]).get_x();
1599         start_xfade_rect->property_y1() = 1.0;
1600         start_xfade_rect->property_x2() = ((*points)[npoints-1]).get_x();
1601         start_xfade_rect->property_y2() = effective_height;
1602         start_xfade_rect->show ();
1603         start_xfade_rect->raise_to_top ();
1604
1605         start_xfade_in->property_points() = *points;
1606         start_xfade_in->show ();
1607         start_xfade_in->raise_to_top ();
1608
1609         /* fade out line */
1610
1611         boost::shared_ptr<AutomationList> inverse = ar->inverse_fade_in();
1612
1613         if (!inverse) {
1614
1615                 for (int i = 0, pci = 0; i < npoints; ++i) {
1616                         Gnome::Art::Point &p ((*points)[pci++]);
1617                         p.set_x (i);
1618                         p.set_y (1.0 + effective_height - (effective_height * (1.0 - vec.get()[i])));
1619                 }
1620
1621         } else {
1622
1623                 inverse->curve().get_vector (0, inverse->back()->when, vec.get(), npoints);
1624
1625                 for (int i = 0, pci = 0; i < npoints; ++i) {
1626                         Gnome::Art::Point &p ((*points)[pci++]);
1627                         p.set_x (i);
1628                         p.set_y (1.0 + effective_height - (effective_height * vec.get()[i]));
1629                 }
1630         }
1631
1632         start_xfade_out->property_points() = *points;
1633         start_xfade_out->show ();
1634         start_xfade_out->raise_to_top ();
1635
1636         delete points;
1637 }
1638
1639 void
1640 AudioRegionView::redraw_end_xfade ()
1641 {
1642         boost::shared_ptr<AudioRegion> ar (audio_region());
1643
1644         if (!ar->fade_out() || ar->fade_out()->empty()) {
1645                 return;
1646         }
1647
1648         if (!ar->fade_out_is_xfade()) {
1649                 if (end_xfade_in) {
1650                         end_xfade_in->hide ();
1651                         end_xfade_out->hide ();
1652                         end_xfade_rect->hide ();
1653                 }
1654                 return;
1655         }
1656
1657         redraw_end_xfade_to (ar, ar->fade_out()->back()->when);
1658 }
1659
1660 void
1661 AudioRegionView::redraw_end_xfade_to (boost::shared_ptr<AudioRegion> ar, framecnt_t len)
1662 {
1663         int32_t const npoints = trackview.editor().frame_to_pixel (len);
1664
1665         if (npoints < 3) {
1666                 return;
1667         }
1668
1669         if (!end_xfade_in) {
1670                 end_xfade_in = new ArdourCanvas::Line (*group);
1671                 end_xfade_in->property_width_pixels() = 1;
1672                 end_xfade_in->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get();
1673         }
1674
1675         if (!end_xfade_out) {
1676                 end_xfade_out = new ArdourCanvas::Line (*group);
1677                 end_xfade_out->property_width_pixels() = 1;
1678                 end_xfade_out->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_GainLine.get();
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