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.
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>
36 using namespace ARDOUR;
38 using namespace RubberBand;
40 Pitch::Pitch (Session& s, TimeFXRequest& req)
45 Stretch::Stretch (Session& s, TimeFXRequest& req)
50 RBEffect::RBEffect (Session& s, TimeFXRequest& req)
58 RBEffect::~RBEffect ()
63 RBEffect::run (boost::shared_ptr<AudioRegion> region)
68 const nframes_t bufsize = 256;
69 gain_t* gain_buffer = 0;
77 cerr << "RBEffect: source region: position = " << region->position()
78 << ", start = " << region->start()
79 << ", length = " << region->length()
80 << ", ancestral_start = " << region->ancestral_start()
81 << ", ancestral_length = " << region->ancestral_length()
82 << ", stretch " << region->stretch()
83 << ", shift " << region->shift() << endl;
86 We have two cases to consider:
88 1. The region has not been stretched before.
90 In this case, we just want to read region->length() frames
93 We will create a new region of region->length() *
94 tsr.time_fraction frames. The new region will have its
95 start set to 0 (because it has a new audio file that begins
96 at the start of the stretched area) and its ancestral_start
97 set to region->start() (so that we know where to begin
98 reading if we want to stretch it again).
100 2. The region has been stretched before.
102 The region starts at region->start() frames into its
103 (possibly previously stretched) source file. But we don't
104 want to read from its source file; we want to read from the
105 file it was originally stretched from.
107 The region's source begins at region->ancestral_start()
108 frames into its master source file. Thus, we need to start
109 reading at region->ancestral_start() + (region->start() /
110 region->stretch()) frames into the master source. This
111 value will also become the ancestral_start for the new
114 We cannot use region->ancestral_length() to establish how
115 many frames to read, because it won't be up to date if the
116 region has been trimmed since it was last stretched. We
117 must read region->length() / region->stretch() frames and
118 stretch them by tsr.time_fraction * region->stretch(), for
119 a new region of region->length() * tsr.time_fraction
122 Case 1 is of course a special case of 2, where
123 region->ancestral_start() == 0 and region->stretch() == 1.
125 When we ask to read from a region, we supply a position on
126 the global timeline. The read function calculates the
127 offset into the source as (position - region->position()) +
128 region->start(). This calculation is used regardless of
129 whether we are reading from a master or
130 previously-stretched region. In order to read from a point
131 n frames into the master source, we need to provide n -
132 region->start() + region->position() as our position
133 argument to master_read_at().
135 Note that region->ancestral_length() is not used.
137 I hope this is clear.
140 double stretch = region->stretch() * tsr.time_fraction;
141 double shift = region->shift() * tsr.pitch_fraction;
143 nframes_t read_start = region->ancestral_start() +
144 nframes_t(region->start() / (double)region->stretch());
146 nframes_t read_duration =
147 nframes_t(region->length() / (double)region->stretch());
149 uint32_t channels = region->n_channels();
151 RubberBandStretcher stretcher
152 (session.frame_rate(), channels,
153 (RubberBandStretcher::Options) tsr.opts, stretch, shift);
158 stretcher.setExpectedInputDuration(read_duration);
159 stretcher.setDebugLevel(1);
161 stretcher.setExpectedInputDuration(duration);
162 stretcher.setDebugLevel(1);
164 /* the name doesn't need to be super-precise, but allow for 2 fractional
165 digits just to disambiguate close but not identical FX
168 if (stretch == 1.0) {
169 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (shift * 100.0f));
170 } else if (shift == 1.0) {
171 snprintf (suffix, sizeof (suffix), "@%d", (int) floor (stretch * 100.0f));
173 snprintf (suffix, sizeof (suffix), "@%d-%d",
174 (int) floor (stretch * 100.0f),
175 (int) floor (shift * 100.0f));
178 /* create new sources */
180 if (make_new_sources (region, nsrcs, suffix)) {
184 gain_buffer = new gain_t[bufsize];
185 buffers = new float *[channels];
187 for (uint32_t i = 0; i < channels; ++i) {
188 buffers[i] = new float[bufsize];
191 /* we read from the master (original) sources for the region,
192 not the ones currently in use, in case it's already been
193 subject to timefx. */
195 /* study first, process afterwards. */
202 while (pos < read_duration && !tsr.cancel) {
204 nframes_t this_read = 0;
206 for (uint32_t i = 0; i < channels; ++i) {
211 this_time = min(bufsize, read_duration - pos);
213 nframes_t this_position;
214 this_position = read_start + pos -
215 region->start() + region->position();
217 this_read = region->master_read_at
225 if (this_read != this_time) {
226 error << string_compose
227 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
228 region->name(), pos + region->position(), this_time, this_read) << endmsg;
236 tsr.progress = ((float) done / read_duration) * 0.25;
238 stretcher.study(buffers, this_read, pos == read_duration);
244 while (pos < read_duration && !tsr.cancel) {
246 nframes_t this_read = 0;
248 for (uint32_t i = 0; i < channels; ++i) {
252 this_time = min(bufsize, read_duration - pos);
254 nframes_t this_position;
255 this_position = read_start + pos -
256 region->start() + region->position();
258 this_read = region->master_read_at
266 if (this_read != this_time) {
267 error << string_compose
268 (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
269 region->name(), pos + region->position(), this_time, this_read) << endmsg;
277 tsr.progress = 0.25 + ((float) done / read_duration) * 0.75;
279 stretcher.process(buffers, this_read, pos == read_duration);
283 while ((avail = stretcher.available()) > 0) {
285 this_read = min(bufsize, uint32_t(avail));
287 stretcher.retrieve(buffers, this_read);
289 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
291 if (nsrcs[i]->write(buffers[i], this_read) !=
293 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
300 while ((avail = stretcher.available()) >= 0) {
302 uint32_t this_read = min(bufsize, uint32_t(avail));
304 stretcher.retrieve(buffers, this_read);
306 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
308 if (nsrcs[i]->write(buffers[i], this_read) !=
310 error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
316 } catch (runtime_error& err) {
317 error << _("timefx code failure. please notify ardour-developers.") << endmsg;
318 error << err.what() << endmsg;
322 new_name = region->name();
323 at = new_name.find ('@');
325 // remove any existing stretch indicator
327 if (at != string::npos && at > 2) {
328 new_name = new_name.substr (0, at - 1);
333 ret = finish (region, nsrcs, new_name);
335 /* now reset ancestral data for each new region */
337 for (vector<boost::shared_ptr<AudioRegion> >::iterator x = results.begin(); x != results.end(); ++x) {
340 (*x)->set_ancestral_data (read_start,
344 (*x)->set_master_sources (region->get_master_sources());
350 delete [] gain_buffer;
354 for (uint32_t i = 0; i < channels; ++i) {
360 if (ret || tsr.cancel) {
361 for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
362 (*si)->mark_for_remove ();