diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-11-08 01:40:25 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-11-08 01:40:25 +0000 |
commit | badc087263990ecf360792c10e4d9f2d60828d43 (patch) | |
tree | 7e4b7e0afea47be51cbad48f06bb1779f483f56f /libs | |
parent | df20e5935fbdaf7d27f924e4e2ea87707d8a2314 (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/SConscript | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/filter.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/region.h | 16 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 15 | ||||
-rw-r--r-- | libs/ardour/ardour/stretch.h | 51 | ||||
-rw-r--r-- | libs/ardour/audioregion.cc | 28 | ||||
-rw-r--r-- | libs/ardour/filter.cc | 23 | ||||
-rw-r--r-- | libs/ardour/midi_region.cc | 8 | ||||
-rw-r--r-- | libs/ardour/region.cc | 49 | ||||
-rw-r--r-- | libs/ardour/session.cc | 5 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 31 | ||||
-rw-r--r-- | libs/ardour/session_timefx.cc | 236 | ||||
-rw-r--r-- | libs/ardour/stretch.cc | 227 |
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; +} |