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