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