rollback to 3428, before the mysterious removal of libs/* at 3431/3432
[ardour.git] / libs / ardour / rb_effect.cc
1 /*
2     Copyright (C) 2004-2007 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 <algorithm>
21 #include <cmath>
22
23 #include <pbd/error.h>
24 #include <rubberband/RubberBandStretcher.h>
25
26 #include <ardour/types.h>
27 #include <ardour/stretch.h>
28 #include <ardour/pitch.h>
29 #include <ardour/audiofilesource.h>
30 #include <ardour/session.h>
31 #include <ardour/audioregion.h>
32
33 #include "i18n.h"
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38 using namespace RubberBand;
39
40 Pitch::Pitch (Session& s, TimeFXRequest& req)
41         : RBEffect (s, req)
42 {
43 }
44
45 Stretch::Stretch (Session& s, TimeFXRequest& req)
46         : RBEffect (s, req)
47 {
48 }
49
50 RBEffect::RBEffect (Session& s, TimeFXRequest& req)
51         : Filter (s)
52         , tsr (req)
53
54 {
55         tsr.progress = 0.0f;
56 }
57
58 RBEffect::~RBEffect ()
59 {
60 }
61
62 int
63 RBEffect::run (boost::shared_ptr<AudioRegion> region)
64 {
65         SourceList nsrcs;
66         nframes_t done;
67         int ret = -1;
68         const nframes_t bufsize = 256;
69         gain_t* gain_buffer = 0;
70         Sample** buffers = 0;
71         char suffix[32];
72         string new_name;
73         string::size_type at;
74         nframes_t pos = 0;
75         int avail = 0;
76
77         // note: this_time_fraction is a ratio of original length. 1.0 = no change, 
78         // 0.5 is half as long, 2.0 is twice as long, etc.
79
80         double this_time_fraction = tsr.time_fraction * region->stretch ();
81         double this_pitch_fraction = tsr.pitch_fraction * region->shift ();
82
83         RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(),
84                                        (RubberBandStretcher::Options) tsr.opts,
85                                        this_time_fraction, this_pitch_fraction);
86         
87         tsr.progress = 0.0f;
88         tsr.done = false;
89
90         uint32_t channels = region->n_channels();
91         nframes_t duration = region->ancestral_length();
92
93         stretcher.setExpectedInputDuration(duration);
94         stretcher.setDebugLevel(1);
95
96         /* the name doesn't need to be super-precise, but allow for 2 fractional
97            digits just to disambiguate close but not identical FX
98         */
99
100         if (this_time_fraction == 1.0) {
101                 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_pitch_fraction * 100.0f));
102         } else if (this_pitch_fraction == 1.0) {
103                 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_time_fraction * 100.0f));
104         } else {
105                 snprintf (suffix, sizeof (suffix), "@%d-%d", 
106                           (int) floor (this_time_fraction * 100.0f),
107                           (int) floor (this_pitch_fraction * 100.0f));
108         }
109
110         /* create new sources */
111
112         if (make_new_sources (region, nsrcs, suffix)) {
113                 goto out;
114         }
115
116         gain_buffer = new gain_t[bufsize];
117         buffers = new float *[channels];
118
119         for (uint32_t i = 0; i < channels; ++i) {
120                 buffers[i] = new float[bufsize];
121         }
122
123         /* we read from the master (original) sources for the region,
124            not the ones currently in use, in case it's already been
125            subject to timefx.  */
126
127         /* study first, process afterwards. */
128
129         pos = 0;
130         avail = 0;
131         done = 0;
132
133         try { 
134                 while (pos < duration && !tsr.cancel) {
135                         
136                         nframes_t this_read = 0;
137                         
138                         for (uint32_t i = 0; i < channels; ++i) {
139                                 
140                                 this_read = 0;
141                                 nframes_t this_time;
142                                 
143                                 this_time = min(bufsize, duration - pos);
144                                 
145                                 this_read = region->master_read_at
146                                         (buffers[i],
147                                          buffers[i],
148                                          gain_buffer,
149                                          pos + region->position(),
150                                          this_time,
151                                          i);
152                                 
153                                 if (this_read != this_time) {
154                                         error << string_compose
155                                                 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
156                                                  region->name(), pos + region->position(), this_time, this_read) << endmsg;
157                                         goto out;
158                                 }
159                         }
160                         
161                         pos += this_read;
162                         done += this_read;
163
164                         tsr.progress = ((float) done / duration) * 0.25;
165
166                         stretcher.study(buffers, this_read, pos == duration);
167                 }
168                 
169                 done = 0;
170                 pos = 0;
171
172                 while (pos < duration && !tsr.cancel) {
173                         
174                         nframes_t this_read = 0;
175                         
176                         for (uint32_t i = 0; i < channels; ++i) {
177                                 
178                                 this_read = 0;
179                                 nframes_t this_time;
180                                 
181                                 this_time = min(bufsize, duration - pos);
182                                 
183                                 this_read = region->master_read_at
184                                         (buffers[i],
185                                          buffers[i],
186                                          gain_buffer,
187                                          pos + region->position(),
188                                          this_time,
189                                          i);
190                                 
191                                 if (this_read != this_time) {
192                                         error << string_compose
193                                                 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
194                                                  region->name(), pos + region->position(), this_time, this_read) << endmsg;
195                                         goto out;
196                                 }
197                         }
198
199                         pos += this_read;
200                         done += this_read;
201
202                         tsr.progress = 0.25 + ((float) done / duration) * 0.75;
203
204                         stretcher.process(buffers, this_read, pos == duration);
205
206                         int avail = 0;
207
208                         while ((avail = stretcher.available()) > 0) {
209
210                                 this_read = min(bufsize, uint32_t(avail));
211
212                                 stretcher.retrieve(buffers, this_read);
213                         
214                                 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
215
216                                         if (nsrcs[i]->write(buffers[i], this_read) !=
217                                             this_read) {
218                                                 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
219                                                 goto out;
220                                         }
221                                 }
222                         }
223                 }
224
225                 while ((avail = stretcher.available()) >= 0) {
226
227                         uint32_t this_read = min(bufsize, uint32_t(avail));
228
229                         stretcher.retrieve(buffers, this_read);
230
231                         for (uint32_t i = 0; i < nsrcs.size(); ++i) {
232
233                                 if (nsrcs[i]->write(buffers[i], this_read) !=
234                                     this_read) {
235                                         error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
236                                         goto out;
237                                 }
238                         }
239                 }
240
241         } catch (runtime_error& err) {
242                 error << _("timefx code failure. please notify ardour-developers.") << endmsg;
243                 error << err.what() << endmsg;
244                 goto out;
245         }
246
247         new_name = region->name();
248         at = new_name.find ('@');
249
250         // remove any existing stretch indicator
251
252         if (at != string::npos && at > 2) {
253                 new_name = new_name.substr (0, at - 1);
254         }
255
256         new_name += suffix;
257
258         ret = finish (region, nsrcs, new_name);
259
260         /* now reset ancestral data for each new region */
261
262         for (vector<boost::shared_ptr<AudioRegion> >::iterator x = results.begin(); x != results.end(); ++x) {
263
264                 (*x)->set_ancestral_data (region->ancestral_start(),
265                                           region->ancestral_length(),
266                                           this_time_fraction,
267                                           this_pitch_fraction );
268                 (*x)->set_master_sources (region->get_master_sources());
269         }
270
271   out:
272
273         if (gain_buffer) {
274                 delete [] gain_buffer;
275         }
276
277         if (buffers) {
278                 for (uint32_t i = 0; i < channels; ++i) {
279                         delete buffers[i];
280                 }
281                 delete [] buffers;
282         }
283
284         if (ret || tsr.cancel) {
285                 for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
286                         (*si)->mark_for_remove ();
287                 }
288         }
289         
290         tsr.done = true;
291
292         return ret;
293 }
294
295
296
297
298