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