remove unused SHUTTLE_FRACT constant
[ardour2.git] / libs / ardour / rb_effect.cc
blob805782807ae37198c1c7279efad4f5d7f2949227
1 /*
2 Copyright (C) 2004-2007 Paul Davis
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.
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.
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.
20 #include <algorithm>
21 #include <cmath>
23 #include "pbd/error.h"
24 #include "rubberband/RubberBandStretcher.h"
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"
33 #include "i18n.h"
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38 using namespace RubberBand;
40 Pitch::Pitch (Session& s, TimeFXRequest& req)
41 : RBEffect (s, req)
45 RBStretch::RBStretch (Session& s, TimeFXRequest& req)
46 : RBEffect (s, req)
50 RBEffect::RBEffect (Session& s, TimeFXRequest& req)
51 : Filter (s)
52 , tsr (req)
55 tsr.progress = 0.0f;
58 RBEffect::~RBEffect ()
62 int
63 RBEffect::run (boost::shared_ptr<Region> r, Progress*)
65 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (r);
67 if (!region) {
68 error << "RBEffect::run() passed a non-audio region! WTF?" << endmsg;
69 return -1;
72 SourceList nsrcs;
73 framecnt_t done;
74 int ret = -1;
75 const framecnt_t bufsize = 256;
76 gain_t* gain_buffer = 0;
77 Sample** buffers = 0;
78 char suffix[32];
79 string new_name;
80 string::size_type at;
81 framepos_t pos = 0;
82 framecnt_t avail = 0;
83 boost::shared_ptr<AudioRegion> result;
85 cerr << "RBEffect: source region: position = " << region->position()
86 << ", start = " << region->start()
87 << ", length = " << region->length()
88 << ", ancestral_start = " << region->ancestral_start()
89 << ", ancestral_length = " << region->ancestral_length()
90 << ", stretch " << region->stretch()
91 << ", shift " << region->shift() << endl;
94 We have two cases to consider:
96 1. The region has not been stretched before.
98 In this case, we just want to read region->length() frames
99 from region->start().
101 We will create a new region of region->length() *
102 tsr.time_fraction frames. The new region will have its
103 start set to 0 (because it has a new audio file that begins
104 at the start of the stretched area) and its ancestral_start
105 set to region->start() (so that we know where to begin
106 reading if we want to stretch it again).
108 2. The region has been stretched before.
110 The region starts at region->start() frames into its
111 (possibly previously stretched) source file. But we don't
112 want to read from its source file; we want to read from the
113 file it was originally stretched from.
115 The region's source begins at region->ancestral_start()
116 frames into its master source file. Thus, we need to start
117 reading at region->ancestral_start() + (region->start() /
118 region->stretch()) frames into the master source. This
119 value will also become the ancestral_start for the new
120 region.
122 We cannot use region->ancestral_length() to establish how
123 many frames to read, because it won't be up to date if the
124 region has been trimmed since it was last stretched. We
125 must read region->length() / region->stretch() frames and
126 stretch them by tsr.time_fraction * region->stretch(), for
127 a new region of region->length() * tsr.time_fraction
128 frames.
130 Case 1 is of course a special case of 2, where
131 region->ancestral_start() == 0 and region->stretch() == 1.
133 When we ask to read from a region, we supply a position on
134 the global timeline. The read function calculates the
135 offset into the source as (position - region->position()) +
136 region->start(). This calculation is used regardless of
137 whether we are reading from a master or
138 previously-stretched region. In order to read from a point
139 n frames into the master source, we need to provide n -
140 region->start() + region->position() as our position
141 argument to master_read_at().
143 Note that region->ancestral_length() is not used.
145 I hope this is clear.
148 double stretch = region->stretch() * tsr.time_fraction;
149 double shift = region->shift() * tsr.pitch_fraction;
151 framecnt_t read_start = region->ancestral_start() +
152 framecnt_t(region->start() / (double)region->stretch());
154 framecnt_t read_duration =
155 framecnt_t(region->length() / (double)region->stretch());
157 uint32_t channels = region->n_channels();
159 RubberBandStretcher stretcher
160 (session.frame_rate(), channels,
161 (RubberBandStretcher::Options) tsr.opts, stretch, shift);
163 tsr.progress = 0.0f;
164 tsr.done = false;
166 stretcher.setExpectedInputDuration(read_duration);
167 stretcher.setDebugLevel(1);
169 /* the name doesn't need to be super-precise, but allow for 2 fractional
170 digits just to disambiguate close but not identical FX
173 if (stretch == 1.0) {
174 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (shift * 100.0f));
175 } else if (shift == 1.0) {
176 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (stretch * 100.0f));
177 } else {
178 snprintf (suffix, sizeof (suffix), "@%d-%d",
179 (int) floor (stretch * 100.0f),
180 (int) floor (shift * 100.0f));
183 /* create new sources */
185 if (make_new_sources (region, nsrcs, suffix)) {
186 goto out;
189 gain_buffer = new gain_t[bufsize];
190 buffers = new float *[channels];
192 for (uint32_t i = 0; i < channels; ++i) {
193 buffers[i] = new float[bufsize];
196 /* we read from the master (original) sources for the region,
197 not the ones currently in use, in case it's already been
198 subject to timefx. */
200 /* study first, process afterwards. */
202 pos = 0;
203 avail = 0;
204 done = 0;
206 try {
207 while (pos < read_duration && !tsr.cancel) {
209 framecnt_t this_read = 0;
211 for (uint32_t i = 0; i < channels; ++i) {
213 this_read = 0;
215 framepos_t this_time;
216 this_time = min(bufsize, read_duration - pos);
218 framepos_t this_position;
219 this_position = read_start + pos -
220 region->start() + region->position();
222 this_read = region->master_read_at
223 (buffers[i],
224 buffers[i],
225 gain_buffer,
226 this_position,
227 this_time,
230 if (this_read != this_time) {
231 error << string_compose
232 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
233 region->name(), this_position, this_time, this_read) << endmsg;
234 goto out;
238 pos += this_read;
239 done += this_read;
241 tsr.progress = ((float) done / read_duration) * 0.25;
243 stretcher.study(buffers, this_read, pos == read_duration);
246 done = 0;
247 pos = 0;
249 while (pos < read_duration && !tsr.cancel) {
251 framecnt_t this_read = 0;
253 for (uint32_t i = 0; i < channels; ++i) {
255 this_read = 0;
256 framepos_t this_time;
257 this_time = min(bufsize, read_duration - pos);
259 framepos_t this_position;
260 this_position = read_start + pos -
261 region->start() + region->position();
263 this_read = region->master_read_at
264 (buffers[i],
265 buffers[i],
266 gain_buffer,
267 this_position,
268 this_time,
271 if (this_read != this_time) {
272 error << string_compose
273 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
274 region->name(), pos + region->position(), this_time, this_read) << endmsg;
275 goto out;
279 pos += this_read;
280 done += this_read;
282 tsr.progress = 0.25 + ((float) done / read_duration) * 0.75;
284 stretcher.process(buffers, this_read, pos == read_duration);
286 framecnt_t avail = 0;
288 while ((avail = stretcher.available()) > 0) {
290 this_read = min (bufsize, avail);
292 stretcher.retrieve(buffers, this_read);
294 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
296 boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
297 if (!asrc) {
298 continue;
301 if (asrc->write(buffers[i], this_read) != this_read) {
302 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
303 goto out;
309 while ((avail = stretcher.available()) >= 0) {
311 framecnt_t this_read = min (bufsize, avail);
313 stretcher.retrieve(buffers, this_read);
315 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
317 boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
318 if (!asrc) {
319 continue;
322 if (asrc->write(buffers[i], this_read) !=
323 this_read) {
324 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
325 goto out;
330 } catch (runtime_error& err) {
331 error << _("timefx code failure. please notify ardour-developers.") << endmsg;
332 error << err.what() << endmsg;
333 goto out;
336 new_name = region->name();
337 at = new_name.find ('@');
339 // remove any existing stretch indicator
341 if (at != string::npos && at > 2) {
342 new_name = new_name.substr (0, at - 1);
345 new_name += suffix;
347 ret = finish (region, nsrcs, new_name);
349 /* now reset ancestral data for each new region */
351 for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
353 (*x)->set_ancestral_data (read_start,
354 read_duration,
355 stretch,
356 shift);
357 (*x)->set_master_sources (region->master_sources());
358 (*x)->set_length( (*x)->length() * stretch, this);
361 /* stretch region gain envelope */
362 /* XXX: assuming we've only processed one input region into one result here */
364 if (tsr.time_fraction != 1) {
365 result = boost::dynamic_pointer_cast<AudioRegion> (results.front());
366 assert (result);
367 result->envelope()->x_scale (tsr.time_fraction);
370 out:
372 delete [] gain_buffer;
374 if (buffers) {
375 for (uint32_t i = 0; i < channels; ++i) {
376 delete buffers[i];
378 delete [] buffers;
381 if (ret || tsr.cancel) {
382 for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
383 (*si)->mark_for_remove ();
387 tsr.done = true;
389 return ret;