2 Copyright (C) 2001, 2006 Paul Davis
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.
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.
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.
24 #include <gtkmm2ext/gtk_ui.h>
26 #include <ardour/audioplaylist.h>
27 #include <ardour/audioregion.h>
28 #include <ardour/audiofilesource.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 #include <ardour/region_factory.h>
34 #include <ardour/profile.h>
36 #include "audio_streamview.h"
37 #include "audio_region_view.h"
38 #include "tape_region_view.h"
39 #include "audio_time_axis.h"
40 #include "canvas-waveview.h"
41 #include "canvas-simplerect.h"
42 #include "region_selection.h"
43 #include "selection.h"
44 #include "public_editor.h"
45 #include "ardour_ui.h"
46 #include "crossfade_view.h"
47 #include "rgb_macros.h"
48 #include "gui_thread.h"
53 using namespace ARDOUR
;
55 using namespace Editing
;
57 AudioStreamView::AudioStreamView (AudioTimeAxisView
& tv
)
60 crossfades_visible
= true;
61 _waveform_scale
= LinearWaveform
;
62 _waveform_shape
= Traditional
;
66 _amplitude_above_axis
= 1.0;
68 use_rec_regions
= tv
.editor
.show_waveforms_recording ();
69 last_rec_peak_frame
= 0;
73 AudioStreamView::~AudioStreamView ()
78 AudioStreamView::set_height (gdouble h
)
80 /* limit the values to something sane-ish */
81 if (h
< 10.0 || h
> 1000.0) {
85 StreamView::set_height(h
);
87 for (CrossfadeViewList::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
95 AudioStreamView::set_samples_per_unit (gdouble spp
)
97 StreamView::set_samples_per_unit(spp
);
99 for (CrossfadeViewList::iterator xi
= crossfade_views
.begin(); xi
!= crossfade_views
.end(); ++xi
) {
100 (*xi
)->set_samples_per_unit (spp
);
107 AudioStreamView::set_amplitude_above_axis (gdouble app
)
109 RegionViewList::iterator i
;
115 _amplitude_above_axis
= app
;
117 for (i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
118 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
120 arv
->set_amplitude_above_axis (app
);
127 AudioStreamView::add_region_view_internal (boost::shared_ptr
<Region
> r
, bool wait_for_waves
, bool recording
)
129 AudioRegionView
*region_view
= 0;
131 ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_region_view
), r
));
133 boost::shared_ptr
<AudioRegion
> region
= boost::dynamic_pointer_cast
<AudioRegion
> (r
);
139 for (list
<RegionView
*>::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
140 if ((*i
)->region() == r
) {
142 /* great. we already have a AudioRegionView for this Region. use it again. */
144 (*i
)->set_valid (true);
146 // this might not be necessary
147 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
149 arv
->set_waveform_scale (_waveform_scale
);
150 arv
->set_waveform_shape (_waveform_shape
);
157 switch (_trackview
.audio_track()->mode()) {
160 region_view
= new AudioRegionView (canvas_group
, _trackview
, region
,
161 _samples_per_unit
, region_color
, recording
, TimeAxisViewItem::Visibility(TimeAxisViewItem::ShowFrame
| TimeAxisViewItem::HideFrameRight
));
163 region_view
= new AudioRegionView (canvas_group
, _trackview
, region
,
164 _samples_per_unit
, region_color
);
168 region_view
= new TapeAudioRegionView (canvas_group
, _trackview
, region
,
169 _samples_per_unit
, region_color
);
172 fatal
<< string_compose (_("programming error: %1"), "illegal track mode in ::add_region_view_internal") << endmsg
;
177 region_view
->init (region_color
, wait_for_waves
);
178 region_view
->set_amplitude_above_axis(_amplitude_above_axis
);
179 region_views
.push_front (region_view
);
181 /* if its the special single-sample length that we use for rec-regions, make it
182 insensitive to events
185 if (region
->length() == 1) {
186 region_view
->set_sensitive (false);
189 /* if this was the first one, then lets query the waveform scale and shape.
190 otherwise, we set it to the current value */
192 if (region_views
.size() == 1) {
193 if (region_view
->waveform_logscaled()) {
194 _waveform_scale
= LogWaveform
;
196 _waveform_scale
= LinearWaveform
;
199 if (region_view
->waveform_rectified()) {
200 _waveform_shape
= Rectified
;
202 _waveform_shape
= Traditional
;
206 region_view
->set_waveform_scale(_waveform_scale
);
207 region_view
->set_waveform_shape(_waveform_shape
);
210 /* follow global waveform setting */
212 region_view
->set_waveform_visible(_trackview
.editor
.show_waveforms());
214 /* catch regionview going away */
215 region
->GoingAway
.connect (bind (mem_fun (*this, &AudioStreamView::remove_region_view
), boost::weak_ptr
<Region
> (r
)));
217 RegionViewAdded (region_view
);
221 AudioStreamView::remove_region_view (boost::weak_ptr
<Region
> weak_r
)
223 ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::remove_region_view
), weak_r
));
225 boost::shared_ptr
<Region
> r (weak_r
.lock());
231 if (!_trackview
.session().deletion_in_progress()) {
233 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end();) {
234 list
<CrossfadeView
*>::iterator tmp
;
239 boost::shared_ptr
<AudioRegion
> ar
= boost::dynamic_pointer_cast
<AudioRegion
>(r
);
240 if (ar
&& (*i
)->crossfade
->involves (ar
)) {
242 crossfade_views
.erase (i
);
250 StreamView::remove_region_view(r
);
254 AudioStreamView::undisplay_diskstream ()
256 StreamView::undisplay_diskstream();
258 for (CrossfadeViewList::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
262 crossfade_views
.clear ();
266 AudioStreamView::playlist_modified ()
268 ENSURE_GUI_THREAD (mem_fun (*this, &AudioStreamView::playlist_modified
));
270 StreamView::playlist_modified();
272 /* make sure xfades are on top and all the regionviews are stacked correctly. */
274 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
275 (*i
)->get_canvas_group()->raise_to_top();
280 AudioStreamView::playlist_changed (boost::weak_ptr
<Diskstream
> wptr
)
282 ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::playlist_changed
), wptr
));
284 boost::shared_ptr
<Diskstream
> ds
= wptr
.lock();
290 StreamView::playlist_changed(ds
);
292 boost::shared_ptr
<AudioPlaylist
> apl
= boost::dynamic_pointer_cast
<AudioPlaylist
>(ds
->playlist());
294 playlist_connections
.push_back (apl
->NewCrossfade
.connect (mem_fun (*this, &AudioStreamView::add_crossfade
)));
298 AudioStreamView::add_crossfade_weak (boost::weak_ptr
<Crossfade
> crossfade
)
300 boost::shared_ptr
<Crossfade
> sp (crossfade
.lock());
310 AudioStreamView::add_crossfade (boost::shared_ptr
<Crossfade
> crossfade
)
312 AudioRegionView
* lview
= 0;
313 AudioRegionView
* rview
= 0;
315 /* we do not allow shared_ptr<T> to be bound to slots */
317 ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_crossfade_weak
), boost::weak_ptr
<Crossfade
> (crossfade
)));
319 /* first see if we already have a CrossfadeView for this Crossfade */
321 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
322 if ((*i
)->crossfade
== crossfade
) {
323 if (!crossfades_visible
) {
328 (*i
)->set_valid (true);
333 /* create a new one */
335 for (list
<RegionView
*>::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
336 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*>(*i
);
338 if (!lview
&& arv
&& (arv
->region() == crossfade
->out())) {
341 if (!rview
&& arv
&& (arv
->region() == crossfade
->in())) {
346 CrossfadeView
*cv
= new CrossfadeView (_trackview
.canvas_display
,
353 crossfade
->Invalidated
.connect (mem_fun (*this, &AudioStreamView::remove_crossfade
));
354 crossfade_views
.push_back (cv
);
356 if (!Config
->get_xfades_visible() || !crossfades_visible
) {
362 AudioStreamView::remove_crossfade (boost::shared_ptr
<Crossfade
> xfade
)
364 ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::remove_crossfade
), xfade
));
366 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
367 if ((*i
)->crossfade
== xfade
) {
369 crossfade_views
.erase (i
);
376 AudioStreamView::redisplay_diskstream ()
378 list
<RegionView
*>::iterator i
, tmp
;
379 list
<CrossfadeView
*>::iterator xi
, tmpx
;
381 for (i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
382 (*i
)->set_valid (false);
385 for (xi
= crossfade_views
.begin(); xi
!= crossfade_views
.end(); ++xi
) {
386 (*xi
)->set_valid (false);
387 if ((*xi
)->visible()) {
392 if (_trackview
.is_audio_track()) {
393 _trackview
.get_diskstream()->playlist()->foreach_region (static_cast<StreamView
*>(this), &StreamView::add_region_view
);
395 boost::shared_ptr
<AudioPlaylist
> apl
= boost::dynamic_pointer_cast
<AudioPlaylist
>(_trackview
.get_diskstream()->playlist());
397 apl
->foreach_crossfade (this, &AudioStreamView::add_crossfade
);
400 for (i
= region_views
.begin(); i
!= region_views
.end(); ) {
404 if (!(*i
)->is_valid()) {
406 region_views
.erase (i
);
412 for (xi
= crossfade_views
.begin(); xi
!= crossfade_views
.end();) {
416 if (!(*xi
)->valid()) {
418 crossfade_views
.erase (xi
);
424 /* now fix layering */
426 for (RegionViewList::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
432 AudioStreamView::set_show_waveforms (bool yn
)
434 for (list
<RegionView
*>::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
435 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
437 arv
->set_waveform_visible (yn
);
443 AudioStreamView::set_waveform_shape (WaveformShape shape
)
445 for (RegionViewList::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
446 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
448 arv
->set_waveform_shape (shape
);
450 _waveform_shape
= shape
;
454 AudioStreamView::set_waveform_scale (WaveformScale scale
)
456 for (RegionViewList::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
457 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
459 arv
->set_waveform_scale (scale
);
461 _waveform_scale
= scale
;
465 AudioStreamView::setup_rec_box ()
467 //cerr << _trackview.name() << " streamview SRB region_views.size() = " << region_views.size() << endl;
469 if (_trackview
.session().transport_rolling()) {
471 // cerr << "\trolling\n";
474 _trackview
.session().record_status() == Session::Recording
&&
475 _trackview
.get_diskstream()->record_enabled()) {
476 if (_trackview
.audio_track()->mode() == Normal
&& use_rec_regions
&& rec_regions
.size() == rec_rects
.size()) {
478 /* add a new region, but don't bother if they set use_rec_regions mid-record */
482 for (list
<sigc::connection
>::iterator prc
= peak_ready_connections
.begin(); prc
!= peak_ready_connections
.end(); ++prc
) {
485 peak_ready_connections
.clear();
488 boost::shared_ptr
<AudioDiskstream
> ads
= boost::dynamic_pointer_cast
<AudioDiskstream
>(_trackview
.get_diskstream());
491 for (uint32_t n
=0; n
< ads
->n_channels(); ++n
) {
492 boost::shared_ptr
<AudioFileSource
> src
= boost::static_pointer_cast
<AudioFileSource
> (ads
->write_source (n
));
494 sources
.push_back (src
);
495 peak_ready_connections
.push_back (src
->PeakRangeReady
.connect (bind (mem_fun (*this, &AudioStreamView::rec_peak_range_ready
), boost::weak_ptr
<Source
>(src
))));
502 if (rec_regions
.size() > 0) {
503 start
= rec_regions
.back()->start() + _trackview
.get_diskstream()->get_captured_frames(rec_regions
.size()-1);
506 boost::shared_ptr
<AudioRegion
> region (boost::dynamic_pointer_cast
<AudioRegion
>
507 (RegionFactory::create (sources
, start
, 1 , "", 0, (Region::Flag
)(Region::DefaultFlags
| Region::DoNotSaveState
), false)));
508 region
->set_position (_trackview
.session().transport_frame(), this);
510 rec_regions
.push_back (region
);
513 /* start a new rec box */
515 boost::shared_ptr
<AudioTrack
> at
= _trackview
.audio_track ();
516 boost::shared_ptr
<AudioDiskstream
> ds
= at
->audio_diskstream();
517 nframes_t frame_pos
= ds
->current_capture_start ();
518 gdouble xstart
= _trackview
.editor
.frame_to_pixel (frame_pos
);
522 switch (at
->mode()) {
525 fill_color
= ARDOUR_UI::config()->canvasvar_RecordingRect
.get();
530 fill_color
= ARDOUR_UI::config()->canvasvar_RecordingRect
.get();
531 /* make the recording rect translucent to allow
532 the user to see the peak data coming in, etc.
534 fill_color
= UINT_RGBA_CHANGE_A (fill_color
, 120);
538 ArdourCanvas::SimpleRect
* rec_rect
= new Gnome::Canvas::SimpleRect (*canvas_group
);
539 rec_rect
->property_x1() = xstart
;
540 rec_rect
->property_y1() = 1.0;
541 rec_rect
->property_x2() = xend
;
542 rec_rect
->property_y2() = (double) _trackview
.current_height() - 1;
543 rec_rect
->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeAxisFrame
.get();
544 rec_rect
->property_outline_what() = 0x1 | 0x2 | 0x4 | 0x8;
545 rec_rect
->property_fill_color_rgba() = fill_color
;
548 recbox
.rectangle
= rec_rect
;
549 recbox
.start
= _trackview
.session().transport_frame();
552 rec_rects
.push_back (recbox
);
554 screen_update_connection
.disconnect();
555 screen_update_connection
= ARDOUR_UI::instance()->SuperRapidScreenUpdate
.connect (mem_fun (*this, &AudioStreamView::update_rec_box
));
559 } else if (rec_active
&&
560 (_trackview
.session().record_status() != Session::Recording
||
561 !_trackview
.get_diskstream()->record_enabled())) {
562 screen_update_connection
.disconnect();
564 rec_updating
= false;
569 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
571 if (!rec_rects
.empty() || !rec_regions
.empty()) {
573 /* disconnect rapid update */
574 screen_update_connection
.disconnect();
576 for (list
<sigc::connection
>::iterator prc
= peak_ready_connections
.begin(); prc
!= peak_ready_connections
.end(); ++prc
) {
579 peak_ready_connections
.clear();
581 rec_updating
= false;
583 last_rec_peak_frame
= 0;
585 /* remove temp regions */
587 for (list
<boost::shared_ptr
<Region
> >::iterator iter
= rec_regions
.begin(); iter
!= rec_regions
.end(); ) {
588 list
<boost::shared_ptr
<Region
> >::iterator tmp
;
593 (*iter
)->drop_references ();
600 // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
602 /* transport stopped, clear boxes */
603 for (vector
<RecBoxInfo
>::iterator iter
=rec_rects
.begin(); iter
!= rec_rects
.end(); ++iter
) {
604 RecBoxInfo
&rect
= (*iter
);
605 delete rect
.rectangle
;
615 AudioStreamView::foreach_crossfadeview (void (CrossfadeView::*pmf
)(void))
617 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
623 AudioStreamView::rec_peak_range_ready (nframes_t start
, nframes_t cnt
, boost::weak_ptr
<Source
> weak_src
)
625 ENSURE_GUI_THREAD(bind (mem_fun (*this, &AudioStreamView::rec_peak_range_ready
), start
, cnt
, weak_src
));
627 boost::shared_ptr
<Source
> src (weak_src
.lock());
633 // this is called from the peak building thread
635 if (rec_peak_ready_map
.size() == 0 || start
+cnt
> last_rec_peak_frame
) {
636 last_rec_peak_frame
= start
+ cnt
;
639 rec_peak_ready_map
[src
] = true;
641 if (rec_peak_ready_map
.size() == _trackview
.get_diskstream()->n_channels()) {
642 this->update_rec_regions ();
643 rec_peak_ready_map
.clear();
648 AudioStreamView::update_rec_regions ()
650 if (use_rec_regions
) {
653 for (list
<boost::shared_ptr
<Region
> >::iterator iter
= rec_regions
.begin(); iter
!= rec_regions
.end(); n
++) {
655 list
<boost::shared_ptr
<Region
> >::iterator tmp
;
660 if (!canvas_item_visible (rec_rects
[n
].rectangle
)) {
661 /* rect already hidden, this region is done */
666 boost::shared_ptr
<AudioRegion
> region
= boost::dynamic_pointer_cast
<AudioRegion
>(*iter
);
671 nframes_t origlen
= region
->length();
673 if (region
== rec_regions
.back() && rec_active
) {
675 if (last_rec_peak_frame
> region
->start()) {
677 nframes_t nlen
= last_rec_peak_frame
- region
->start();
679 if (nlen
!= region
->length()) {
682 region
->set_position (_trackview
.get_diskstream()->get_capture_start_frame(n
), this);
683 region
->set_length (nlen
, this);
684 region
->thaw ("updated");
687 /* our special initial length */
688 add_region_view_internal (region
, false, true);
691 /* also update rect */
692 ArdourCanvas::SimpleRect
* rect
= rec_rects
[n
].rectangle
;
693 gdouble xend
= _trackview
.editor
.frame_to_pixel (region
->position() + region
->length());
694 rect
->property_x2() = xend
;
700 nframes_t nlen
= _trackview
.get_diskstream()->get_captured_frames(n
);
702 if (nlen
!= region
->length()) {
704 if (region
->source(0)->length() >= region
->start() + nlen
) {
707 region
->set_position (_trackview
.get_diskstream()->get_capture_start_frame(n
), this);
708 region
->set_length (nlen
, this);
709 region
->thaw ("updated");
712 /* our special initial length */
713 add_region_view_internal (region
, false, true);
717 ArdourCanvas::Item
* rect
= rec_rects
[n
].rectangle
;
730 AudioStreamView::show_all_fades ()
732 for (list
<RegionView
*>::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
733 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
735 arv
->set_fade_visibility (true);
741 AudioStreamView::hide_all_fades ()
743 for (list
<RegionView
*>::iterator i
= region_views
.begin(); i
!= region_views
.end(); ++i
) {
744 AudioRegionView
* const arv
= dynamic_cast<AudioRegionView
*>(*i
);
746 arv
->set_fade_visibility (false);
752 AudioStreamView::show_all_xfades ()
754 foreach_crossfadeview (&CrossfadeView::show
);
755 crossfades_visible
= true;
759 AudioStreamView::hide_all_xfades ()
761 foreach_crossfadeview (&CrossfadeView::hide
);
762 crossfades_visible
= false;
766 AudioStreamView::hide_xfades_involving (AudioRegionView
& rv
)
768 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
769 if ((*i
)->crossfade
->involves (rv
.audio_region())) {
776 AudioStreamView::reveal_xfades_involving (AudioRegionView
& rv
)
778 for (list
<CrossfadeView
*>::iterator i
= crossfade_views
.begin(); i
!= crossfade_views
.end(); ++i
) {
779 if ((*i
)->crossfade
->involves (rv
.audio_region()) && (*i
)->visible()) {
786 AudioStreamView::color_handler ()
788 if (_trackview
.is_track()) {
789 canvas_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AudioTrackBase
.get();
792 if (!_trackview
.is_track()) {
793 if (Profile
->get_sae() && _trackview
.route()->master()) {
794 canvas_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AudioMasterBusBase
.get();
796 canvas_rect
->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_AudioBusBase
.get();