fix up wscript/build issues in exportvis after merge with master
[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 <rubberband/RubberBandStretcher.h>
24
25 #include "pbd/error.h"
26
27 #include "ardour/audioregion.h"
28 #include "ardour/audiosource.h"
29 #include "ardour/pitch.h"
30 #include "ardour/progress.h"
31 #include "ardour/session.h"
32 #include "ardour/stretch.h"
33 #include "ardour/types.h"
34
35 #include "i18n.h"
36
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace PBD;
40 using namespace RubberBand;
41
42 Pitch::Pitch (Session& s, TimeFXRequest& req)
43         : RBEffect (s, req)
44 {
45 }
46
47 RBStretch::RBStretch (Session& s, TimeFXRequest& req)
48         : RBEffect (s, req)
49 {
50 }
51
52 RBEffect::RBEffect (Session& s, TimeFXRequest& req)
53         : Filter (s)
54         , tsr (req)
55
56 {
57
58 }
59
60 RBEffect::~RBEffect ()
61 {
62 }
63
64 int
65 RBEffect::run (boost::shared_ptr<Region> r, Progress* progress)
66 {
67         boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (r);
68
69         if (!region) {
70                 error << "RBEffect::run() passed a non-audio region! WTF?" << endmsg;
71                 return -1;
72         }
73
74         SourceList nsrcs;
75         framecnt_t done;
76         int ret = -1;
77         const framecnt_t bufsize = 256;
78         gain_t* gain_buffer = 0;
79         Sample** buffers = 0;
80         char suffix[32];
81         string new_name;
82         string::size_type at;
83         framepos_t pos = 0;
84         framecnt_t avail = 0;
85         boost::shared_ptr<AudioRegion> result;
86
87         cerr << "RBEffect: source region: position = " << region->position()
88              << ", start = " << region->start()
89              << ", length = " << region->length()
90              << ", ancestral_start = " << region->ancestral_start()
91              << ", ancestral_length = " << region->ancestral_length()
92              << ", stretch " << region->stretch()
93              << ", shift " << region->shift() << endl;
94
95         /*
96            We have two cases to consider:
97
98            1. The region has not been stretched before.
99
100            In this case, we just want to read region->length() frames
101            from region->start().
102
103            We will create a new region of region->length() *
104            tsr.time_fraction frames.  The new region will have its
105            start set to 0 (because it has a new audio file that begins
106            at the start of the stretched area) and its ancestral_start
107            set to region->start() (so that we know where to begin
108            reading if we want to stretch it again).
109
110            2. The region has been stretched before.
111
112            The region starts at region->start() frames into its
113            (possibly previously stretched) source file.  But we don't
114            want to read from its source file; we want to read from the
115            file it was originally stretched from.
116
117            The region's source begins at region->ancestral_start()
118            frames into its master source file.  Thus, we need to start
119            reading at region->ancestral_start() + (region->start() /
120            region->stretch()) frames into the master source.  This
121            value will also become the ancestral_start for the new
122            region.
123
124            We cannot use region->ancestral_length() to establish how
125            many frames to read, because it won't be up to date if the
126            region has been trimmed since it was last stretched.  We
127            must read region->length() / region->stretch() frames and
128            stretch them by tsr.time_fraction * region->stretch(), for
129            a new region of region->length() * tsr.time_fraction
130            frames.
131
132            Case 1 is of course a special case of 2, where
133            region->ancestral_start() == 0 and region->stretch() == 1.
134
135            When we ask to read from a region, we supply a position on
136            the global timeline.  The read function calculates the
137            offset into the source as (position - region->position()) +
138            region->start().  This calculation is used regardless of
139            whether we are reading from a master or
140            previously-stretched region.  In order to read from a point
141            n frames into the master source, we need to provide n -
142            region->start() + region->position() as our position
143            argument to master_read_at().
144
145            Note that region->ancestral_length() is not used.
146
147            I hope this is clear.
148         */
149
150         double stretch = region->stretch() * tsr.time_fraction;
151         double shift = region->shift() * tsr.pitch_fraction;
152
153         framecnt_t read_start = region->ancestral_start() +
154                 framecnt_t(region->start() / (double)region->stretch());
155
156         framecnt_t read_duration =
157                 framecnt_t(region->length() / (double)region->stretch());
158
159         uint32_t channels = region->n_channels();
160
161         RubberBandStretcher stretcher
162                 (session.frame_rate(), channels,
163                  (RubberBandStretcher::Options) tsr.opts, stretch, shift);
164
165         progress->set_progress (0);
166         tsr.done = false;
167
168         stretcher.setExpectedInputDuration(read_duration);
169         stretcher.setDebugLevel(1);
170
171         /* the name doesn't need to be super-precise, but allow for 2 fractional
172            digits just to disambiguate close but not identical FX
173         */
174
175         if (stretch == 1.0) {
176                 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (shift * 100.0f));
177         } else if (shift == 1.0) {
178                 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (stretch * 100.0f));
179         } else {
180                 snprintf (suffix, sizeof (suffix), "@%d-%d",
181                           (int) floor (stretch * 100.0f),
182                           (int) floor (shift * 100.0f));
183         }
184
185         /* create new sources */
186
187         if (make_new_sources (region, nsrcs, suffix)) {
188                 goto out;
189         }
190
191         gain_buffer = new gain_t[bufsize];
192         buffers = new float *[channels];
193
194         for (uint32_t i = 0; i < channels; ++i) {
195                 buffers[i] = new float[bufsize];
196         }
197
198         /* we read from the master (original) sources for the region,
199            not the ones currently in use, in case it's already been
200            subject to timefx.  */
201
202         /* study first, process afterwards. */
203
204         pos = 0;
205         avail = 0;
206         done = 0;
207
208         try {
209                 while (pos < read_duration && !tsr.cancel) {
210
211                         framecnt_t this_read = 0;
212
213                         for (uint32_t i = 0; i < channels; ++i) {
214
215                                 this_read = 0;
216
217                                 framepos_t this_time;
218                                 this_time = min(bufsize, read_duration - pos);
219
220                                 framepos_t this_position;
221                                 this_position = read_start + pos -
222                                         region->start() + region->position();
223
224                                 this_read = region->master_read_at
225                                         (buffers[i],
226                                          buffers[i],
227                                          gain_buffer,
228                                          this_position,
229                                          this_time,
230                                          i);
231
232                                 if (this_read != this_time) {
233                                         error << string_compose
234                                                 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
235                                                  region->name(), this_position, this_time, this_read) << endmsg;
236                                         goto out;
237                                 }
238                         }
239
240                         pos += this_read;
241                         done += this_read;
242
243                         progress->set_progress (((float) done / read_duration) * 0.25);
244
245                         stretcher.study(buffers, this_read, pos == read_duration);
246                 }
247
248                 done = 0;
249                 pos = 0;
250
251                 while (pos < read_duration && !tsr.cancel) {
252
253                         framecnt_t this_read = 0;
254
255                         for (uint32_t i = 0; i < channels; ++i) {
256
257                                 this_read = 0;
258                                 framepos_t this_time;
259                                 this_time = min(bufsize, read_duration - pos);
260
261                                 framepos_t this_position;
262                                 this_position = read_start + pos -
263                                         region->start() + region->position();
264
265                                 this_read = region->master_read_at
266                                         (buffers[i],
267                                          buffers[i],
268                                          gain_buffer,
269                                          this_position,
270                                          this_time,
271                                          i);
272
273                                 if (this_read != this_time) {
274                                         error << string_compose
275                                                 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
276                                                  region->name(), pos + region->position(), this_time, this_read) << endmsg;
277                                         goto out;
278                                 }
279                         }
280
281                         pos += this_read;
282                         done += this_read;
283
284                         progress->set_progress (0.25 + ((float) done / read_duration) * 0.75);
285
286                         stretcher.process(buffers, this_read, pos == read_duration);
287
288                         framecnt_t avail = 0;
289
290                         while ((avail = stretcher.available()) > 0) {
291
292                                 this_read = min (bufsize, avail);
293
294                                 stretcher.retrieve(buffers, this_read);
295
296                                 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
297
298                                         boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
299                                         if (!asrc) {
300                                                 continue;
301                                         }
302
303                                         if (asrc->write(buffers[i], this_read) != this_read) {
304                                                 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
305                                                 goto out;
306                                         }
307                                 }
308                         }
309                 }
310
311                 while ((avail = stretcher.available()) >= 0) {
312
313                         framecnt_t this_read = min (bufsize, avail);
314
315                         stretcher.retrieve(buffers, this_read);
316
317                         for (uint32_t i = 0; i < nsrcs.size(); ++i) {
318
319                                 boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
320                                 if (!asrc) {
321                                         continue;
322                                 }
323
324                                 if (asrc->write(buffers[i], this_read) !=
325                                     this_read) {
326                                         error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
327                                         goto out;
328                                 }
329                         }
330                 }
331
332         } catch (runtime_error& err) {
333                 error << string_compose (_("programming error: %1"), X_("timefx code failure")) << endmsg;
334                 error << err.what() << endmsg;
335                 goto out;
336         }
337
338         new_name = region->name();
339         at = new_name.find ('@');
340
341         // remove any existing stretch indicator
342
343         if (at != string::npos && at > 2) {
344                 new_name = new_name.substr (0, at - 1);
345         }
346
347         new_name += suffix;
348
349         ret = finish (region, nsrcs, new_name);
350
351         /* now reset ancestral data for each new region */
352
353         for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
354
355                 (*x)->set_ancestral_data (read_start,
356                                           read_duration,
357                                           stretch,
358                                           shift);
359                 (*x)->set_master_sources (region->master_sources());
360                 /* multiply the old (possibly previously stretched) region length by the extra
361                    stretch this time around to get its new length
362                 */
363                 (*x)->set_length ((*x)->length() * tsr.time_fraction);
364         }
365
366         /* stretch region gain envelope */
367         /* XXX: assuming we've only processed one input region into one result here */
368
369         if (tsr.time_fraction != 1) {
370                 result = boost::dynamic_pointer_cast<AudioRegion> (results.front());
371                 assert (result);
372                 result->envelope()->x_scale (tsr.time_fraction);
373         }
374
375   out:
376
377         delete [] gain_buffer;
378
379         if (buffers) {
380                 for (uint32_t i = 0; i < channels; ++i) {
381                         delete buffers[i];
382                 }
383                 delete [] buffers;
384         }
385
386         if (ret || tsr.cancel) {
387                 for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
388                         (*si)->mark_for_remove ();
389                 }
390         }
391
392         return ret;
393 }
394
395
396
397
398