ardour
rb_effect.cc
Go to the documentation of this file.
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 
53  : Filter (s)
54  , tsr (req)
55 
56 {
57 
58 }
59 
61 {
62 }
63 
64 int
66 {
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;
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  framepos_t this_time;
213  this_time = min(bufsize, read_duration - pos);
214 
215  framepos_t this_position;
216  this_position = read_start + pos -
217  region->start() + region->position();
218 
219  this_read = region->master_read_at
220  (buffers[i],
221  buffers[i],
222  gain_buffer,
223  this_position,
224  this_time,
225  i);
226 
227  if (this_read != this_time) {
229  (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
230  region->name(), this_position, this_time, this_read) << endmsg;
231  goto out;
232  }
233  }
234 
235  pos += this_read;
236  done += this_read;
237 
238  progress->set_progress (((float) done / read_duration) * 0.25);
239 
240  stretcher.study(buffers, this_read, pos == read_duration);
241  }
242 
243  done = 0;
244  pos = 0;
245 
246  while (pos < read_duration && !tsr.cancel) {
247 
248  framecnt_t this_read = 0;
249 
250  for (uint32_t i = 0; i < channels; ++i) {
251 
252  framepos_t this_time;
253  this_time = min(bufsize, read_duration - pos);
254 
255  framepos_t this_position;
256  this_position = read_start + pos -
257  region->start() + region->position();
258 
259  this_read = region->master_read_at
260  (buffers[i],
261  buffers[i],
262  gain_buffer,
263  this_position,
264  this_time,
265  i);
266 
267  if (this_read != this_time) {
269  (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
270  region->name(), pos + region->position(), this_time, this_read) << endmsg;
271  goto out;
272  }
273  }
274 
275  pos += this_read;
276  done += this_read;
277 
278  progress->set_progress (0.25 + ((float) done / read_duration) * 0.75);
279 
280  stretcher.process(buffers, this_read, pos == read_duration);
281 
282  framecnt_t avail = 0;
283 
284  while ((avail = stretcher.available()) > 0) {
285 
286  this_read = min (bufsize, avail);
287 
288  stretcher.retrieve(buffers, this_read);
289 
290  for (uint32_t i = 0; i < nsrcs.size(); ++i) {
291 
293  if (!asrc) {
294  continue;
295  }
296 
297  if (asrc->write(buffers[i], this_read) != this_read) {
298  error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
299  goto out;
300  }
301  }
302  }
303  }
304 
305  while ((avail = stretcher.available()) >= 0) {
306 
307  framecnt_t this_read = min (bufsize, avail);
308 
309  stretcher.retrieve(buffers, this_read);
310 
311  for (uint32_t i = 0; i < nsrcs.size(); ++i) {
312 
314  if (!asrc) {
315  continue;
316  }
317 
318  if (asrc->write(buffers[i], this_read) !=
319  this_read) {
320  error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
321  goto out;
322  }
323  }
324  }
325 
326  } catch (runtime_error& err) {
327  error << string_compose (_("programming error: %1"), X_("timefx code failure")) << endmsg;
328  error << err.what() << endmsg;
329  goto out;
330  }
331 
332  new_name = region->name();
333  at = new_name.find ('@');
334 
335  // remove any existing stretch indicator
336 
337  if (at != string::npos && at > 2) {
338  new_name = new_name.substr (0, at - 1);
339  }
340 
341  new_name += suffix;
342 
343  ret = finish (region, nsrcs, new_name);
344 
345  /* now reset ancestral data for each new region */
346 
347  for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
348 
349  (*x)->set_ancestral_data (read_start,
350  read_duration,
351  stretch,
352  shift);
353  (*x)->set_master_sources (region->master_sources());
354  /* multiply the old (possibly previously stretched) region length by the extra
355  stretch this time around to get its new length
356  */
357  (*x)->set_length ((*x)->length() * tsr.time_fraction);
358  }
359 
360  /* stretch region gain envelope */
361  /* XXX: assuming we've only processed one input region into one result here */
362 
363  if (tsr.time_fraction != 1) {
364  result = boost::dynamic_pointer_cast<AudioRegion> (results.front());
365  assert (result);
366  result->envelope()->x_scale (tsr.time_fraction);
367  }
368 
369  out:
370 
371  delete [] gain_buffer;
372 
373  if (buffers) {
374  for (uint32_t i = 0; i < channels; ++i) {
375  delete buffers[i];
376  }
377  delete [] buffers;
378  }
379 
380  if (ret || tsr.cancel) {
381  for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
382  (*si)->mark_for_remove ();
383  }
384  }
385 
386  return ret;
387 }
388 
389 
390 
391 
392 
const SourceList & master_sources() const
Definition: region.h:262
void x_scale(double factor)
int make_new_sources(boost::shared_ptr< ARDOUR::Region >, ARDOUR::SourceList &, std::string suffix="")
Definition: filter.cc:42
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
int run(boost::shared_ptr< ARDOUR::Region >, Progress *progress=0)
Definition: rb_effect.cc:65
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
float gain_t
Definition: types.h:58
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
framecnt_t frame_rate() const
Definition: session.h:365
LIBARDOUR_API PBD::PropertyDescriptor< float > stretch
Definition: region.cc:70
#define _(Text)
Definition: i18n.h:11
ARDOUR::Session & session
Definition: filter.h:49
boost::shared_ptr< AutomationList > envelope()
Definition: audioregion.h:91
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
float Sample
Definition: types.h:54
void set_progress(float)
Definition: progress.cc:59
framecnt_t ancestral_length() const
Definition: region.h:126
LIBARDOUR_API PBD::PropertyDescriptor< float > shift
Definition: region.cc:71
Definition: amp.h:29
TimeFXRequest & tsr
Definition: rb_effect.h:38
int64_t framepos_t
Definition: types.h:66
virtual framecnt_t master_read_at(Sample *buf, Sample *mixdown_buf, float *gain_buf, framepos_t position, framecnt_t cnt, uint32_t chan_n=0) const
Definition: audioregion.cc:453
framepos_t position() const
Definition: region.h:112
std::vector< boost::shared_ptr< ARDOUR::Region > > results
Definition: filter.h:41
const char * name
float shift() const
Definition: region.h:129
uint32_t n_channels() const
Definition: region.h:259
std::string name() const
Definition: debug.h:30
virtual framecnt_t write(Sample *src, framecnt_t cnt)
Definition: audiosource.cc:321
framecnt_t length() const
Definition: region.h:114
framepos_t start() const
Definition: region.h:113
framepos_t ancestral_start() const
Definition: region.h:125
int finish(boost::shared_ptr< ARDOUR::Region >, ARDOUR::SourceList &, std::string region_name="")
Definition: filter.cc:88
RBEffect(ARDOUR::Session &, TimeFXRequest &)
Definition: rb_effect.cc:52
std::vector< boost::shared_ptr< Source > > SourceList
Definition: types.h:520
float stretch() const
Definition: region.h:128
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208