X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Frb_effect.cc;h=d928377e2523aa38758d190941a128937a0f7021;hb=e7c091925757f72ab1230cc77de6269e18a659fd;hp=4daf5cb33a233569eb99c80d6fce52b1a51017a0;hpb=449aab3c465bbbf66d221fac3d7ea559f1720357;p=ardour.git diff --git a/libs/ardour/rb_effect.cc b/libs/ardour/rb_effect.cc index 4daf5cb33a..d928377e25 100644 --- a/libs/ardour/rb_effect.cc +++ b/libs/ardour/rb_effect.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2004-2007 Paul Davis + Copyright (C) 2004-2007 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,15 +20,16 @@ #include #include -#include -#include +#include "pbd/error.h" +#include "rubberband/RubberBandStretcher.h" -#include -#include -#include -#include -#include -#include +#include "ardour/types.h" +#include "ardour/stretch.h" +#include "ardour/pitch.h" +#include "ardour/audiofilesource.h" +#include "ardour/session.h" +#include "ardour/audioregion.h" +#include "ardour/progress.h" #include "i18n.h" @@ -42,7 +43,7 @@ Pitch::Pitch (Session& s, TimeFXRequest& req) { } -Stretch::Stretch (Session& s, TimeFXRequest& req) +RBStretch::RBStretch (Session& s, TimeFXRequest& req) : RBEffect (s, req) { } @@ -52,7 +53,7 @@ RBEffect::RBEffect (Session& s, TimeFXRequest& req) , tsr (req) { - tsr.progress = 0.0f; + } RBEffect::~RBEffect () @@ -60,51 +61,124 @@ RBEffect::~RBEffect () } int -RBEffect::run (boost::shared_ptr region) +RBEffect::run (boost::shared_ptr r, Progress* progress) { + boost::shared_ptr region = boost::dynamic_pointer_cast (r); + + if (!region) { + error << "RBEffect::run() passed a non-audio region! WTF?" << endmsg; + return -1; + } + SourceList nsrcs; - nframes_t done; + framecnt_t done; int ret = -1; - const nframes_t bufsize = 256; + const framecnt_t bufsize = 256; gain_t* gain_buffer = 0; Sample** buffers = 0; char suffix[32]; string new_name; string::size_type at; - nframes_t pos = 0; - int avail = 0; + framepos_t pos = 0; + framecnt_t avail = 0; + boost::shared_ptr result; + + cerr << "RBEffect: source region: position = " << region->position() + << ", start = " << region->start() + << ", length = " << region->length() + << ", ancestral_start = " << region->ancestral_start() + << ", ancestral_length = " << region->ancestral_length() + << ", stretch " << region->stretch() + << ", shift " << region->shift() << endl; + + /* + We have two cases to consider: + + 1. The region has not been stretched before. + + In this case, we just want to read region->length() frames + from region->start(). + + We will create a new region of region->length() * + tsr.time_fraction frames. The new region will have its + start set to 0 (because it has a new audio file that begins + at the start of the stretched area) and its ancestral_start + set to region->start() (so that we know where to begin + reading if we want to stretch it again). + + 2. The region has been stretched before. + + The region starts at region->start() frames into its + (possibly previously stretched) source file. But we don't + want to read from its source file; we want to read from the + file it was originally stretched from. + + The region's source begins at region->ancestral_start() + frames into its master source file. Thus, we need to start + reading at region->ancestral_start() + (region->start() / + region->stretch()) frames into the master source. This + value will also become the ancestral_start for the new + region. + + We cannot use region->ancestral_length() to establish how + many frames to read, because it won't be up to date if the + region has been trimmed since it was last stretched. We + must read region->length() / region->stretch() frames and + stretch them by tsr.time_fraction * region->stretch(), for + a new region of region->length() * tsr.time_fraction + frames. + + Case 1 is of course a special case of 2, where + region->ancestral_start() == 0 and region->stretch() == 1. + + When we ask to read from a region, we supply a position on + the global timeline. The read function calculates the + offset into the source as (position - region->position()) + + region->start(). This calculation is used regardless of + whether we are reading from a master or + previously-stretched region. In order to read from a point + n frames into the master source, we need to provide n - + region->start() + region->position() as our position + argument to master_read_at(). + + Note that region->ancestral_length() is not used. + + I hope this is clear. + */ - // note: this_time_fraction is a ratio of original length. 1.0 = no change, - // 0.5 is half as long, 2.0 is twice as long, etc. + double stretch = region->stretch() * tsr.time_fraction; + double shift = region->shift() * tsr.pitch_fraction; - double this_time_fraction = tsr.time_fraction * region->stretch (); - double this_pitch_fraction = tsr.pitch_fraction * region->shift (); + framecnt_t read_start = region->ancestral_start() + + framecnt_t(region->start() / (double)region->stretch()); - RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(), - (RubberBandStretcher::Options) tsr.opts, - this_time_fraction, this_pitch_fraction); - - tsr.progress = 0.0f; - tsr.done = false; + framecnt_t read_duration = + framecnt_t(region->length() / (double)region->stretch()); uint32_t channels = region->n_channels(); - nframes_t duration = region->ancestral_length(); - stretcher.setExpectedInputDuration(duration); + RubberBandStretcher stretcher + (session.frame_rate(), channels, + (RubberBandStretcher::Options) tsr.opts, stretch, shift); + + progress->set_progress (0); + tsr.done = false; + + stretcher.setExpectedInputDuration(read_duration); stretcher.setDebugLevel(1); /* the name doesn't need to be super-precise, but allow for 2 fractional digits just to disambiguate close but not identical FX */ - if (this_time_fraction == 1.0) { - snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_pitch_fraction * 100.0f)); - } else if (this_pitch_fraction == 1.0) { - snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_time_fraction * 100.0f)); + if (stretch == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (shift * 100.0f)); + } else if (shift == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (stretch * 100.0f)); } else { - snprintf (suffix, sizeof (suffix), "@%d-%d", - (int) floor (this_time_fraction * 100.0f), - (int) floor (this_pitch_fraction * 100.0f)); + snprintf (suffix, sizeof (suffix), "@%d-%d", + (int) floor (stretch * 100.0f), + (int) floor (shift * 100.0f)); } /* create new sources */ @@ -130,64 +204,71 @@ RBEffect::run (boost::shared_ptr region) avail = 0; done = 0; - try { - while (pos < duration && !tsr.cancel) { - - nframes_t this_read = 0; - + try { + while (pos < read_duration && !tsr.cancel) { + + framecnt_t this_read = 0; + for (uint32_t i = 0; i < channels; ++i) { - + this_read = 0; - nframes_t this_time; - - this_time = min(bufsize, duration - pos); - + + framepos_t this_time; + this_time = min(bufsize, read_duration - pos); + + framepos_t this_position; + this_position = read_start + pos - + region->start() + region->position(); + this_read = region->master_read_at (buffers[i], buffers[i], gain_buffer, - pos + region->position(), + this_position, this_time, i); - + if (this_read != this_time) { error << string_compose (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"), - region->name(), pos + region->position(), this_time, this_read) << endmsg; + region->name(), this_position, this_time, this_read) << endmsg; goto out; } } - + pos += this_read; done += this_read; - tsr.progress = ((float) done / duration) * 0.25; + progress->set_progress (((float) done / read_duration) * 0.25); - stretcher.study(buffers, this_read, pos == duration); + stretcher.study(buffers, this_read, pos == read_duration); } - + done = 0; pos = 0; - while (pos < duration && !tsr.cancel) { - - nframes_t this_read = 0; - + while (pos < read_duration && !tsr.cancel) { + + framecnt_t this_read = 0; + for (uint32_t i = 0; i < channels; ++i) { - + this_read = 0; - nframes_t this_time; - - this_time = min(bufsize, duration - pos); - + framepos_t this_time; + this_time = min(bufsize, read_duration - pos); + + framepos_t this_position; + this_position = read_start + pos - + region->start() + region->position(); + this_read = region->master_read_at (buffers[i], buffers[i], gain_buffer, - pos + region->position(), + this_position, this_time, i); - + if (this_read != this_time) { error << string_compose (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"), @@ -199,22 +280,26 @@ RBEffect::run (boost::shared_ptr region) pos += this_read; done += this_read; - tsr.progress = 0.25 + ((float) done / duration) * 0.75; + progress->set_progress (0.25 + ((float) done / read_duration) * 0.75); - stretcher.process(buffers, this_read, pos == duration); + stretcher.process(buffers, this_read, pos == read_duration); - int avail = 0; + framecnt_t avail = 0; while ((avail = stretcher.available()) > 0) { - this_read = min(bufsize, uint32_t(avail)); + this_read = min (bufsize, avail); stretcher.retrieve(buffers, this_read); - + for (uint32_t i = 0; i < nsrcs.size(); ++i) { - if (nsrcs[i]->write(buffers[i], this_read) != - this_read) { + boost::shared_ptr asrc = boost::dynamic_pointer_cast(nsrcs[i]); + if (!asrc) { + continue; + } + + if (asrc->write(buffers[i], this_read) != this_read) { error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg; goto out; } @@ -224,13 +309,18 @@ RBEffect::run (boost::shared_ptr region) while ((avail = stretcher.available()) >= 0) { - uint32_t this_read = min(bufsize, uint32_t(avail)); + framecnt_t this_read = min (bufsize, avail); stretcher.retrieve(buffers, this_read); for (uint32_t i = 0; i < nsrcs.size(); ++i) { - if (nsrcs[i]->write(buffers[i], this_read) != + boost::shared_ptr asrc = boost::dynamic_pointer_cast(nsrcs[i]); + if (!asrc) { + continue; + } + + if (asrc->write(buffers[i], this_read) != this_read) { error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg; goto out; @@ -259,20 +349,31 @@ RBEffect::run (boost::shared_ptr region) /* now reset ancestral data for each new region */ - for (vector >::iterator x = results.begin(); x != results.end(); ++x) { + for (vector >::iterator x = results.begin(); x != results.end(); ++x) { + + (*x)->set_ancestral_data (read_start, + read_duration, + stretch, + shift); + (*x)->set_master_sources (region->master_sources()); + /* multiply the old (possibly previously stretched) region length by the extra + stretch this time around to get its new length + */ + (*x)->set_length ((*x)->length() * tsr.time_fraction); + } + + /* stretch region gain envelope */ + /* XXX: assuming we've only processed one input region into one result here */ - (*x)->set_ancestral_data (region->ancestral_start(), - region->ancestral_length(), - this_time_fraction, - this_pitch_fraction ); - (*x)->set_master_sources (region->get_master_sources()); + if (tsr.time_fraction != 1) { + result = boost::dynamic_pointer_cast (results.front()); + assert (result); + result->envelope()->x_scale (tsr.time_fraction); } out: - if (gain_buffer) { - delete [] gain_buffer; - } + delete [] gain_buffer; if (buffers) { for (uint32_t i = 0; i < channels; ++i) { @@ -286,7 +387,7 @@ RBEffect::run (boost::shared_ptr region) (*si)->mark_for_remove (); } } - + tsr.done = true; return ret; @@ -295,4 +396,4 @@ RBEffect::run (boost::shared_ptr region) - +