possible fix for race between diskstream buffer overwrite and channel setup
[ardour2.git] / libs / ardour / rb_effect.cc
blob4cb77d1c5822862e2ca95ea8d712075c02f8d5ba
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)
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 nframes_t done;
74 int ret = -1;
75 const nframes_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 nframes_t pos = 0;
82 int avail = 0;
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;
93 We have two cases to consider:
95 1. The region has not been stretched before.
97 In this case, we just want to read region->length() frames
98 from region->start().
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).
107 2. The region has been stretched before.
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.
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.
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.
129 Case 1 is of course a special case of 2, where
130 region->ancestral_start() == 0 and region->stretch() == 1.
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().
142 Note that region->ancestral_length() is not used.
144 I hope this is clear.
147 double stretch = region->stretch() * tsr.time_fraction;
148 double shift = region->shift() * tsr.pitch_fraction;
150 nframes_t read_start = region->ancestral_start() +
151 nframes_t(region->start() / (double)region->stretch());
153 nframes_t read_duration =
154 nframes_t(region->length() / (double)region->stretch());
156 uint32_t channels = region->n_channels();
158 RubberBandStretcher stretcher
159 (session.frame_rate(), channels,
160 (RubberBandStretcher::Options) tsr.opts, stretch, shift);
162 tsr.progress = 0.0f;
163 tsr.done = false;
165 stretcher.setExpectedInputDuration(read_duration);
166 stretcher.setDebugLevel(1);
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
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));
182 /* create new sources */
184 if (make_new_sources (region, nsrcs, suffix)) {
185 goto out;
188 gain_buffer = new gain_t[bufsize];
189 buffers = new float *[channels];
191 for (uint32_t i = 0; i < channels; ++i) {
192 buffers[i] = new float[bufsize];
195 /* we read from the master (original) sources for the region,
196 not the ones currently in use, in case it's already been
197 subject to timefx. */
199 /* study first, process afterwards. */
201 pos = 0;
202 avail = 0;
203 done = 0;
205 try {
206 while (pos < read_duration && !tsr.cancel) {
208 nframes_t this_read = 0;
210 for (uint32_t i = 0; i < channels; ++i) {
212 this_read = 0;
214 nframes_t this_time;
215 this_time = min(bufsize, read_duration - pos);
217 nframes_t this_position;
218 this_position = read_start + pos -
219 region->start() + region->position();
221 this_read = region->master_read_at
222 (buffers[i],
223 buffers[i],
224 gain_buffer,
225 this_position,
226 this_time,
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;
237 pos += this_read;
238 done += this_read;
240 tsr.progress = ((float) done / read_duration) * 0.25;
242 stretcher.study(buffers, this_read, pos == read_duration);
245 done = 0;
246 pos = 0;
248 while (pos < read_duration && !tsr.cancel) {
250 nframes_t this_read = 0;
252 for (uint32_t i = 0; i < channels; ++i) {
254 this_read = 0;
255 nframes_t this_time;
256 this_time = min(bufsize, read_duration - pos);
258 nframes_t this_position;
259 this_position = read_start + pos -
260 region->start() + region->position();
262 this_read = region->master_read_at
263 (buffers[i],
264 buffers[i],
265 gain_buffer,
266 this_position,
267 this_time,
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;
278 pos += this_read;
279 done += this_read;
281 tsr.progress = 0.25 + ((float) done / read_duration) * 0.75;
283 stretcher.process(buffers, this_read, pos == read_duration);
285 int avail = 0;
287 while ((avail = stretcher.available()) > 0) {
289 this_read = min(bufsize, uint32_t(avail));
291 stretcher.retrieve(buffers, this_read);
293 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
295 boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
296 if (!asrc) {
297 continue;
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;
308 while ((avail = stretcher.available()) >= 0) {
310 uint32_t this_read = min(bufsize, uint32_t(avail));
312 stretcher.retrieve(buffers, this_read);
314 for (uint32_t i = 0; i < nsrcs.size(); ++i) {
316 boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
317 if (!asrc) {
318 continue;
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;
329 } catch (runtime_error& err) {
330 error << _("timefx code failure. please notify ardour-developers.") << endmsg;
331 error << err.what() << endmsg;
332 goto out;
335 new_name = region->name();
336 at = new_name.find ('@');
338 // remove any existing stretch indicator
340 if (at != string::npos && at > 2) {
341 new_name = new_name.substr (0, at - 1);
344 new_name += suffix;
346 ret = finish (region, nsrcs, new_name);
348 /* now reset ancestral data for each new region */
350 for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
352 (*x)->set_ancestral_data (read_start,
353 read_duration,
354 stretch,
355 shift);
356 (*x)->set_master_sources (region->master_sources());
357 (*x)->set_length( (*x)->length() * stretch, this);
360 out:
362 delete [] gain_buffer;
364 if (buffers) {
365 for (uint32_t i = 0; i < channels; ++i) {
366 delete buffers[i];
368 delete [] buffers;
371 if (ret || tsr.cancel) {
372 for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
373 (*si)->mark_for_remove ();
377 tsr.done = true;
379 return ret;