Split pretty much the entire GUI in 3. Audio and Midi "editor strips" and
[ardour.git] / gtk2_ardour / audio_streamview.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
19 #include <cmath>
20 #include <cassert>
21
22 #include <gtkmm.h>
23
24 #include <gtkmm2ext/gtk_ui.h>
25
26 #include <ardour/audioplaylist.h>
27 #include <ardour/audioregion.h>
28 #include <ardour/audiosource.h>
29 #include <ardour/audio_diskstream.h>
30 #include <ardour/audio_track.h>
31 #include <ardour/playlist_templates.h>
32 #include <ardour/source.h>
33
34 #include "audio_streamview.h"
35 #include "audio_regionview.h"
36 #include "taperegionview.h"
37 #include "audio_time_axis.h"
38 #include "canvas-waveview.h"
39 #include "canvas-simplerect.h"
40 #include "region_selection.h"
41 #include "selection.h"
42 #include "public_editor.h"
43 #include "ardour_ui.h"
44 #include "crossfade_view.h"
45 #include "rgb_macros.h"
46 #include "gui_thread.h"
47 #include "utils.h"
48 #include "color.h"
49
50 using namespace ARDOUR;
51 using namespace PBD;
52 using namespace Editing;
53
54 AudioStreamView::AudioStreamView (AudioTimeAxisView& tv)
55         : StreamView (tv)
56 {
57         region_color = _trackview.color();
58         crossfades_visible = true;
59
60         if (tv.is_audio_track())
61                 stream_base_color = color_map[cAudioTrackBase];
62         else
63                 stream_base_color = color_map[cAudioBusBase];
64
65         /* set_position() will position the group */
66
67         canvas_group = new ArdourCanvas::Group(*_trackview.canvas_display);
68
69         canvas_rect = new ArdourCanvas::SimpleRect (*canvas_group);
70         canvas_rect->property_x1() = 0.0;
71         canvas_rect->property_y1() = 0.0;
72         canvas_rect->property_x2() = 1000000.0;
73         canvas_rect->property_y2() = (double) tv.height;
74         canvas_rect->property_outline_color_rgba() = color_map[cAudioTrackOutline];
75         canvas_rect->property_outline_what() = (guint32) (0x1|0x2|0x8);  // outline ends and bottom 
76         canvas_rect->property_fill_color_rgba() = stream_base_color;
77
78         canvas_rect->signal_event().connect (bind (mem_fun (_trackview.editor, &PublicEditor::canvas_stream_view_event), canvas_rect, &_trackview));
79
80         _samples_per_unit = _trackview.editor.get_current_zoom();
81         _amplitude_above_axis = 1.0;
82
83         if (_trackview.is_audio_track()) {
84                 _trackview.audio_track()->DiskstreamChanged.connect (mem_fun (*this, &AudioStreamView::diskstream_changed));
85                 _trackview.session().TransportStateChange.connect (mem_fun (*this, &AudioStreamView::transport_changed));
86                 _trackview.get_diskstream()->RecordEnableChanged.connect (mem_fun (*this, &AudioStreamView::rec_enable_changed));
87                 _trackview.session().RecordStateChanged.connect (mem_fun (*this, &AudioStreamView::sess_rec_enable_changed));
88         } 
89
90         rec_updating = false;
91         rec_active = false;
92         use_rec_regions = tv.editor.show_waveforms_recording ();
93         last_rec_peak_frame = 0;
94
95         ColorChanged.connect (mem_fun (*this, &AudioStreamView::color_handler));
96 }
97
98 AudioStreamView::~AudioStreamView ()
99 {
100         undisplay_diskstream ();
101         delete canvas_group;
102 }
103
104 int
105 AudioStreamView::set_height (gdouble h)
106 {
107         /* limit the values to something sane-ish */
108         if (h < 10.0 || h > 1000.0) {
109                 return -1;
110         }
111
112         StreamView::set_height(h);
113
114         for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
115                 (*i)->set_height (h);
116         }
117
118         return 0;
119 }
120
121 int 
122 AudioStreamView::set_samples_per_unit (gdouble spp)
123 {
124         StreamView::set_samples_per_unit(spp);
125
126         for (CrossfadeViewList::iterator xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
127                 (*xi)->set_samples_per_unit (spp);
128         }
129
130         return 0;
131 }
132
133 int 
134 AudioStreamView::set_amplitude_above_axis (gdouble app)
135
136 {
137         RegionViewList::iterator i;
138
139         if (app < 1.0) {
140                 return -1;
141         }
142
143         _amplitude_above_axis = app;
144
145         for (i = region_views.begin(); i != region_views.end(); ++i) {
146                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
147                 if (arv)
148                         arv->set_amplitude_above_axis (app);
149         }
150
151         return 0;
152 }
153
154 void
155 AudioStreamView::add_region_view_internal (Region *r, bool wait_for_waves)
156 {
157         ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_region_view), r));
158
159         AudioRegion* region = dynamic_cast<AudioRegion*> (r);
160
161         if (region == 0) {
162                 return;
163         }
164
165         AudioRegionView *region_view;
166         list<RegionView *>::iterator i;
167
168         for (i = region_views.begin(); i != region_views.end(); ++i) {
169                 if (&(*i)->region() == r) {
170                         
171                         /* great. we already have a AudioRegionView for this Region. use it again. */
172
173                         (*i)->set_valid (true);
174                         return;
175                 }
176         }
177         
178         switch (_trackview.audio_track()->mode()) {
179         case Normal:
180                 region_view = new AudioRegionView (canvas_group, _trackview, *region, 
181                                                    _samples_per_unit, region_color);
182                 break;
183         case Destructive:
184                 region_view = new TapeAudioRegionView (canvas_group, _trackview, *region, 
185                                                        _samples_per_unit, region_color);
186                 break;
187         }
188
189         region_view->init (region_color, wait_for_waves);
190         region_view->set_amplitude_above_axis(_amplitude_above_axis);
191         region_views.push_front (region_view);
192         
193         /* follow global waveform setting */
194
195         region_view->set_waveform_visible(_trackview.editor.show_waveforms());
196
197         /* catch regionview going away */
198
199         region->GoingAway.connect (mem_fun (*this, &AudioStreamView::remove_region_view));
200         
201         RegionViewAdded (region_view);
202 }
203
204 void
205 AudioStreamView::remove_region_view (Region *r)
206 {
207         ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::remove_region_view), r));
208
209         StreamView::remove_region_view(r);
210         
211         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end();) {
212                 list<CrossfadeView*>::iterator tmp;
213                 
214                 tmp = i;
215                 ++tmp;
216                 
217                 AudioRegion* ar = dynamic_cast<AudioRegion*>(r);
218                 if (ar && (*i)->crossfade.involves (*ar)) {
219                         delete *i;
220                         crossfade_views.erase (i);
221                 }
222                 
223                 i = tmp;
224         }
225 }
226
227 void
228 AudioStreamView::undisplay_diskstream ()
229 {
230         StreamView::undisplay_diskstream();
231
232         for (CrossfadeViewList::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
233                 delete *i;
234         }
235
236         crossfade_views.clear ();
237 }
238
239 void
240 AudioStreamView::playlist_modified ()
241 {
242         ENSURE_GUI_THREAD (mem_fun (*this, &AudioStreamView::playlist_modified));
243
244         StreamView::playlist_modified();
245         
246         /* if the playlist is modified, make sure xfades are on top and all the regionviews are stacked 
247            correctly.
248         */
249
250         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
251                 (*i)->get_canvas_group()->raise_to_top();
252         }
253 }
254
255 void
256 AudioStreamView::playlist_changed (Diskstream *ds)
257 {
258         ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::playlist_changed), ds));
259
260         StreamView::playlist_changed(ds);
261
262         AudioPlaylist* apl = dynamic_cast<AudioPlaylist*>(ds->playlist());
263         if (apl)
264                 playlist_connections.push_back (apl->NewCrossfade.connect (mem_fun (*this, &AudioStreamView::add_crossfade)));
265 }
266
267 void
268 AudioStreamView::add_crossfade (Crossfade *crossfade)
269 {
270         AudioRegionView* lview = 0;
271         AudioRegionView* rview = 0;
272
273         ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_crossfade), crossfade));
274
275         /* first see if we already have a CrossfadeView for this Crossfade */
276
277         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
278                 if (&(*i)->crossfade == crossfade) {
279                         if (!crossfades_visible) {
280                                 (*i)->hide();
281                         } else {
282                                 (*i)->show ();
283                         }
284                         (*i)->set_valid (true);
285                         return;
286                 }
287         }
288
289         /* create a new one */
290
291         for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
292                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
293
294                 if (!lview && arv && &(arv->region()) == &crossfade->out()) {
295                         lview = arv;
296                 }
297                 if (!rview && arv && &(arv->region()) == &crossfade->in()) {
298                         rview = arv;
299                 }
300         }
301
302         CrossfadeView *cv = new CrossfadeView (_trackview.canvas_display,
303                                                _trackview,
304                                                *crossfade,
305                                                _samples_per_unit,
306                                                region_color,
307                                                *lview, *rview);
308
309         crossfade->Invalidated.connect (mem_fun (*this, &AudioStreamView::remove_crossfade));
310         crossfade_views.push_back (cv);
311
312         if (!crossfades_visible) {
313                 cv->hide ();
314         }
315 }
316
317 void
318 AudioStreamView::remove_crossfade (Crossfade *xfade)
319 {
320         ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::remove_crossfade), xfade));
321
322         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
323                 if (&(*i)->crossfade == xfade) {
324                         delete *i;
325                         crossfade_views.erase (i);
326                         break;
327                 }
328         }
329 }
330
331 void
332 AudioStreamView::redisplay_diskstream ()
333 {
334         list<RegionView *>::iterator i, tmp;
335         list<CrossfadeView*>::iterator xi, tmpx;
336
337         
338         for (i = region_views.begin(); i != region_views.end(); ++i) {
339                 (*i)->set_valid (false);
340         }
341
342         for (xi = crossfade_views.begin(); xi != crossfade_views.end(); ++xi) {
343                 (*xi)->set_valid (false);
344                 if ((*xi)->visible()) {
345                         (*xi)->show ();
346                 }
347         }
348
349         if (_trackview.is_audio_track()) {
350                 _trackview.get_diskstream()->playlist()->foreach_region (static_cast<StreamView*>(this), &StreamView::add_region_view);
351                 AudioPlaylist* apl = dynamic_cast<AudioPlaylist*>(_trackview.get_diskstream()->playlist());
352                 if (apl)
353                         apl->foreach_crossfade (this, &AudioStreamView::add_crossfade);
354         }
355
356         for (i = region_views.begin(); i != region_views.end(); ) {
357                 tmp = i;
358                 tmp++;
359
360                 if (!(*i)->is_valid()) {
361                         delete *i;
362                         region_views.erase (i);
363                 } 
364
365                 i = tmp;
366         }
367
368         for (xi = crossfade_views.begin(); xi != crossfade_views.end();) {
369                 tmpx = xi;
370                 tmpx++;
371
372                 if (!(*xi)->valid()) {
373                         delete *xi;
374                         crossfade_views.erase (xi);
375                 }
376
377                 xi = tmpx;
378         }
379
380         /* now fix layering */
381
382         playlist_modified ();
383 }
384
385 void
386 AudioStreamView::set_show_waveforms (bool yn)
387 {
388         for (list<RegionView *>::iterator i = region_views.begin(); i != region_views.end(); ++i) {
389                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
390                 if (arv)
391                         arv->set_waveform_visible (yn);
392         }
393 }
394
395 void
396 AudioStreamView::set_waveform_shape (WaveformShape shape)
397 {
398         for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
399                 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
400                 if (arv)
401                         arv->set_waveform_shape (shape);
402         }
403 }               
404                 
405 void
406 AudioStreamView::setup_rec_box ()
407 {
408         // cerr << _trackview.name() << " streamview SRB\n";
409
410         if (_trackview.session().transport_rolling()) {
411
412                 // cerr << "\trolling\n";
413
414                 if (!rec_active && 
415                     _trackview.session().record_status() == Session::Recording && 
416                     _trackview.get_diskstream()->record_enabled()) {
417
418                         if (_trackview.audio_track()->mode() == Normal && use_rec_regions && rec_regions.size() == rec_rects.size()) {
419
420                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
421
422                                 AudioRegion::SourceList sources;
423
424                                 for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
425                                         (*prc).disconnect();
426                                 }
427                                 peak_ready_connections.clear();
428                                         
429                                 // FIXME
430                                 AudioDiskstream* ads = dynamic_cast<AudioDiskstream*>(_trackview.get_diskstream());
431                                 assert(ads);
432
433                                 for (uint32_t n=0; n < ads->n_channels(); ++n) {
434                                         AudioSource *src = (AudioSource *) ads->write_source (n);
435                                         if (src) {
436                                                 sources.push_back (src);
437                                                 peak_ready_connections.push_back (src->PeakRangeReady.connect (bind (mem_fun (*this, &AudioStreamView::rec_peak_range_ready), src))); 
438                                         }
439                                 }
440
441                                 // handle multi
442                                 
443                                 jack_nframes_t start = 0;
444                                 if (rec_regions.size() > 0) {
445                                         start = rec_regions.back()->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
446                                 }
447                                 
448                                 AudioRegion * region = new AudioRegion(sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false);
449                                 region->set_position (_trackview.session().transport_frame(), this);
450                                 rec_regions.push_back (region);
451                                 /* catch it if it goes away */
452                                 region->GoingAway.connect (mem_fun (*this, &AudioStreamView::remove_rec_region));
453
454                                 /* we add the region later */
455                         }
456                         
457                         /* start a new rec box */
458
459                         AudioTrack* at;
460
461                         at = _trackview.audio_track(); /* we know what it is already */
462                         AudioDiskstream& ds = at->audio_diskstream();
463                         jack_nframes_t frame_pos = ds.current_capture_start ();
464                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
465                         gdouble xend;
466                         uint32_t fill_color;
467
468                         switch (_trackview.audio_track()->mode()) {
469                         case Normal:
470                                 xend = xstart;
471                                 fill_color = color_map[cRecordingRectFill];
472                                 break;
473
474                         case Destructive:
475                                 xend = xstart + 2;
476                                 fill_color = color_map[cRecordingRectFill];
477                                 /* make the recording rect translucent to allow
478                                    the user to see the peak data coming in, etc.
479                                 */
480                                 fill_color = UINT_RGBA_CHANGE_A (fill_color, 120);
481                                 break;
482                         }
483                         
484                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
485                         rec_rect->property_x1() = xstart;
486                         rec_rect->property_y1() = 1.0;
487                         rec_rect->property_x2() = xend;
488                         rec_rect->property_y2() = (double) _trackview.height - 1;
489                         rec_rect->property_outline_color_rgba() = color_map[cRecordingRectOutline];
490                         rec_rect->property_fill_color_rgba() = fill_color;
491                         
492                         RecBoxInfo recbox;
493                         recbox.rectangle = rec_rect;
494                         recbox.start = _trackview.session().transport_frame();
495                         recbox.length = 0;
496                         
497                         rec_rects.push_back (recbox);
498                         
499                         screen_update_connection.disconnect();
500                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &AudioStreamView::update_rec_box));   
501                         rec_updating = true;
502                         rec_active = true;
503
504                 } else if (rec_active &&
505                            (_trackview.session().record_status() != Session::Recording ||
506                             !_trackview.get_diskstream()->record_enabled())) {
507
508                         screen_update_connection.disconnect();
509                         rec_active = false;
510                         rec_updating = false;
511
512                 }
513                 
514         } else {
515
516                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
517
518                 if (!rec_rects.empty() || !rec_regions.empty()) {
519
520                         /* disconnect rapid update */
521                         screen_update_connection.disconnect();
522
523                         for (list<sigc::connection>::iterator prc = peak_ready_connections.begin(); prc != peak_ready_connections.end(); ++prc) {
524                                 (*prc).disconnect();
525                         }
526                         peak_ready_connections.clear();
527
528                         rec_updating = false;
529                         rec_active = false;
530                         last_rec_peak_frame = 0;
531                         
532                         /* remove temp regions */
533                         for (list<Region*>::iterator iter=rec_regions.begin(); iter != rec_regions.end(); )
534                         {
535                                 list<Region*>::iterator tmp;
536
537                                 tmp = iter;
538                                 ++tmp;
539
540                                 /* this will trigger the remove_region_view */
541                                 delete *iter;
542
543                                 iter = tmp;
544                         }
545                         
546                         rec_regions.clear();
547
548                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
549                 
550
551                         /* transport stopped, clear boxes */
552                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
553                                 RecBoxInfo &rect = (*iter);
554                                 delete rect.rectangle;
555                         }
556                         
557                         rec_rects.clear();
558                         
559                 }
560         }
561 }
562
563 void
564 AudioStreamView::foreach_crossfadeview (void (CrossfadeView::*pmf)(void))
565 {
566         for (list<CrossfadeView*>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
567                 ((*i)->*pmf) ();
568         }
569 }
570
571 void
572 AudioStreamView::rec_peak_range_ready (jack_nframes_t start, jack_nframes_t cnt, Source * src)
573 {
574         // this is called from the peak building thread
575
576         ENSURE_GUI_THREAD(bind (mem_fun (*this, &AudioStreamView::rec_peak_range_ready), start, cnt, src));
577         
578         if (rec_peak_ready_map.size() == 0 || start+cnt > last_rec_peak_frame) {
579                 last_rec_peak_frame = start + cnt;
580         }
581
582         rec_peak_ready_map[src] = true;
583
584         if (rec_peak_ready_map.size() == _trackview.get_diskstream()->n_channels()) {
585                 this->update_rec_regions ();
586                 rec_peak_ready_map.clear();
587         }
588 }
589
590 void
591 AudioStreamView::update_rec_regions ()
592 {
593         if (use_rec_regions) {
594
595                 uint32_t n = 0;
596
597                 for (list<Region*>::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
598
599                         list<Region*>::iterator tmp;
600
601                         tmp = iter;
602                         ++tmp;
603
604                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
605                                 /* rect already hidden, this region is done */
606                                 iter = tmp;
607                                 continue;
608                         }
609                         
610                         // FIXME
611                         AudioRegion * region = dynamic_cast<AudioRegion*>(*iter);
612                         assert(region);
613
614                         jack_nframes_t origlen = region->length();
615
616                         if (region == rec_regions.back() && rec_active) {
617
618                                 if (last_rec_peak_frame > region->start()) {
619
620                                         jack_nframes_t nlen = last_rec_peak_frame - region->start();
621
622                                         if (nlen != region->length()) {
623
624                                                 region->freeze ();
625                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
626                                                 region->set_length (nlen, this);
627                                                 region->thaw ("updated");
628
629                                                 if (origlen == 1) {
630                                                         /* our special initial length */
631                                                         add_region_view_internal (region, false);
632                                                 }
633
634                                                 /* also update rect */
635                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
636                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
637                                                 rect->property_x2() = xend;
638                                         }
639                                 }
640
641                         } else {
642
643                                 jack_nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
644
645                                 if (nlen != region->length()) {
646
647                                         if (region->source(0).length() >= region->start() + nlen) {
648
649                                                 region->freeze ();
650                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
651                                                 region->set_length (nlen, this);
652                                                 region->thaw ("updated");
653                                                 
654                                                 if (origlen == 1) {
655                                                         /* our special initial length */
656                                                         add_region_view_internal (region, false);
657                                                 }
658                                                 
659                                                 /* also hide rect */
660                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
661                                                 rect->hide();
662
663                                         }
664                                 }
665                         }
666
667                         iter = tmp;
668                 }
669         }
670 }
671
672 void
673 AudioStreamView::show_all_xfades ()
674 {
675         foreach_crossfadeview (&CrossfadeView::show);
676         crossfades_visible = true;
677 }
678
679 void
680 AudioStreamView::hide_all_xfades ()
681 {
682         foreach_crossfadeview (&CrossfadeView::hide);
683         crossfades_visible = false;
684 }
685
686 void
687 AudioStreamView::hide_xfades_involving (AudioRegionView& rv)
688 {
689         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
690                 if ((*i)->crossfade.involves (rv.audio_region())) {
691                         (*i)->fake_hide ();
692                 }
693         }
694 }
695
696 void
697 AudioStreamView::reveal_xfades_involving (AudioRegionView& rv)
698 {
699         for (list<CrossfadeView *>::iterator i = crossfade_views.begin(); i != crossfade_views.end(); ++i) {
700                 if ((*i)->crossfade.involves (rv.audio_region()) && (*i)->visible()) {
701                         (*i)->show ();
702                 }
703         }
704 }
705
706 void
707 AudioStreamView::color_handler (ColorID id, uint32_t val)
708 {
709         switch (id) {
710         case cAudioTrackBase:
711                 if (_trackview.is_audio_track()) {
712                         canvas_rect->property_fill_color_rgba() = val;
713                 } 
714                 break;
715         case cAudioBusBase:
716                 if (!_trackview.is_audio_track()) {
717                         canvas_rect->property_fill_color_rgba() = val;
718                 }
719                 break;
720         case cAudioTrackOutline:
721                 canvas_rect->property_outline_color_rgba() = val;
722                 break;
723
724         default:
725                 break;
726         }
727 }