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