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