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