colinf's patch to make the cursor be the dbl vertical arrow when over the track resiz...
[ardour2.git] / gtk2_ardour / crossfade_edit.cc
blob124b650ee31f0533fb211818cd858f13bb876c0c
1 /*
2 Copyright (C) 2004 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.
20 #include <cmath>
22 #include <sigc++/bind.h>
24 #include <gtkmm/frame.h>
25 #include <gtkmm/image.h>
26 #include <gtkmm/scrolledwindow.h>
28 #include <libgnomecanvasmm/line.h>
30 #include <ardour/automation_event.h>
31 #include <ardour/curve.h>
32 #include <ardour/crossfade.h>
33 #include <ardour/session.h>
34 #include <ardour/auditioner.h>
35 #include <ardour/audioplaylist.h>
36 #include <ardour/audiosource.h>
37 #include <ardour/playlist_templates.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/profile.h>
41 #include <gtkmm2ext/gtk_ui.h>
43 #include "ardour_ui.h"
44 #include "crossfade_edit.h"
45 #include "rgb_macros.h"
46 #include "keyboard.h"
47 #include "utils.h"
48 #include "gui_thread.h"
49 #include "canvas_impl.h"
50 #include "simplerect.h"
51 #include "waveview.h"
52 #include "actions.h"
54 using namespace std;
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace sigc;
59 using namespace Editing;
61 #include "i18n.h"
63 const int32_t CrossfadeEditor::Point::size = 7;
64 const double CrossfadeEditor::canvas_border = 10;
65 CrossfadeEditor::Presets* CrossfadeEditor::fade_in_presets = 0;
66 CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
68 CrossfadeEditor::Half::Half ()
69 : line (0),
70 normative_curve (0.0, 1.0, 1.0, true),
71 gain_curve (0.0, 2.0, 1.0, true)
75 CrossfadeEditor::CrossfadeEditor (Session& s, boost::shared_ptr<Crossfade> xf, double my, double mxy)
76 : ArdourDialog (_("ardour: x-fade edit")),
77 xfade (xf),
78 session (s),
79 clear_button (_("Clear")),
80 revert_button (_("Reset")),
81 audition_both_button (_("Fade")),
82 audition_left_dry_button (_("Out (dry)")),
83 audition_left_button (_("Out")),
84 audition_right_dry_button (_("In (dry)")),
85 audition_right_button (_("In")),
87 preroll_button (_("With Pre-roll")),
88 postroll_button (_("With Post-roll")),
90 miny (my),
91 maxy (mxy),
93 fade_in_table (3, 3),
94 fade_out_table (3, 3),
96 select_in_button (_("Fade In")),
97 select_out_button (_("Fade Out"))
99 set_wmclass (X_("ardour_automationedit"), "Ardour");
100 set_name ("CrossfadeEditWindow");
101 set_position (Gtk::WIN_POS_MOUSE);
103 add_accel_group (ActionManager::ui_manager->get_accel_group());
105 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
107 RadioButtonGroup sel_but_group = select_in_button.get_group();
108 select_out_button.set_group (sel_but_group);
109 select_out_button.set_mode (false);
110 select_in_button.set_mode (false);
112 get_action_area()->set_layout(BUTTONBOX_SPREAD);
113 get_action_area()->pack_start(clear_button);
114 get_action_area()->pack_start(revert_button);
115 cancel_button = add_button ("Cancel", RESPONSE_CANCEL);
116 ok_button = add_button ("OK", RESPONSE_ACCEPT);
118 if (fade_in_presets == 0) {
119 build_presets ();
122 point_grabbed = false;
123 toplevel = 0;
125 canvas = new ArdourCanvas::CanvasAA ();
126 canvas->signal_size_allocate().connect (mem_fun(*this, &CrossfadeEditor::canvas_allocation));
127 canvas->set_size_request (425, 200);
129 toplevel = new ArdourCanvas::SimpleRect (*(canvas->root()));
130 toplevel->property_x1() = 0.0;
131 toplevel->property_y1() = 0.0;
132 toplevel->property_x2() = 10.0;
133 toplevel->property_y2() = 10.0;
134 toplevel->property_fill() = true;
135 toplevel->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorBase.get();
136 toplevel->property_outline_pixels() = 0;
137 toplevel->signal_event().connect (mem_fun (*this, &CrossfadeEditor::canvas_event));
139 fade[Out].line = new ArdourCanvas::Line (*(canvas->root()));
140 fade[Out].line->property_width_pixels() = 1;
141 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
143 fade[Out].shading = new ArdourCanvas::Polygon (*(canvas->root()));
144 fade[Out].shading->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLineShading.get();
146 fade[In].line = new ArdourCanvas::Line (*(canvas->root()));
147 fade[In].line->property_width_pixels() = 1;
148 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
150 fade[In].shading = new ArdourCanvas::Polygon (*(canvas->root()));
151 fade[In].shading->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLineShading.get();
153 fade[In].shading->signal_event().connect (mem_fun (*this, &CrossfadeEditor::canvas_event));
154 fade[In].line->signal_event().connect (mem_fun (*this, &CrossfadeEditor::curve_event));
155 fade[Out].shading->signal_event().connect (mem_fun (*this, &CrossfadeEditor::canvas_event));
156 fade[Out].line->signal_event().connect (mem_fun (*this, &CrossfadeEditor::curve_event));
158 select_in_button.set_name (X_("CrossfadeEditCurveButton"));
159 select_out_button.set_name (X_("CrossfadeEditCurveButton"));
161 select_in_button.signal_clicked().connect (bind (mem_fun (*this, &CrossfadeEditor::curve_select_clicked), In));
162 select_out_button.signal_clicked().connect (bind (mem_fun (*this, &CrossfadeEditor::curve_select_clicked), Out));
164 HBox* acbox = manage (new HBox);
166 audition_box.set_border_width (7);
167 audition_box.set_spacing (5);
168 audition_box.set_homogeneous (false);
169 audition_box.pack_start (audition_left_dry_button, false, false);
170 audition_box.pack_start (audition_left_button, false, false);
171 audition_box.pack_start (audition_both_button, false, false);
172 audition_box.pack_start (audition_right_button, false, false);
173 audition_box.pack_start (audition_right_dry_button, false, false);
175 Frame* audition_frame = manage (new Frame (_("Audition")));
177 audition_frame->set_name (X_("CrossfadeEditFrame"));
178 audition_frame->add (audition_box);
180 acbox->pack_start (*audition_frame, true, false);
182 Frame* canvas_frame = manage (new Frame);
183 canvas_frame->add (*canvas);
184 canvas_frame->set_shadow_type (Gtk::SHADOW_IN);
186 fade_in_table.attach (select_in_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
187 fade_out_table.attach (select_out_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
189 Image *pxmap;
190 Button* pbutton;
191 int row;
192 int col;
194 row = 1;
195 col = 0;
197 for (list<Preset*>::iterator i = fade_in_presets->begin(); i != fade_in_presets->end(); ++i) {
199 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
200 pbutton = manage (new Button);
201 pbutton->add (*pxmap);
202 pbutton->set_name ("CrossfadeEditButton");
203 pbutton->signal_clicked().connect (bind (mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
204 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
205 fade_in_table.attach (*pbutton, col, col+1, row, row+1);
206 fade_in_buttons.push_back (pbutton);
208 col++;
210 if (col == 2) {
211 col = 0;
212 row++;
216 row = 1;
217 col = 0;
219 for (list<Preset*>::iterator i = fade_out_presets->begin(); i != fade_out_presets->end(); ++i) {
221 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
222 pbutton = manage (new Button);
223 pbutton->add (*pxmap);
224 pbutton->set_name ("CrossfadeEditButton");
225 pbutton->signal_clicked().connect (bind (mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
226 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
227 fade_out_table.attach (*pbutton, col, col+1, row, row+1);
228 fade_out_buttons.push_back (pbutton);
230 col++;
232 if (col == 2) {
233 col = 0;
234 row++;
238 clear_button.set_name ("CrossfadeEditButton");
239 revert_button.set_name ("CrossfadeEditButton");
240 ok_button->set_name ("CrossfadeEditButton");
241 cancel_button->set_name ("CrossfadeEditButton");
242 preroll_button.set_name ("CrossfadeEditButton");
243 postroll_button.set_name ("CrossfadeEditButton");
244 audition_both_button.set_name ("CrossfadeEditAuditionButton");
245 audition_left_dry_button.set_name ("CrossfadeEditAuditionButton");
246 audition_left_button.set_name ("CrossfadeEditAuditionButton");
247 audition_right_dry_button.set_name ("CrossfadeEditAuditionButton");
248 audition_right_button.set_name ("CrossfadeEditAuditionButton");
250 clear_button.signal_clicked().connect (mem_fun(*this, &CrossfadeEditor::clear));
251 revert_button.signal_clicked().connect (mem_fun(*this, &CrossfadeEditor::reset));
252 audition_both_button.signal_toggled().connect (mem_fun(*this, &CrossfadeEditor::audition_toggled));
253 audition_right_button.signal_toggled().connect (mem_fun(*this, &CrossfadeEditor::audition_right_toggled));
254 audition_right_dry_button.signal_toggled().connect (mem_fun(*this, &CrossfadeEditor::audition_right_dry_toggled));
255 audition_left_button.signal_toggled().connect (mem_fun(*this, &CrossfadeEditor::audition_left_toggled));
256 audition_left_dry_button.signal_toggled().connect (mem_fun(*this, &CrossfadeEditor::audition_left_dry_toggled));
258 roll_box.pack_start (preroll_button, false, false);
259 roll_box.pack_start (postroll_button, false, false);
261 Gtk::HBox* rcenter_box = manage (new HBox);
262 rcenter_box->pack_start (roll_box, true, false);
264 VBox* vpacker2 = manage (new (VBox));
266 vpacker2->set_border_width (12);
267 vpacker2->set_spacing (7);
268 vpacker2->pack_start (*acbox, false, false);
269 vpacker2->pack_start (*rcenter_box, false, false);
271 curve_button_box.set_spacing (7);
272 curve_button_box.pack_start (fade_out_table, false, false, 12);
273 curve_button_box.pack_start (*vpacker2, false, false, 12);
274 curve_button_box.pack_start (fade_in_table, false, false, 12);
276 get_vbox()->pack_start (*canvas_frame, true, true);
277 get_vbox()->pack_start (curve_button_box, false, false);
279 /* button to allow hackers to check the actual curve values */
281 // Button* foobut = manage (new Button ("dump"));
282 // foobut-.signal_clicked().connect (mem_fun(*this, &CrossfadeEditor::dump));
283 // vpacker.pack_start (*foobut, false, false);
285 current = In;
286 set (xfade->fade_in(), In);
288 current = Out;
289 set (xfade->fade_out(), Out);
291 curve_select_clicked (In);
293 xfade->StateChanged.connect (mem_fun(*this, &CrossfadeEditor::xfade_changed));
295 session.AuditionActive.connect (mem_fun(*this, &CrossfadeEditor::audition_state_changed));
296 show_all_children();
299 CrossfadeEditor::~CrossfadeEditor()
301 /* most objects will be destroyed when the toplevel window is. */
303 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
304 delete *i;
307 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
308 delete *i;
312 void
313 CrossfadeEditor::dump ()
315 for (AutomationList::iterator i = fade[Out].normative_curve.begin(); i != fade[Out].normative_curve.end(); ++i) {
316 cerr << (*i)->when << ' ' << (*i)->value << endl;
320 void
321 CrossfadeEditor::audition_state_changed (bool yn)
323 ENSURE_GUI_THREAD (bind (mem_fun(*this, &CrossfadeEditor::audition_state_changed), yn));
325 if (!yn) {
326 audition_both_button.set_active (false);
327 audition_left_button.set_active (false);
328 audition_right_button.set_active (false);
329 audition_left_dry_button.set_active (false);
330 audition_right_dry_button.set_active (false);
334 void
335 CrossfadeEditor::set (const ARDOUR::Curve& curve, WhichFade which)
337 double firstx, endx;
338 ARDOUR::Curve::const_iterator the_end;
340 for (list<Point*>::iterator i = fade[which].points.begin(); i != fade[which].points.end(); ++i) {
341 delete *i;
344 fade[which].points.clear ();
345 fade[which].gain_curve.clear ();
346 fade[which].normative_curve.clear ();
348 if (curve.empty()) {
349 goto out;
352 the_end = curve.const_end();
353 --the_end;
355 firstx = (*curve.const_begin())->when;
356 endx = (*the_end)->when;
358 for (ARDOUR::Curve::const_iterator i = curve.const_begin(); i != curve.const_end(); ++i) {
360 double xfract = ((*i)->when - firstx) / (endx - firstx);
361 double yfract = ((*i)->value - miny) / (maxy - miny);
363 Point* p = make_point ();
365 p->move_to (x_coordinate (xfract), y_coordinate (yfract),
366 xfract, yfract);
368 fade[which].points.push_back (p);
371 /* no need to sort because curve is already time-ordered */
373 out:
375 swap (which, current);
376 redraw ();
377 swap (which, current);
380 bool
381 CrossfadeEditor::curve_event (GdkEvent* event)
383 /* treat it like a toplevel event */
385 return canvas_event (event);
388 bool
389 CrossfadeEditor::point_event (GdkEvent* event, Point* point)
392 if (point->curve != fade[current].line) {
393 return FALSE;
396 switch (event->type) {
397 case GDK_BUTTON_PRESS:
398 point_grabbed = true;
399 break;
400 case GDK_BUTTON_RELEASE:
401 point_grabbed = false;
403 if (Keyboard::is_delete_event (&event->button)) {
404 fade[current].points.remove (point);
405 delete point;
408 redraw ();
409 break;
411 case GDK_MOTION_NOTIFY:
412 if (point_grabbed) {
413 double new_x, new_y;
415 /* can't drag first or last points horizontally */
417 if (point == fade[current].points.front() || point == fade[current].points.back()) {
418 new_x = point->x;
419 } else {
420 new_x = (event->motion.x - canvas_border)/effective_width();
423 new_y = 1.0 - ((event->motion.y - canvas_border)/effective_height());
424 point->move_to (x_coordinate (new_x), y_coordinate (new_y),
425 new_x, new_y);
426 redraw ();
428 break;
429 default:
430 break;
432 return TRUE;
435 bool
436 CrossfadeEditor::canvas_event (GdkEvent* event)
438 switch (event->type) {
439 case GDK_BUTTON_PRESS:
440 add_control_point ((event->button.x - canvas_border)/effective_width(),
441 1.0 - ((event->button.y - canvas_border)/effective_height()));
442 return TRUE;
443 break;
444 default:
445 break;
447 return FALSE;
450 CrossfadeEditor::Point::~Point()
452 delete box;
455 CrossfadeEditor::Point*
456 CrossfadeEditor::make_point ()
458 Point* p = new Point;
460 p->box = new ArdourCanvas::SimpleRect (*(canvas->root()));
461 p->box->property_fill() = true;
462 p->box->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorPointFill.get();
463 p->box->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorPointOutline.get();
464 p->box->property_outline_pixels() = 1;
466 p->curve = fade[current].line;
468 p->box->signal_event().connect (bind (mem_fun (*this, &CrossfadeEditor::point_event), p));
470 return p;
473 void
474 CrossfadeEditor::add_control_point (double x, double y)
476 PointSorter cmp;
478 /* enforce end point x location */
480 if (fade[current].points.empty()) {
481 x = 0.0;
482 } else if (fade[current].points.size() == 1) {
483 x = 1.0;
486 Point* p = make_point ();
488 p->move_to (x_coordinate (x), y_coordinate (y), x, y);
490 fade[current].points.push_back (p);
491 fade[current].points.sort (cmp);
493 redraw ();
496 void
497 CrossfadeEditor::Point::move_to (double nx, double ny, double xfract, double yfract)
499 if ( xfract < 0.0 ) {
500 xfract = 0.0;
501 } else if ( xfract > 1.0 ) {
502 xfract = 1.0;
505 if ( yfract < 0.0 ) {
506 yfract = 0.0;
507 } else if ( yfract > 1.0 ) {
508 yfract = 1.0;
511 const double half_size = rint(size/2.0);
512 double x1 = nx - half_size;
513 double x2 = nx + half_size;
515 box->property_x1() = x1;
516 box->property_x2() = x2;
518 box->property_y1() = ny - half_size;
519 box->property_y2() = ny + half_size;
521 x = xfract;
522 y = yfract;
525 void
526 CrossfadeEditor::canvas_allocation (Gtk::Allocation& alloc)
528 if (toplevel) {
529 toplevel->property_x1() = 0.0;
530 toplevel->property_y1() = 0.0;
531 toplevel->property_x2() = (double) canvas->get_allocation().get_width() + canvas_border;
532 toplevel->property_y2() = (double) canvas->get_allocation().get_height() + canvas_border;
535 canvas->set_scroll_region (0.0, 0.0,
536 canvas->get_allocation().get_width(),
537 canvas->get_allocation().get_height());
539 Point* end = make_point ();
540 PointSorter cmp;
542 if (fade[In].points.size() > 1) {
543 Point* old_end = fade[In].points.back();
544 fade[In].points.pop_back ();
545 end->move_to (x_coordinate (old_end->x),
546 y_coordinate (old_end->y),
547 old_end->x, old_end->y);
548 delete old_end;
549 } else {
550 double x = 1.0;
551 double y = 0.5;
552 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
556 fade[In].points.push_back (end);
557 fade[In].points.sort (cmp);
559 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
560 (*i)->move_to (x_coordinate((*i)->x), y_coordinate((*i)->y),
561 (*i)->x, (*i)->y);
564 end = make_point ();
566 if (fade[Out].points.size() > 1) {
567 Point* old_end = fade[Out].points.back();
568 fade[Out].points.pop_back ();
569 end->move_to (x_coordinate (old_end->x),
570 y_coordinate (old_end->y),
571 old_end->x, old_end->y);
572 delete old_end;
573 } else {
574 double x = 1.0;
575 double y = 0.5;
576 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
580 fade[Out].points.push_back (end);
581 fade[Out].points.sort (cmp);
583 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
584 (*i)->move_to (x_coordinate ((*i)->x),
585 y_coordinate ((*i)->y),
586 (*i)->x, (*i)->y);
589 WhichFade old_current = current;
590 current = In;
591 redraw ();
592 current = Out;
593 redraw ();
594 current = old_current;
596 double spu = xfade->length() / (double) effective_width();
598 if (fade[In].waves.empty()) {
599 make_waves (xfade->in(), In);
602 if (fade[Out].waves.empty()) {
603 make_waves (xfade->out(), Out);
606 double ht;
607 vector<ArdourCanvas::WaveView*>::iterator i;
608 uint32_t n;
610 ht = canvas->get_allocation().get_height() / xfade->in()->n_channels();
612 for (n = 0, i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i, ++n) {
613 double yoff;
615 yoff = n * ht;
617 (*i)->property_y() = yoff;
618 (*i)->property_height() = ht;
619 (*i)->property_samples_per_unit() = spu;
622 ht = canvas->get_allocation().get_height() / xfade->out()->n_channels();
624 for (n = 0, i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i, ++n) {
625 double yoff;
627 yoff = n * ht;
629 (*i)->property_y() = yoff;
630 (*i)->property_height() = ht;
631 (*i)->property_samples_per_unit() = spu;
637 void
638 CrossfadeEditor::xfade_changed (Change ignored)
640 set (xfade->fade_in(), In);
641 set (xfade->fade_out(), Out);
644 void
645 CrossfadeEditor::redraw ()
647 if (canvas->get_allocation().get_width() < 2) {
648 return;
651 nframes_t len = xfade->length ();
653 fade[current].normative_curve.clear ();
654 fade[current].gain_curve.clear ();
656 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
657 fade[current].normative_curve.add ((*i)->x, (*i)->y);
658 double offset;
659 if (current==In)
660 offset = xfade->in()->start();
661 else
662 offset = xfade->out()->start()+xfade->out()->length()-xfade->length();
663 fade[current].gain_curve.add (((*i)->x * len) + offset, (*i)->y);
667 size_t npoints = (size_t) effective_width();
668 float vec[npoints];
670 fade[current].normative_curve.get_vector (0, 1.0, vec, npoints);
672 ArdourCanvas::Points pts;
673 ArdourCanvas::Points spts;
675 while (pts.size() < npoints) {
676 pts.push_back (Gnome::Art::Point (0,0));
679 while (spts.size() < npoints + 3) {
680 spts.push_back (Gnome::Art::Point (0,0));
683 /* the shade coordinates *MUST* be in anti-clockwise order.
686 if (current == In) {
688 /* lower left */
690 spts[0].set_x (canvas_border);
691 spts[0].set_y (effective_height() + canvas_border);
693 /* lower right */
695 spts[1].set_x (effective_width() + canvas_border);
696 spts[1].set_y (effective_height() + canvas_border);
698 /* upper right */
700 spts[2].set_x (effective_width() + canvas_border);
701 spts[2].set_y (canvas_border);
704 } else {
706 /* upper left */
708 spts[0].set_x (canvas_border);
709 spts[0].set_y (canvas_border);
711 /* lower left */
713 spts[1].set_x (canvas_border);
714 spts[1].set_y (effective_height() + canvas_border);
716 /* lower right */
718 spts[2].set_x (effective_width() + canvas_border);
719 spts[2].set_y (effective_height() + canvas_border);
723 size_t last_spt = (npoints + 3) - 1;
725 for (size_t i = 0; i < npoints; ++i) {
727 double y = vec[i];
729 pts[i].set_x (canvas_border + i);
730 pts[i].set_y (y_coordinate (y));
732 spts[last_spt - i].set_x (canvas_border + i);
733 spts[last_spt - i].set_y (pts[i].get_y());
736 fade[current].line->property_points() = pts;
737 fade[current].shading->property_points() = spts;
739 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[current].waves.begin(); i != fade[current].waves.end(); ++i) {
740 (*i)->property_gain_src() = &fade[current].gain_curve;
744 void
745 CrossfadeEditor::apply_preset (Preset *preset)
748 WhichFade wf = find(fade_in_presets->begin(), fade_in_presets->end(), preset) != fade_in_presets->end() ? In : Out;
750 if (current != wf) {
752 if (wf == In) {
753 select_in_button.clicked();
754 } else {
755 select_out_button.clicked();
758 curve_select_clicked (wf);
761 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
762 delete *i;
765 fade[current].points.clear ();
767 for (Preset::iterator i = preset->begin(); i != preset->end(); ++i) {
768 Point* p = make_point ();
769 p->move_to (x_coordinate ((*i).x), y_coordinate ((*i).y),
770 (*i).x, (*i).y);
771 fade[current].points.push_back (p);
774 redraw ();
777 void
778 CrossfadeEditor::apply ()
780 _apply_to (xfade);
783 void
784 CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
786 ARDOUR::Curve& in (xf->fade_in());
787 ARDOUR::Curve& out (xf->fade_out());
789 /* IN */
792 ARDOUR::Curve::const_iterator the_end = in.const_end();
793 --the_end;
795 double firstx = (*in.begin())->when;
796 double endx = (*the_end)->when;
797 double miny = in.get_min_y ();
798 double maxy = in.get_max_y ();
800 in.freeze ();
801 in.clear ();
803 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
805 double when = firstx + ((*i)->x * (endx - firstx));
806 double value = (*i)->y; // miny + ((*i)->y * (maxy - miny));
807 in.add (when, value);
810 /* OUT */
812 the_end = out.const_end();
813 --the_end;
815 firstx = (*out.begin())->when;
816 endx = (*the_end)->when;
817 miny = out.get_min_y ();
818 maxy = out.get_max_y ();
820 out.freeze ();
821 out.clear ();
823 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
825 double when = firstx + ((*i)->x * (endx - firstx));
826 double value = (*i)->y; // miny + ((*i)->y * (maxy - miny));
827 out.add (when, value);
830 in.thaw ();
831 out.thaw ();
834 void
835 CrossfadeEditor::setup (boost::shared_ptr<Crossfade> xfade)
837 _apply_to (xfade);
838 xfade->set_active (true);
839 xfade->fade_in().solve ();
840 xfade->fade_out().solve ();
843 void
844 CrossfadeEditor::clear ()
846 for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
847 delete *i;
850 fade[current].points.clear ();
852 redraw ();
855 void
856 CrossfadeEditor::reset ()
858 set (xfade->fade_in(), In);
859 set (xfade->fade_out(), Out);
861 curve_select_clicked (current);
864 void
865 CrossfadeEditor::build_presets ()
867 Preset* p;
869 fade_in_presets = new Presets;
870 fade_out_presets = new Presets;
872 /* FADE IN */
874 p = new Preset ("Linear (-6dB)", "crossfade-in-linear");
875 p->push_back (PresetPoint (0, 0));
876 p->push_back (PresetPoint (0.000000, 0.000000));
877 p->push_back (PresetPoint (0.166667, 0.166366));
878 p->push_back (PresetPoint (0.333333, 0.332853));
879 p->push_back (PresetPoint (0.500000, 0.499459));
880 p->push_back (PresetPoint (0.666667, 0.666186));
881 p->push_back (PresetPoint (0.833333, 0.833033));
882 p->push_back (PresetPoint (1.000000, 1.000000));
883 fade_in_presets->push_back (p);
885 p = new Preset ("S(1)-curve", "crossfade-in-S1");
886 p->push_back (PresetPoint (0, 0));
887 p->push_back (PresetPoint (0.1, 0.01));
888 p->push_back (PresetPoint (0.2, 0.03));
889 p->push_back (PresetPoint (0.8, 0.97));
890 p->push_back (PresetPoint (0.9, 0.99));
891 p->push_back (PresetPoint (1, 1));
892 fade_in_presets->push_back (p);
894 p = new Preset ("S(2)-curve", "crossfade-in-S2");
895 p->push_back (PresetPoint (0.0, 0.0));
896 p->push_back (PresetPoint (0.055, 0.222));
897 p->push_back (PresetPoint (0.163, 0.35));
898 p->push_back (PresetPoint (0.837, 0.678));
899 p->push_back (PresetPoint (0.945, 0.783));
900 p->push_back (PresetPoint (1.0, 1.0));
901 fade_in_presets->push_back (p);
903 p = new Preset ("Constant Power (-3dB)", "crossfade-in-constant-power");
905 p->push_back (PresetPoint (0.000000, 0.000000));
906 p->push_back (PresetPoint (0.166667, 0.282192));
907 p->push_back (PresetPoint (0.333333, 0.518174));
908 p->push_back (PresetPoint (0.500000, 0.707946));
909 p->push_back (PresetPoint (0.666667, 0.851507));
910 p->push_back (PresetPoint (0.833333, 0.948859));
911 p->push_back (PresetPoint (1.000000, 1.000000));
913 fade_in_presets->push_back (p);
915 if (!Profile->get_sae()) {
917 p = new Preset ("Short cut", "crossfade-in-short-cut");
918 p->push_back (PresetPoint (0, 0));
919 p->push_back (PresetPoint (0.389401, 0.0333333));
920 p->push_back (PresetPoint (0.629032, 0.0861111));
921 p->push_back (PresetPoint (0.829493, 0.233333));
922 p->push_back (PresetPoint (0.9447, 0.483333));
923 p->push_back (PresetPoint (0.976959, 0.697222));
924 p->push_back (PresetPoint (1, 1));
925 fade_in_presets->push_back (p);
927 p = new Preset ("Slow cut", "crossfade-in-slow-cut");
928 p->push_back (PresetPoint (0, 0));
929 p->push_back (PresetPoint (0.304147, 0.0694444));
930 p->push_back (PresetPoint (0.529954, 0.152778));
931 p->push_back (PresetPoint (0.725806, 0.333333));
932 p->push_back (PresetPoint (0.847926, 0.558333));
933 p->push_back (PresetPoint (0.919355, 0.730556));
934 p->push_back (PresetPoint (1, 1));
935 fade_in_presets->push_back (p);
937 p = new Preset ("Fast cut", "crossfade-in-fast-cut");
938 p->push_back (PresetPoint (0, 0));
939 p->push_back (PresetPoint (0.0737327, 0.308333));
940 p->push_back (PresetPoint (0.246544, 0.658333));
941 p->push_back (PresetPoint (0.470046, 0.886111));
942 p->push_back (PresetPoint (0.652074, 0.972222));
943 p->push_back (PresetPoint (0.771889, 0.988889));
944 p->push_back (PresetPoint (1, 1));
945 fade_in_presets->push_back (p);
947 p = new Preset ("Long cut", "crossfade-in-long-cut");
948 p->push_back (PresetPoint (0, 0));
949 p->push_back (PresetPoint (0.0207373, 0.197222));
950 p->push_back (PresetPoint (0.0645161, 0.525));
951 p->push_back (PresetPoint (0.152074, 0.802778));
952 p->push_back (PresetPoint (0.276498, 0.919444));
953 p->push_back (PresetPoint (0.481567, 0.980556));
954 p->push_back (PresetPoint (0.767281, 1));
955 p->push_back (PresetPoint (1, 1));
956 fade_in_presets->push_back (p);
959 /* FADE OUT */
961 // p = new Preset ("regout.xpm");
962 p = new Preset ("Linear (-6dB cut)", "crossfade-out-linear");
963 p->push_back (PresetPoint (0, 1));
964 p->push_back (PresetPoint (0.000000, 1.000000));
965 p->push_back (PresetPoint (0.166667, 0.833033));
966 p->push_back (PresetPoint (0.333333, 0.666186));
967 p->push_back (PresetPoint (0.500000, 0.499459));
968 p->push_back (PresetPoint (0.666667, 0.332853));
969 p->push_back (PresetPoint (0.833333, 0.166366));
970 p->push_back (PresetPoint (1.000000, 0.000000));
971 fade_out_presets->push_back (p);
973 p = new Preset ("S(1)-Curve", "crossfade-out-S1");
974 p->push_back (PresetPoint (0, 1));
975 p->push_back (PresetPoint (0.1, 0.99));
976 p->push_back (PresetPoint (0.2, 0.97));
977 p->push_back (PresetPoint (0.8, 0.03));
978 p->push_back (PresetPoint (0.9, 0.01));
979 p->push_back (PresetPoint (1, 0));
980 fade_out_presets->push_back (p);
982 p = new Preset ("S(2)-Curve", "crossfade-out-S2");
983 p->push_back (PresetPoint (0.0, 1.0));
984 p->push_back (PresetPoint (0.163, 0.678));
985 p->push_back (PresetPoint (0.055, 0.783));
986 p->push_back (PresetPoint (0.837, 0.35));
987 p->push_back (PresetPoint (0.945, 0.222));
988 p->push_back (PresetPoint (1.0, 0.0));
989 fade_out_presets->push_back (p);
991 // p = new Preset ("linout.xpm");
992 p = new Preset ("Constant Power (-3dB cut)", "crossfade-out-constant-power");
993 p->push_back (PresetPoint (0.000000, 1.000000));
994 p->push_back (PresetPoint (0.166667, 0.948859));
995 p->push_back (PresetPoint (0.333333, 0.851507));
996 p->push_back (PresetPoint (0.500000, 0.707946));
997 p->push_back (PresetPoint (0.666667, 0.518174));
998 p->push_back (PresetPoint (0.833333, 0.282192));
999 p->push_back (PresetPoint (1.000000, 0.000000));
1000 fade_out_presets->push_back (p);
1002 if (!Profile->get_sae()) {
1003 // p = new Preset ("hiout.xpm");
1004 p = new Preset ("Short cut", "crossfade-out-short-cut");
1005 p->push_back (PresetPoint (0, 1));
1006 p->push_back (PresetPoint (0.305556, 1));
1007 p->push_back (PresetPoint (0.548611, 0.991736));
1008 p->push_back (PresetPoint (0.759259, 0.931129));
1009 p->push_back (PresetPoint (0.918981, 0.68595));
1010 p->push_back (PresetPoint (0.976852, 0.22865));
1011 p->push_back (PresetPoint (1, 0));
1012 fade_out_presets->push_back (p);
1014 p = new Preset ("Slow cut", "crossfade-out-slow-cut");
1015 p->push_back (PresetPoint (0, 1));
1016 p->push_back (PresetPoint (0.228111, 0.988889));
1017 p->push_back (PresetPoint (0.347926, 0.972222));
1018 p->push_back (PresetPoint (0.529954, 0.886111));
1019 p->push_back (PresetPoint (0.753456, 0.658333));
1020 p->push_back (PresetPoint (0.9262673, 0.308333));
1021 p->push_back (PresetPoint (1, 0));
1022 fade_out_presets->push_back (p);
1024 p = new Preset ("Fast cut", "crossfade-out-fast-cut");
1025 p->push_back (PresetPoint (0, 1));
1026 p->push_back (PresetPoint (0.080645, 0.730556));
1027 p->push_back (PresetPoint (0.277778, 0.289256));
1028 p->push_back (PresetPoint (0.470046, 0.152778));
1029 p->push_back (PresetPoint (0.695853, 0.0694444));
1030 p->push_back (PresetPoint (1, 0));
1031 fade_out_presets->push_back (p);
1033 // p = new Preset ("loout.xpm");
1034 p = new Preset ("Long cut", "crossfade-out-long-cut");
1035 p->push_back (PresetPoint (0, 1));
1036 p->push_back (PresetPoint (0.023041, 0.697222));
1037 p->push_back (PresetPoint (0.0553, 0.483333));
1038 p->push_back (PresetPoint (0.170507, 0.233333));
1039 p->push_back (PresetPoint (0.370968, 0.0861111));
1040 p->push_back (PresetPoint (0.610599, 0.0333333));
1041 p->push_back (PresetPoint (1, 0));
1042 fade_out_presets->push_back (p);
1047 void
1048 CrossfadeEditor::curve_select_clicked (WhichFade wf)
1050 current = wf;
1052 if (wf == In) {
1054 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1055 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1056 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1059 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1060 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1061 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1064 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
1065 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
1066 fade[Out].shading->hide();
1067 fade[In].shading->show();
1069 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1070 (*i)->box->hide();
1073 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1074 (*i)->box->show ();
1077 } else {
1079 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1080 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1081 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1084 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1085 (*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1086 (*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1089 fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
1090 fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorLine.get();
1091 fade[In].shading->hide();
1092 fade[Out].shading->show();
1094 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1095 (*i)->box->hide();
1098 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1099 (*i)->box->show();
1105 double
1106 CrossfadeEditor::x_coordinate (double& xfract) const
1108 xfract = min (1.0, xfract);
1109 xfract = max (0.0, xfract);
1111 return canvas_border + (xfract * effective_width());
1114 double
1115 CrossfadeEditor::y_coordinate (double& yfract) const
1117 yfract = min (1.0, yfract);
1118 yfract = max (0.0, yfract);
1120 return (canvas->get_allocation().get_height() - (canvas_border)) - (yfract * effective_height());
1123 void
1124 CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade which)
1126 gdouble ht;
1127 uint32_t nchans = region->n_channels();
1128 guint32 color;
1129 double spu;
1131 if (which == In) {
1132 color = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
1133 } else {
1134 color = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
1137 ht = canvas->get_allocation().get_height() / (double) nchans;
1138 spu = xfade->length() / (double) effective_width();
1140 for (uint32_t n = 0; n < nchans; ++n) {
1142 gdouble yoff = n * ht;
1144 if (region->source(n)->peaks_ready (bind (mem_fun(*this, &CrossfadeEditor::peaks_ready), region, which), peaks_ready_connection)) {
1146 WaveView* waveview = new WaveView (*(canvas->root()));
1148 waveview->property_data_src() = region.get();
1149 waveview->property_cache_updater() = true;
1150 waveview->property_cache() = WaveView::create_cache();
1151 waveview->property_channel() = n;
1152 waveview->property_length_function() = (void*) region_length_from_c;
1153 waveview->property_sourcefile_length_function() = (void*) sourcefile_length_from_c;
1154 waveview->property_peak_function() = (void*) region_read_peaks_from_c;
1155 waveview->property_gain_function() = (void*) curve_get_vector_from_c;
1156 waveview->property_gain_src() = &fade[which].gain_curve;
1157 waveview->property_x() = canvas_border;
1158 waveview->property_y() = yoff;
1159 waveview->property_height() = ht;
1160 waveview->property_samples_per_unit() = spu;
1161 waveview->property_amplitude_above_axis() = 2.0;
1162 waveview->property_wave_color() = color;
1163 waveview->property_fill_color() = color;
1165 if (which==In)
1166 waveview->property_region_start() = region->start();
1167 else
1168 waveview->property_region_start() = region->start()+region->length()-xfade->length();
1170 waveview->lower_to_bottom();
1171 fade[which].waves.push_back (waveview);
1175 toplevel->lower_to_bottom();
1178 void
1179 CrossfadeEditor::peaks_ready (boost::shared_ptr<AudioRegion> r, WhichFade which)
1181 /* this should never be called, because the peak files for an xfade
1182 will be ready by the time we want them. but our API forces us
1183 to provide this, so ..
1185 peaks_ready_connection.disconnect ();
1186 make_waves (r, which);
1189 void
1190 CrossfadeEditor::audition (Audition which)
1192 AudioPlaylist& pl (session.the_auditioner()->prepare_playlist());
1193 nframes_t preroll;
1194 nframes_t postroll;
1195 nframes_t left_start_offset;
1196 nframes_t right_length;
1197 nframes_t left_length;
1199 if (which != Right && preroll_button.get_active()) {
1200 preroll = session.frame_rate() * 2; //2 second hardcoded preroll for now
1201 } else {
1202 preroll = 0;
1205 if (which != Left && postroll_button.get_active()) {
1206 postroll = session.frame_rate() * 2; //2 second hardcoded postroll for now
1207 } else {
1208 postroll = 0;
1211 // Is there enough data for the whole preroll?
1212 left_length = xfade->length();
1213 if ((left_start_offset = xfade->out()->length() - xfade->length()) > preroll) {
1214 left_start_offset -= preroll;
1215 } else {
1216 preroll = left_start_offset;
1217 left_start_offset = 0;
1219 left_length += preroll;
1221 // Is there enough data for the whole postroll?
1222 right_length = xfade->length();
1223 if ((xfade->in()->length() - right_length) > postroll) {
1224 right_length += postroll;
1225 } else {
1226 right_length = xfade->in()->length();
1229 boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (xfade->out(), left_start_offset, left_length, "xfade out",
1230 0, Region::DefaultFlags, false)));
1231 boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (xfade->in(), 0, right_length, "xfade in",
1232 0, Region::DefaultFlags, false)));
1234 //apply a 20ms declicking fade at the start and end of auditioning
1235 left->set_fade_in_active(true);
1236 left->set_fade_in_length(session.frame_rate() / 50);
1237 right->set_fade_out_active(true);
1238 right->set_fade_out_length(session.frame_rate() / 50);
1240 pl.add_region (left, 0);
1241 pl.add_region (right, 1 + preroll);
1243 if (which == Left) {
1244 right->set_scale_amplitude (0.0);
1245 } else if (which == Right) {
1246 left->set_scale_amplitude (0.0);
1249 /* there is only one ... */
1250 pl.foreach_crossfade (this, &CrossfadeEditor::setup);
1252 session.audition_playlist ();
1255 void
1256 CrossfadeEditor::audition_both ()
1258 audition (Both);
1261 void
1262 CrossfadeEditor::audition_left_dry ()
1264 boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (xfade->out(), xfade->out()->length() - xfade->length(), xfade->length(), "xfade left",
1265 0, Region::DefaultFlags, false)));
1267 session.audition_region (left);
1270 void
1271 CrossfadeEditor::audition_left ()
1273 audition (Left);
1276 void
1277 CrossfadeEditor::audition_right_dry ()
1279 boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (xfade->in(), 0, xfade->length(), "xfade in",
1280 0, Region::DefaultFlags, false)));
1281 session.audition_region (right);
1284 void
1285 CrossfadeEditor::audition_right ()
1287 audition (Right);
1290 void
1291 CrossfadeEditor::cancel_audition ()
1293 session.cancel_audition ();
1296 void
1297 CrossfadeEditor::audition_toggled ()
1299 bool x;
1301 if ((x = audition_both_button.get_active ()) != session.is_auditioning()) {
1303 if (x) {
1304 audition_both ();
1305 } else {
1306 cancel_audition ();
1311 void
1312 CrossfadeEditor::audition_right_toggled ()
1314 bool x;
1316 if ((x = audition_right_button.get_active ()) != session.is_auditioning()) {
1318 if (x) {
1319 audition_right ();
1320 } else {
1321 cancel_audition ();
1326 void
1327 CrossfadeEditor::audition_right_dry_toggled ()
1329 bool x;
1331 if ((x = audition_right_dry_button.get_active ()) != session.is_auditioning()) {
1333 if (x) {
1334 audition_right_dry ();
1335 } else {
1336 cancel_audition ();
1341 void
1342 CrossfadeEditor::audition_left_toggled ()
1344 bool x;
1346 if ((x = audition_left_button.get_active ()) != session.is_auditioning()) {
1348 if (x) {
1349 audition_left ();
1350 } else {
1351 cancel_audition ();
1356 void
1357 CrossfadeEditor::audition_left_dry_toggled ()
1359 bool x;
1361 if ((x = audition_left_dry_button.get_active ()) != session.is_auditioning()) {
1363 if (x) {
1364 audition_left_dry ();
1365 } else {
1366 cancel_audition ();
1371 bool
1372 CrossfadeEditor::on_key_press_event (GdkEventKey *ev)
1374 return true;
1377 bool
1378 CrossfadeEditor::on_key_release_event (GdkEventKey* ev)
1380 switch (ev->keyval) {
1381 case GDK_Right:
1382 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1383 audition_right_dry_button.set_active (!audition_right_dry_button.get_active());
1384 } else {
1385 audition_right_button.set_active (!audition_right_button.get_active());
1387 break;
1389 case GDK_Left:
1390 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1391 audition_left_dry_button.set_active (!audition_left_dry_button.get_active());
1392 } else {
1393 audition_left_button.set_active (!audition_left_button.get_active());
1395 break;
1397 case GDK_space:
1398 if (session.is_auditioning()) {
1399 cancel_audition ();
1400 } else {
1401 audition_both_button.set_active (!audition_both_button.get_active());
1403 break;
1405 default:
1406 break;
1409 return true;