fix rect redraw problem(s); make zoom range rect visible again at the right time...
[ardour.git] / gtk2_ardour / editor_timefx.cc
1 /*
2     Copyright (C) 2000 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 <cstdlib>
21 #include <cmath>
22
23 #include <string>
24
25 #include <pbd/error.h>
26 #include <pbd/pthread_utils.h>
27 #include <pbd/memento_command.h>
28
29 #include <gtkmm2ext/window_title.h>
30
31 #include "editor.h"
32 #include "audio_time_axis.h"
33 #include "audio_region_view.h"
34 #include "region_selection.h"
35
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/audioplaylist.h>
39 #include <ardour/audio_track.h>
40 #include <ardour/audioregion.h>
41 #include <ardour/audio_diskstream.h>
42
43 #include "i18n.h"
44
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace sigc;
48 using namespace Gtk;
49 using namespace Gtkmm2ext;
50
51 Editor::TimeStretchDialog::TimeStretchDialog (Editor& e)
52         : ArdourDialog ("time stretch dialog"),
53           editor (e),
54           quick_button (_("Quick but Ugly")),
55           antialias_button (_("Skip Anti-aliasing"))
56 {
57         set_modal (true);
58         set_position (Gtk::WIN_POS_MOUSE);
59         set_name (N_("TimeStretchDialog"));
60
61         WindowTitle title(Glib::get_application_name());
62         title += _("Timestretch");
63         set_title(title.get_string());
64
65         get_vbox()->set_spacing (5);
66         get_vbox()->set_border_width (5);
67         get_vbox()->pack_start (upper_button_box);
68         get_vbox()->pack_start (progress_bar);
69
70         upper_button_box.set_homogeneous (true);
71         upper_button_box.set_spacing (5);
72         upper_button_box.set_border_width (5);
73         upper_button_box.pack_start (quick_button, true, true);
74         upper_button_box.pack_start (antialias_button, true, true);
75
76         action_button = add_button (_("Stretch/Shrink it"), Gtk::RESPONSE_ACCEPT);
77         cancel_button = add_button (_("Cancel"), Gtk::RESPONSE_CANCEL);
78
79         quick_button.set_name (N_("TimeStretchButton"));
80         antialias_button.set_name (N_("TimeStretchButton"));
81         progress_bar.set_name (N_("TimeStretchProgress"));
82
83         show_all_children ();
84 }
85
86 gint
87 Editor::TimeStretchDialog::update_progress ()
88 {
89         progress_bar.set_fraction (request.progress);
90         return request.running;
91 }
92
93 void
94 Editor::TimeStretchDialog::cancel_timestretch_in_progress ()
95 {
96         status = -2;
97         request.running = false;
98 }
99
100 gint
101 Editor::TimeStretchDialog::delete_timestretch_in_progress (GdkEventAny* ev)
102 {
103         status = -2;
104         request.running = false;
105         return TRUE;
106 }
107
108 int
109 Editor::run_timestretch (RegionSelection& regions, float fraction)
110 {
111         pthread_t thread;
112
113         if (current_timestretch == 0) {
114                 current_timestretch = new TimeStretchDialog (*this);
115         }
116
117         current_timestretch->progress_bar.set_fraction (0.0f);
118
119         switch (current_timestretch->run ()) {
120         case RESPONSE_ACCEPT:
121                 break;
122         default:
123                 current_timestretch->hide ();
124                 return 1;
125         }
126
127         current_timestretch->status = 0;
128         current_timestretch->regions = regions;
129         current_timestretch->request.fraction = fraction;
130         current_timestretch->request.quick_seek = current_timestretch->quick_button.get_active();
131         current_timestretch->request.antialias = !current_timestretch->antialias_button.get_active();
132         current_timestretch->request.progress = 0.0f;
133         current_timestretch->request.running = true;
134         
135         /* re-connect the cancel button and delete events */
136         
137         current_timestretch->first_cancel.disconnect();
138         current_timestretch->first_delete.disconnect();
139         
140         current_timestretch->cancel_button->signal_clicked().connect (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress));
141         current_timestretch->signal_delete_event().connect (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress));
142
143         if (pthread_create_and_store ("timestretch", &thread, 0, timestretch_thread, current_timestretch)) {
144                 current_timestretch->hide ();
145                 error << _("timestretch cannot be started - thread creation error") << endmsg;
146                 return -1;
147         }
148
149         pthread_detach (thread);
150
151         sigc::connection c = Glib::signal_timeout().connect (mem_fun (current_timestretch, &TimeStretchDialog::update_progress), 100);
152
153         while (current_timestretch->request.running) {
154                 gtk_main_iteration ();
155         }
156
157         c.disconnect ();
158         
159         current_timestretch->hide ();
160         return current_timestretch->status;
161 }
162
163 void
164 Editor::do_timestretch (TimeStretchDialog& dialog)
165 {
166         Track*    t;
167         boost::shared_ptr<Playlist> playlist;
168         boost::shared_ptr<Region>   new_region;
169
170         for (RegionSelection::iterator i = dialog.regions.begin(); i != dialog.regions.end(); ) {
171                 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
172                 if (!arv)
173                         continue;
174
175                 boost::shared_ptr<AudioRegion> region (arv->audio_region());
176                 TimeAxisView* tv = &(arv->get_time_axis_view());
177                 RouteTimeAxisView* rtv;
178                 RegionSelection::iterator tmp;
179                 
180                 tmp = i;
181                 ++tmp;
182
183                 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv)) == 0) {
184                         i = tmp;
185                         continue;
186                 }
187
188                 if ((t = dynamic_cast<Track*> (rtv->route().get())) == 0) {
189                         i = tmp;
190                         continue;
191                 }
192         
193                 if ((playlist = t->diskstream()->playlist()) == 0) {
194                         i = tmp;
195                         continue;
196                 }
197
198                 dialog.request.region = region;
199
200                 if (!dialog.request.running) {
201                         /* we were cancelled */
202                         dialog.status = 1;
203                         return;
204                 }
205
206                 if ((new_region = session->tempoize_region (dialog.request)) == 0) {
207                         dialog.status = -1;
208                         dialog.request.running = false;
209                         return;
210                 }
211
212                 XMLNode &before = playlist->get_state();
213                 playlist->replace_region (region, new_region, region->position());
214                 XMLNode &after = playlist->get_state();
215                 session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after));
216
217                 i = tmp;
218         }
219
220         dialog.status = 0;
221         dialog.request.running = false;
222         dialog.request.region.reset ();
223 }
224
225 void*
226 Editor::timestretch_thread (void *arg)
227 {
228         PBD::ThreadCreated (pthread_self(), X_("TimeFX"));
229
230         TimeStretchDialog* tsd = static_cast<TimeStretchDialog*>(arg);
231
232         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
233
234         tsd->editor.do_timestretch (*tsd);
235
236         return 0;
237 }
238