Merge branch 'export-dialog' into cairocanvas
[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 "utils.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 (ARDOUR_UI::config()->get_canvasvar_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 (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorLine());
144
145         fade[Out].shading = new ArdourCanvas::Polygon (canvas->root());
146         fade[Out].shading->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorLineShading());
147
148         fade[In].line = new ArdourCanvas::PolyLine (canvas->root());
149         fade[In].line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorLine());
150
151         fade[In].shading = new ArdourCanvas::Polygon (canvas->root());
152         fade[In].shading->set_fill_color (ARDOUR_UI::config()->get_canvasvar_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 (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorPointFill());
467         p->box->set_outline_color (ARDOUR_UI::config()->get_canvasvar_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         _session->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         _session->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, 0));
889         p->push_back (PresetPoint (0.000000, 0.000000));
890         p->push_back (PresetPoint (0.166667, 0.166366));
891         p->push_back (PresetPoint (0.333333, 0.332853));
892         p->push_back (PresetPoint (0.500000, 0.499459));
893         p->push_back (PresetPoint (0.666667, 0.666186));
894         p->push_back (PresetPoint (0.833333, 0.833033));
895         p->push_back (PresetPoint (1.000000, 1.000000));
896         fade_in_presets->push_back (p);
897
898         p = new Preset ("S(1)-curve", "fadein-S1");
899         p->push_back (PresetPoint (0, 0));
900         p->push_back (PresetPoint (0.1, 0.01));
901         p->push_back (PresetPoint (0.2, 0.03));
902         p->push_back (PresetPoint (0.8, 0.97));
903         p->push_back (PresetPoint (0.9, 0.99));
904         p->push_back (PresetPoint (1, 1));
905         fade_in_presets->push_back (p);
906
907         p = new Preset ("S(2)-curve", "fadein-S2");
908         p->push_back (PresetPoint (0.0, 0.0));
909         p->push_back (PresetPoint (0.055, 0.222));
910         p->push_back (PresetPoint (0.163, 0.35));
911         p->push_back (PresetPoint (0.837, 0.678));
912         p->push_back (PresetPoint (0.945, 0.783));
913         p->push_back (PresetPoint (1.0, 1.0));
914         fade_in_presets->push_back (p);
915
916         p = new Preset ("Constant power (-3dB)", "fadein-constant-power");
917
918         p->push_back (PresetPoint (0.000000, 0.000000));
919         p->push_back (PresetPoint (0.166667, 0.282192));
920         p->push_back (PresetPoint (0.333333, 0.518174));
921         p->push_back (PresetPoint (0.500000, 0.707946));
922         p->push_back (PresetPoint (0.666667, 0.851507));
923         p->push_back (PresetPoint (0.833333, 0.948859));
924         p->push_back (PresetPoint (1.000000, 1.000000));
925
926         fade_in_presets->push_back (p);
927
928         if (!Profile->get_sae()) {
929
930                 p = new Preset ("Short cut", "fadein-short-cut");
931                 p->push_back (PresetPoint (0, 0));
932                 p->push_back (PresetPoint (0.389401, 0.0333333));
933                 p->push_back (PresetPoint (0.629032, 0.0861111));
934                 p->push_back (PresetPoint (0.829493, 0.233333));
935                 p->push_back (PresetPoint (0.9447, 0.483333));
936                 p->push_back (PresetPoint (0.976959, 0.697222));
937                 p->push_back (PresetPoint (1, 1));
938                 fade_in_presets->push_back (p);
939
940                 p = new Preset ("Slow cut", "fadein-slow-cut");
941                 p->push_back (PresetPoint (0, 0));
942                 p->push_back (PresetPoint (0.304147, 0.0694444));
943                 p->push_back (PresetPoint (0.529954, 0.152778));
944                 p->push_back (PresetPoint (0.725806, 0.333333));
945                 p->push_back (PresetPoint (0.847926, 0.558333));
946                 p->push_back (PresetPoint (0.919355, 0.730556));
947                 p->push_back (PresetPoint (1, 1));
948                 fade_in_presets->push_back (p);
949
950                 p = new Preset ("Fast cut", "fadein-fast-cut");
951                 p->push_back (PresetPoint (0, 0));
952                 p->push_back (PresetPoint (0.0737327, 0.308333));
953                 p->push_back (PresetPoint (0.246544, 0.658333));
954                 p->push_back (PresetPoint (0.470046, 0.886111));
955                 p->push_back (PresetPoint (0.652074, 0.972222));
956                 p->push_back (PresetPoint (0.771889, 0.988889));
957                 p->push_back (PresetPoint (1, 1));
958                 fade_in_presets->push_back (p);
959
960                 p = new Preset ("Long cut", "fadein-long-cut");
961                 p->push_back (PresetPoint (0, 0));
962                 p->push_back (PresetPoint (0.0207373, 0.197222));
963                 p->push_back (PresetPoint (0.0645161, 0.525));
964                 p->push_back (PresetPoint (0.152074, 0.802778));
965                 p->push_back (PresetPoint (0.276498, 0.919444));
966                 p->push_back (PresetPoint (0.481567, 0.980556));
967                 p->push_back (PresetPoint (0.767281, 1));
968                 p->push_back (PresetPoint (1, 1));
969                 fade_in_presets->push_back (p);
970         }
971
972         /* FADE OUT */
973
974         // p = new Preset ("regout.xpm");
975         p = new Preset ("Linear (-6dB cut)", "fadeout-linear");
976         p->push_back (PresetPoint (0, 1));
977         p->push_back (PresetPoint (0.000000, 1.000000));
978         p->push_back (PresetPoint (0.166667, 0.833033));
979         p->push_back (PresetPoint (0.333333, 0.666186));
980         p->push_back (PresetPoint (0.500000, 0.499459));
981         p->push_back (PresetPoint (0.666667, 0.332853));
982         p->push_back (PresetPoint (0.833333, 0.166366));
983         p->push_back (PresetPoint (1.000000, 0.000000));
984         fade_out_presets->push_back (p);
985
986         p = new Preset ("S(1)-Curve", "fadeout-S1");
987         p->push_back (PresetPoint (0, 1));
988         p->push_back (PresetPoint (0.1, 0.99));
989         p->push_back (PresetPoint (0.2, 0.97));
990         p->push_back (PresetPoint (0.8, 0.03));
991         p->push_back (PresetPoint (0.9, 0.01));
992         p->push_back (PresetPoint (1, 0));
993         fade_out_presets->push_back (p);
994
995         p = new Preset ("S(2)-Curve", "fadeout-S2");
996         p->push_back (PresetPoint (0.0, 1.0));
997         p->push_back (PresetPoint (0.163, 0.678));
998         p->push_back (PresetPoint (0.055, 0.783));
999         p->push_back (PresetPoint (0.837, 0.35));
1000         p->push_back (PresetPoint (0.945, 0.222));
1001         p->push_back (PresetPoint (1.0, 0.0));
1002         fade_out_presets->push_back (p);
1003
1004         // p = new Preset ("linout.xpm");
1005         p = new Preset ("Constant power (-3dB cut)", "fadeout-constant-power");
1006         p->push_back (PresetPoint (0.000000, 1.000000));
1007         p->push_back (PresetPoint (0.166667, 0.948859));
1008         p->push_back (PresetPoint (0.333333, 0.851507));
1009         p->push_back (PresetPoint (0.500000, 0.707946));
1010         p->push_back (PresetPoint (0.666667, 0.518174));
1011         p->push_back (PresetPoint (0.833333, 0.282192));
1012         p->push_back (PresetPoint (1.000000, 0.000000));
1013         fade_out_presets->push_back (p);
1014
1015         if (!Profile->get_sae()) {
1016                 // p = new Preset ("hiout.xpm");
1017                 p = new Preset ("Short cut", "fadeout-short-cut");
1018                 p->push_back (PresetPoint (0, 1));
1019                 p->push_back (PresetPoint (0.305556, 1));
1020                 p->push_back (PresetPoint (0.548611, 0.991736));
1021                 p->push_back (PresetPoint (0.759259, 0.931129));
1022                 p->push_back (PresetPoint (0.918981, 0.68595));
1023                 p->push_back (PresetPoint (0.976852, 0.22865));
1024                 p->push_back (PresetPoint (1, 0));
1025                 fade_out_presets->push_back (p);
1026
1027                 p = new Preset ("Slow cut", "fadeout-slow-cut");
1028                 p->push_back (PresetPoint (0, 1));
1029                 p->push_back (PresetPoint (0.228111, 0.988889));
1030                 p->push_back (PresetPoint (0.347926, 0.972222));
1031                 p->push_back (PresetPoint (0.529954, 0.886111));
1032                 p->push_back (PresetPoint (0.753456, 0.658333));
1033                 p->push_back (PresetPoint (0.9262673, 0.308333));
1034                 p->push_back (PresetPoint (1, 0));
1035                 fade_out_presets->push_back (p);
1036
1037                 p = new Preset ("Fast cut", "fadeout-fast-cut");
1038                 p->push_back (PresetPoint (0, 1));
1039                 p->push_back (PresetPoint (0.080645, 0.730556));
1040                 p->push_back (PresetPoint (0.277778, 0.289256));
1041                 p->push_back (PresetPoint (0.470046, 0.152778));
1042                 p->push_back (PresetPoint (0.695853, 0.0694444));
1043                 p->push_back (PresetPoint (1, 0));
1044                 fade_out_presets->push_back (p);
1045
1046                 // p = new Preset ("loout.xpm");
1047                 p = new Preset ("Long cut", "fadeout-long-cut");
1048                 p->push_back (PresetPoint (0, 1));
1049                 p->push_back (PresetPoint (0.023041, 0.697222));
1050                 p->push_back (PresetPoint (0.0553,   0.483333));
1051                 p->push_back (PresetPoint (0.170507, 0.233333));
1052                 p->push_back (PresetPoint (0.370968, 0.0861111));
1053                 p->push_back (PresetPoint (0.610599, 0.0333333));
1054                 p->push_back (PresetPoint (1, 0));
1055                 fade_out_presets->push_back (p);
1056
1057         }
1058 }
1059
1060 void
1061 CrossfadeEditor::curve_select_clicked (WhichFade wf)
1062 {
1063         current = wf;
1064
1065         if (wf == In) {
1066
1067                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1068                         (*i)->set_outline_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorWave());
1069                         (*i)->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorWave());
1070                 }
1071
1072                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1073                         (*i)->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorWave());
1074                         (*i)->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorWave());
1075                 }
1076
1077                 fade[In].line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorLine());
1078                 fade[Out].line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorLine());
1079                 fade[Out].shading->hide();
1080                 fade[In].shading->show();
1081
1082                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1083                         (*i)->box->hide();
1084                 }
1085
1086                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1087                         (*i)->box->show ();
1088                 }
1089
1090         } else {
1091
1092                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
1093                         (*i)->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorWave());
1094                         (*i)->set_fill_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorWave());
1095                 }
1096
1097                 for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
1098                         (*i)->set_outline_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorWave());
1099                         (*i)->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorWave());
1100                 }
1101
1102                 fade[Out].line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorLine());
1103                 fade[In].line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorLine());
1104                 fade[In].shading->hide();
1105                 fade[Out].shading->show();
1106
1107                 for (list<Point*>::iterator i = fade[In].points.begin(); i != fade[In].points.end(); ++i) {
1108                         (*i)->box->hide();
1109                 }
1110
1111                 for (list<Point*>::iterator i = fade[Out].points.begin(); i != fade[Out].points.end(); ++i) {
1112                         (*i)->box->show();
1113                 }
1114
1115         }
1116 }
1117
1118 double
1119 CrossfadeEditor::x_coordinate (double& xfract) const
1120 {
1121         xfract = min (1.0, xfract);
1122         xfract = max (0.0, xfract);
1123
1124         return canvas_border + (xfract * effective_width());
1125 }
1126
1127 double
1128 CrossfadeEditor::y_coordinate (double& yfract) const
1129 {
1130         yfract = min (1.0, yfract);
1131         yfract = max (0.0, yfract);
1132
1133         return (canvas->get_allocation().get_height() - (canvas_border)) - (yfract * effective_height());
1134 }
1135
1136 void
1137 CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade which)
1138 {
1139         gdouble ht;
1140         uint32_t nchans = region->n_channels();
1141         guint32 color;
1142         double spu;
1143
1144         if (which == In) {
1145                 color = ARDOUR_UI::config()->get_canvasvar_SelectedCrossfadeEditorWave();
1146         } else {
1147                 color = ARDOUR_UI::config()->get_canvasvar_CrossfadeEditorWave();
1148         }
1149
1150         ht = canvas->get_allocation().get_height() / (double) nchans;
1151         spu = xfade->length() / (double) effective_width();
1152
1153         delete _peaks_ready_connection;
1154         _peaks_ready_connection = 0;
1155
1156         for (uint32_t n = 0; n < nchans; ++n) {
1157
1158                 gdouble yoff = n * ht;
1159
1160                 if (region->audio_source(n)->peaks_ready (boost::bind (&CrossfadeEditor::peaks_ready, this, boost::weak_ptr<AudioRegion>(region), which), &_peaks_ready_connection, gui_context())) {
1161                         ArdourCanvas::WaveView* waveview = new ArdourCanvas::WaveView (canvas->root(), region);
1162
1163                         waveview->set_channel (n);
1164                         waveview->property_gain_function() = (void*) curve_get_vector_from_c;
1165                         waveview->property_gain_src() = static_cast<Evoral::Curve*>(&fade[which].gain_curve.curve());
1166                         waveview->set_x_position (canvas_border);
1167                         waveview->set_y_position (yoff);
1168                         waveview->set_height (ht);
1169                         waveview->set_samples_per_pixel (spu);
1170                         waveview->property_amplitude_above_axis() = 2.0;
1171                         waveview->set_outline_color (color);
1172                         waveview->set_fill_color (color);
1173
1174                         if (which != In) {
1175                                 waveview->set_region_start (region->start() + region->length() - xfade->length());
1176                         }
1177
1178                         waveview->lower_to_bottom();
1179                         fade[which].waves.push_back (waveview);
1180                 }
1181         }
1182
1183         toplevel->lower_to_bottom();
1184 }
1185
1186 void
1187 CrossfadeEditor::peaks_ready (boost::weak_ptr<AudioRegion> wr, WhichFade which)
1188 {
1189         boost::shared_ptr<AudioRegion> r (wr.lock());
1190
1191         if (!r) {
1192                 return;
1193         }
1194
1195         /* this should never be called, because the peak files for an xfade
1196            will be ready by the time we want them. but our API forces us
1197            to provide this, so ..
1198         */
1199         delete _peaks_ready_connection;
1200         _peaks_ready_connection = 0;
1201
1202         make_waves (r, which);
1203 }
1204
1205 void
1206 CrossfadeEditor::audition (Audition which)
1207 {
1208         AudioPlaylist& pl (_session->the_auditioner()->prepare_playlist());
1209         framecnt_t preroll;
1210         framecnt_t postroll;
1211         framecnt_t left_start_offset;
1212         framecnt_t right_length;
1213         framecnt_t left_length;
1214
1215         if (which != Right && preroll_button.get_active()) {
1216                 preroll = _session->frame_rate() * 2;  //2 second hardcoded preroll for now
1217         } else {
1218                 preroll = 0;
1219         }
1220
1221         if (which != Left && postroll_button.get_active()) {
1222                 postroll = _session->frame_rate() * 2;  //2 second hardcoded postroll for now
1223         } else {
1224                 postroll = 0;
1225         }
1226
1227         // Is there enough data for the whole preroll?
1228         left_length = xfade->length();
1229         if ((left_start_offset = xfade->out()->length() - xfade->length()) > preroll) {
1230                 left_start_offset -= preroll;
1231         } else {
1232                 preroll = left_start_offset;
1233                 left_start_offset = 0;
1234         }
1235         left_length += preroll;
1236
1237         // Is there enough data for the whole postroll?
1238         right_length = xfade->length();
1239         if ((xfade->in()->length() - right_length) > postroll) {
1240                 right_length += postroll;
1241         } else {
1242                 right_length = xfade->in()->length();
1243         }
1244
1245         PropertyList left_plist;
1246         PropertyList right_plist;
1247
1248
1249         left_plist.add (ARDOUR::Properties::start, left_start_offset);
1250         left_plist.add (ARDOUR::Properties::length, left_length);
1251         left_plist.add (ARDOUR::Properties::name, string ("xfade out"));
1252         left_plist.add (ARDOUR::Properties::layer, 0);
1253         left_plist.add (ARDOUR::Properties::fade_in_active, true);
1254
1255         right_plist.add (ARDOUR::Properties::start, 0);
1256         right_plist.add (ARDOUR::Properties::length, right_length);
1257         right_plist.add (ARDOUR::Properties::name, string("xfade in"));
1258         right_plist.add (ARDOUR::Properties::layer, 0);
1259         right_plist.add (ARDOUR::Properties::fade_out_active, true);
1260
1261         if (which == Left) {
1262                 right_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1263         } else if (which == Right) {
1264                 left_plist.add (ARDOUR::Properties::scale_amplitude, 0.0f);
1265         }
1266
1267         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1268                                                      (RegionFactory::create (xfade->out(), left_plist, false)));
1269         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1270                                               (RegionFactory::create (xfade->in(), right_plist, false)));
1271
1272         // apply a 20ms declicking fade at the start and end of auditioning
1273         // XXX this should really be a property
1274
1275         left->set_fade_in_length (_session->frame_rate() / 50);
1276         right->set_fade_out_length (_session->frame_rate() / 50);
1277
1278         pl.add_region (left, 0);
1279         pl.add_region (right, 1 + preroll);
1280
1281         /* there is only one ... */
1282         pl.foreach_crossfade (sigc::mem_fun (*this, &CrossfadeEditor::setup));
1283
1284         _session->audition_playlist ();
1285 }
1286
1287 void
1288 CrossfadeEditor::audition_both ()
1289 {
1290         audition (Both);
1291 }
1292
1293 void
1294 CrossfadeEditor::audition_left_dry ()
1295 {
1296         PropertyList plist;
1297
1298         plist.add (ARDOUR::Properties::start, xfade->out()->length() - xfade->length());
1299         plist.add (ARDOUR::Properties::length, xfade->length());
1300         plist.add (ARDOUR::Properties::name, string("xfade left"));
1301         plist.add (ARDOUR::Properties::layer, 0);
1302
1303         boost::shared_ptr<AudioRegion> left (boost::dynamic_pointer_cast<AudioRegion>
1304                                              (RegionFactory::create (xfade->out(), plist, false)));
1305
1306         _session->audition_region (left);
1307 }
1308
1309 void
1310 CrossfadeEditor::audition_left ()
1311 {
1312         audition (Left);
1313 }
1314
1315 void
1316 CrossfadeEditor::audition_right_dry ()
1317 {
1318         PropertyList plist;
1319
1320         plist.add (ARDOUR::Properties::start, 0);
1321         plist.add (ARDOUR::Properties::length, xfade->length());
1322         plist.add (ARDOUR::Properties::name, string ("xfade right"));
1323         plist.add (ARDOUR::Properties::layer, 0);
1324
1325         boost::shared_ptr<AudioRegion> right (boost::dynamic_pointer_cast<AudioRegion>
1326                                               (RegionFactory::create (xfade->in(), plist, false)));
1327
1328         _session->audition_region (right);
1329 }
1330
1331 void
1332 CrossfadeEditor::audition_right ()
1333 {
1334         audition (Right);
1335 }
1336
1337 void
1338 CrossfadeEditor::cancel_audition ()
1339 {
1340         _session->cancel_audition ();
1341 }
1342
1343 void
1344 CrossfadeEditor::audition_toggled ()
1345 {
1346         bool x;
1347
1348         if ((x = audition_both_button.get_active ()) != _session->is_auditioning()) {
1349
1350                 if (x) {
1351                         audition_both ();
1352                 } else {
1353                         cancel_audition ();
1354                 }
1355         }
1356 }
1357
1358 void
1359 CrossfadeEditor::audition_right_toggled ()
1360 {
1361         bool x;
1362
1363         if ((x = audition_right_button.get_active ()) != _session->is_auditioning()) {
1364
1365                 if (x) {
1366                         audition_right ();
1367                 } else {
1368                         cancel_audition ();
1369                 }
1370         }
1371 }
1372
1373 void
1374 CrossfadeEditor::audition_right_dry_toggled ()
1375 {
1376         bool x;
1377
1378         if ((x = audition_right_dry_button.get_active ()) != _session->is_auditioning()) {
1379
1380                 if (x) {
1381                         audition_right_dry ();
1382                 } else {
1383                         cancel_audition ();
1384                 }
1385         }
1386 }
1387
1388 void
1389 CrossfadeEditor::audition_left_toggled ()
1390 {
1391         bool x;
1392
1393         if ((x = audition_left_button.get_active ()) != _session->is_auditioning()) {
1394
1395                 if (x) {
1396                         audition_left ();
1397                 } else {
1398                         cancel_audition ();
1399                 }
1400         }
1401 }
1402
1403 void
1404 CrossfadeEditor::audition_left_dry_toggled ()
1405 {
1406         bool x;
1407
1408         if ((x = audition_left_dry_button.get_active ()) != _session->is_auditioning()) {
1409
1410                 if (x) {
1411                         audition_left_dry ();
1412                 } else {
1413                         cancel_audition ();
1414                 }
1415         }
1416 }
1417
1418 bool
1419 CrossfadeEditor::on_key_press_event (GdkEventKey */*ev*/)
1420 {
1421         return true;
1422 }
1423
1424 bool
1425 CrossfadeEditor::on_key_release_event (GdkEventKey* ev)
1426 {
1427         switch (ev->keyval) {
1428         case GDK_Right:
1429                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1430                         audition_right_dry_button.set_active (!audition_right_dry_button.get_active());
1431                 } else {
1432                         audition_right_button.set_active (!audition_right_button.get_active());
1433                 }
1434                 break;
1435
1436         case GDK_Left:
1437                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
1438                         audition_left_dry_button.set_active (!audition_left_dry_button.get_active());
1439                 } else {
1440                         audition_left_button.set_active (!audition_left_button.get_active());
1441                 }
1442                 break;
1443
1444         case GDK_space:
1445                 if (_session->is_auditioning()) {
1446                         cancel_audition ();
1447                 } else {
1448                         audition_both_button.set_active (!audition_both_button.get_active());
1449                 }
1450                 break;
1451
1452         default:
1453                 break;
1454         }
1455
1456         return true;
1457 }