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