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