OSC: Changed gainVCA to gainfader as VCA is already used.
[ardour.git] / gtk2_ardour / crossfade_edit.cc
1 /*
2     Copyright (C) 2004 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
20 #include <cmath>
21
22 #include <sigc++/bind.h>
23
24 #include <gtkmm/frame.h>
25 #include <gtkmm/image.h>
26 #include <gtkmm/scrolledwindow.h>
27
28
29 #include "pbd/memento_command.h"
30 #include "ardour/automation_list.h"
31 #include "evoral/Curve.hpp"
32 #include "ardour/crossfade.h"
33 #include "ardour/dB.h"
34 #include "ardour/session.h"
35 #include "ardour/auditioner.h"
36 #include "ardour/audioplaylist.h"
37 #include "ardour/audiosource.h"
38 #include "ardour/region_factory.h"
39 #include "ardour/profile.h"
40 #include "ardour/crossfade_binder.h"
41
42 #include <gtkmm2ext/gtk_ui.h>
43
44 #include "canvas/rectangle.h"
45 #include "canvas/wave_view.h"
46 #include "canvas/line.h"
47 #include "canvas/polygon.h"
48
49 #include "ardour_ui.h"
50 #include "crossfade_edit.h"
51 #include "rgb_macros.h"
52 #include "keyboard.h"
53 #include "gui_thread.h"
54 #include "actions.h"
55
56 using namespace std;
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace Gtk;
60 using namespace Editing;
61
62 using Gtkmm2ext::Keyboard;
63
64 #include "i18n.h"
65
66 const int32_t CrossfadeEditor::Point::size = 7;
67 const double CrossfadeEditor::canvas_border = 10;
68 CrossfadeEditor::Presets* CrossfadeEditor::fade_in_presets = 0;
69 CrossfadeEditor::Presets* CrossfadeEditor::fade_out_presets = 0;
70
71 CrossfadeEditor::Half::Half ()
72         : line (0)
73         , normative_curve (Evoral::Parameter(GainAutomation))
74         , gain_curve (Evoral::Parameter(GainAutomation))
75 {
76 }
77
78 CrossfadeEditor::CrossfadeEditor (Session* s, boost::shared_ptr<Crossfade> xf, double my, double mxy)
79         : ArdourDialog (_("Edit Crossfade")),
80           xfade (xf),
81           clear_button (_("Clear")),
82           revert_button (_("Reset")),
83           audition_both_button (_("Fade")),
84           audition_left_dry_button (_("Out (dry)")),
85           audition_left_button (_("Out")),
86           audition_right_dry_button (_("In (dry)")),
87           audition_right_button (_("In")),
88
89           preroll_button (_("With Pre-roll")),
90           postroll_button (_("With Post-roll")),
91
92           miny (my),
93           maxy (mxy),
94
95           fade_in_table (3, 3),
96           fade_out_table (3, 3),
97
98           select_in_button (_("Fade In")),
99           select_out_button (_("Fade Out")),
100
101           _peaks_ready_connection (0)
102
103 {
104         set_session (s);
105
106         set_wmclass (X_("ardour_automationedit"), PROGRAM_NAME);
107         set_name ("CrossfadeEditWindow");
108
109         add_accel_group (ActionManager::ui_manager->get_accel_group());
110
111         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
112
113         RadioButtonGroup sel_but_group = select_in_button.get_group();
114         select_out_button.set_group (sel_but_group);
115         select_out_button.set_mode (false);
116         select_in_button.set_mode (false);
117
118         get_action_area()->set_layout(BUTTONBOX_SPREAD);
119         get_action_area()->pack_start(clear_button);
120         get_action_area()->pack_start(revert_button);
121         cancel_button = add_button ("Cancel", RESPONSE_CANCEL);
122         ok_button = add_button ("OK", RESPONSE_ACCEPT);
123
124         if (fade_in_presets == 0) {
125                 build_presets ();
126         }
127
128         point_grabbed = false;
129         toplevel = 0;
130
131         canvas = new ArdourCanvas::GtkCanvas ();
132         canvas->signal_size_allocate().connect (sigc::mem_fun(*this, &CrossfadeEditor::canvas_allocation));
133         canvas->set_size_request (425, 200);
134
135         toplevel = new ArdourCanvas::Rectangle (canvas->root());
136         toplevel->set (ArdourCanvas::Rect (0, 0, 10, 10));
137         toplevel->set_fill (true);
138         toplevel->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorBase());
139         toplevel->set_outline (false);
140         toplevel->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
141
142         fade[Out].line = new ArdourCanvas::PolyLine (canvas->root());
143         fade[Out].line->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorLine());
144
145         fade[Out].shading = new ArdourCanvas::Polygon (canvas->root());
146         fade[Out].shading->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorLineShading());
147
148         fade[In].line = new ArdourCanvas::PolyLine (canvas->root());
149         fade[In].line->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorLine());
150
151         fade[In].shading = new ArdourCanvas::Polygon (canvas->root());
152         fade[In].shading->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorLineShading());
153
154         fade[In].shading->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
155         fade[In].line->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
156         fade[Out].shading->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::canvas_event));
157         fade[Out].line->Event.connect (sigc::mem_fun (*this, &CrossfadeEditor::curve_event));
158
159         select_in_button.set_name (X_("CrossfadeEditCurveButton"));
160         select_out_button.set_name (X_("CrossfadeEditCurveButton"));
161
162         select_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::curve_select_clicked), In));
163         select_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::curve_select_clicked), Out));
164
165         HBox* acbox = manage (new HBox);
166
167         audition_box.set_border_width (7);
168         audition_box.set_spacing (5);
169         audition_box.set_homogeneous (false);
170         audition_box.pack_start (audition_left_dry_button, false, false);
171         audition_box.pack_start (audition_left_button, false, false);
172         audition_box.pack_start (audition_both_button, false, false);
173         audition_box.pack_start (audition_right_button, false, false);
174         audition_box.pack_start (audition_right_dry_button, false, false);
175
176         Frame* audition_frame = manage (new Frame (_("Audition")));
177
178         audition_frame->set_name (X_("CrossfadeEditFrame"));
179         audition_frame->add (audition_box);
180
181         acbox->pack_start (*audition_frame, true, false);
182
183         Frame* canvas_frame = manage (new Frame);
184         canvas_frame->add (*canvas);
185         canvas_frame->set_shadow_type (Gtk::SHADOW_IN);
186
187         fade_in_table.attach (select_in_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
188         fade_out_table.attach (select_out_button, 0, 2, 0, 1, Gtk::FILL|Gtk::EXPAND);
189
190         Image *pxmap;
191         Button* pbutton;
192         int row;
193         int col;
194
195         row = 1;
196         col = 0;
197
198         for (list<Preset*>::iterator i = fade_in_presets->begin(); i != fade_in_presets->end(); ++i) {
199
200                 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
201                 pbutton = manage (new Button);
202                 pbutton->add (*pxmap);
203                 pbutton->set_name ("CrossfadeEditButton");
204                 pbutton->signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
205                 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
206                 fade_in_table.attach (*pbutton, col, col+1, row, row+1);
207                 fade_in_buttons.push_back (pbutton);
208
209                 col++;
210
211                 if (col == 2) {
212                         col = 0;
213                         row++;
214                 }
215         }
216
217         row = 1;
218         col = 0;
219
220         for (list<Preset*>::iterator i = fade_out_presets->begin(); i != fade_out_presets->end(); ++i) {
221
222                 pxmap = manage (new Image (::get_icon ((*i)->image_name)));
223                 pbutton = manage (new Button);
224                 pbutton->add (*pxmap);
225                 pbutton->set_name ("CrossfadeEditButton");
226                 pbutton->signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &CrossfadeEditor::apply_preset), *i));
227                 ARDOUR_UI::instance()->set_tip (pbutton, (*i)->name, "");
228                 fade_out_table.attach (*pbutton, col, col+1, row, row+1);
229                 fade_out_buttons.push_back (pbutton);
230
231                 col++;
232
233                 if (col == 2) {
234                         col = 0;
235                         row++;
236                 }
237         }
238
239         clear_button.set_name ("CrossfadeEditButton");
240         revert_button.set_name ("CrossfadeEditButton");
241         ok_button->set_name ("CrossfadeEditButton");
242         cancel_button->set_name ("CrossfadeEditButton");
243         preroll_button.set_name ("CrossfadeEditButton");
244         postroll_button.set_name ("CrossfadeEditButton");
245         audition_both_button.set_name ("CrossfadeEditAuditionButton");
246         audition_left_dry_button.set_name ("CrossfadeEditAuditionButton");
247         audition_left_button.set_name ("CrossfadeEditAuditionButton");
248         audition_right_dry_button.set_name ("CrossfadeEditAuditionButton");
249         audition_right_button.set_name ("CrossfadeEditAuditionButton");
250
251         clear_button.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::clear));
252         revert_button.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::reset));
253         audition_both_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_toggled));
254         audition_right_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_right_toggled));
255         audition_right_dry_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_right_dry_toggled));
256         audition_left_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_left_toggled));
257         audition_left_dry_button.signal_toggled().connect (sigc::mem_fun(*this, &CrossfadeEditor::audition_left_dry_toggled));
258
259         roll_box.pack_start (preroll_button, false, false);
260         roll_box.pack_start (postroll_button, false, false);
261
262         Gtk::HBox* rcenter_box = manage (new HBox);
263         rcenter_box->pack_start (roll_box, true, false);
264
265         VBox* vpacker2 = manage (new (VBox));
266
267         vpacker2->set_border_width (12);
268         vpacker2->set_spacing (7);
269         vpacker2->pack_start (*acbox, false, false);
270         vpacker2->pack_start (*rcenter_box, false, false);
271
272         curve_button_box.set_spacing (7);
273         curve_button_box.pack_start (fade_out_table, false, false, 12);
274         curve_button_box.pack_start (*vpacker2, false, false, 12);
275         curve_button_box.pack_start (fade_in_table, false, false, 12);
276
277         get_vbox()->pack_start (*canvas_frame, true, true);
278         get_vbox()->pack_start (curve_button_box, false, false);
279
280         /* button to allow hackers to check the actual curve values */
281
282 //      Button* foobut = manage (new Button ("dump"));
283 //      foobut-.signal_clicked().connect (sigc::mem_fun(*this, &CrossfadeEditor::dump));
284 //      vpacker.pack_start (*foobut, false, false);
285
286         current = In;
287         set (xfade->fade_in(), In);
288
289         current = Out;
290         set (xfade->fade_out(), Out);
291
292         curve_select_clicked (In);
293
294         xfade->PropertyChanged.connect (state_connection, invalidator (*this), boost::bind (&CrossfadeEditor::xfade_changed, this, _1), gui_context());
295
296         _session->AuditionActive.connect (_session_connections, invalidator (*this), boost::bind (&CrossfadeEditor::audition_state_changed, this, _1), gui_context());
297         show_all_children();
298 }
299
300 CrossfadeEditor::~CrossfadeEditor()
301 {
302         /* most objects will be destroyed when the toplevel window is. */
303
304         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
305                 delete *i;
306         }
307
308         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
309                 delete *i;
310         }
311
312         delete _peaks_ready_connection;
313 }
314
315 void
316 CrossfadeEditor::dump ()
317 {
318         for (AutomationList::iterator i = fade[Out].normative_curve.begin(); i != fade[Out].normative_curve.end(); ++i) {
319                 cerr << (*i)->when << ' ' << (*i)->value << endl;
320         }
321 }
322
323 void
324 CrossfadeEditor::audition_state_changed (bool yn)
325 {
326         ENSURE_GUI_THREAD (*this, &CrossfadeEditor::audition_state_changed, yn)
327
328         if (!yn) {
329                 audition_both_button.set_active (false);
330                 audition_left_button.set_active (false);
331                 audition_right_button.set_active (false);
332                 audition_left_dry_button.set_active (false);
333                 audition_right_dry_button.set_active (false);
334         }
335 }
336
337 void
338 CrossfadeEditor::set (const ARDOUR::AutomationList& curve, WhichFade which)
339 {
340         double firstx, endx;
341         ARDOUR::AutomationList::const_iterator the_end;
342
343         for (list<Point*>::iterator i = fade[which].points.begin(); i != fade[which].points.end(); ++i) {
344                         delete *i;
345         }
346
347         fade[which].points.clear ();
348         fade[which].gain_curve.clear ();
349         fade[which].normative_curve.clear ();
350
351         if (curve.empty()) {
352                 goto out;
353         }
354
355         the_end = curve.end();
356         --the_end;
357
358         firstx = (*curve.begin())->when;
359         endx = (*the_end)->when;
360
361         for (ARDOUR::AutomationList::const_iterator i = curve.begin(); i != curve.end(); ++i) {
362
363                 double xfract = ((*i)->when - firstx) / (endx - firstx);
364                 double yfract = ((*i)->value - miny) / (maxy - miny);
365
366                 Point* p = make_point ();
367
368                 p->move_to (x_coordinate (xfract), y_coordinate (yfract),
369                             xfract, yfract);
370
371                 fade[which].points.push_back (p);
372         }
373
374         /* no need to sort because curve is already time-ordered */
375
376   out:
377
378         swap (which, current);
379         redraw ();
380         swap (which, current);
381 }
382
383 bool
384 CrossfadeEditor::curve_event (GdkEvent* event)
385 {
386         /* treat it like a toplevel event */
387
388         return canvas_event (event);
389 }
390
391 bool
392 CrossfadeEditor::point_event (GdkEvent* event, Point* point)
393 {
394
395         if (point->curve != fade[current].line) {
396                 return FALSE;
397         }
398
399         switch (event->type) {
400         case GDK_BUTTON_PRESS:
401                 point_grabbed = true;
402                 break;
403         case GDK_BUTTON_RELEASE:
404                 point_grabbed = false;
405
406                 if (Keyboard::is_delete_event (&event->button)) {
407                         fade[current].points.remove (point);
408                         delete point;
409                 }
410
411                 redraw ();
412                 break;
413
414         case GDK_MOTION_NOTIFY:
415                 if (point_grabbed) {
416                         double new_x, new_y;
417
418                         /* can't drag first or last points horizontally or vertically */
419
420                         if (point == fade[current].points.front() || point == fade[current].points.back()) {
421                                 new_x = point->x;
422                                 new_y = point->y;
423                         } else {
424                                 new_x = (event->motion.x - canvas_border)/effective_width();
425                                 new_y = 1.0 - ((event->motion.y - canvas_border)/effective_height());
426                         }
427
428                         point->move_to (x_coordinate (new_x), y_coordinate (new_y),
429                                         new_x, new_y);
430                         redraw ();
431                 }
432                 break;
433         default:
434                 break;
435         }
436         return TRUE;
437 }
438
439 bool
440 CrossfadeEditor::canvas_event (GdkEvent* event)
441 {
442         switch (event->type) {
443         case GDK_BUTTON_PRESS:
444                 add_control_point ((event->button.x - canvas_border)/effective_width(),
445                                    1.0 - ((event->button.y - canvas_border)/effective_height()));
446                 return true;
447                 break;
448         default:
449                 break;
450         }
451         return false;
452 }
453
454 CrossfadeEditor::Point::~Point()
455 {
456         delete box;
457 }
458
459 CrossfadeEditor::Point*
460 CrossfadeEditor::make_point ()
461 {
462         Point* p = new Point;
463
464         p->box = new ArdourCanvas::Rectangle (canvas->root());
465         p->box->set_fill (true);
466         p->box->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorPointFill());
467         p->box->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorPointOutline());
468
469         p->curve = fade[current].line;
470
471         p->box->Event.connect (sigc::bind (sigc::mem_fun (*this, &CrossfadeEditor::point_event), p));
472
473         return p;
474 }
475
476 void
477 CrossfadeEditor::add_control_point (double x, double y)
478 {
479         PointSorter cmp;
480
481         /* enforce end point x location */
482
483         if (fade[current].points.empty()) {
484                 x = 0.0;
485         } else if (fade[current].points.size() == 1) {
486                 x = 1.0;
487         }
488
489         Point* p = make_point ();
490
491         p->move_to (x_coordinate (x), y_coordinate (y), x, y);
492
493         fade[current].points.push_back (p);
494         fade[current].points.sort (cmp);
495
496         redraw ();
497 }
498
499 void
500 CrossfadeEditor::Point::move_to (double nx, double ny, double xfract, double yfract)
501 {
502         if ( xfract < 0.0 ) {
503                 xfract = 0.0;
504         } else if ( xfract > 1.0 ) {
505                 xfract = 1.0;
506         }
507
508         if ( yfract < 0.0 ) {
509                 yfract = 0.0;
510         } else if ( yfract > 1.0 ) {
511                 yfract = 1.0;
512         }
513
514         const double half_size = rint(size/2.0);
515         double x1 = nx - half_size;
516         double x2 = nx + half_size;
517
518         box->set (ArdourCanvas::Rect (x1, ny - half_size, x2, ny + half_size));
519
520         x = xfract;
521         y = yfract;
522 }
523
524 void
525 CrossfadeEditor::canvas_allocation (Gtk::Allocation& /*alloc*/)
526 {
527         if (toplevel) {
528                 toplevel->set (
529                         ArdourCanvas::Rect (
530                                 0,
531                                 0,
532                                 canvas->get_allocation().get_width() + canvas_border,
533                                 canvas->get_allocation().get_height() + canvas_border
534                                 )
535                         );
536         }
537
538         /* XXX: CANVAS */
539 //      canvas->set_scroll_region (0.0, 0.0,
540 //                                 canvas->get_allocation().get_width(),
541 //                                 canvas->get_allocation().get_height());
542
543         Point* end = make_point ();
544         PointSorter cmp;
545
546         if (fade[In].points.size() > 1) {
547                 Point* old_end = fade[In].points.back();
548                 fade[In].points.pop_back ();
549                 end->move_to (x_coordinate (old_end->x),
550                               y_coordinate (old_end->y),
551                               old_end->x, old_end->y);
552                 delete old_end;
553         } else {
554                 double x = 1.0;
555                 double y = 0.5;
556                 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
557
558         }
559
560         fade[In].points.push_back (end);
561         fade[In].points.sort (cmp);
562
563         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
564                 (*i)->move_to (x_coordinate((*i)->x), y_coordinate((*i)->y),
565                                (*i)->x, (*i)->y);
566         }
567
568         end = make_point ();
569
570         if (fade[Out].points.size() > 1) {
571                 Point* old_end = fade[Out].points.back();
572                 fade[Out].points.pop_back ();
573                 end->move_to (x_coordinate (old_end->x),
574                               y_coordinate (old_end->y),
575                               old_end->x, old_end->y);
576                 delete old_end;
577         } else {
578                 double x = 1.0;
579                 double y = 0.5;
580                 end->move_to (x_coordinate (x), y_coordinate (y), x, y);
581
582         }
583
584         fade[Out].points.push_back (end);
585         fade[Out].points.sort (cmp);
586
587         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
588                 (*i)->move_to (x_coordinate ((*i)->x),
589                                y_coordinate ((*i)->y),
590                                (*i)->x, (*i)->y);
591         }
592
593         WhichFade old_current = current;
594         current = In;
595         redraw ();
596         current = Out;
597         redraw ();
598         current = old_current;
599
600         double spu = xfade->length() / (double) effective_width();
601
602         if (fade[In].waves.empty()) {
603                 make_waves (xfade->in(), In);
604         }
605
606         if (fade[Out].waves.empty()) {
607                 make_waves (xfade->out(), Out);
608         }
609
610         double ht;
611         vector<ArdourCanvas::WaveView*>::iterator i;
612         uint32_t n;
613
614         ht = canvas->get_allocation().get_height() / xfade->in()->n_channels();
615
616         for (n = 0, i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i, ++n) {
617                 double yoff;
618
619                 yoff = n * ht;
620
621                 (*i)->set_y_position (yoff);
622                 (*i)->set_height (ht);
623                 (*i)->set_samples_per_pixel (spu);
624         }
625
626         ht = canvas->get_allocation().get_height() / xfade->out()->n_channels();
627
628         for (n = 0, i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i, ++n) {
629                 double yoff;
630
631                 yoff = n * ht;
632
633                 (*i)->set_y_position (yoff);
634                 (*i)->set_height (ht);
635                 (*i)->set_samples_per_pixel (spu);
636         }
637
638 }
639
640
641 void
642 CrossfadeEditor::xfade_changed (const PropertyChange&)
643 {
644         set (xfade->fade_in(), In);
645         set (xfade->fade_out(), Out);
646 }
647
648 void
649 CrossfadeEditor::redraw ()
650 {
651         if (canvas->get_allocation().get_width() < 2) {
652                 return;
653         }
654
655         framecnt_t len = xfade->length ();
656
657         fade[current].normative_curve.clear ();
658         fade[current].gain_curve.clear ();
659
660         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
661                 fade[current].normative_curve.add ((*i)->x, (*i)->y);
662                 double offset;
663                 if (current==In)
664                         offset = xfade->in()->start();
665                 else
666                         offset = xfade->out()->start()+xfade->out()->length()-xfade->length();
667                 fade[current].gain_curve.add (((*i)->x * len) + offset, (*i)->y);
668         }
669
670
671         size_t npoints = (size_t) effective_width();
672         float vec[npoints];
673
674         fade[current].normative_curve.curve().get_vector (0, 1.0, vec, npoints);
675
676         ArdourCanvas::Points pts;
677         ArdourCanvas::Points spts;
678
679         while (pts.size() < npoints) {
680                 pts.push_back (ArdourCanvas::Duple (0,0));
681         }
682
683         while (spts.size() < npoints + 3) {
684                 spts.push_back (ArdourCanvas::Duple (0,0));
685         }
686
687         /* the shade coordinates *MUST* be in anti-clockwise order.
688          */
689
690         if (current == In) {
691
692                 /* lower left */
693
694                 spts[0].x = canvas_border;
695                 spts[0].y = effective_height() + canvas_border;
696
697                 /* lower right */
698
699                 spts[1].x = effective_width() + canvas_border;
700                 spts[1].y = effective_height() + canvas_border;
701
702                 /* upper right */
703
704                 spts[2].x = effective_width() + canvas_border;
705                 spts[2].y = canvas_border;
706
707
708         } else {
709
710                 /*  upper left */
711
712                 spts[0].x = canvas_border;
713                 spts[0].y = canvas_border;
714
715                 /* lower left */
716
717                 spts[1].x = canvas_border;
718                 spts[1].y = effective_height() + canvas_border;
719
720                 /* lower right */
721
722                 spts[2].x = effective_width() + canvas_border;
723                 spts[2].y = effective_height() + canvas_border;
724
725         }
726
727         size_t last_spt = (npoints + 3) - 1;
728
729         for (size_t i = 0; i < npoints; ++i) {
730
731                 double y = vec[i];
732
733                 pts[i].x = canvas_border + i;
734                 pts[i].y = y_coordinate (y);
735
736                 spts[last_spt - i].x = canvas_border + i;
737                 spts[last_spt - i].y = pts[i].y;
738         }
739
740         fade[current].line->set (pts);
741         fade[current].shading->set (pts);
742
743         for (vector<ArdourCanvas::WaveView*>::iterator i = fade[current].waves.begin(); i != fade[current].waves.end(); ++i) {
744                 (*i)->property_gain_src() = static_cast<Evoral::Curve*>(&fade[current].gain_curve.curve());
745         }
746 }
747
748 void
749 CrossfadeEditor::apply_preset (Preset *preset)
750 {
751
752         WhichFade wf =  find(fade_in_presets->begin(), fade_in_presets->end(), preset) != fade_in_presets->end() ? In : Out;
753
754         if (current != wf) {
755
756                 if (wf == In) {
757                         select_in_button.clicked();
758                 } else {
759                         select_out_button.clicked();
760                 }
761
762                 curve_select_clicked (wf);
763         }
764
765         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
766                 delete *i;
767         }
768
769         fade[current].points.clear ();
770
771         for (Preset::iterator i = preset->begin(); i != preset->end(); ++i) {
772                 Point* p = make_point ();
773                 p->move_to (x_coordinate ((*i).x), y_coordinate ((*i).y),
774                             (*i).x, (*i).y);
775                 fade[current].points.push_back (p);
776         }
777
778         redraw ();
779 }
780
781 void
782 CrossfadeEditor::apply ()
783 {
784         the_editor().begin_reversible_command (_("Edit crossfade"));
785
786         XMLNode& before = xfade->get_state ();
787
788         _apply_to (xfade);
789
790         _session->add_command (
791                 new MementoCommand<Crossfade> (
792                         new ARDOUR::CrossfadeBinder (_session->playlists, xfade->id ()),
793                         &before, &xfade->get_state ()
794                         )
795                 );
796
797         the_editor().commit_reversible_command ();
798 }
799
800 void
801 CrossfadeEditor::_apply_to (boost::shared_ptr<Crossfade> xf)
802 {
803         ARDOUR::AutomationList& in (xf->fade_in());
804         ARDOUR::AutomationList& out (xf->fade_out());
805
806         /* IN */
807
808
809         ARDOUR::AutomationList::const_iterator the_end = in.end();
810         --the_end;
811
812         double firstx = (*in.begin())->when;
813         double endx = (*the_end)->when;
814
815         in.freeze ();
816         in.clear ();
817
818         for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
819
820                 double when = firstx + ((*i)->x * (endx - firstx));
821                 double value = (*i)->y;
822                 in.add (when, value);
823         }
824
825         /* OUT */
826
827         the_end = out.end();
828         --the_end;
829
830         firstx = (*out.begin())->when;
831         endx = (*the_end)->when;
832
833         out.freeze ();
834         out.clear ();
835
836         for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
837
838                 double when = firstx + ((*i)->x * (endx - firstx));
839                 double value = (*i)->y;
840                 out.add (when, value);
841         }
842
843         in.thaw ();
844         out.thaw ();
845 }
846
847 void
848 CrossfadeEditor::setup (boost::shared_ptr<Crossfade> xfade)
849 {
850         _apply_to (xfade);
851         xfade->set_active (true);
852         xfade->fade_in().curve().solve ();
853         xfade->fade_out().curve().solve ();
854 }
855
856 void
857 CrossfadeEditor::clear ()
858 {
859         for (list<Point*>::iterator i = fade[current].points.begin(); i != fade[current].points.end(); ++i) {
860                 delete *i;
861         }
862
863         fade[current].points.clear ();
864
865         redraw ();
866 }
867
868 void
869 CrossfadeEditor::reset ()
870 {
871         set (xfade->fade_in(),  In);
872         set (xfade->fade_out(), Out);
873
874         curve_select_clicked (current);
875 }
876
877 void
878 CrossfadeEditor::build_presets ()
879 {
880         Preset* p;
881
882         fade_in_presets = new Presets;
883         fade_out_presets = new Presets;
884
885         /* FADE IN */
886
887         p = new Preset ("Linear (-6dB)", "fadein-linear");
888         p->push_back (PresetPoint (0.000000, GAIN_COEFF_SMALL));
889         p->push_back (PresetPoint (0.166667, 0.166366));
890         p->push_back (PresetPoint (0.333333, 0.332853));
891         p->push_back (PresetPoint (0.500000, 0.499459));
892         p->push_back (PresetPoint (0.666667, 0.666186));
893         p->push_back (PresetPoint (0.833333, 0.833033));
894         p->push_back (PresetPoint (1.000000, GAIN_COEFF_UNITY));
895         fade_in_presets->push_back (p);
896
897         p = new Preset ("S(1)-curve", "fadein-S1");
898         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
899         p->push_back (PresetPoint (0.1, 0.01));
900         p->push_back (PresetPoint (0.2, 0.03));
901         p->push_back (PresetPoint (0.8, 0.97));
902         p->push_back (PresetPoint (0.9, 0.99));
903         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
904         fade_in_presets->push_back (p);
905
906         p = new Preset ("S(2)-curve", "fadein-S2");
907         p->push_back (PresetPoint (0.0, GAIN_COEFF_SMALL));
908         p->push_back (PresetPoint (0.055, 0.222));
909         p->push_back (PresetPoint (0.163, 0.35));
910         p->push_back (PresetPoint (0.837, 0.678));
911         p->push_back (PresetPoint (0.945, 0.783));
912         p->push_back (PresetPoint (1.0, GAIN_COEFF_UNITY));
913         fade_in_presets->push_back (p);
914
915         p = new Preset ("Constant power (-3dB)", "fadein-constant-power");
916
917         p->push_back (PresetPoint (0.000000, GAIN_COEFF_SMALL));
918         p->push_back (PresetPoint (0.166667, 0.282192));
919         p->push_back (PresetPoint (0.333333, 0.518174));
920         p->push_back (PresetPoint (0.500000, 0.707946));
921         p->push_back (PresetPoint (0.666667, 0.851507));
922         p->push_back (PresetPoint (0.833333, 0.948859));
923         p->push_back (PresetPoint (1.000000, GAIN_COEFF_UNITY));
924
925         fade_in_presets->push_back (p);
926
927         p = new Preset ("Short cut", "fadein-short-cut");
928         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
929         p->push_back (PresetPoint (0.389401, 0.0333333));
930         p->push_back (PresetPoint (0.629032, 0.0861111));
931         p->push_back (PresetPoint (0.829493, 0.233333));
932         p->push_back (PresetPoint (0.9447, 0.483333));
933         p->push_back (PresetPoint (0.976959, 0.697222));
934         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
935         fade_in_presets->push_back (p);
936
937         p = new Preset ("Slow cut", "fadein-slow-cut");
938         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
939         p->push_back (PresetPoint (0.304147, 0.0694444));
940         p->push_back (PresetPoint (0.529954, 0.152778));
941         p->push_back (PresetPoint (0.725806, 0.333333));
942         p->push_back (PresetPoint (0.847926, 0.558333));
943         p->push_back (PresetPoint (0.919355, 0.730556));
944         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
945         fade_in_presets->push_back (p);
946
947         p = new Preset ("Fast cut", "fadein-fast-cut");
948         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
949         p->push_back (PresetPoint (0.0737327, 0.308333));
950         p->push_back (PresetPoint (0.246544, 0.658333));
951         p->push_back (PresetPoint (0.470046, 0.886111));
952         p->push_back (PresetPoint (0.652074, 0.972222));
953         p->push_back (PresetPoint (0.771889, 0.988889));
954         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
955         fade_in_presets->push_back (p);
956
957         p = new Preset ("Long cut", "fadein-long-cut");
958         p->push_back (PresetPoint (0, GAIN_COEFF_SMALL));
959         p->push_back (PresetPoint (0.0207373, 0.197222));
960         p->push_back (PresetPoint (0.0645161, 0.525));
961         p->push_back (PresetPoint (0.152074, 0.802778));
962         p->push_back (PresetPoint (0.276498, 0.919444));
963         p->push_back (PresetPoint (0.481567, 0.980556));
964         p->push_back (PresetPoint (0.767281, 1));
965         p->push_back (PresetPoint (1, GAIN_COEFF_UNITY));
966         fade_in_presets->push_back (p);
967
968         /* FADE OUT */
969
970         // p = new Preset ("regout.xpm");
971         p = new Preset ("Linear (-6dB cut)", "fadeout-linear");
972         p->push_back (PresetPoint (0.000000, GAIN_COEFF_UNITY));
973         p->push_back (PresetPoint (0.166667, 0.833033));
974         p->push_back (PresetPoint (0.333333, 0.666186));
975         p->push_back (PresetPoint (0.500000, 0.499459));
976         p->push_back (PresetPoint (0.666667, 0.332853));
977         p->push_back (PresetPoint (0.833333, 0.166366));
978         p->push_back (PresetPoint (1.000000, GAIN_COEFF_SMALL));
979         fade_out_presets->push_back (p);
980
981         p = new Preset ("S(1)-Curve", "fadeout-S1");
982         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
983         p->push_back (PresetPoint (0.1, 0.99));
984         p->push_back (PresetPoint (0.2, 0.97));
985         p->push_back (PresetPoint (0.8, 0.03));
986         p->push_back (PresetPoint (0.9, 0.01));
987         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
988         fade_out_presets->push_back (p);
989
990         p = new Preset ("S(2)-Curve", "fadeout-S2");
991         p->push_back (PresetPoint (0.0, GAIN_COEFF_UNITY));
992         p->push_back (PresetPoint (0.163, 0.678));
993         p->push_back (PresetPoint (0.055, 0.783));
994         p->push_back (PresetPoint (0.837, 0.35));
995         p->push_back (PresetPoint (0.945, 0.222));
996         p->push_back (PresetPoint (1.0, GAIN_COEFF_SMALL));
997         fade_out_presets->push_back (p);
998
999         // p = new Preset ("linout.xpm");
1000         p = new Preset ("Constant power (-3dB cut)", "fadeout-constant-power");
1001         p->push_back (PresetPoint (0.000000, GAIN_COEFF_UNITY));
1002         p->push_back (PresetPoint (0.166667, 0.948859));
1003         p->push_back (PresetPoint (0.333333, 0.851507));
1004         p->push_back (PresetPoint (0.500000, 0.707946));
1005         p->push_back (PresetPoint (0.666667, 0.518174));
1006         p->push_back (PresetPoint (0.833333, 0.282192));
1007         p->push_back (PresetPoint (1.000000, GAIN_COEFF_SMALL));
1008         fade_out_presets->push_back (p);
1009
1010         // p = new Preset ("hiout.xpm");
1011         p = new Preset ("Short cut", "fadeout-short-cut");
1012         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1013         p->push_back (PresetPoint (0.305556, GAIN_COEFF_UNITY));
1014         p->push_back (PresetPoint (0.548611, 0.991736));
1015         p->push_back (PresetPoint (0.759259, 0.931129));
1016         p->push_back (PresetPoint (0.918981, 0.68595));
1017         p->push_back (PresetPoint (0.976852, 0.22865));
1018         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1019         fade_out_presets->push_back (p);
1020
1021         p = new Preset ("Slow cut", "fadeout-slow-cut");
1022         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1023         p->push_back (PresetPoint (0.228111, 0.988889));
1024         p->push_back (PresetPoint (0.347926, 0.972222));
1025         p->push_back (PresetPoint (0.529954, 0.886111));
1026         p->push_back (PresetPoint (0.753456, 0.658333));
1027         p->push_back (PresetPoint (0.9262673, 0.308333));
1028         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1029         fade_out_presets->push_back (p);
1030
1031         p = new Preset ("Fast cut", "fadeout-fast-cut");
1032         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1033         p->push_back (PresetPoint (0.080645, 0.730556));
1034         p->push_back (PresetPoint (0.277778, 0.289256));
1035         p->push_back (PresetPoint (0.470046, 0.152778));
1036         p->push_back (PresetPoint (0.695853, 0.0694444));
1037         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1038         fade_out_presets->push_back (p);
1039
1040         // p = new Preset ("loout.xpm");
1041         p = new Preset ("Long cut", "fadeout-long-cut");
1042         p->push_back (PresetPoint (0, GAIN_COEFF_UNITY));
1043         p->push_back (PresetPoint (0.023041, 0.697222));
1044         p->push_back (PresetPoint (0.0553,   0.483333));
1045         p->push_back (PresetPoint (0.170507, 0.233333));
1046         p->push_back (PresetPoint (0.370968, 0.0861111));
1047         p->push_back (PresetPoint (0.610599, 0.0333333));
1048         p->push_back (PresetPoint (1, GAIN_COEFF_SMALL));
1049         fade_out_presets->push_back (p);
1050 }
1051
1052 void
1053 CrossfadeEditor::curve_select_clicked (WhichFade wf)
1054 {
1055         current = wf;
1056
1057         if (wf == In) {
1058
1059                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1060                         (*i)->set_outline_color (UIConfiguration::instance().get_SelectedCrossfadeEditorWave());
1061                         (*i)->set_fill_color (UIConfiguration::instance().get_SelectedCrossfadeEditorWave());
1062                 }
1063
1064                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1065                         (*i)->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorWave());
1066                         (*i)->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorWave());
1067                 }
1068
1069                 fade[In].line->set_outline_color (UIConfiguration::instance().get_SelectedCrossfadeEditorLine());
1070                 fade[Out].line->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorLine());
1071                 fade[Out].shading->hide();
1072                 fade[In].shading->show();
1073
1074                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1075                         (*i)->box->hide();
1076                 }
1077
1078                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1079                         (*i)->box->show ();
1080                 }
1081
1082         } else {
1083
1084                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1085                         (*i)->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorWave());
1086                         (*i)->set_fill_color (UIConfiguration::instance().get_CrossfadeEditorWave());
1087                 }
1088
1089                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1090                         (*i)->set_outline_color (UIConfiguration::instance().get_SelectedCrossfadeEditorWave());
1091                         (*i)->set_fill_color (UIConfiguration::instance().get_SelectedCrossfadeEditorWave());
1092                 }
1093
1094                 fade[Out].line->set_outline_color (UIConfiguration::instance().get_SelectedCrossfadeEditorLine());
1095                 fade[In].line->set_outline_color (UIConfiguration::instance().get_CrossfadeEditorLine());
1096                 fade[In].shading->hide();
1097                 fade[Out].shading->show();
1098
1099                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1100                         (*i)->box->hide();
1101                 }
1102
1103                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1104                         (*i)->box->show();
1105                 }
1106
1107         }
1108 }
1109
1110 double
1111 CrossfadeEditor::x_coordinate (double& xfract) const
1112 {
1113         xfract = min (1.0, xfract);
1114         xfract = max (0.0, xfract);
1115
1116         return canvas_border + (xfract * effective_width());
1117 }
1118
1119 double
1120 CrossfadeEditor::y_coordinate (double& yfract) const
1121 {
1122         yfract = min (1.0, yfract);
1123         yfract = max (0.0, yfract);
1124
1125         return (canvas->get_allocation().get_height() - (canvas_border)) - (yfract * effective_height());
1126 }
1127
1128 void
1129 CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade which)
1130 {
1131         gdouble ht;
1132         uint32_t nchans = region->n_channels();
1133         guint32 color;
1134         double spu;
1135
1136         if (which == In) {
1137                 color = UIConfiguration::instance().get_SelectedCrossfadeEditorWave();
1138         } else {
1139                 color = UIConfiguration::instance().get_CrossfadeEditorWave();
1140         }
1141
1142         ht = canvas->get_allocation().get_height() / (double) nchans;
1143         spu = xfade->length() / (double) effective_width();
1144
1145         delete _peaks_ready_connection;
1146         _peaks_ready_connection = 0;
1147
1148         for (uint32_t n = 0; n < nchans; ++n) {
1149
1150                 gdouble yoff = n * ht;
1151
1152                 if (region->audio_source(n)->peaks_ready (boost::bind (&CrossfadeEditor::peaks_ready, this, boost::weak_ptr<AudioRegion>(region), which), &_peaks_ready_connection, gui_context())) {
1153                         ArdourCanvas::WaveView* waveview = new ArdourCanvas::WaveView (canvas->root(), region);
1154
1155                         waveview->set_channel (n);
1156                         waveview->property_gain_function() = (void*) curve_get_vector_from_c;
1157                         waveview->property_gain_src() = static_cast<Evoral::Curve*>(&fade[which].gain_curve.curve());
1158                         waveview->set_x_position (canvas_border);
1159                         waveview->set_y_position (yoff);
1160                         waveview->set_height (ht);
1161                         waveview->set_samples_per_pixel (spu);
1162                         waveview->property_amplitude_above_axis() = 2.0;
1163                         waveview->set_outline_color (color);
1164                         waveview->set_fill_color (color);
1165
1166                         if (which != In) {
1167                                 waveview->set_region_start (region->start() + region->length() - xfade->length());
1168                         }
1169
1170                         waveview->lower_to_bottom();
1171                         fade[which].waves.push_back (waveview);
1172                 }
1173         }
1174
1175         toplevel->lower_to_bottom();
1176 }
1177
1178 void
1179 CrossfadeEditor::peaks_ready (boost::weak_ptr<AudioRegion> wr, WhichFade which)
1180 {
1181         boost::shared_ptr<AudioRegion> r (wr.lock());
1182
1183         if (!r) {
1184                 return;
1185         }
1186
1187         /* this should never be called, because the peak files for an xfade
1188            will be ready by the time we want them. but our API forces us
1189            to provide this, so ..
1190         */
1191         delete _peaks_ready_connection;
1192         _peaks_ready_connection = 0;
1193
1194         make_waves (r, which);
1195 }
1196
1197 void
1198 CrossfadeEditor::audition (Audition which)
1199 {
1200         AudioPlaylist& pl (_session->the_auditioner()->prepare_playlist());
1201         framecnt_t preroll;
1202         framecnt_t postroll;
1203         framecnt_t left_start_offset;
1204         framecnt_t right_length;
1205         framecnt_t left_length;
1206
1207         if (which != Right && preroll_button.get_active()) {
1208                 preroll = _session->frame_rate() * 2;  //2 second hardcoded preroll for now
1209         } else {
1210                 preroll = 0;
1211         }
1212
1213         if (which != Left && postroll_button.get_active()) {
1214                 postroll = _session->frame_rate() * 2;  //2 second hardcoded postroll for now
1215         } else {
1216                 postroll = 0;
1217         }
1218
1219         // Is there enough data for the whole preroll?
1220         left_length = xfade->length();
1221         if ((left_start_offset = xfade->out()->length() - xfade->length()) > preroll) {
1222                 left_start_offset -= preroll;
1223         } else {
1224                 preroll = left_start_offset;
1225                 left_start_offset = 0;
1226         }
1227         left_length += preroll;
1228
1229         // Is there enough data for the whole postroll?
1230         right_length = xfade->length();
1231         if ((xfade->in()->length() - right_length) > postroll) {
1232                 right_length += postroll;
1233         } else {
1234                 right_length = xfade->in()->length();
1235         }
1236
1237         PropertyList left_plist;
1238         PropertyList right_plist;
1239
1240
1241         left_plist.add (ARDOUR::Properties::start, left_start_offset);
1242         left_plist.add (ARDOUR::Properties::length, left_length);
1243         left_plist.add (ARDOUR::Properties::name, string ("xfade out"));
1244         left_plist.add (ARDOUR::Properties::layer, 0);
1245         left_plist.add (ARDOUR::Properties::fade_in_active, true);
1246
1247         right_plist.add (ARDOUR::Properties::start, 0);
1248         right_plist.add (ARDOUR::Properties::length, right_length);
1249         right_plist.add (ARDOUR::Properties::name, string("xfade in"));
1250         right_plist.add (ARDOUR::Properties::layer, 0);
1251         right_plist.add (ARDOUR::Properties::fade_out_active, true);
1252
1253         if (which == Left) {
1254                 right_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1255         } else if (which == Right) {
1256                 left_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1257         }
1258
1259         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1260                                                      (RegionFactory::create (xfade->out(), left_plist, false)));
1261         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1262                                               (RegionFactory::create (xfade->in(), right_plist, false)));
1263
1264         // apply a 20ms declicking fade at the start and end of auditioning
1265         // XXX this should really be a property
1266
1267         left->set_fade_in_length (_session->frame_rate() / 50);
1268         right->set_fade_out_length (_session->frame_rate() / 50);
1269
1270         pl.add_region (left, 0);
1271         pl.add_region (right, 1 + preroll);
1272
1273         /* there is only one ... */
1274         pl.foreach_crossfade (sigc::mem_fun (*this, &CrossfadeEditor::setup));
1275
1276         _session->audition_playlist ();
1277 }
1278
1279 void
1280 CrossfadeEditor::audition_both ()
1281 {
1282         audition (Both);
1283 }
1284
1285 void
1286 CrossfadeEditor::audition_left_dry ()
1287 {
1288         PropertyList plist;
1289
1290         plist.add (ARDOUR::Properties::start, xfade->out()->length() - xfade->length());
1291         plist.add (ARDOUR::Properties::length, xfade->length());
1292         plist.add (ARDOUR::Properties::name, string("xfade left"));
1293         plist.add (ARDOUR::Properties::layer, 0);
1294
1295         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1296                                              (RegionFactory::create (xfade->out(), plist, false)));
1297
1298         _session->audition_region (left);
1299 }
1300
1301 void
1302 CrossfadeEditor::audition_left ()
1303 {
1304         audition (Left);
1305 }
1306
1307 void
1308 CrossfadeEditor::audition_right_dry ()
1309 {
1310         PropertyList plist;
1311
1312         plist.add (ARDOUR::Properties::start, 0);
1313         plist.add (ARDOUR::Properties::length, xfade->length());
1314         plist.add (ARDOUR::Properties::name, string ("xfade right"));
1315         plist.add (ARDOUR::Properties::layer, 0);
1316
1317         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1318                                               (RegionFactory::create (xfade->in(), plist, false)));
1319
1320         _session->audition_region (right);
1321 }
1322
1323 void
1324 CrossfadeEditor::audition_right ()
1325 {
1326         audition (Right);
1327 }
1328
1329 void
1330 CrossfadeEditor::cancel_audition ()
1331 {
1332         _session->cancel_audition ();
1333 }
1334
1335 void
1336 CrossfadeEditor::audition_toggled ()
1337 {
1338         bool x;
1339
1340         if ((x = audition_both_button.get_active ()) != _session->is_auditioning()) {
1341
1342                 if (x) {
1343                         audition_both ();
1344                 } else {
1345                         cancel_audition ();
1346                 }
1347         }
1348 }
1349
1350 void
1351 CrossfadeEditor::audition_right_toggled ()
1352 {
1353         bool x;
1354
1355         if ((x = audition_right_button.get_active ()) != _session->is_auditioning()) {
1356
1357                 if (x) {
1358                         audition_right ();
1359                 } else {
1360                         cancel_audition ();
1361                 }
1362         }
1363 }
1364
1365 void
1366 CrossfadeEditor::audition_right_dry_toggled ()
1367 {
1368         bool x;
1369
1370         if ((x = audition_right_dry_button.get_active ()) != _session->is_auditioning()) {
1371
1372                 if (x) {
1373                         audition_right_dry ();
1374                 } else {
1375                         cancel_audition ();
1376                 }
1377         }
1378 }
1379
1380 void
1381 CrossfadeEditor::audition_left_toggled ()
1382 {
1383         bool x;
1384
1385         if ((x = audition_left_button.get_active ()) != _session->is_auditioning()) {
1386
1387                 if (x) {
1388                         audition_left ();
1389                 } else {
1390                         cancel_audition ();
1391                 }
1392         }
1393 }
1394
1395 void
1396 CrossfadeEditor::audition_left_dry_toggled ()
1397 {
1398         bool x;
1399
1400         if ((x = audition_left_dry_button.get_active ()) != _session->is_auditioning()) {
1401
1402                 if (x) {
1403                         audition_left_dry ();
1404                 } else {
1405                         cancel_audition ();
1406                 }
1407         }
1408 }
1409
1410 bool
1411 CrossfadeEditor::on_key_press_event (GdkEventKey */*ev*/)
1412 {
1413         return true;
1414 }
1415
1416 bool
1417 CrossfadeEditor::on_key_release_event (GdkEventKey* ev)
1418 {
1419         switch (ev->keyval) {
1420         case GDK_Right:
1421                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1422                         audition_right_dry_button.set_active (!audition_right_dry_button.get_active());
1423                 } else {
1424                         audition_right_button.set_active (!audition_right_button.get_active());
1425                 }
1426                 break;
1427
1428         case GDK_Left:
1429                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1430                         audition_left_dry_button.set_active (!audition_left_dry_button.get_active());
1431                 } else {
1432                         audition_left_button.set_active (!audition_left_button.get_active());
1433                 }
1434                 break;
1435
1436         case GDK_space:
1437                 if (_session->is_auditioning()) {
1438                         cancel_audition ();
1439                 } else {
1440                         audition_both_button.set_active (!audition_both_button.get_active());
1441                 }
1442                 break;
1443
1444         default:
1445                 break;
1446         }
1447
1448         return true;
1449 }