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