summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2007-11-08 01:40:25 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2007-11-08 01:40:25 +0000
commitbadc087263990ecf360792c10e4d9f2d60828d43 (patch)
tree7e4b7e0afea47be51cbad48f06bb1779f483f56f /libs
parentdf20e5935fbdaf7d27f924e4e2ea87707d8a2314 (diff)
merged with 2.0-ongoing changes 2582-2605 (not thoroughly tested but it compiles, start up, and creates a new session)
git-svn-id: svn://localhost/ardour2/trunk@2606 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/SConscript2
-rw-r--r--libs/ardour/ardour/filter.h4
-rw-r--r--libs/ardour/ardour/region.h16
-rw-r--r--libs/ardour/ardour/session.h15
-rw-r--r--libs/ardour/ardour/stretch.h51
-rw-r--r--libs/ardour/audioregion.cc28
-rw-r--r--libs/ardour/filter.cc23
-rw-r--r--libs/ardour/midi_region.cc8
-rw-r--r--libs/ardour/region.cc49
-rw-r--r--libs/ardour/session.cc5
-rw-r--r--libs/ardour/session_state.cc31
-rw-r--r--libs/ardour/session_timefx.cc236
-rw-r--r--libs/ardour/stretch.cc227
13 files changed, 432 insertions, 263 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index f40752167d..2d030ac70a 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -121,7 +121,6 @@ session_process.cc
session_state.cc
session_state_utils.cc
session_time.cc
-session_timefx.cc
session_transport.cc
session_utils.cc
silentfilesource.cc
@@ -130,6 +129,7 @@ sndfile_helpers.cc
sndfilesource.cc
source.cc
source_factory.cc
+stretch.cc
tape_file_matcher.cc
template_utils.cc
tempo.cc
diff --git a/libs/ardour/ardour/filter.h b/libs/ardour/ardour/filter.h
index 0e4c6b3de0..b659873bdb 100644
--- a/libs/ardour/ardour/filter.h
+++ b/libs/ardour/ardour/filter.h
@@ -40,8 +40,8 @@ class Filter {
protected:
Filter (ARDOUR::Session& s) : session(s) {}
- int make_new_sources (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&);
- int finish (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&);
+ int make_new_sources (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string suffix = "");
+ int finish (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string region_name = "");
ARDOUR::Session& session;
};
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index e62b59ca61..c246da9cce 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -95,7 +95,13 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
nframes_t start () const { return _start; }
nframes_t length() const { return _length; }
layer_t layer () const { return _layer; }
-
+
+ nframes64_t ancestral_start () const { return _ancestral_start; }
+ nframes64_t ancestral_length () const { return _ancestral_length; }
+ float stretch() const { return _stretch; }
+
+ void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch);
+
nframes_t sync_offset(int& dir) const;
nframes_t sync_position() const;
@@ -177,11 +183,12 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
boost::shared_ptr<Source> source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; }
uint32_t n_channels() const { return _sources.size(); }
- std::vector<string> master_source_names();
-
const SourceList& sources() const { return _sources; }
const SourceList& master_sources() const { return _master_sources; }
+ std::vector<string> master_source_names();
+ void set_master_sources (SourceList&);
+
/* serialization */
XMLNode& get_state ();
@@ -241,6 +248,9 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
layer_t _layer;
mutable RegionEditState _first_edit;
int _frozen;
+ nframes64_t _ancestral_start;
+ nframes64_t _ancestral_length;
+ float _stretch;
mutable uint32_t _read_data_count; ///< modified in read()
Change _pending_changed;
uint64_t _last_layer_op; ///< timestamp
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index e153914a09..579521fd2c 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -860,21 +860,6 @@ class Session : public PBD::StatefulDestructible
boost::shared_ptr<IO> click_io() { return _click_io; }
- /* tempo FX */
-
- struct TimeStretchRequest {
- boost::shared_ptr<ARDOUR::AudioRegion> region;
- float fraction; /* session: read ; GUI: write */
- float progress; /* session: write ; GUI: read */
- bool running; /* read/write */
- bool quick_seek; /* GUI: write */
- bool antialias; /* GUI: write */
-
- TimeStretchRequest () {}
- };
-
- boost::shared_ptr<AudioRegion> tempoize_region (TimeStretchRequest&);
-
/* disk, buffer loads */
uint32_t playback_load ();
diff --git a/libs/ardour/ardour/stretch.h b/libs/ardour/ardour/stretch.h
new file mode 100644
index 0000000000..4d00c9b17b
--- /dev/null
+++ b/libs/ardour/ardour/stretch.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_stretch_h__
+#define __ardour_stretch_h__
+
+#include <ardour/filter.h>
+#include <soundtouch/SoundTouch.h>
+
+namespace ARDOUR {
+
+class AudioRegion;
+
+struct TimeStretchRequest : public InterThreadInfo {
+ float fraction;
+ bool quick_seek;
+ bool antialias;
+};
+
+class Stretch : public Filter {
+ public:
+ Stretch (ARDOUR::Session&, TimeStretchRequest&);
+ ~Stretch ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+ private:
+ TimeStretchRequest& tsr;
+ soundtouch::SoundTouch st;
+
+};
+
+} /* namespace */
+
+#endif /* __ardour_stretch_h__ */
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
index c6f4d3c480..91654643d9 100644
--- a/libs/ardour/audioregion.cc
+++ b/libs/ardour/audioregion.cc
@@ -32,6 +32,7 @@
#include <pbd/xml++.h>
#include <pbd/stacktrace.h>
#include <pbd/enumwriter.h>
+#include <pbd/convert.h>
#include <ardour/audioregion.h>
#include <ardour/session.h>
@@ -47,6 +48,7 @@
using namespace std;
using namespace ARDOUR;
+using namespace PBD;
/* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
@@ -127,6 +129,23 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
{
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
+ _sources.push_back (*i);
+
+ pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
+
+ result = unique_srcs.insert (*i);
+
+ if (result.second) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+ }
+ }
+
/* return to default fades if the existing ones are too long */
init ();
@@ -494,12 +513,20 @@ AudioRegion::state (bool full)
snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
node.add_property ("scale-gain", buf);
+ // XXX these should move into Region
+
for (uint32_t n=0; n < _sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "source-%d", n);
_sources[n]->id().print (buf, sizeof (buf));
node.add_property (buf2, buf);
}
+ for (uint32_t n=0; n < _master_sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+ _master_sources[n]->id().print (buf, sizeof (buf));
+ node.add_property (buf2, buf);
+ }
+
snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
node.add_property ("channels", buf);
@@ -973,6 +1000,7 @@ AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
return audio_source()->read (buf, pos, cnt);
}
+
int
AudioRegion::exportme (Session& session, AudioExportSpecification& spec)
{
diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc
index 208377cc4a..dc8185db95 100644
--- a/libs/ardour/filter.cc
+++ b/libs/ardour/filter.cc
@@ -35,12 +35,25 @@ using namespace ARDOUR;
using namespace PBD;
int
-Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs)
+Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs, string suffix)
{
vector<string> names = region->master_source_names();
for (uint32_t i = 0; i < region->n_channels(); ++i) {
+ string name = PBD::basename_nosuffix (names[i]);
+
+ /* remove any existing version of suffix by assuming it starts
+ with some kind of "special" character.
+ */
+
+ if (!suffix.empty()) {
+ string::size_type pos = name.find (suffix[0]);
+ if (pos != string::npos && pos > 2) {
+ name = name.substr (0, pos - 1);
+ }
+ }
+
string path = session.path_from_region_name (region->data_type(),
PBD::basename_nosuffix (names[i]), string (""));
@@ -65,10 +78,8 @@ Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs)
}
int
-Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs)
+Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs, string region_name)
{
- string region_name;
-
/* update headers on new sources */
time_t xnow;
@@ -94,7 +105,9 @@ Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs)
/* create a new region */
- region_name = session.new_region_name (region->name());
+ if (region_name.empty()) {
+ region_name = session.new_region_name (region->name());
+ }
results.clear ();
results.push_back (RegionFactory::create (nsrcs, 0, region->length(), region_name, 0,
Region::Flag (Region::WholeFile|Region::DefaultFlags)));
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
index dc115cd55a..cc1ba4b2a8 100644
--- a/libs/ardour/midi_region.cc
+++ b/libs/ardour/midi_region.cc
@@ -189,6 +189,8 @@ MidiRegion::state (bool full)
LocaleGuard lg (X_("POSIX"));
node.add_property ("flags", enum_2_string (_flags));
+
+ // XXX these should move into Region
for (uint32_t n=0; n < _sources.size(); ++n) {
snprintf (buf2, sizeof(buf2), "source-%d", n);
@@ -196,6 +198,12 @@ MidiRegion::state (bool full)
node.add_property (buf2, buf);
}
+ for (uint32_t n=0; n < _master_sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+ _master_sources[n]->id().print (buf, sizeof (buf));
+ node.add_property (buf2, buf);
+ }
+
if (full && _extra_xml) {
node.add_child_copy (*_extra_xml);
}
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
index 1809af671c..ecc7b5e305 100644
--- a/libs/ardour/region.cc
+++ b/libs/ardour/region.cc
@@ -83,6 +83,9 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
, _layer(layer)
, _first_edit(EditChangesNothing)
, _frozen(0)
+ , _ancestral_start (start)
+ , _ancestral_length (length)
+ , _stretch (1.0)
, _read_data_count(0)
, _pending_changed(Change (0))
, _last_layer_op(0)
@@ -142,6 +145,9 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
, _layer(layer)
, _first_edit(EditChangesNothing)
, _frozen(0)
+ , _ancestral_start (other->_ancestral_start + offset)
+ , _ancestral_length (length)
+ , _stretch (1.0)
, _read_data_count(0)
, _pending_changed(Change (0))
, _last_layer_op(0)
@@ -183,6 +189,9 @@ Region::Region (boost::shared_ptr<const Region> other)
, _layer(other->_layer)
, _first_edit(EditChangesID)
, _frozen(0)
+ , _ancestral_start (_start)
+ , _ancestral_length (_length)
+ , _stretch (1.0)
, _read_data_count(0)
, _pending_changed(Change(0))
, _last_layer_op(other->_last_layer_op)
@@ -518,6 +527,14 @@ Region::nudge_position (long n, void *src)
}
void
+Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st)
+{
+ _ancestral_length = l;
+ _ancestral_start = s;
+ _stretch = st;
+}
+
+void
Region::set_start (nframes_t pos, void *src)
{
if (_flags & (Locked|PositionLocked)) {
@@ -922,6 +939,12 @@ Region::state (bool full_state)
node->add_property ("length", buf);
snprintf (buf, sizeof (buf), "%u", _position);
node->add_property ("position", buf);
+ snprintf (buf, sizeof (buf), "%lu", _ancestral_start);
+ node->add_property ("ancestral-start", buf);
+ snprintf (buf, sizeof (buf), "%lu", _ancestral_length);
+ node->add_property ("ancestral-length", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _stretch);
+ node->add_property ("stretch", buf);
switch (_first_edit) {
case EditChangesNothing:
@@ -1033,6 +1056,26 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
/* XXX FIRST EDIT !!! */
+ /* these 3 properties never change as a result of any editing */
+
+ if ((prop = node.property ("ancestral-start")) != 0) {
+ _ancestral_start = atoi (prop->value());
+ } else {
+ _ancestral_start = _start;
+ }
+
+ if ((prop = node.property ("ancestral-length")) != 0) {
+ _ancestral_length = atoi (prop->value());
+ } else {
+ _ancestral_length = _length;
+ }
+
+ if ((prop = node.property ("stretch")) != 0) {
+ _stretch = atof (prop->value());
+ } else {
+ _stretch = 1.0;
+ }
+
/* note: derived classes set flags */
if (_extra_xml) {
@@ -1185,6 +1228,12 @@ Region::master_source_names ()
return names;
}
+void
+Region::set_master_sources (SourceList& srcs)
+{
+ _master_sources = srcs;
+}
+
bool
Region::source_equivalent (boost::shared_ptr<const Region> other) const
{
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index e155800d23..a664ca44c7 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -2113,8 +2113,13 @@ Session::remove_route (shared_ptr<Route> route)
/* try to cause everyone to drop their references */
+ cerr << "pre drop, Route now has " << route.use_count() << " refs\n";
+ cerr << "sig has " << route->GoingAway.size() << endl;
+
route->drop_references ();
+ cerr << "route dangling refs = " << route.use_count() << endl;
+
/* save the new state of the world */
if (save_state (_current_snapshot_name)) {
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index c711b227e7..052931f8da 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -1373,6 +1373,7 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
boost::shared_ptr<Source> source;
boost::shared_ptr<AudioSource> as;
SourceList sources;
+ SourceList master_sources;
uint32_t nchans = 1;
char buf[128];
@@ -1432,7 +1433,27 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
sources.push_back (as);
}
}
-
+
+ for (uint32_t n=1; n < nchans; ++n) {
+ snprintf (buf, sizeof(buf), X_("master-source-%d"), n);
+ if ((prop = node.property (buf)) != 0) {
+
+ PBD::ID id2 (prop->value());
+
+ if ((source = source_by_id (id2)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ as = boost::dynamic_pointer_cast<AudioSource>(source);
+ if (!as) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+ master_sources.push_back (as);
+ }
+ }
+
try {
boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node)));
@@ -1447,6 +1468,14 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
}
}
+ if (!master_sources.empty()) {
+ if (master_sources.size() == nchans) {
+ error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg;
+ } else {
+ region->set_master_sources (master_sources);
+ }
+ }
+
return region;
}
diff --git a/libs/ardour/session_timefx.cc b/libs/ardour/session_timefx.cc
deleted file mode 100644
index 115d3eeeec..0000000000
--- a/libs/ardour/session_timefx.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- Copyright (C) 2003 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
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <cerrno>
-#include <stdexcept>
-
-#include <pbd/basename.h>
-
-#include <soundtouch/SoundTouch.h>
-
-#include <ardour/session.h>
-#include <ardour/audioregion.h>
-#include <ardour/sndfilesource.h>
-#include <ardour/region_factory.h>
-#include <ardour/source_factory.h>
-
-#include "i18n.h"
-
-using namespace std;
-using namespace ARDOUR;
-using namespace PBD;
-using namespace soundtouch;
-
-boost::shared_ptr<AudioRegion>
-Session::tempoize_region (TimeStretchRequest& tsr)
-{
- SourceList sources;
- SourceList::iterator it;
- boost::shared_ptr<AudioRegion> r;
- SoundTouch st;
- string region_name;
- string ident = X_("-TIMEFX-");
- float percentage;
- nframes_t total_frames;
- nframes_t done;
- int c;
- char buf[64];
- string::size_type len;
-
- /* the soundtouch code wants a *tempo* change percentage, which is
- of opposite sign to the length change.
- */
-
- percentage = -tsr.fraction;
-
- st.setSampleRate (frame_rate());
- st.setChannels (1);
- st.setTempoChange (percentage);
- st.setPitchSemiTones (0);
- st.setRateChange (0);
-
- st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek);
- st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias);
-
- vector<string> names = tsr.region->master_source_names();
-
- tsr.progress = 0.0f;
- total_frames = tsr.region->length() * tsr.region->n_channels();
- done = 0;
-
- for (uint32_t i = 0; i < tsr.region->n_channels(); ++i) {
-
- string rstr;
- string::size_type existing_ident;
-
- if ((existing_ident = names[i].find (ident)) != string::npos) {
- rstr = names[i].substr (0, existing_ident);
- } else {
- rstr = names[i];
- }
-
- string path = path_from_region_name (DataType::AUDIO, PBD::basename_nosuffix (rstr), ident);
-
- if (path.length() == 0) {
- error << string_compose (_("tempoize: error creating name for new audio file based on %1"), tsr.region->name())
- << endmsg;
- goto out;
- }
-
- try {
- sources.push_back (boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate())));
-
- } catch (failed_constructor& err) {
- error << string_compose (_("tempoize: error creating new audio file %1 (%2)"), path, strerror (errno)) << endmsg;
- goto out;
- }
-
- }
-
- try {
- const nframes_t bufsize = 16384;
-
- for (uint32_t i = 0; i < sources.size(); ++i) {
- gain_t gain_buffer[bufsize];
- Sample buffer[bufsize];
- nframes_t pos = 0;
- nframes_t this_read = 0;
-
- boost::shared_ptr<AudioSource> asrc = boost::dynamic_pointer_cast<AudioSource>(sources[i]);
- if (!asrc) {
- cerr << "FIXME: TimeFX for non-audio" << endl;
- continue;
- }
-
- st.clear();
- while (tsr.running && pos < tsr.region->length()) {
- nframes_t this_time;
-
- this_time = min (bufsize, tsr.region->length() - pos);
-
- /* read from the master (original) sources for the region,
- not the ones currently in use, in case it's already been
- subject to timefx. */
-
- if ((this_read = tsr.region->master_read_at (buffer, buffer, gain_buffer, pos + tsr.region->position(), this_time)) != this_time) {
- error << string_compose (_("tempoize: error reading data from %1"), sources[i]->name()) << endmsg;
- goto out;
- }
-
- pos += this_read;
- done += this_read;
-
- tsr.progress = (float) done / total_frames;
-
- st.putSamples (buffer, this_read);
-
- while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && tsr.running) {
- if (asrc->write (buffer, this_read) != this_read) {
- error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg;
- goto out;
- }
- }
- }
-
- if (tsr.running) {
- st.flush ();
- }
-
- while (tsr.running && (this_read = st.receiveSamples (buffer, bufsize)) > 0) {
- if (asrc->write (buffer, this_read) != this_read) {
- error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg;
- goto out;
- }
- }
- }
- } catch (runtime_error& err) {
- error << _("timefx code failure. please notify ardour-developers.") << endmsg;
- error << err.what() << endmsg;
- goto out;
- }
-
- time_t now;
- struct tm* xnow;
- time (&now);
- xnow = localtime (&now);
-
- for (it = sources.begin(); it != sources.end(); ++it) {
- boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*it);
- if (afs) {
- afs->update_header (tsr.region->position(), *xnow, now);
- }
- }
-
- len = tsr.region->name().length();
-
- while (--len) {
- if (!isdigit (tsr.region->name()[len])) {
- break;
- }
- }
-
- if (len == 0) {
-
- region_name = tsr.region->name() + ".t000";
-
- } else {
-
- if (tsr.region->name()[len] == 't') {
- c = atoi (tsr.region->name().substr(len+1).c_str());
-
- snprintf (buf, sizeof (buf), "t%03d", ++c);
- region_name = tsr.region->name().substr (0, len) + buf;
-
- } else {
-
- /* not sure what this is, just tack the suffix on to it */
-
- region_name = tsr.region->name() + ".t000";
- }
-
- }
-
- r = (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, sources.front()->length(), region_name,
- 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile))));
-
- out:
-
- if (sources.size()) {
-
- /* if we failed to complete for any reason, mark the new file
- for deletion.
- */
-
- if ((!r || !tsr.running)) {
- for (it = sources.begin(); it != sources.end(); ++it) {
- (*it)->mark_for_remove ();
- }
- }
-
- sources.clear ();
- }
-
- /* if the process was cancelled, delete the region */
-
- if (!tsr.running) {
- r.reset ();
- }
-
- return r;
-}
diff --git a/libs/ardour/stretch.cc b/libs/ardour/stretch.cc
new file mode 100644
index 0000000000..64b741d1af
--- /dev/null
+++ b/libs/ardour/stretch.cc
@@ -0,0 +1,227 @@
+/*
+ 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <cmath>
+
+#include <pbd/error.h>
+
+#include <ardour/types.h>
+#include <ardour/stretch.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace soundtouch;
+
+Stretch::Stretch (Session& s, TimeStretchRequest& req)
+ : Filter (s)
+ , tsr (req)
+{
+ float percentage;
+
+ /* the soundtouch code wants a *tempo* change percentage, which is
+ of opposite sign to the length change.
+ */
+
+ percentage = -tsr.fraction;
+
+ st.setSampleRate (s.frame_rate());
+ st.setChannels (1);
+ st.setTempoChange (percentage);
+ st.setPitchSemiTones (0);
+ st.setRateChange (0);
+
+ st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek);
+ st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias);
+
+ tsr.progress = 0.0f;
+}
+
+Stretch::~Stretch ()
+{
+}
+
+int
+Stretch::run (boost::shared_ptr<Region> r)
+{
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (r);
+
+ if (!region) {
+ return -1;
+ }
+
+ SourceList nsrcs;
+ nframes_t total_frames;
+ nframes_t done;
+ int ret = -1;
+ const nframes_t bufsize = 16384;
+ gain_t *gain_buffer = 0;
+ Sample *buffer = 0;
+ char suffix[32];
+ string new_name;
+ string::size_type at;
+
+ tsr.progress = 0.0f;
+ tsr.done = false;
+
+ total_frames = region->length() * region->n_channels();
+ done = 0;
+
+ /* the name doesn't need to be super-precise, but allow for 2 fractional
+ digits just to disambiguate close but not identical stretches.
+ */
+
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.fraction * 100.0f));
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs, suffix)) {
+ goto out;
+ }
+
+ gain_buffer = new gain_t[bufsize];
+ buffer = new Sample[bufsize];
+
+ // soundtouch throws runtime_error on error
+
+ try {
+ for (uint32_t i = 0; i < nsrcs.size(); ++i) {
+
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (nsrcs[i]);
+
+ if (!afs) {
+ continue;
+ }
+
+ nframes_t pos = 0;
+ nframes_t this_read = 0;
+
+ st.clear();
+
+ while (!tsr.cancel && pos < region->length()) {
+ nframes_t this_time;
+
+ this_time = min (bufsize, region->length() - pos);
+
+ /* read from the master (original) sources for the region,
+ not the ones currently in use, in case it's already been
+ subject to timefx.
+ */
+
+ if ((this_read = region->master_read_at (buffer, buffer, gain_buffer, pos + region->position(), this_time)) != this_time) {
+ error << string_compose (_("tempoize: error reading data from %1"), afs->name()) << endmsg;
+ goto out;
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = (float) done / total_frames;
+
+ st.putSamples (buffer, this_read);
+
+ while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && !tsr.cancel) {
+ if (afs->write (buffer, this_read) != this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), afs->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ if (!tsr.cancel) {
+ st.flush ();
+ }
+
+ while (!tsr.cancel && (this_read = st.receiveSamples (buffer, bufsize)) > 0) {
+ if (afs->write (buffer, this_read) != this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), afs->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ } catch (runtime_error& err) {
+ error << _("timefx code failure. please notify ardour-developers.") << endmsg;
+ error << err.what() << endmsg;
+ goto out;
+ }
+
+ new_name = region->name();
+ at = new_name.find ('@');
+
+ // remove any existing stretch indicator
+
+ if (at != string::npos && at > 2) {
+ new_name = new_name.substr (0, at - 1);
+ }
+
+ new_name += suffix;
+
+ ret = finish (region, nsrcs, new_name);
+
+ /* now reset ancestral data for each new region */
+
+ for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
+
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*x);
+
+ assert (region != 0);
+
+ nframes64_t astart = region->ancestral_start();
+ nframes64_t alength = region->ancestral_length();
+ nframes_t start;
+ nframes_t length;
+
+ // note: tsr.fraction is a percentage of original length. 100 = no change,
+ // 50 is half as long, 200 is twice as long, etc.
+
+ float stretch = region->stretch() * (tsr.fraction/100.0);
+
+ start = (nframes_t) floor (astart + ((astart - region->start()) / stretch));
+ length = (nframes_t) floor (alength / stretch);
+
+ region->set_ancestral_data (start, length, stretch);
+ }
+
+ out:
+
+ if (gain_buffer) {
+ delete [] gain_buffer;
+ }
+
+ if (buffer) {
+ delete [] buffer;
+ }
+
+ if (ret || tsr.cancel) {
+ for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ (*si)->mark_for_remove ();
+ }
+ }
+
+ tsr.done = true;
+
+ return ret;
+}