diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-11-01 15:28:42 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-11-01 15:28:42 +0000 |
commit | cb534fd536125fc0a1654f51d077424a58fda06e (patch) | |
tree | e745902b79e49b751cf3a738085b413e66a8208b | |
parent | 33cde64ba350219e5642dc0ad05d532e9fa51c83 (diff) |
new ancestral data handling for regions; new Stretch AudioFilter replaces session member function; fix for "+" in XML node name; fix up async peak build flag in SourceFactory
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2583 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r-- | gtk2_ardour/ardour_ui.cc | 4 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui_ed.cc | 3 | ||||
-rw-r--r-- | gtk2_ardour/editor.h | 3 | ||||
-rw-r--r-- | gtk2_ardour/editor_timefx.cc | 51 | ||||
-rw-r--r-- | gtk2_ardour/route_time_axis.cc | 2 | ||||
-rw-r--r-- | libs/ardour/SConscript | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/audiofilter.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/audioregion.h | 3 | ||||
-rw-r--r-- | libs/ardour/ardour/region.h | 23 | ||||
-rw-r--r-- | libs/ardour/ardour/session.h | 15 | ||||
-rw-r--r-- | libs/ardour/ardour/stretch.h | 51 | ||||
-rw-r--r-- | libs/ardour/audio_diskstream.cc | 2 | ||||
-rw-r--r-- | libs/ardour/audiofilter.cc | 27 | ||||
-rw-r--r-- | libs/ardour/audioregion.cc | 17 | ||||
-rw-r--r-- | libs/ardour/region.cc | 43 | ||||
-rw-r--r-- | libs/ardour/session.cc | 11 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 30 | ||||
-rw-r--r-- | libs/ardour/session_timefx.cc | 236 | ||||
-rw-r--r-- | libs/ardour/source_factory.cc | 48 | ||||
-rw-r--r-- | libs/ardour/stretch.cc | 210 |
20 files changed, 447 insertions, 338 deletions
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 2eed08ab6f..4e40f2a907 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -3126,8 +3126,4 @@ ARDOUR_UI::setup_profile () } } -void -ARDOUR_UI::audioengine_setup () -{ -} diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index 4ee6ccc2fc..0143f3c60e 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -159,9 +159,6 @@ ARDOUR_UI::install_actions () ActionManager::register_action (jack_actions, X_("JACK"), _("JACK")); ActionManager::register_action (jack_actions, X_("Latency"), _("Latency")); - /* not sensitive to the presence or absence of JACK */ - act = ActionManager::register_action (jack_actions, X_("AudioEngineSetup"), _("Setup"), mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::audioengine_setup)); - act = ActionManager::register_action (jack_actions, X_("JACKReconnect"), _("Reconnect"), mem_fun (*(ARDOUR_UI::instance()), &ARDOUR_UI::reconnect_to_jack)); ActionManager::jack_opposite_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 4cfb1b0fda..4dba9d3fb4 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -45,6 +45,7 @@ #include <pbd/stateful.h> #include <ardour/session.h> +#include <ardour/stretch.h> #include <ardour/tempo.h> #include <ardour/location.h> #include <ardour/audioregion.h> @@ -1744,7 +1745,7 @@ class Editor : public PublicEditor void end_time_fx (ArdourCanvas::Item*, GdkEvent*); struct TimeStretchDialog : public ArdourDialog { - ARDOUR::Session::TimeStretchRequest request; + ARDOUR::TimeStretchRequest request; Editor& editor; RegionSelection regions; Gtk::ProgressBar progress_bar; diff --git a/gtk2_ardour/editor_timefx.cc b/gtk2_ardour/editor_timefx.cc index 2efdc03f8f..df0a73c965 100644 --- a/gtk2_ardour/editor_timefx.cc +++ b/gtk2_ardour/editor_timefx.cc @@ -39,6 +39,7 @@ #include <ardour/audio_track.h> #include <ardour/audioregion.h> #include <ardour/audio_diskstream.h> +#include <ardour/stretch.h> #include "i18n.h" @@ -87,29 +88,29 @@ gint Editor::TimeStretchDialog::update_progress () { progress_bar.set_fraction (request.progress); - return request.running; + return !request.done; } void Editor::TimeStretchDialog::cancel_timestretch_in_progress () { status = -2; - request.running = false; + request.cancel = true; + first_cancel.disconnect(); } gint Editor::TimeStretchDialog::delete_timestretch_in_progress (GdkEventAny* ev) { status = -2; - request.running = false; + request.cancel = true; + first_delete.disconnect(); return TRUE; } int Editor::run_timestretch (RegionSelection& regions, float fraction) { - pthread_t thread; - if (current_timestretch == 0) { current_timestretch = new TimeStretchDialog (*this); } @@ -130,27 +131,30 @@ Editor::run_timestretch (RegionSelection& regions, float fraction) current_timestretch->request.quick_seek = current_timestretch->quick_button.get_active(); current_timestretch->request.antialias = !current_timestretch->antialias_button.get_active(); current_timestretch->request.progress = 0.0f; - current_timestretch->request.running = true; + current_timestretch->request.done = false; + current_timestretch->request.cancel = false; /* re-connect the cancel button and delete events */ current_timestretch->first_cancel.disconnect(); current_timestretch->first_delete.disconnect(); - current_timestretch->cancel_button->signal_clicked().connect (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress)); - current_timestretch->signal_delete_event().connect (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress)); + current_timestretch->first_cancel = current_timestretch->cancel_button->signal_clicked().connect + (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress)); + current_timestretch->first_delete = current_timestretch->signal_delete_event().connect + (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress)); - if (pthread_create_and_store ("timestretch", &thread, 0, timestretch_thread, current_timestretch)) { + if (pthread_create_and_store ("timestretch", ¤t_timestretch->request.thread, 0, timestretch_thread, current_timestretch)) { current_timestretch->hide (); error << _("timestretch cannot be started - thread creation error") << endmsg; return -1; } - pthread_detach (thread); + pthread_detach (current_timestretch->request.thread); sigc::connection c = Glib::signal_timeout().connect (mem_fun (current_timestretch, &TimeStretchDialog::update_progress), 100); - while (current_timestretch->request.running) { + while (!current_timestretch->request.done) { gtk_main_iteration (); } @@ -195,31 +199,34 @@ Editor::do_timestretch (TimeStretchDialog& dialog) continue; } - dialog.request.region = region; - - if (!dialog.request.running) { + if (dialog.request.cancel) { /* we were cancelled */ dialog.status = 1; return; } - if ((new_region = session->tempoize_region (dialog.request)) == 0) { + Stretch stretch (*session, dialog.request); + + if (stretch.run (region)) { dialog.status = -1; - dialog.request.running = false; + dialog.request.done = true; return; } - XMLNode &before = playlist->get_state(); - playlist->replace_region (region, new_region, region->position()); - XMLNode &after = playlist->get_state(); - session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after)); + if (!stretch.results.empty()) { + new_region = stretch.results.front(); + + XMLNode &before = playlist->get_state(); + playlist->replace_region (region, new_region, region->position()); + XMLNode &after = playlist->get_state(); + session->add_command (new MementoCommand<Playlist>(*playlist, &before, &after)); + } i = tmp; } dialog.status = 0; - dialog.request.running = false; - dialog.request.region.reset (); + dialog.request.done = true; } void* diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 93419b813c..13fa435b16 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1490,7 +1490,7 @@ static string legalize_for_xml_node (string str) { string::size_type pos; - string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=:"; + string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:"; string legal; legal = str; diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index dfa7396bcc..224fcc8656 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -86,13 +86,13 @@ session_midi.cc session_process.cc session_state.cc session_time.cc -session_timefx.cc session_transport.cc silentfilesource.cc sndfile_helpers.cc sndfilesource.cc source.cc source_factory.cc +stretch.cc tempo.cc utils.cc version.cc diff --git a/libs/ardour/ardour/audiofilter.h b/libs/ardour/ardour/audiofilter.h index 6b60544942..18751a2bde 100644 --- a/libs/ardour/ardour/audiofilter.h +++ b/libs/ardour/ardour/audiofilter.h @@ -41,8 +41,8 @@ class AudioFilter { protected: ARDOUR::Session& session; - int make_new_sources (boost::shared_ptr<ARDOUR::AudioRegion>, ARDOUR::SourceList&); - int finish (boost::shared_ptr<ARDOUR::AudioRegion>, ARDOUR::SourceList&); + int make_new_sources (boost::shared_ptr<ARDOUR::AudioRegion>, ARDOUR::SourceList&, std::string suffix = ""); + int finish (boost::shared_ptr<ARDOUR::AudioRegion>, ARDOUR::SourceList&, std::string region_name = ""); }; } /* namespace */ diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index a450d90008..ff32199771 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -67,7 +67,8 @@ class AudioRegion : public Region uint32_t n_channels() const { return sources.size(); } vector<string> master_source_names(); - + void set_master_sources (SourceList&); + bool envelope_active () const { return _flags & Region::EnvelopeActive; } bool fade_in_active () const { return _flags & Region::FadeIn; } bool fade_out_active () const { return _flags & Region::FadeOut; } diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 0002356f41..16242a2f42 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -88,8 +88,14 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro nframes_t position () const { return _position; } nframes_t start () const { return _start; } nframes_t length() const { return _length; } - layer_t layer () const { return _layer; } - + 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; @@ -205,11 +211,11 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro virtual void recompute_at_end () = 0; - nframes_t _start; - nframes_t _length; - nframes_t _position; - Flag _flags; - nframes_t _sync_position; + nframes_t _start; + nframes_t _length; + nframes_t _position; + Flag _flags; + nframes_t _sync_position; layer_t _layer; string _name; mutable RegionEditState _first_edit; @@ -219,6 +225,9 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro mutable uint32_t _read_data_count; // modified in read() Change pending_changed; uint64_t _last_layer_op; // timestamp + nframes64_t _ancestral_start; + nframes64_t _ancestral_length; + float _stretch; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 0097080c9d..b1a1254009 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -845,21 +845,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..02cc930ee5 --- /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/audiofilter.h> +#include <soundtouch/SoundTouch.h> + +namespace ARDOUR { + +class AudioRegion; + +struct TimeStretchRequest : public InterThreadInfo { + float fraction; + bool quick_seek; + bool antialias; +}; + +class Stretch : public AudioFilter { + public: + Stretch (ARDOUR::Session&, TimeStretchRequest&); + ~Stretch (); + + int run (boost::shared_ptr<ARDOUR::AudioRegion>); + + private: + TimeStretchRequest& tsr; + soundtouch::SoundTouch st; + +}; + +} /* namespace */ + +#endif /* __ardour_stretch_h__ */ diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index f7e982b502..6642cee369 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -792,7 +792,7 @@ AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_ chaninfo->current_playback_buffer = chaninfo->speed_buffer; } - playback_distance = i; + playback_distance = i; // + 1; last_phase = (phase & 0xFFFFFF); } else { diff --git a/libs/ardour/audiofilter.cc b/libs/ardour/audiofilter.cc index fb4e5af1ae..ee38bff89c 100644 --- a/libs/ardour/audiofilter.cc +++ b/libs/ardour/audiofilter.cc @@ -34,13 +34,26 @@ using namespace ARDOUR; using namespace PBD; int -AudioFilter::make_new_sources (boost::shared_ptr<AudioRegion> region, SourceList& nsrcs) +AudioFilter::make_new_sources (boost::shared_ptr<AudioRegion> region, SourceList& nsrcs, string suffix) { vector<string> names = region->master_source_names(); for (uint32_t i = 0; i < region->n_channels(); ++i) { - string path = session.path_from_region_name (PBD::basename_nosuffix (names[i]), string ("")); + 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 (name, suffix); if (path.length() == 0) { error << string_compose (_("audiofilter: error creating name for new audio file based on %1"), region->name()) @@ -63,10 +76,8 @@ AudioFilter::make_new_sources (boost::shared_ptr<AudioRegion> region, SourceList } int -AudioFilter::finish (boost::shared_ptr<AudioRegion> region, SourceList& nsrcs) +AudioFilter::finish (boost::shared_ptr<AudioRegion> region, SourceList& nsrcs, string region_name) { - string region_name; - /* update headers on new sources */ time_t xnow; @@ -91,9 +102,11 @@ AudioFilter::finish (boost::shared_ptr<AudioRegion> 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 (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (nsrcs, 0, region->length(), region_name, 0, + results.push_back (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (nsrcs, 0, nsrcs.front()->length(), region_name, 0, Region::Flag (Region::WholeFile|Region::DefaultFlags)))); return 0; diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 0a141ee3fa..4a82303cdb 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() */ @@ -92,7 +94,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n /* basic AudioRegion constructor */ sources.push_back (src); - master_sources.push_back (src); + master_sources.push_back (src); src->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted)); boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); @@ -167,6 +169,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t (*i)->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted)); } master_sources.push_back (*i); + } /* return to default fades if the existing ones are too long */ @@ -678,6 +681,12 @@ AudioRegion::state (bool full) child->add_property ("default", "yes"); } + 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); } @@ -1113,6 +1122,12 @@ AudioRegion::master_source_names () return names; } +void +AudioRegion::set_master_sources (SourceList& srcs) +{ + master_sources = srcs; +} + bool AudioRegion::source_equivalent (boost::shared_ptr<const Region> o) const { diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 7e131eab51..cd91772a3b 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -61,6 +61,9 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l _start = start; _sync_position = _start; _length = length; + _ancestral_start = start; + _ancestral_length = length; + _stretch = 1.0; _position = 0; _layer = layer; _read_data_count = 0; @@ -83,6 +86,9 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes _sync_position = _start; } _length = length; + _ancestral_start = other->_ancestral_start + offset; + _ancestral_length = length; + _stretch = 1.0; _name = name; _position = 0; _layer = layer; @@ -111,6 +117,9 @@ Region::Region (boost::shared_ptr<const Region> other) _start = other->_start; _sync_position = other->_sync_position; _length = other->_length; + _ancestral_start = _start; + _ancestral_length = _length; + _stretch = 1.0; _name = other->_name; _position = other->_position; _layer = other->_layer; @@ -340,6 +349,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) { @@ -748,6 +765,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: @@ -854,6 +877,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) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 2ecf3ccae7..1dc682d4e9 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2097,14 +2097,14 @@ Session::remove_route (shared_ptr<Route> route) find_current_end (); - update_latency_compensation (false, false); - set_dirty(); - // We need to disconnect the routes inputs and outputs route->disconnect_inputs (0); route->disconnect_outputs (0); + update_latency_compensation (false, false); + set_dirty(); + /* get rid of it from the dead wood collection in the route list manager */ /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */ @@ -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 f704bbad5f..e1afae1443 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -1398,6 +1398,7 @@ Session::XMLRegionFactory (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]; @@ -1458,7 +1459,27 @@ Session::XMLRegionFactory (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))); @@ -1473,6 +1494,13 @@ Session::XMLRegionFactory (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 e2cd6f6543..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 (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 (*this, path, false, frame_rate()))); - sources.back()->prepare_for_peakfile_writes (); - - } 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; - - 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 (sources[i]->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 (sources[i]->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); - boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource>(*it); - - if (as) { - as->done_with_peakfile_writes (); - } - - 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)); - - 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/source_factory.cc b/libs/ardour/source_factory.cc index ddd2f8e95e..cee0173387 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -126,10 +126,8 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) { try { boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } SourceCreated (ret); return ret; @@ -141,10 +139,8 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) /* this is allowed to throw */ boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } SourceCreated (ret); return ret; @@ -162,10 +158,8 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } SourceCreated (ret); @@ -182,10 +176,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource try { boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, chn, flags)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } if (announce) { SourceCreated (ret); @@ -198,10 +190,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource /* this is allowed to throw */ boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } if (announce) { SourceCreated (ret); @@ -212,10 +202,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource } else { boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } if (announce) { SourceCreated (ret); @@ -233,10 +221,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource { boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } if (announce) { @@ -261,10 +247,8 @@ SourceFactory::createWritable (Session& s, std::string path, bool destructive, n (destructive ? AudioFileSource::Flag (SndFileSource::default_writable_flags | AudioFileSource::Destructive) : SndFileSource::default_writable_flags))); - if (!defer_peaks) { - if (setup_peakfile (ret, false)) { - return boost::shared_ptr<Source>(); - } + if (setup_peakfile (ret, defer_peaks)) { + return boost::shared_ptr<Source>(); } if (announce) { diff --git a/libs/ardour/stretch.cc b/libs/ardour/stretch.cc new file mode 100644 index 0000000000..4cabfa79df --- /dev/null +++ b/libs/ardour/stretch.cc @@ -0,0 +1,210 @@ +/* + 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) + : AudioFilter (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<AudioRegion> region) +{ + 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) { + + 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"), nsrcs[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.cancel) { + if (nsrcs[i]->write (buffer, this_read) != this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg; + goto out; + } + } + } + + if (!tsr.cancel) { + st.flush (); + } + + while (!tsr.cancel && (this_read = st.receiveSamples (buffer, bufsize)) > 0) { + if (nsrcs[i]->write (buffer, this_read) != this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->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<AudioRegion> >::iterator x = results.begin(); x != results.end(); ++x) { + nframes64_t astart = (*x)->ancestral_start(); + nframes64_t alength = (*x)->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 = (*x)->stretch() * (tsr.fraction/100.0); + + start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch)); + length = (nframes_t) floor (alength / stretch); + + (*x)->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; +} |