Abstraction cleanups/polish, towards merging with trunk
[ardour.git] / gtk2_ardour / audio_regionview.cc
1 /*
2     Copyright (C) 2001-2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id: regionview.cc 682 2006-07-14 03:43:32Z drobilla $
19 */
20
21 #include <cmath>
22 #include <cassert>
23 #include <algorithm>
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/audio_diskstream.h>
33
34 #include "streamview.h"
35 #include "audio_regionview.h"
36 #include "audio_time_axis.h"
37 #include "simplerect.h"
38 #include "simpleline.h"
39 #include "waveview.h"
40 #include "public_editor.h"
41 #include "audio_region_editor.h"
42 #include "region_gain_line.h"
43 #include "ghostregion.h"
44 #include "audio_time_axis.h"
45 #include "utils.h"
46 #include "rgb_macros.h"
47 #include "gui_thread.h"
48
49 #include "i18n.h"
50
51 using namespace sigc;
52 using namespace ARDOUR;
53 using namespace PBD;
54 using namespace Editing;
55 using namespace ArdourCanvas;
56
57 static const int32_t sync_mark_width = 9;
58
59 AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, AudioRegion& r, double spu,
60                                   Gdk::Color& basic_color)
61         : RegionView (parent, tv, r, spu, basic_color)
62 {
63 }
64
65 AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, AudioRegion& r, double spu, 
66                                   Gdk::Color& basic_color, TimeAxisViewItem::Visibility visibility)
67         : RegionView (parent, tv, r, spu, basic_color, visibility)
68 {
69 }
70
71 void
72 AudioRegionView::init (Gdk::Color& basic_color, bool wfd)
73 {
74         // FIXME: Some redundancy here with RegionView::init.  Need to figure out
75         // where order is important and where it isn't...
76         
77         RegionView::init(basic_color, wfd);
78
79         XMLNode *node;
80
81         _amplitude_above_axis = 1.0;
82         zero_line             = 0;
83         _flags                = 0;
84
85         if ((node = _region.extra_xml ("GUI")) != 0) {
86                 set_flags (node);
87         } else {
88                 _flags = WaveformVisible;
89                 store_flags ();
90         }
91
92         if (trackview.editor.new_regionviews_display_gain()) {
93                 _flags |= EnvelopeVisible;
94         }
95
96         compute_colors (basic_color);
97
98         create_waves ();
99
100         fade_in_shape = new ArdourCanvas::Polygon (*group);
101         fade_in_shape->property_fill_color_rgba() = fade_color;
102         fade_in_shape->set_data ("regionview", this);
103         
104         fade_out_shape = new ArdourCanvas::Polygon (*group);
105         fade_out_shape->property_fill_color_rgba() = fade_color;
106         fade_out_shape->set_data ("regionview", this);
107
108
109         {
110                 uint32_t r,g,b,a;
111                 UINT_TO_RGBA(fill_color,&r,&g,&b,&a);
112         
113
114                 fade_in_handle = new ArdourCanvas::SimpleRect (*group);
115                 fade_in_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,0);
116                 fade_in_handle->property_outline_pixels() = 0;
117                 fade_in_handle->property_y1() = 2.0;
118                 fade_in_handle->property_y2() = 7.0;
119                 
120                 fade_in_handle->set_data ("regionview", this);
121                 
122                 fade_out_handle = new ArdourCanvas::SimpleRect (*group);
123                 fade_out_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,0);
124                 fade_out_handle->property_outline_pixels() = 0;
125                 fade_out_handle->property_y1() = 2.0;
126                 fade_out_handle->property_y2() = 7.0;
127                 
128                 fade_out_handle->set_data ("regionview", this);
129         }
130
131         string foo = _region.name();
132         foo += ':';
133         foo += "gain";
134
135         gain_line = new AudioRegionGainLine (foo, trackview.session(), *this, *group, audio_region().envelope());
136
137         if (!(_flags & EnvelopeVisible)) {
138                 gain_line->hide ();
139         } else {
140                 gain_line->show ();
141         }
142
143         reset_width_dependent_items ((double) _region.length() / samples_per_unit);
144
145         gain_line->reset ();
146
147         set_height (trackview.height);
148
149         region_muted ();
150         region_sync_changed ();
151         region_resized (BoundsChanged);
152         set_waveview_data_src();
153         region_locked ();
154         envelope_active_changed ();
155         fade_in_active_changed ();
156         fade_out_active_changed ();
157
158         _region.StateChanged.connect (mem_fun(*this, &AudioRegionView::region_changed));
159
160         fade_in_shape->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_event), fade_in_shape, this));
161         fade_in_handle->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this));
162         fade_out_shape->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_event), fade_out_shape, this));
163         fade_out_handle->signal_event().connect (bind (mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this));
164
165         set_colors ();
166
167         /* XXX sync mark drag? */
168 }
169
170 AudioRegionView::~AudioRegionView ()
171 {
172         in_destructor = true;
173
174         RegionViewGoingAway (this); /* EMIT_SIGNAL */
175
176         for (vector<GnomeCanvasWaveViewCache *>::iterator cache = wave_caches.begin(); cache != wave_caches.end() ; ++cache) {
177                 gnome_canvas_waveview_cache_destroy (*cache);
178         }
179
180         /* all waveviews etc will be destroyed when the group is destroyed */
181
182         if (gain_line) {
183                 delete gain_line;
184         }
185 }
186
187 ARDOUR::AudioRegion&
188 AudioRegionView::audio_region() const
189 {
190         // "Guaranteed" to succeed...
191         return dynamic_cast<AudioRegion&>(_region);
192 }
193
194 void
195 AudioRegionView::region_changed (Change what_changed)
196 {
197         ENSURE_GUI_THREAD (bind (mem_fun(*this, &AudioRegionView::region_changed), what_changed));
198
199         RegionView::region_changed(what_changed);
200
201         if (what_changed & AudioRegion::ScaleAmplitudeChanged) {
202                 region_scale_amplitude_changed ();
203         }
204         if (what_changed & AudioRegion::FadeInChanged) {
205                 fade_in_changed ();
206         }
207         if (what_changed & AudioRegion::FadeOutChanged) {
208                 fade_out_changed ();
209         }
210         if (what_changed & AudioRegion::FadeInActiveChanged) {
211                 fade_in_active_changed ();
212         }
213         if (what_changed & AudioRegion::FadeOutActiveChanged) {
214                 fade_out_active_changed ();
215         }
216         if (what_changed & AudioRegion::EnvelopeActiveChanged) {
217                 envelope_active_changed ();
218         }
219 }
220
221 void
222 AudioRegionView::fade_in_changed ()
223 {
224         reset_fade_in_shape ();
225 }
226
227 void
228 AudioRegionView::fade_out_changed ()
229 {
230         reset_fade_out_shape ();
231 }
232
233 void
234 AudioRegionView::set_fade_in_active (bool yn)
235 {
236         audio_region().set_fade_in_active (yn);
237 }
238
239 void
240 AudioRegionView::set_fade_out_active (bool yn)
241 {
242         audio_region().set_fade_out_active (yn);
243 }
244
245 void
246 AudioRegionView::fade_in_active_changed ()
247 {
248         uint32_t r,g,b,a;
249         uint32_t col;
250         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
251
252         if (audio_region().fade_in_active()) {
253                 col = RGBA_TO_UINT(r,g,b,120);
254                 fade_in_shape->property_fill_color_rgba() = col;
255                 fade_in_shape->property_width_pixels() = 0;
256                 fade_in_shape->property_outline_color_rgba() = RGBA_TO_UINT(r,g,b,0);
257         } else { 
258                 col = RGBA_TO_UINT(r,g,b,0);
259                 fade_in_shape->property_fill_color_rgba() = col;
260                 fade_in_shape->property_width_pixels() = 1;
261                 fade_in_shape->property_outline_color_rgba() = RGBA_TO_UINT(r,g,b,255);
262         }
263 }
264
265 void
266 AudioRegionView::fade_out_active_changed ()
267 {
268         uint32_t r,g,b,a;
269         uint32_t col;
270         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
271
272         if (audio_region().fade_out_active()) {
273                 col = RGBA_TO_UINT(r,g,b,120);
274                 fade_out_shape->property_fill_color_rgba() = col;
275                 fade_out_shape->property_width_pixels() = 0;
276                 fade_out_shape->property_outline_color_rgba() = RGBA_TO_UINT(r,g,b,0);
277         } else { 
278                 col = RGBA_TO_UINT(r,g,b,0);
279                 fade_out_shape->property_fill_color_rgba() = col;
280                 fade_out_shape->property_width_pixels() = 1;
281                 fade_out_shape->property_outline_color_rgba() = RGBA_TO_UINT(r,g,b,255);
282         }
283 }
284
285
286 void
287 AudioRegionView::region_scale_amplitude_changed ()
288 {
289         ENSURE_GUI_THREAD (mem_fun(*this, &AudioRegionView::region_scale_amplitude_changed));
290
291         for (uint32_t n = 0; n < waves.size(); ++n) {
292                 // force a reload of the cache
293                 waves[n]->property_data_src() = &_region;
294         }
295 }
296
297 void
298 AudioRegionView::region_resized (Change what_changed)
299 {
300         RegionView::region_resized(what_changed);
301
302         if (what_changed & Change (StartChanged|LengthChanged)) {
303
304                 for (uint32_t n = 0; n < waves.size(); ++n) {
305                         waves[n]->property_region_start() = _region.start();
306                 }
307                 
308                 for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
309
310                         for (vector<WaveView*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
311                                 (*w)->property_region_start() = _region.start();
312                         }
313                 }
314         }
315 }
316
317 void
318 AudioRegionView::reset_width_dependent_items (double pixel_width)
319 {
320         RegionView::reset_width_dependent_items(pixel_width);
321         assert(_pixel_width == pixel_width);
322
323         if (zero_line) {
324                 zero_line->property_x2() = pixel_width - 1.0;
325         }
326
327         if (fade_in_handle) {
328                 if (pixel_width <= 6.0) {
329                         fade_in_handle->hide();
330                         fade_out_handle->hide();
331                 } else {
332                         if (_height < 5.0) {
333                                 fade_in_handle->hide();
334                                 fade_out_handle->hide();
335                         } else {
336                                 fade_in_handle->show();
337                                 fade_out_handle->show();
338                         }
339                 }
340         }
341
342         reset_fade_shapes ();
343 }
344
345 void
346 AudioRegionView::region_muted ()
347 {
348         RegionView::region_muted();
349
350         for (uint32_t n=0; n < waves.size(); ++n) {
351                 if (_region.muted()) {
352                         waves[n]->property_wave_color() = color_map[cMutedWaveForm];
353                 } else {
354                         waves[n]->property_wave_color() = color_map[cWaveForm];
355                 }
356         }
357 }
358
359
360 void
361 AudioRegionView::set_height (gdouble height)
362 {
363         uint32_t wcnt = waves.size();
364
365         // FIXME: ick
366         TimeAxisViewItem::set_height (height - 2);
367         
368         _height = height;
369
370         for (uint32_t n=0; n < wcnt; ++n) {
371                 gdouble ht;
372
373                 if ((height) <= NAME_HIGHLIGHT_THRESH) {
374                         ht = ((height-2*wcnt) / (double) wcnt);
375                 } else {
376                         ht = (((height-2*wcnt) - NAME_HIGHLIGHT_SIZE) / (double) wcnt);
377                 }
378                 
379                 gdouble yoff = n * (ht+1);
380                 
381                 waves[n]->property_height() = ht;
382                 waves[n]->property_y() = yoff + 2;
383         }
384
385         if (gain_line) {
386                 if ((height/wcnt) < NAME_HIGHLIGHT_SIZE) {
387                         gain_line->hide ();
388                 } else {
389                         if (_flags & EnvelopeVisible) {
390                                 gain_line->show ();
391                         }
392                 }
393                 gain_line->set_height ((uint32_t) rint (height - NAME_HIGHLIGHT_SIZE));
394         }
395
396         manage_zero_line ();
397         reset_fade_shapes ();
398         
399         if (name_text) {
400                 name_text->raise_to_top();
401         }
402 }
403
404 void
405 AudioRegionView::manage_zero_line ()
406 {
407         if (!zero_line) {
408                 return;
409         }
410
411         if (_height >= 100) {
412                 gdouble wave_midpoint = (_height - NAME_HIGHLIGHT_SIZE) / 2.0;
413                 zero_line->property_y1() = wave_midpoint;
414                 zero_line->property_y2() = wave_midpoint;
415                 zero_line->show();
416         } else {
417                 zero_line->hide();
418         }
419 }
420
421 void
422 AudioRegionView::reset_fade_shapes ()
423 {
424         reset_fade_in_shape ();
425         reset_fade_out_shape ();
426 }
427
428 void
429 AudioRegionView::reset_fade_in_shape ()
430 {
431         reset_fade_in_shape_width ((jack_nframes_t) audio_region().fade_in().back()->when);
432 }
433         
434 void
435 AudioRegionView::reset_fade_in_shape_width (jack_nframes_t width)
436 {
437         if (fade_in_handle == 0) {
438                 return;
439         }
440
441         /* smallest size for a fade is 64 frames */
442
443         width = std::max ((jack_nframes_t) 64, width);
444
445         Points* points;
446         double pwidth = width / samples_per_unit;
447         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
448         double h; 
449         
450         if (_height < 5) {
451                 fade_in_shape->hide();
452                 fade_in_handle->hide();
453                 return;
454         }
455
456         double handle_center;
457         handle_center = pwidth;
458         
459         if (handle_center > 7.0) {
460                 handle_center -= 3.0;
461         } else {
462                 handle_center = 3.0;
463         }
464         
465         fade_in_handle->property_x1() =  handle_center - 3.0;
466         fade_in_handle->property_x2() =  handle_center + 3.0;
467         
468         if (pwidth < 5) {
469                 fade_in_shape->hide();
470                 return;
471         }
472
473         fade_in_shape->show();
474
475         float curve[npoints];
476         audio_region().fade_in().get_vector (0, audio_region().fade_in().back()->when, curve, npoints);
477
478         points = get_canvas_points ("fade in shape", npoints+3);
479
480         if (_height > NAME_HIGHLIGHT_THRESH) {
481                 h = _height - NAME_HIGHLIGHT_SIZE;
482         } else {
483                 h = _height;
484         }
485
486         /* points *MUST* be in anti-clockwise order */
487
488         uint32_t pi, pc;
489         double xdelta = pwidth/npoints;
490
491         for (pi = 0, pc = 0; pc < npoints; ++pc) {
492                 (*points)[pi].set_x(1 + (pc * xdelta));
493                 (*points)[pi++].set_y(2 + (h - (curve[pc] * h)));
494         }
495         
496         /* fold back */
497
498         (*points)[pi].set_x(pwidth);
499         (*points)[pi++].set_y(2);
500
501         (*points)[pi].set_x(1);
502         (*points)[pi++].set_y(2);
503
504         /* connect the dots ... */
505
506         (*points)[pi] = (*points)[0];
507         
508         fade_in_shape->property_points() = *points;
509         delete points;
510 }
511
512 void
513 AudioRegionView::reset_fade_out_shape ()
514 {
515         reset_fade_out_shape_width ((jack_nframes_t) audio_region().fade_out().back()->when);
516 }
517
518 void
519 AudioRegionView::reset_fade_out_shape_width (jack_nframes_t width)
520 {       
521         if (fade_out_handle == 0) {
522                 return;
523         }
524
525         /* smallest size for a fade is 64 frames */
526
527         width = std::max ((jack_nframes_t) 64, width);
528
529         Points* points;
530         double pwidth = width / samples_per_unit;
531         uint32_t npoints = std::min (gdk_screen_width(), (int) pwidth);
532         double h;
533
534         if (_height < 5) {
535                 fade_out_shape->hide();
536                 fade_out_handle->hide();
537                 return;
538         }
539
540         double handle_center;
541         handle_center = (_region.length() - width) / samples_per_unit;
542         
543         if (handle_center > 7.0) {
544                 handle_center -= 3.0;
545         } else {
546                 handle_center = 3.0;
547         }
548         
549         fade_out_handle->property_x1() =  handle_center - 3.0;
550         fade_out_handle->property_x2() =  handle_center + 3.0;
551
552         /* don't show shape if its too small */
553         
554         if (pwidth < 5) {
555                 fade_out_shape->hide();
556                 return;
557         } 
558         
559         fade_out_shape->show();
560
561         float curve[npoints];
562         audio_region().fade_out().get_vector (0, audio_region().fade_out().back()->when, curve, npoints);
563
564         if (_height > NAME_HIGHLIGHT_THRESH) {
565                 h = _height - NAME_HIGHLIGHT_SIZE;
566         } else {
567                 h = _height;
568         }
569
570         /* points *MUST* be in anti-clockwise order */
571
572         points = get_canvas_points ("fade out shape", npoints+3);
573
574         uint32_t pi, pc;
575         double xdelta = pwidth/npoints;
576
577         for (pi = 0, pc = 0; pc < npoints; ++pc) {
578                 (*points)[pi].set_x(_pixel_width - 1 - pwidth + (pc*xdelta));
579                 (*points)[pi++].set_y(2 + (h - (curve[pc] * h)));
580         }
581         
582         /* fold back */
583
584         (*points)[pi].set_x(_pixel_width);
585         (*points)[pi++].set_y(h);
586
587         (*points)[pi].set_x(_pixel_width);
588         (*points)[pi++].set_y(2);
589
590         /* connect the dots ... */
591
592         (*points)[pi] = (*points)[0];
593
594         fade_out_shape->property_points() = *points;
595         delete points;
596 }
597
598 void
599 AudioRegionView::set_samples_per_unit (gdouble spu)
600 {
601         RegionView::set_samples_per_unit (spu);
602
603         for (uint32_t n=0; n < waves.size(); ++n) {
604                 waves[n]->property_samples_per_unit() = spu;
605         }
606
607         if (gain_line) {
608                 gain_line->reset ();
609         }
610         reset_fade_shapes ();
611 }
612
613 void
614 AudioRegionView::set_amplitude_above_axis (gdouble spp)
615 {
616         for (uint32_t n=0; n < waves.size(); ++n) {
617                 waves[n]->property_amplitude_above_axis() = spp;
618         }
619 }
620
621 void
622 AudioRegionView::compute_colors (Gdk::Color& basic_color)
623 {
624         RegionView::compute_colors(basic_color);
625         
626         uint32_t r, g, b, a;
627
628         /* gain color computed in envelope_active_changed() */
629
630         UINT_TO_RGBA (fill_color, &r, &g, &b, &a);
631         fade_color = RGBA_TO_UINT(r,g,b,120);
632 }
633
634 void
635 AudioRegionView::set_colors ()
636 {
637         RegionView::set_colors();
638         
639         if (gain_line) {
640                 gain_line->set_line_color (audio_region().envelope_active() ? color_map[cGainLine] : color_map[cGainLineInactive]);
641         }
642
643         for (uint32_t n=0; n < waves.size(); ++n) {
644                 if (_region.muted()) {
645                         waves[n]->property_wave_color() = color_map[cMutedWaveForm];
646                 } else {
647                         waves[n]->property_wave_color() = color_map[cWaveForm];
648                 }
649         }
650 }
651
652 void
653 AudioRegionView::show_region_editor ()
654 {
655         if (editor == 0) {
656                 editor = new AudioRegionEditor (trackview.session(), audio_region(), *this);
657                 // GTK2FIX : how to ensure float without realizing
658                 // editor->realize ();
659                 // trackview.editor.ensure_float (*editor);
660         } 
661
662         editor->show_all ();
663         editor->get_window()->raise();
664 }
665
666 void
667 AudioRegionView::set_waveform_visible (bool yn)
668 {
669         if (((_flags & WaveformVisible) != yn)) {
670                 if (yn) {
671                         for (uint32_t n=0; n < waves.size(); ++n) {
672                                 waves[n]->show();
673                         }
674                         _flags |= WaveformVisible;
675                 } else {
676                         for (uint32_t n=0; n < waves.size(); ++n) {
677                                 waves[n]->hide();
678                         }
679                         _flags &= ~WaveformVisible;
680                 }
681                 store_flags ();
682         }
683 }
684
685 void
686 AudioRegionView::temporarily_hide_envelope ()
687 {
688         if (gain_line) {
689                 gain_line->hide ();
690         }
691 }
692
693 void
694 AudioRegionView::unhide_envelope ()
695 {
696         if (gain_line && (_flags & EnvelopeVisible)) {
697                 gain_line->show ();
698         }
699 }
700
701 void
702 AudioRegionView::set_envelope_visible (bool yn)
703 {
704         if (gain_line && ((_flags & EnvelopeVisible) != yn)) {
705                 if (yn) {
706                         gain_line->show ();
707                         _flags |= EnvelopeVisible;
708                 } else {
709                         gain_line->hide ();
710                         _flags &= ~EnvelopeVisible;
711                 }
712                 store_flags ();
713         }
714 }
715
716 void
717 AudioRegionView::create_waves ()
718 {
719         bool create_zero_line = true;
720
721         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
722
723         if (!atv.get_diskstream()) {
724                 return;
725         }
726
727         uint32_t nchans = atv.get_diskstream()->n_channels();
728         
729         /* in tmp_waves, set up null pointers for each channel so the vector is allocated */
730         for (uint32_t n = 0; n < nchans; ++n) {
731                 tmp_waves.push_back (0);
732         }
733         
734         for (uint32_t n = 0; n < nchans; ++n) {
735                 
736                 if (n >= audio_region().n_channels()) {
737                         break;
738                 }
739                 
740                 wave_caches.push_back (WaveView::create_cache ());
741
742                 if (wait_for_data) {
743                         if (audio_region().source(n).peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n), data_ready_connection)) {
744                                 create_one_wave (n, true);
745                         } else {
746                                 create_zero_line = false;
747                         }
748                 } else {
749                         create_one_wave (n, true);
750                 }
751         }
752
753         if (create_zero_line) {
754                 zero_line = new ArdourCanvas::SimpleLine (*group);
755                 zero_line->property_x1() = (gdouble) 1.0;
756                 zero_line->property_x2() = (gdouble) (_region.length() / samples_per_unit) - 1.0;
757                 zero_line->property_color_rgba() = (guint) color_map[cZeroLine];
758                 manage_zero_line ();
759         }
760 }
761
762 void
763 AudioRegionView::create_one_wave (uint32_t which, bool direct)
764 {
765         RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
766         uint32_t nchans = atv.get_diskstream()->n_channels();
767         uint32_t n;
768         uint32_t nwaves = std::min (nchans, audio_region().n_channels());
769         gdouble ht;
770
771         if (trackview.height < NAME_HIGHLIGHT_SIZE) {
772                 ht = ((trackview.height) / (double) nchans);
773         } else {
774                 ht = ((trackview.height - NAME_HIGHLIGHT_SIZE) / (double) nchans);
775         }
776
777         gdouble yoff = which * ht;
778
779         WaveView *wave = new WaveView(*group);
780
781         wave->property_data_src() = (gpointer) &_region;
782         wave->property_cache() =  wave_caches[which];
783         wave->property_cache_updater() = true;
784         wave->property_channel() =  which;
785         wave->property_length_function() = (gpointer) region_length_from_c;
786         wave->property_sourcefile_length_function() = (gpointer) sourcefile_length_from_c;
787         wave->property_peak_function() =  (gpointer) region_read_peaks_from_c;
788         wave->property_x() =  0.0;
789         wave->property_y() =  yoff;
790         wave->property_height() =  (double) ht;
791         wave->property_samples_per_unit() =  samples_per_unit;
792         wave->property_amplitude_above_axis() =  _amplitude_above_axis;
793         wave->property_wave_color() = _region.muted() ? color_map[cMutedWaveForm] : color_map[cWaveForm];
794         wave->property_region_start() = _region.start();
795
796         if (!(_flags & WaveformVisible)) {
797                 wave->hide();
798         }
799
800         /* note: calling this function is serialized by the lock
801            held in the peak building thread that signals that
802            peaks are ready for use *or* by the fact that it is
803            called one by one from the GUI thread.
804         */
805
806         if (which < nchans) {
807                 tmp_waves[which] = wave;
808         } else {
809                 /* n-channel track, >n-channel source */
810         }
811         
812         /* see if we're all ready */
813         
814         for (n = 0; n < nchans; ++n) {
815                 if (tmp_waves[n] == 0) {
816                         break;
817                 }
818         }
819         
820         if (n == nwaves && waves.empty()) {
821                 /* all waves are ready */
822                 tmp_waves.resize(nwaves);
823
824                 waves = tmp_waves;
825                 tmp_waves.clear ();
826
827                 if (!zero_line) {
828                         zero_line = new ArdourCanvas::SimpleLine (*group);
829                         zero_line->property_x1() = (gdouble) 1.0;
830                         zero_line->property_x2() = (gdouble) (_region.length() / samples_per_unit) - 1.0;
831                         zero_line->property_color_rgba() = (guint) color_map[cZeroLine];
832                         manage_zero_line ();
833                 }
834         }
835 }
836
837 void
838 AudioRegionView::peaks_ready_handler (uint32_t which)
839 {
840         Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &AudioRegionView::create_one_wave), which, false));
841
842         if (!waves.empty()) {
843                 /* all waves created, don't hook into peaks ready anymore */
844                 data_ready_connection.disconnect ();            
845         }
846 }
847
848 void
849 AudioRegionView::add_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
850 {
851         if (gain_line == 0) {
852                 return;
853         }
854
855         double x, y;
856
857         /* don't create points that can't be seen */
858
859         set_envelope_visible (true);
860         
861         x = ev->button.x;
862         y = ev->button.y;
863
864         item->w2i (x, y);
865
866         jack_nframes_t fx = trackview.editor.pixel_to_frame (x);
867
868         if (fx > _region.length()) {
869                 return;
870         }
871
872         /* compute vertical fractional position */
873
874         y = 1.0 - (y / (trackview.height - NAME_HIGHLIGHT_SIZE));
875         
876         /* map using gain line */
877
878         gain_line->view_to_model_y (y);
879
880         trackview.session().begin_reversible_command (_("add gain control point"));
881         trackview.session().add_undo (audio_region().envelope().get_memento());
882
883
884         if (!audio_region().envelope_active()) {
885                 trackview.session().add_undo( bind( mem_fun(audio_region(), &AudioRegion::set_envelope_active), false) );
886                 audio_region().set_envelope_active(true);
887                 trackview.session().add_redo( bind( mem_fun(audio_region(), &AudioRegion::set_envelope_active), true) );
888         }
889
890         audio_region().envelope().add (fx, y);
891         
892         trackview.session().add_redo_no_execute (audio_region().envelope().get_memento());
893         trackview.session().commit_reversible_command ();
894 }
895
896 void
897 AudioRegionView::remove_gain_point_event (ArdourCanvas::Item *item, GdkEvent *ev)
898 {
899         ControlPoint *cp = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
900         audio_region().envelope().erase (cp->model);
901 }
902
903 void
904 AudioRegionView::store_flags()
905 {
906         XMLNode *node = new XMLNode ("GUI");
907
908         node->add_property ("waveform-visible", (_flags & WaveformVisible) ? "yes" : "no");
909         node->add_property ("envelope-visible", (_flags & EnvelopeVisible) ? "yes" : "no");
910
911         _region.add_extra_xml (*node);
912 }
913
914 void
915 AudioRegionView::set_flags (XMLNode* node)
916 {
917         XMLProperty *prop;
918
919         if ((prop = node->property ("waveform-visible")) != 0) {
920                 if (prop->value() == "yes") {
921                         _flags |= WaveformVisible;
922                 }
923         }
924
925         if ((prop = node->property ("envelope-visible")) != 0) {
926                 if (prop->value() == "yes") {
927                         _flags |= EnvelopeVisible;
928                 }
929         }
930 }
931         
932 void
933 AudioRegionView::set_waveform_shape (WaveformShape shape)
934 {
935         bool yn;
936
937         /* this slightly odd approach is to leave the door open to 
938            other "shapes" such as spectral displays, etc.
939         */
940
941         switch (shape) {
942         case Rectified:
943                 yn = true;
944                 break;
945
946         default:
947                 yn = false;
948                 break;
949         }
950
951         if (yn != (bool) (_flags & WaveformRectified)) {
952                 for (vector<WaveView *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
953                         (*wave)->property_rectified() = yn;
954                 }
955
956                 if (zero_line) {
957                         if (yn) {
958                                 zero_line->hide();
959                         } else {
960                                 zero_line->show();
961                         }
962                 }
963
964                 if (yn) {
965                         _flags |= WaveformRectified;
966                 } else {
967                         _flags &= ~WaveformRectified;
968                 }
969         }
970 }
971
972 GhostRegion*
973 AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
974 {
975         RouteTimeAxisView& myatv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
976         double unit_position = _region.position () / samples_per_unit;
977         GhostRegion* ghost = new GhostRegion (atv, unit_position);
978         uint32_t nchans;
979         
980         nchans = myatv.get_diskstream()->n_channels();
981
982         for (uint32_t n = 0; n < nchans; ++n) {
983                 
984                 if (n >= audio_region().n_channels()) {
985                         break;
986                 }
987                 
988                 WaveView *wave = new WaveView(*ghost->group);
989
990                 wave->property_data_src() =  &_region;
991                 wave->property_cache() =  wave_caches[n];
992                 wave->property_cache_updater() = false;
993                 wave->property_channel() = n;
994                 wave->property_length_function() = (gpointer)region_length_from_c;
995                 wave->property_sourcefile_length_function() = (gpointer) sourcefile_length_from_c;
996                 wave->property_peak_function() =  (gpointer) region_read_peaks_from_c;
997                 wave->property_x() =  0.0;
998                 wave->property_samples_per_unit() =  samples_per_unit;
999                 wave->property_amplitude_above_axis() =  _amplitude_above_axis;
1000                 wave->property_wave_color() = color_map[cGhostTrackWave];
1001                 wave->property_region_start() = _region.start();
1002
1003                 ghost->waves.push_back(wave);
1004         }
1005
1006         ghost->set_height ();
1007         ghost->set_duration (_region.length() / samples_per_unit);
1008         ghosts.push_back (ghost);
1009
1010         ghost->GoingAway.connect (mem_fun(*this, &AudioRegionView::remove_ghost));
1011
1012         return ghost;
1013 }
1014
1015 void
1016 AudioRegionView::entered ()
1017 {
1018         if (gain_line && _flags & EnvelopeVisible) {
1019                 gain_line->show_all_control_points ();
1020         }
1021
1022         uint32_t r,g,b,a;
1023         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
1024         a=255;
1025         
1026         if (fade_in_handle) {
1027                 fade_in_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,a);
1028                 fade_out_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,a);
1029         }
1030 }
1031
1032 void
1033 AudioRegionView::exited ()
1034 {
1035         if (gain_line) {
1036                 gain_line->hide_all_but_selected_control_points ();
1037         }
1038         
1039         uint32_t r,g,b,a;
1040         UINT_TO_RGBA(fade_color,&r,&g,&b,&a);
1041         a=0;
1042         
1043         if (fade_in_handle) {
1044                 fade_in_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,a);
1045                 fade_out_handle->property_fill_color_rgba() = RGBA_TO_UINT(r,g,b,a);
1046         }
1047 }
1048
1049 void
1050 AudioRegionView::envelope_active_changed ()
1051 {
1052         if (gain_line) {
1053                 gain_line->set_line_color (audio_region().envelope_active() ? color_map[cGainLine] : color_map[cGainLineInactive]);
1054         }
1055 }
1056
1057 void
1058 AudioRegionView::set_waveview_data_src()
1059 {
1060
1061         double unit_length= _region.length() / samples_per_unit;
1062
1063         for (uint32_t n = 0; n < waves.size(); ++n) {
1064                 // TODO: something else to let it know the channel
1065                 waves[n]->property_data_src() = &_region;
1066         }
1067         
1068         for (vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1069                 
1070                 (*i)->set_duration (unit_length);
1071                 
1072                 for (vector<WaveView*>::iterator w = (*i)->waves.begin(); w != (*i)->waves.end(); ++w) {
1073                         (*w)->property_data_src() = &_region;
1074                 }
1075         }
1076
1077 }
1078
1079 void
1080 AudioRegionView::color_handler (ColorID id, uint32_t val)
1081 {
1082         switch (id) {
1083         case cMutedWaveForm:
1084         case cWaveForm:
1085                 set_colors ();
1086                 break;
1087
1088         case cGainLineInactive:
1089         case cGainLine:
1090                 envelope_active_changed();
1091                 break;
1092                 
1093         case cZeroLine:
1094                 if (zero_line) {
1095                         zero_line->property_color_rgba() = (guint) color_map[cZeroLine];
1096                 }
1097                 break;
1098
1099         case cGhostTrackWave:
1100                 break;
1101
1102         default:
1103                 break;
1104         }
1105 }