From bb457bb960c5bd7ed538f9d31477293415739f68 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Thu, 10 Jan 2008 21:20:59 +0000 Subject: Merge libs/ardour and gtk2_ardour with 2.0-ongoing R2837. git-svn-id: svn://localhost/ardour2/trunk@2883 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/SConscript | 28 +- libs/ardour/ardour/ardour.h | 2 + libs/ardour/ardour/audio_buffer.h | 1 + libs/ardour/ardour/audio_unit.h | 68 +++- libs/ardour/ardour/audioengine.h | 10 +- libs/ardour/ardour/audioregion.h | 19 +- libs/ardour/ardour/audiosource.h | 13 +- libs/ardour/ardour/configuration_vars.h | 4 + libs/ardour/ardour/crossfade.h | 6 +- libs/ardour/ardour/ladspa_plugin.h | 2 +- libs/ardour/ardour/location.h | 2 + libs/ardour/ardour/pitch.h | 62 ++++ libs/ardour/ardour/playlist.h | 18 +- libs/ardour/ardour/plugin.h | 6 +- libs/ardour/ardour/plugin_manager.h | 7 + libs/ardour/ardour/rb_effect.h | 42 +++ libs/ardour/ardour/region.h | 26 +- libs/ardour/ardour/route.h | 2 +- libs/ardour/ardour/session.h | 74 ++-- libs/ardour/ardour/silentfilesource.h | 10 +- libs/ardour/ardour/slave.h | 2 + libs/ardour/ardour/sndfile_helpers.h | 2 +- libs/ardour/ardour/source.h | 2 + libs/ardour/ardour/stretch.h | 33 +- libs/ardour/ardour/tempo.h | 20 +- libs/ardour/ardour/types.h | 7 + libs/ardour/ardour/vst_plugin.h | 2 +- libs/ardour/audio_buffer.cc | 2 +- libs/ardour/audio_diskstream.cc | 15 +- libs/ardour/audio_playlist.cc | 20 +- libs/ardour/audio_port.cc | 11 +- libs/ardour/audio_unit.cc | 535 ++++++++++++++++++++++++----- libs/ardour/audioengine.cc | 58 +++- libs/ardour/audiofilesource.cc | 7 +- libs/ardour/audioregion.cc | 72 +--- libs/ardour/audiosource.cc | 106 +++--- libs/ardour/auditioner.cc | 15 +- libs/ardour/crossfade.cc | 9 +- libs/ardour/enums.cc | 2 +- libs/ardour/import.cc | 82 +++-- libs/ardour/io.cc | 7 +- libs/ardour/jack_audio_port.cc | 4 + libs/ardour/jack_midi_port.cc | 4 +- libs/ardour/ladspa_plugin.cc | 8 + libs/ardour/meter.cc | 2 +- libs/ardour/midi_buffer.cc | 14 +- libs/ardour/midi_port.cc | 33 +- libs/ardour/playlist.cc | 590 ++++++++++++++++++++++++++++---- libs/ardour/plugin.cc | 48 ++- libs/ardour/plugin_insert.cc | 28 +- libs/ardour/plugin_manager.cc | 58 +++- libs/ardour/rb_effect.cc | 302 ++++++++++++++++ libs/ardour/region.cc | 94 ++++- libs/ardour/route.cc | 19 +- libs/ardour/session.cc | 85 ++--- libs/ardour/session_butler.cc | 20 +- libs/ardour/session_events.cc | 8 + libs/ardour/session_export.cc | 4 + libs/ardour/session_process.cc | 97 +++--- libs/ardour/session_state.cc | 121 +++++-- libs/ardour/session_transport.cc | 37 +- libs/ardour/smf_source.cc | 11 +- libs/ardour/sndfile_helpers.cc | 20 +- libs/ardour/source_factory.cc | 3 +- libs/ardour/st_pitch.cc | 52 +++ libs/ardour/st_stretch.cc | 215 ++++++++++++ libs/ardour/stretch.cc | 227 ------------ libs/ardour/tempo.cc | 37 +- libs/ardour/vst_plugin.cc | 8 +- 69 files changed, 2625 insertions(+), 935 deletions(-) create mode 100644 libs/ardour/ardour/pitch.h create mode 100644 libs/ardour/ardour/rb_effect.h create mode 100644 libs/ardour/rb_effect.cc create mode 100644 libs/ardour/st_pitch.cc create mode 100644 libs/ardour/st_stretch.cc delete mode 100644 libs/ardour/stretch.cc (limited to 'libs') diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 6661ea16a2..6a05bb2295 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -129,7 +129,6 @@ sndfile_helpers.cc sndfilesource.cc source.cc source_factory.cc -stretch.cc tape_file_matcher.cc template_utils.cc tempo.cc @@ -145,6 +144,7 @@ vst_files = [ 'vst_plugin.cc', 'session_vst.cc' ] audiounit_files = [ 'audio_unit.cc' ] coreaudio_files = [ 'coreaudiosource.cc' ] extra_sources = [ ] +timefx_sources = [ ] if ardour['VST']: extra_sources += vst_files @@ -296,22 +296,28 @@ ardour.Merge ([ libraries['samplerate'], libraries['sigc2'], libraries['pbd'], - libraries['soundtouch'], libraries['midi++2'], libraries['glib2'], libraries['glibmm2'] ]) +#if ardour['RUBBERBAND']: +# ardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'] ]) +# timefx_sources += [ 'rb_effect.cc' ] +#else: +ardour.Merge ([ libraries['soundtouch'] ]) +timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ] + if ardour['LIBLO']: - ardour.Merge ([ libraries['lo'] ]) + ardour.Merge ([ libraries['lo'] ]) if ardour['COREAUDIO'] or ardour['AUDIOUNITS']: - ardour.Merge ([ libraries['appleutility'] ]) + ardour.Merge ([ libraries['appleutility'] ]) def SharedAsmObjectEmitter(target, source, env): - for tgt in target: - tgt.attributes.shared = 1 - return (target, source) + for tgt in target: + tgt.attributes.shared = 1 + return (target, source) env['BUILDERS']['SharedAsmObject'] = Builder (action = '$CXX -c -fPIC $SOURCE -o $TARGET', @@ -341,12 +347,12 @@ if env['FPU_OPTIMIZATION']: arch_specific_objects = env.SharedAsmObject('sse_functions_64bit.os', 'sse_functions_64bit.s') always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ] -libardour = ardour.SharedLibrary('ardour', ardour_files + always_sse_objects + extra_sources + arch_specific_objects) +libardour = ardour.SharedLibrary('ardour', ardour_files + always_sse_objects + timefx_sources + extra_sources + arch_specific_objects) Default(libardour) if env['NLS']: - i18n (ardour, ardour_files + vst_files + coreaudio_files + audiounit_files, env) + i18n (ardour, ardour_files + vst_files + coreaudio_files + timefx_sources + audiounit_files, env) env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libardour)) @@ -354,6 +360,8 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar env.Alias('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], [])) env.Alias('tarball', env.Distribute (env['DISTTREE'], - [ 'SConscript', 'i18n.h', 'gettext.h', 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] + + [ 'SConscript', 'i18n.h', 'gettext.h' ] + + [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] + + [ 'rb_effect.cc', 'st_stretch.cc', 'st_pitch.cc' ] + ardour_files + osc_files + vst_files + coreaudio_files + audiounit_files + glob.glob('po/*.po') + glob.glob('ardour/*.h'))) diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h index 6e7b494441..fcec83394f 100644 --- a/libs/ardour/ardour/ardour.h +++ b/libs/ardour/ardour/ardour.h @@ -48,6 +48,8 @@ namespace ARDOUR { int cleanup (); std::string get_ardour_revision (); + + const layer_t max_layer = UCHAR_MAX; microseconds_t get_microseconds (); diff --git a/libs/ardour/ardour/audio_buffer.h b/libs/ardour/ardour/audio_buffer.h index 09bf5946fa..1f6c2f63bf 100644 --- a/libs/ardour/ardour/audio_buffer.h +++ b/libs/ardour/ardour/audio_buffer.h @@ -43,6 +43,7 @@ public: /** Read @a len frames FROM THE START OF @a src into self at @a offset */ void read_from(const Buffer& src, nframes_t len, nframes_t offset) { + assert(&src != this); assert(_capacity > 0); assert(src.type() == DataType::AUDIO); assert(offset + len <= _capacity); diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index 7b31b1937f..b0e4d34c28 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -22,6 +22,7 @@ #define __ardour_audio_unit_h__ #include +#include #include #include @@ -30,6 +31,8 @@ #include +#include + #include class CAComponent; @@ -45,13 +48,13 @@ class Session; class AUPlugin : public ARDOUR::Plugin { public: - AUPlugin (AudioEngine& engine, Session& session, CAComponent* comp); + AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr comp); virtual ~AUPlugin (); - uint32_t unique_id () const; + std::string unique_id () const; const char * label () const; const char * name () const { return _info->name.c_str(); } - const char * maker () const; + const char * maker () const { return _info->creator.c_str(); } uint32_t parameter_count () const; float default_value (uint32_t port); nframes_t signal_latency () const; @@ -87,34 +90,65 @@ class AUPlugin : public ARDOUR::Plugin bool has_editor () const; - CAAudioUnit* get_au () { return unit; } - CAComponent* get_comp () { return comp; } - + bool fixed_io() const { return false; } + int32_t can_support_input_configuration (int32_t in); + int32_t compute_output_streams (int32_t nplugins); + uint32_t output_streams() const; + uint32_t input_streams() const; + + boost::shared_ptr get_au () { return unit; } + boost::shared_ptr get_comp () { return comp; } + + OSStatus render_callback(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); private: - CAComponent* comp; - CAAudioUnit* unit; - - AudioBufferList* in_list; - AudioBufferList* out_list; + boost::shared_ptr comp; + boost::shared_ptr unit; + + AudioStreamBasicDescription streamFormat; + bool initialized; + int format_set; + AudioBufferList* buffers; + + UInt32 global_elements; + UInt32 output_elements; + UInt32 input_elements; + + int set_output_format (); + int set_input_format (); + int set_stream_format (int scope, uint32_t cnt); + int _set_block_size (nframes_t nframes); std::vector > parameter_map; + uint32_t current_maxbuf; + nframes_t current_offset; + nframes_t cb_offset; + vector* current_buffers; + nframes_t frames_processed; }; - + typedef boost::shared_ptr AUPluginPtr; class AUPluginInfo : public PluginInfo { public: - AUPluginInfo () { }; + AUPluginInfo (boost::shared_ptr); ~AUPluginInfo (); - CAComponentDescription* desc; + PluginPtr load (Session& session); static PluginInfoList discover (); - PluginPtr load (Session& session); + static void get_names (CAComponentDescription&, std::string& name, Glib::ustring& maker); + static std::string stringify_descriptor (const CAComponentDescription&); private: - static std::string get_name (CAComponentDescription&); - void setup_nchannels (CAComponentDescription&); + boost::shared_ptr descriptor; + + static void discover_music (PluginInfoList&); + static void discover_fx (PluginInfoList&); + static void discover_by_description (PluginInfoList&, CAComponentDescription&); }; typedef boost::shared_ptr AUPluginInfoPtr; diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index dc7ae8e000..88f1111a6c 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -106,7 +106,13 @@ class AudioEngine : public sigc::trackable class PortRegistrationFailure : public std::exception { public: - virtual const char *what() const throw() { return "failed port registration"; } + PortRegistrationFailure (const char* why = "") { + reason = why; + } + virtual const char *what() const throw() { return reason; } + + private: + const char* reason; }; class NoBackendAvailable : public std::exception { @@ -235,6 +241,8 @@ class AudioEngine : public sigc::trackable std::string get_nth_physical (DataType type, uint32_t n, int flags); + void port_registration_failure (const std::string& portname); + static int _xrun_callback (void *arg); static int _graph_order_callback (void *arg); static int _process_callback (nframes_t nframes, void *arg); diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h index ba2bbaee22..b84d197c3f 100644 --- a/libs/ardour/ardour/audioregion.h +++ b/libs/ardour/ardour/audioregion.h @@ -76,8 +76,10 @@ class AudioRegion : public Region uint32_t chan_n=0, double samples_per_unit= 1.0) const; virtual nframes_t read_at (Sample *buf, Sample *mixdown_buf, - float *gain_buf, nframes_t position, nframes_t cnt, - uint32_t chan_n = 0) const; + float *gain_buf, nframes_t position, nframes_t cnt, + uint32_t chan_n = 0, + nframes_t read_frames = 0, + nframes_t skip_frames = 0) const; virtual nframes_t master_read_at (Sample *buf, Sample *mixdown_buf, float *gain_buf, @@ -146,8 +148,10 @@ class AudioRegion : public Region void recompute_gain_at_start (); nframes_t _read_at (const SourceList&, Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, nframes_t position, nframes_t cnt, - uint32_t chan_n = 0) const; + float *gain_buffer, nframes_t position, nframes_t cnt, + uint32_t chan_n = 0, + nframes_t read_frames = 0, + nframes_t skip_frames = 0) const; void recompute_at_start (); void recompute_at_end (); @@ -174,13 +178,6 @@ class AudioRegion : public Region AudioRegion (boost::shared_ptr); int set_live_state (const XMLNode&, Change&, bool send); - - virtual bool verify_start (nframes_t); - virtual bool verify_start_and_length (nframes_t, nframes_t); - virtual bool verify_start_mutable (nframes_t&_start); - virtual bool verify_length (nframes_t); - /*virtual void recompute_at_start () = 0; - virtual void recompute_at_end () = 0;*/ }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index f02b28d9f5..93708a5b07 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -43,9 +43,7 @@ using std::vector; namespace ARDOUR { -const nframes_t frames_per_peak = 256; - - class AudioSource : public Source, public boost::enable_shared_from_this +class AudioSource : public Source, public boost::enable_shared_from_this { public: AudioSource (Session&, Glib::ustring name); @@ -72,7 +70,8 @@ const nframes_t frames_per_peak = 256; uint32_t read_data_count() const { return _read_data_count; } uint32_t write_data_count() const { return _write_data_count; } - virtual int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const; + int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const; + int build_peaks (); bool peaks_ready (sigc::slot, sigc::connection&) const; @@ -129,6 +128,12 @@ const nframes_t frames_per_peak = 256; void update_length (nframes_t pos, nframes_t cnt); + virtual int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, + double samples_per_visual_peak, nframes_t fpp) const; + + int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, + bool intermediate_peaks_ready_signal, nframes_t frames_per_peak); + private: int peakfile; nframes_t peak_leftover_cnt; diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index edae45a56a..aa27a3ef24 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -72,6 +72,8 @@ CONFIG_VARIABLE (uint32_t, destructive_xfade_msecs, "destructive-xfade-msecs", CONFIG_VARIABLE (EditMode, edit_mode, "edit-mode", Slide) CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", MoveAddHigher) +CONFIG_VARIABLE (bool, link_region_and_track_selection, "link-region-and-track-selection", false) +CONFIG_VARIABLE (std::string, keyboard_layout_name, "keyboard-layout-name", "ansi") /* monitoring, mute, solo etc */ @@ -148,6 +150,8 @@ CONFIG_VARIABLE (uint32_t, periodic_safety_backup_interval, "periodic-safety-bac CONFIG_VARIABLE (float, automation_interval, "automation-interval", 50) CONFIG_VARIABLE (bool, sync_all_route_ordering, "sync-all-route-ordering", true) CONFIG_VARIABLE (bool, only_copy_imported_files, "only-copy-imported-files", true) +CONFIG_VARIABLE (std::string, keyboard_layout, "keyboard-layout", "ansi") +CONFIG_VARIABLE (std::string, default_bindings, "default-bindings", "ardour") /* denormal management */ diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h index 78a137bde3..9ba3689e82 100644 --- a/libs/ardour/ardour/crossfade.h +++ b/libs/ardour/ardour/crossfade.h @@ -81,8 +81,10 @@ class Crossfade : public ARDOUR::AudioRegion boost::shared_ptr out() const { return _out; } nframes_t read_at (Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, nframes_t position, nframes_t cnt, - uint32_t chan_n) const; + float *gain_buffer, nframes_t position, nframes_t cnt, + uint32_t chan_n, + nframes_t read_frames = 0, + nframes_t skip_frames = 0) const; bool refresh (); diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h index e466e53215..7c0b0b2abe 100644 --- a/libs/ardour/ardour/ladspa_plugin.h +++ b/libs/ardour/ardour/ladspa_plugin.h @@ -53,7 +53,7 @@ class LadspaPlugin : public ARDOUR::Plugin /* Plugin interface */ - uint32_t unique_id() const { return descriptor->UniqueID; } + std::string unique_id() const; const char * label() const { return descriptor->Label; } const char * name() const { return descriptor->Name; } const char * maker() const { return descriptor->Maker; } diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 5ffb716598..53d9489823 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -146,6 +146,8 @@ class Locations : public PBD::StatefulDestructible Locations (); ~Locations (); + const LocationList& list() { return locations; } + void add (Location *, bool make_current = false); void remove (Location *); void clear (); diff --git a/libs/ardour/ardour/pitch.h b/libs/ardour/ardour/pitch.h new file mode 100644 index 0000000000..38d8380f5d --- /dev/null +++ b/libs/ardour/ardour/pitch.h @@ -0,0 +1,62 @@ +/* + 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_pitch_h__ +#define __ardour_pitch_h__ + +#include + +namespace ARDOUR { + class AudioRegion; +} + +#ifdef USE_RUBBERBAND + +#include + +namespace ARDOUR { + +class Pitch : public RBEffect { + public: + Pitch (ARDOUR::Session&, TimeFXRequest&); + ~Pitch () {} +}; + +} /* namespace */ + +# else + +namespace ARDOUR { + +class Pitch : public Filter { + public: + Pitch (ARDOUR::Session&, TimeFXRequest&); + ~Pitch () {} + + int run (boost::shared_ptr); + + private: + TimeFXRequest& tsr; +}; + +} /* namespace */ + +#endif + +#endif /* __ardour_pitch_h__ */ diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index f2e07aa067..3f328de005 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -95,6 +95,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this, nframes_t position, float times); void nudge_after (nframes_t start, nframes_t distance, bool forwards); + void shuffle (boost::shared_ptr, int dir); boost::shared_ptr cut (list&, bool result_is_hidden = true); boost::shared_ptr copy (list&, bool result_is_hidden = true); @@ -102,9 +103,12 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this find_region (const PBD::ID&) const; boost::shared_ptr top_region_at (nframes_t frame); boost::shared_ptr find_next_region (nframes_t frame, RegionPoint point, int dir); + nframes64_t find_next_region_boundary (nframes64_t frame, int dir); + bool region_is_shuffle_constrained (boost::shared_ptr); template void foreach_region (T *t, void (T::*func)(boost::shared_ptr, void *), void *arg); template void foreach_region (T *t, void (T::*func)(boost::shared_ptr)); @@ -124,6 +128,8 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this); + void lower_region (boost::shared_ptr); void raise_region_to_top (boost::shared_ptr); void lower_region_to_bottom (boost::shared_ptr); @@ -182,6 +188,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this exclude = boost::shared_ptr()); + void possibly_splice_unlocked(nframes_t at, nframes64_t distance, boost::shared_ptr exclude = boost::shared_ptr()); + void core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr exclude); + void splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr exclude); + void splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr exclude); virtual void finalize_split_region (boost::shared_ptr original, boost::shared_ptr left, boost::shared_ptr right) {} @@ -258,6 +265,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this cut (nframes_t start, nframes_t cnt, bool result_is_hidden); boost::shared_ptr copy (nframes_t start, nframes_t cnt, bool result_is_hidden); + int move_region_to_layer (layer_t, boost::shared_ptr r, int dir); void relayer (); void unset_freeze_parent (Playlist*); diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h index 830ed7c025..d721476db7 100644 --- a/libs/ardour/ardour/plugin.h +++ b/libs/ardour/ardour/plugin.h @@ -77,7 +77,7 @@ class PluginInfo { ChanCount n_outputs; ARDOUR::PluginType type; - long unique_id; + std::string unique_id; virtual PluginPtr load (Session& session) = 0; @@ -114,7 +114,7 @@ class Plugin : public PBD::StatefulDestructible, public Latent bool max_unbound; }; - virtual uint32_t unique_id() const = 0; + virtual std::string unique_id() const = 0; virtual const char * label() const = 0; virtual const char * name() const = 0; virtual const char * maker() const = 0; @@ -170,7 +170,7 @@ class Plugin : public PBD::StatefulDestructible, public Latent bool save_preset(string name, string domain /* vst, ladspa etc. */); }; -PluginPtr find_plugin(ARDOUR::Session&, string name, long unique_id, ARDOUR::PluginType); +PluginPtr find_plugin(ARDOUR::Session&, string unique_id, ARDOUR::PluginType); } // namespace ARDOUR diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h index bce723d857..64b871104b 100644 --- a/libs/ardour/ardour/plugin_manager.h +++ b/libs/ardour/ardour/plugin_manager.h @@ -40,6 +40,7 @@ class PluginManager { ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; } ARDOUR::PluginInfoList &ladspa_plugin_info () { return _ladspa_plugin_info; } + ARDOUR::PluginInfoList &au_plugin_info () { return _au_plugin_info; } void refresh (); @@ -51,6 +52,8 @@ class PluginManager { private: ARDOUR::PluginInfoList _vst_plugin_info; ARDOUR::PluginInfoList _ladspa_plugin_info; + ARDOUR::PluginInfoList _au_plugin_info; + std::map rdf_type; std::string ladspa_path; @@ -64,6 +67,9 @@ class PluginManager { void add_vst_presets (); void add_presets (std::string domain); + int au_discover (); + void au_refresh (); + int vst_discover_from_path (std::string path); int vst_discover (std::string path); @@ -71,6 +77,7 @@ class PluginManager { int ladspa_discover (std::string path); std::string get_ladspa_category (uint32_t id); + std::vector ladspa_plugin_whitelist; static PluginManager* _manager; // singleton }; diff --git a/libs/ardour/ardour/rb_effect.h b/libs/ardour/ardour/rb_effect.h new file mode 100644 index 0000000000..bde0422335 --- /dev/null +++ b/libs/ardour/ardour/rb_effect.h @@ -0,0 +1,42 @@ +/* + 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_rbeffect_h__ +#define __ardour_rbeffect_h__ + +#include + +namespace ARDOUR { + +class AudioRegion; + +class RBEffect : public Filter { + public: + RBEffect (ARDOUR::Session&, TimeFXRequest&); + ~RBEffect (); + + int run (boost::shared_ptr); + + private: + TimeFXRequest& tsr; +}; + +} /* namespace */ + +#endif /* __ardour_rbeffect_h__ */ diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index c246da9cce..76b41a04cb 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -96,11 +96,17 @@ class Region : public Automatable, public boost::enable_shared_from_this nframes_t length() const { return _length; } layer_t layer () const { return _layer; } + /* these two are valid ONLY during a StateChanged signal handler */ + + nframes_t last_position() const { return _last_position; } + nframes_t last_length() const { return _last_length; } + nframes64_t ancestral_start () const { return _ancestral_start; } nframes64_t ancestral_length () const { return _ancestral_length; } float stretch() const { return _stretch; } + float shift() const { return _shift; } - void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch); + void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch, float shift); nframes_t sync_offset(int& dir) const; nframes_t sync_position() const; @@ -129,7 +135,7 @@ class Region : public Automatable, public boost::enable_shared_from_this void thaw (const string& why); bool covers (nframes_t frame) const { - return first_frame() <= frame && frame < last_frame(); + return first_frame() <= frame && frame <= last_frame(); } OverlapType coverage (nframes_t start, nframes_t end) const { @@ -149,7 +155,7 @@ class Region : public Automatable, public boost::enable_shared_from_this void set_position (nframes_t, void *src); void set_position_on_top (nframes_t, void *src); void special_set_position (nframes_t); - void nudge_position (long, void *src); + void nudge_position (nframes64_t, void *src); bool at_natural_position () const; void move_to_natural_position (void *src); @@ -160,6 +166,8 @@ class Region : public Automatable, public boost::enable_shared_from_this void trim_to (nframes_t position, nframes_t length, void *src); void set_layer (layer_t l); /* ONLY Playlist can call this */ + void raise (); + void lower (); void raise_to_top (); void lower_to_bottom (); @@ -232,10 +240,11 @@ class Region : public Automatable, public boost::enable_shared_from_this void maybe_uncopy (); void first_edit (); - virtual bool verify_start (nframes_t); - virtual bool verify_start_and_length (nframes_t, nframes_t); - virtual bool verify_start_mutable (nframes_t&_start); - virtual bool verify_length (nframes_t); + bool verify_start (nframes_t); + bool verify_start_and_length (nframes_t, nframes_t&); + bool verify_start_mutable (nframes_t&_start); + bool verify_length (nframes_t); + virtual void recompute_at_start () = 0; virtual void recompute_at_end () = 0; @@ -243,7 +252,9 @@ class Region : public Automatable, public boost::enable_shared_from_this Flag _flags; nframes_t _start; nframes_t _length; + nframes_t _last_length; nframes_t _position; + nframes_t _last_position; nframes_t _sync_position; layer_t _layer; mutable RegionEditState _first_edit; @@ -251,6 +262,7 @@ class Region : public Automatable, public boost::enable_shared_from_this nframes64_t _ancestral_start; nframes64_t _ancestral_length; float _stretch; + float _shift; mutable uint32_t _read_data_count; ///< modified in read() Change _pending_changed; uint64_t _last_layer_op; ///< timestamp diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index fc17af06ee..d2e40501f1 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -109,7 +109,7 @@ class Route : public IO void set_gain (gain_t val, void *src); void inc_gain (gain_t delta, void *src); - + bool active() const { return _active; } void set_active (bool yn); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index d0add4e2aa..90a9563ad1 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -138,6 +138,7 @@ class Session : public PBD::StatefulDestructible SetDiskstreamSpeed, Locate, LocateRoll, + LocateRollLocate, SetLoop, PunchIn, PunchOut, @@ -224,9 +225,9 @@ class Session : public PBD::StatefulDestructible /* creating from an XML file */ Session (AudioEngine&, - string fullpath, - string snapshot_name, - string* mix_template = 0); + const string& fullpath, + const string& snapshot_name, + string mix_template = ""); /* creating a new Session */ @@ -354,7 +355,7 @@ class Session : public PBD::StatefulDestructible sigc::signal RouteAdded; - void request_roll (); + void request_roll_at_and_return (nframes_t start, nframes_t return_to); void request_bounded_roll (nframes_t start, nframes_t end); void request_stop (bool abort = false); void request_locate (nframes_t frame, bool with_roll = false); @@ -499,6 +500,7 @@ class Session : public PBD::StatefulDestructible nframes_t transport_frame () const {return _transport_frame; } nframes_t audible_frame () const; + nframes64_t requested_return_frame() const { return _requested_return_frame; } enum PullupFormat { pullup_Plus4Plus1, @@ -542,6 +544,9 @@ class Session : public PBD::StatefulDestructible float transport_speed() const { return _transport_speed; } bool transport_stopped() const { return _transport_speed == 0.0f; } bool transport_rolling() const { return _transport_speed != 0.0f; } + + void set_silent (bool yn); + bool silent () { return _silent; } int jack_slave_sync (nframes_t); @@ -701,6 +706,13 @@ class Session : public PBD::StatefulDestructible uint32_t n_port_inserts() const { return _port_inserts.size(); } uint32_t n_plugin_inserts() const { return _plugin_inserts.size(); } uint32_t n_sends() const { return _sends.size(); } + + static void set_disable_all_loaded_plugins (bool yn) { + _disable_all_loaded_plugins = yn; + } + static bool get_disable_all_loaded_plugins() { + return _disable_all_loaded_plugins; + } uint32_t next_send_id(); uint32_t next_insert_id(); @@ -901,6 +913,18 @@ class Session : public PBD::StatefulDestructible long value, void* ptr, float opt); + + typedef float (*compute_peak_t) (Sample *, nframes_t, float); + typedef void (*find_peaks_t) (Sample *, nframes_t, float *, float*); + typedef void (*apply_gain_to_buffer_t) (Sample *, nframes_t, float); + typedef void (*mix_buffers_with_gain_t) (Sample *, Sample *, nframes_t, float); + typedef void (*mix_buffers_no_gain_t) (Sample *, Sample *, nframes_t); + + static compute_peak_t compute_peak; + static find_peaks_t find_peaks; + static apply_gain_to_buffer_t apply_gain_to_buffer; + static mix_buffers_with_gain_t mix_buffers_with_gain; + static mix_buffers_no_gain_t mix_buffers_no_gain; static sigc::signal SendFeedback; @@ -927,12 +951,9 @@ class Session : public PBD::StatefulDestructible void update_latency_compensation (bool, bool); private: + int create (bool& new_session, const string& mix_template, nframes_t initial_length); void destroy (); - - void initialize_start_and_end_locations(nframes_t start, nframes_t end); - bool create_session_file(); - bool create_session_file_from_template (const string& template_path); - + nframes_t compute_initial_length (); enum SubState { @@ -949,35 +970,36 @@ class Session : public PBD::StatefulDestructible */ typedef void (Session::*process_function_type)(nframes_t); - - AudioEngine &_engine; - mutable gint processing_prohibited; - /// the function called when the main JACK process callback happens + + AudioEngine& _engine; + mutable gint processing_prohibited; process_function_type process_function; process_function_type last_process_function; bool waiting_for_sync_offset; - nframes_t _base_frame_rate; - nframes_t _current_frame_rate; //this includes video pullup offset + nframes_t _base_frame_rate; + nframes_t _current_frame_rate; //this includes video pullup offset int transport_sub_state; - mutable gint _record_status; - nframes_t _transport_frame; + mutable gint _record_status; + volatile nframes_t _transport_frame; Location* end_location; Location* start_location; - Slave *_slave; + Slave* _slave; + bool _silent; volatile float _transport_speed; volatile float _desired_transport_speed; float _last_transport_speed; bool auto_play_legal; - nframes_t _last_slave_transport_frame; - nframes_t maximum_output_latency; - nframes_t last_stop_frame; + nframes_t _last_slave_transport_frame; + nframes_t maximum_output_latency; + nframes_t last_stop_frame; + volatile nframes64_t _requested_return_frame; BufferSet* _scratch_buffers; BufferSet* _silent_buffers; BufferSet* _mix_buffers; - nframes_t current_block_size; - nframes_t _worst_output_latency; - nframes_t _worst_input_latency; - nframes_t _worst_track_latency; + nframes_t current_block_size; + nframes_t _worst_output_latency; + nframes_t _worst_input_latency; + nframes_t _worst_track_latency; bool _have_captured; float _meter_hold; float _meter_falloff; @@ -1675,6 +1697,8 @@ class Session : public PBD::StatefulDestructible void set_history_depth (uint32_t depth); void sync_order_keys (); + + static bool _disable_all_loaded_plugins; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/silentfilesource.h b/libs/ardour/ardour/silentfilesource.h index 92ef076a9b..e0103185c2 100644 --- a/libs/ardour/ardour/silentfilesource.h +++ b/libs/ardour/ardour/silentfilesource.h @@ -34,11 +34,6 @@ class SilentFileSource : public AudioFileSource { void set_length (nframes_t len); - int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit) const { - memset (peaks, 0, sizeof (PeakData) * npeaks); - return 0; - } - bool destructive() const { return false; } protected: @@ -58,6 +53,11 @@ class SilentFileSource : public AudioFileSource { void set_header_timeline_position () {} + int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit, nframes_t fpp) const { + memset (peaks, 0, sizeof (PeakData) * npeaks); + return 0; + } + }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h index 73b66ca20e..509f8fa9d2 100644 --- a/libs/ardour/ardour/slave.h +++ b/libs/ardour/ardour/slave.h @@ -47,6 +47,7 @@ class Slave { virtual bool starting() const { return false; } virtual nframes_t resolution() const = 0; virtual bool requires_seekahead () const = 0; + virtual bool is_always_synced() const { return false; } }; @@ -139,6 +140,7 @@ class JACK_Slave : public Slave nframes_t resolution() const { return 1; } bool requires_seekahead () const { return false; } void reset_client (jack_client_t* jack); + bool is_always_synced() const { return true; } private: jack_client_t* jack; diff --git a/libs/ardour/ardour/sndfile_helpers.h b/libs/ardour/ardour/sndfile_helpers.h index 26a93ad124..cf6b15f3a4 100644 --- a/libs/ardour/ardour/sndfile_helpers.h +++ b/libs/ardour/ardour/sndfile_helpers.h @@ -28,7 +28,7 @@ using std::string; // Use this define when initializing arrarys for use in sndfile_*_format() #define SNDFILE_STR_LENGTH 32 -#define SNDFILE_HEADER_FORMATS 7 +#define SNDFILE_HEADER_FORMATS 5 extern const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1]; extern const char * const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1]; diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index 6f323dd878..869111bb07 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -63,6 +63,8 @@ class Source : public SessionObject XMLNode& get_state (); int set_state (const XMLNode&); + virtual bool destructive() const { return false; } + void use () { _in_use++; } void disuse () { if (_in_use) { _in_use--; } } diff --git a/libs/ardour/ardour/stretch.h b/libs/ardour/ardour/stretch.h index 4d00c9b17b..020d03270d 100644 --- a/libs/ardour/ardour/stretch.h +++ b/libs/ardour/ardour/stretch.h @@ -21,31 +21,46 @@ #define __ardour_stretch_h__ #include -#include namespace ARDOUR { + class AudioRegion; +} + +#ifdef USE_RUBBERBAND -class AudioRegion; +#include + +namespace ARDOUR { -struct TimeStretchRequest : public InterThreadInfo { - float fraction; - bool quick_seek; - bool antialias; +class Stretch : public RBEffect { + public: + Stretch (ARDOUR::Session&, TimeFXRequest&); + ~Stretch() {} }; +} /* namespace */ + +#else + +#include + +namespace ARDOUR { + class Stretch : public Filter { public: - Stretch (ARDOUR::Session&, TimeStretchRequest&); + Stretch (ARDOUR::Session&, TimeFXRequest&); ~Stretch (); int run (boost::shared_ptr); private: - TimeStretchRequest& tsr; - soundtouch::SoundTouch st; + TimeFXRequest& tsr; + soundtouch::SoundTouch st; }; } /* namespace */ +#endif + #endif /* __ardour_stretch_h__ */ diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 2d8462a751..72f24c1054 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -40,27 +40,29 @@ using std::list; using std::vector; namespace ARDOUR { - +class Meter; class Tempo { public: - Tempo (double bpm) - : _beats_per_minute (bpm) {} + Tempo (double bpm, double type=4.0) // defaulting to quarter note + : _beats_per_minute (bpm), _note_type(type) {} Tempo (const Tempo& other) { _beats_per_minute = other._beats_per_minute; + _note_type = other._note_type; } void operator= (const Tempo& other) { if (&other != this) { _beats_per_minute = other._beats_per_minute; + _note_type = other._note_type; } } - double beats_per_minute () const { return _beats_per_minute; } - double frames_per_beat (nframes_t sr) const { - return ((60.0 * sr) / _beats_per_minute); - } + double beats_per_minute () const { return _beats_per_minute;} + double note_type () const { return _note_type;} + double frames_per_beat (nframes_t sr, const Meter& meter) const; protected: double _beats_per_minute; + double _note_type; }; class Meter { @@ -149,8 +151,8 @@ class MeterSection : public MetricSection, public Meter { class TempoSection : public MetricSection, public Tempo { public: - TempoSection (const BBT_Time& start, double qpm) - : MetricSection (start), Tempo (qpm) {} + TempoSection (const BBT_Time& start, double qpm, double note_type) + : MetricSection (start), Tempo (qpm, note_type) {} TempoSection (const XMLNode&); static const string xml_state_node_name; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 0d32d35c7d..d7961babbd 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -379,6 +379,13 @@ namespace ARDOUR { SrcFastest }; + struct TimeFXRequest : public InterThreadInfo { + float time_fraction; + float pitch_fraction; + bool quick_seek; + bool antialias; + }; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h index 1622df0c1a..3a05360f15 100644 --- a/libs/ardour/ardour/vst_plugin.h +++ b/libs/ardour/ardour/vst_plugin.h @@ -56,7 +56,7 @@ class VSTPlugin : public ARDOUR::Plugin /* Plugin interface */ - uint32_t unique_id() const; + std::string unique_id() const; const char * label() const; const char * name() const; const char * maker() const; diff --git a/libs/ardour/audio_buffer.cc b/libs/ardour/audio_buffer.cc index 4871035e80..8444304832 100644 --- a/libs/ardour/audio_buffer.cc +++ b/libs/ardour/audio_buffer.cc @@ -32,7 +32,7 @@ AudioBuffer::AudioBuffer(size_t capacity) , _owns_data (false) , _data (0) { - if (_capacity) { + if (_capacity > 0) { _owns_data = true; // prevent resize() from gagging resize (_capacity); silence (_capacity); diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index bc42cb0b5b..bc4a352c45 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -850,10 +850,18 @@ AudioDiskstream::commit (nframes_t nframes) } if (_slaved) { - need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2; + /*if (_io && _io->active()) {*/ + need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2; + /*} else { + need_butler = false; + }*/ } else { - need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames - || c->front()->capture_buf->read_space() >= disk_io_chunk_frames; + /*if (_io && _io->active()) {*/ + need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames + || c->front()->capture_buf->read_space() >= disk_io_chunk_frames; + /*} else { + need_butler = c->front()->capture_buf->read_space() >= disk_io_chunk_frames; + }*/ } if (commit_should_unlock) { @@ -1940,6 +1948,7 @@ AudioDiskstream::set_state (const XMLNode& node) if (nchans > _n_channels.n_audio()) { add_channel (nchans - _n_channels.n_audio()); + IO::PortCountChanged(_n_channels); } else if (nchans < _n_channels.n_audio()) { diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 0631c9121b..1506d204f1 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -123,7 +123,10 @@ ARDOUR::nframes_t AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start, nframes_t cnt, unsigned chan_n) { + nframes_t ret = cnt; nframes_t end; + nframes_t read_frames; + nframes_t skip_frames; /* optimizing this memset() away involves a lot of conditionals that may well cause more of a hit due to cache misses @@ -147,14 +150,24 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf Glib::Mutex::Lock rm (region_lock); end = start + cnt - 1; + read_frames = 0; + skip_frames = 0; + _read_data_count = 0; _read_data_count = 0; + RegionList* rlist = regions_to_read (start, start+cnt); + + if (rlist->empty()) { + delete rlist; + return cnt; + } + map > > relevant_regions; map > > relevant_xfades; vector relevant_layers; - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) { if ((*i)->coverage (start, end) != OverlapNone) { relevant_regions[(*i)->layer()].push_back (*i); relevant_layers.push_back ((*i)->layer()); @@ -186,7 +199,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf for (vector >::iterator i = r.begin(); i != r.end(); ++i) { boost::shared_ptr ar = boost::dynamic_pointer_cast(*i); assert(ar); - ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); + ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames); _read_data_count += ar->read_data_count(); } @@ -199,7 +212,8 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf } } - return cnt; + delete rlist; + return ret; } diff --git a/libs/ardour/audio_port.cc b/libs/ardour/audio_port.cc index 66d31c63fe..714be28f34 100644 --- a/libs/ardour/audio_port.cc +++ b/libs/ardour/audio_port.cc @@ -25,10 +25,10 @@ using namespace ARDOUR; using namespace std; -AudioPort::AudioPort (const std::string& name, Flags flgs, bool external, nframes_t capacity) - : Port (name, flgs) - , BaseAudioPort (name, flgs) - , PortFacade (name, flgs) +AudioPort::AudioPort (const std::string& name, Flags flags, bool external, nframes_t capacity) + : Port (name, flags) + , BaseAudioPort (name, flags) + , PortFacade (name, flags) { if (!external || receives_input()) { @@ -43,6 +43,7 @@ AudioPort::AudioPort (const std::string& name, Flags flgs, bool external, nframe if (!external) { _ext_port = 0; + set_name (name); } else { @@ -52,7 +53,7 @@ AudioPort::AudioPort (const std::string& name, Flags flgs, bool external, nframe will in turn be using the JACK port buffer for data. */ - _ext_port = new JackAudioPort (name, flgs, 0); + _ext_port = new JackAudioPort (name, flags, 0); if (sends_output()) { _buffer = &dynamic_cast(_ext_port)->get_audio_buffer(); diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index dfcffb8cfe..f86e784169 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -1,6 +1,5 @@ /* Copyright (C) 2006 Paul Davis - Written by Taybin Rutkin 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 @@ -18,10 +17,16 @@ */ +#include + #include #include +#include + +#include #include +#include #include #include #include @@ -37,66 +42,93 @@ using namespace std; using namespace PBD; using namespace ARDOUR; -AUPlugin::AUPlugin (AudioEngine& engine, Session& session, CAComponent* _comp) +static OSStatus +_render_callback(void *userData, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); +} + +AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr _comp) : Plugin (engine, session), comp (_comp), - unit (new CAAudioUnit) + unit (new CAAudioUnit), + initialized (false), + buffers (0), + current_maxbuf (0), + current_offset (0), + current_buffers (0), + frames_processed (0) { - OSErr err = CAAudioUnit::Open (*comp, *unit); + OSErr err = CAAudioUnit::Open (*(comp.get()), *unit); + if (err != noErr) { error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg; - delete unit; - delete comp; throw failed_constructor (); } - unit->Initialize (); + AURenderCallbackStruct renderCallbackInfo; + + renderCallbackInfo.inputProc = _render_callback; + renderCallbackInfo.inputProcRefCon = this; + + if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) { + cerr << "cannot install render callback (err = " << err << ')' << endl; + throw failed_constructor(); + } + + unit->GetElementCount (kAudioUnitScope_Input, input_elements); + unit->GetElementCount (kAudioUnitScope_Output, output_elements); + + // set up the basic stream format. these fields do not change + + streamFormat.mSampleRate = session.frame_rate(); + streamFormat.mFormatID = kAudioFormatLinearPCM; + streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved; + streamFormat.mBitsPerChannel = 32; + streamFormat.mFramesPerPacket = 1; + + // subject to later modification as we discover channel counts + + streamFormat.mBytesPerPacket = 4; + streamFormat.mBytesPerFrame = 4; + streamFormat.mChannelsPerFrame = 1; + + format_set = 0; + + if (_set_block_size (_session.get_block_size())) { + error << _("AUPlugin: cannot set processing block size") << endmsg; + throw failed_constructor(); + } } AUPlugin::~AUPlugin () { if (unit) { unit->Uninitialize (); - delete unit; - } - - if (comp) { - delete comp; - } - - if (in_list) { - delete in_list; } - - if (out_list) { - delete out_list; - } -} -AUPluginInfo::~AUPluginInfo () -{ - if (desc) { - delete desc; + if (buffers) { + free (buffers); } } -uint32_t +string AUPlugin::unique_id () const { - return 0; + return AUPluginInfo::stringify_descriptor (comp->Desc()); } const char * AUPlugin::label () const { - return "AUPlugin label"; -} - -const char * -AUPlugin::maker () const -{ - return "AUplugin maker"; + return _info->name.c_str(); } uint32_t @@ -125,7 +157,7 @@ AUPlugin::signal_latency () const void AUPlugin::set_parameter (uint32_t which, float val) { - unit->SetParameter (parameter_map[which].first, parameter_map[which].second, 0, val); + // unit->SetParameter (parameter_map[which].first, parameter_map[which].second, 0, val); } float @@ -133,7 +165,7 @@ AUPlugin::get_parameter (uint32_t which) const { float outValue = 0.0; - unit->GetParameter(parameter_map[which].first, parameter_map[which].second, 0, outValue); + // unit->GetParameter(parameter_map[which].first, parameter_map[which].second, 0, outValue); return outValue; } @@ -153,19 +185,174 @@ AUPlugin::nth_parameter (uint32_t which, bool& ok) const void AUPlugin::activate () { - unit->GlobalReset (); + if (!initialized) { + OSErr err; + if ((err = unit->Initialize()) != noErr) { + error << string_compose (_("AUPlugin: cannot initialize plugin (err = %1)"), err) << endmsg; + } else { + frames_processed = 0; + initialized = true; + } + } } void AUPlugin::deactivate () { - // not needed. GlobalReset () takes care of it. + unit->GlobalReset (); } void AUPlugin::set_block_size (nframes_t nframes) { + _set_block_size (nframes); +} + +int +AUPlugin::_set_block_size (nframes_t nframes) +{ + bool was_initialized = initialized; + UInt32 numFrames = nframes; + OSErr err; + + if (initialized) { + unit->Uninitialize (); + } + + if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, + 0, &numFrames, sizeof (numFrames))) != noErr) { + cerr << "cannot set max frames (err = " << err << ')' << endl; + return -1; + } + + if (was_initialized) { + activate (); + } + + return 0; +} + +int32_t +AUPlugin::can_support_input_configuration (int32_t in) +{ + streamFormat.mChannelsPerFrame = in; + /* apple says that for non-interleaved data, these + values always refer to a single channel. + */ + streamFormat.mBytesPerPacket = 4; + streamFormat.mBytesPerFrame = 4; + + if (set_input_format () == 0) { + return 1; + } else { + return -1; + } +} + +int +AUPlugin::set_input_format () +{ + return set_stream_format (kAudioUnitScope_Input, input_elements); +} + +int +AUPlugin::set_output_format () +{ + return set_stream_format (kAudioUnitScope_Output, output_elements); +} + +int +AUPlugin::set_stream_format (int scope, uint32_t cnt) +{ + OSErr result; + + for (uint32_t i = 0; i < cnt; ++i) { + if ((result = unit->SetFormat (scope, i, streamFormat)) != 0) { + error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"), + (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg; + return -1; + } + } + + if (scope == kAudioUnitScope_Input) { + format_set |= 0x1; + } else { + format_set |= 0x2; + } + + return 0; +} + +int32_t +AUPlugin::compute_output_streams (int32_t nplugins) +{ + /* we will never replicate AU plugins - either they can do the I/O we need + or not. thus, we can ignore nplugins entirely. + */ + if (set_output_format() == 0) { + + if (buffers) { + free (buffers); + buffers = 0; + } + + buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) + + streamFormat.mChannelsPerFrame * sizeof(AudioBuffer)); + + Glib::Mutex::Lock em (_session.engine().process_lock()); + IO::MoreOutputs (streamFormat.mChannelsPerFrame); + + return streamFormat.mChannelsPerFrame; + } else { + return -1; + } +} + +uint32_t +AUPlugin::output_streams() const +{ + if (!(format_set & 0x2)) { + warning << _("AUPlugin: output_streams() called without any format set!") << endmsg; + return 1; + } + return streamFormat.mChannelsPerFrame; +} + + +uint32_t +AUPlugin::input_streams() const +{ + if (!(format_set & 0x1)) { + warning << _("AUPlugin: input_streams() called without any format set!") << endmsg; + return 1; + } + return streamFormat.mChannelsPerFrame; +} + +OSStatus +AUPlugin::render_callback(AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + /* not much to do - the data is already in the buffers given to us in connect_and_run() */ + + if (current_maxbuf == 0) { + error << _("AUPlugin: render callback called illegally!") << endmsg; + return kAudioUnitErr_CannotDoInCurrentContext; + } + + for (uint32_t i = 0; i < current_maxbuf; ++i) { + ioData->mBuffers[i].mNumberChannels = 1; + ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames; + ioData->mBuffers[i].mData = (*current_buffers)[i] + cb_offset + current_offset; + } + + cb_offset += inNumberFrames; + + return noErr; } int @@ -173,17 +360,37 @@ AUPlugin::connect_and_run (vector& bufs, uint32_t maxbuf, int32_t& in, { AudioUnitRenderActionFlags flags = 0; AudioTimeStamp ts; - - AudioBufferList abl; - abl.mNumberBuffers = 1; - abl.mBuffers[0].mNumberChannels = 1; - abl.mBuffers[0].mDataByteSize = nframes * sizeof(Sample); - abl.mBuffers[0].mData = &bufs[0]; - - - unit->Render (&flags, &ts, 0, 0, &abl); - - return 0; + + current_buffers = &bufs; + current_maxbuf = maxbuf; + current_offset = offset; + cb_offset = 0; + + buffers->mNumberBuffers = maxbuf; + + for (uint32_t i = 0; i < maxbuf; ++i) { + buffers->mBuffers[i].mNumberChannels = 1; + buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample); + buffers->mBuffers[i].mData = 0; + } + + ts.mSampleTime = frames_processed; + ts.mFlags = kAudioTimeStampSampleTimeValid; + + if (unit->Render (&flags, &ts, 0, nframes, buffers) == noErr) { + + current_maxbuf = 0; + frames_processed += nframes; + + for (uint32_t i = 0; i < maxbuf; ++i) { + if (bufs[i] + offset != buffers->mBuffers[i].mData) { + memcpy (bufs[i]+offset, buffers->mBuffers[i].mData, nframes * sizeof (Sample)); + } + } + return 0; + } + + return -1; } set @@ -245,8 +452,8 @@ AUPlugin::parameter_is_output (uint32_t) const XMLNode& AUPlugin::get_state() { - XMLNode* root = new XMLNode (state_node_name()); - + XMLNode *root = new XMLNode (state_node_name()); + LocaleGuard lg (X_("POSIX")); return *root; } @@ -279,7 +486,19 @@ AUPlugin::get_presets () bool AUPlugin::has_editor () const { - return false; + // even if the plugin doesn't have its own editor, the AU API can be used + // to create one that looks native. + return true; +} + +AUPluginInfo::AUPluginInfo (boost::shared_ptr d) + : descriptor (d) +{ + +} + +AUPluginInfo::~AUPluginInfo () +{ } PluginPtr @@ -288,7 +507,7 @@ AUPluginInfo::load (Session& session) try { PluginPtr plugin; - CAComponent* comp = new CAComponent(*desc); + boost::shared_ptr comp (new CAComponent(*descriptor)); if (!comp->IsValid()) { error << ("AudioUnit: not a valid Component") << endmsg; @@ -296,7 +515,7 @@ AUPluginInfo::load (Session& session) plugin.reset (new AUPlugin (session.engine(), session, comp)); } - plugin->set_info(PluginInfoPtr(new AUPluginInfo(*this))); + plugin->set_info (PluginInfoPtr (new AUPluginInfo (*this))); return plugin; } @@ -310,6 +529,28 @@ AUPluginInfo::discover () { PluginInfoList plugs; + discover_fx (plugs); + discover_music (plugs); + + return plugs; +} + +void +AUPluginInfo::discover_music (PluginInfoList& plugs) +{ + CAComponentDescription desc; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + desc.componentSubType = 0; + desc.componentManufacturer = 0; + desc.componentType = kAudioUnitType_MusicEffect; + + discover_by_description (plugs, desc); +} + +void +AUPluginInfo::discover_fx (PluginInfoList& plugs) +{ CAComponentDescription desc; desc.componentFlags = 0; desc.componentFlagsMask = 0; @@ -317,35 +558,146 @@ AUPluginInfo::discover () desc.componentManufacturer = 0; desc.componentType = kAudioUnitType_Effect; + discover_by_description (plugs, desc); +} + +void +AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc) +{ Component comp = 0; comp = FindNextComponent (NULL, &desc); + while (comp != NULL) { CAComponentDescription temp; GetComponentInfo (comp, &temp, NULL, NULL, NULL); + + AUPluginInfoPtr info (new AUPluginInfo + (boost::shared_ptr (new CAComponentDescription(temp)))); + + /* no panners, format converters or i/o AU's for our purposes + */ + + switch (info->descriptor->Type()) { + case kAudioUnitType_Panner: + case kAudioUnitType_OfflineEffect: + case kAudioUnitType_FormatConverter: + continue; + default: + break; + } + + switch (info->descriptor->SubType()) { + case kAudioUnitSubType_DefaultOutput: + case kAudioUnitSubType_SystemOutput: + case kAudioUnitSubType_GenericOutput: + case kAudioUnitSubType_AUConverter: + continue; + break; + + case kAudioUnitSubType_DLSSynth: + info->category = "DLSSynth"; + break; + + case kAudioUnitType_MusicEffect: + info->category = "MusicEffect"; + break; + + case kAudioUnitSubType_Varispeed: + info->category = "Varispeed"; + break; + + case kAudioUnitSubType_Delay: + info->category = "Delay"; + break; + + case kAudioUnitSubType_LowPassFilter: + info->category = "LowPassFilter"; + break; + + case kAudioUnitSubType_HighPassFilter: + info->category = "HighPassFilter"; + break; + + case kAudioUnitSubType_BandPassFilter: + info->category = "BandPassFilter"; + break; + + case kAudioUnitSubType_HighShelfFilter: + info->category = "HighShelfFilter"; + break; + + case kAudioUnitSubType_LowShelfFilter: + info->category = "LowShelfFilter"; + break; + + case kAudioUnitSubType_ParametricEQ: + info->category = "ParametricEQ"; + break; + + case kAudioUnitSubType_GraphicEQ: + info->category = "GraphicEQ"; + break; + + case kAudioUnitSubType_PeakLimiter: + info->category = "PeakLimiter"; + break; + + case kAudioUnitSubType_DynamicsProcessor: + info->category = "DynamicsProcessor"; + break; + + case kAudioUnitSubType_MultiBandCompressor: + info->category = "MultiBandCompressor"; + break; + + case kAudioUnitSubType_MatrixReverb: + info->category = "MatrixReverb"; + break; + + case kAudioUnitType_Mixer: + info->category = "Mixer"; + break; + + case kAudioUnitSubType_StereoMixer: + info->category = "StereoMixer"; + break; + + case kAudioUnitSubType_3DMixer: + info->category = "3DMixer"; + break; + + case kAudioUnitSubType_MatrixMixer: + info->category = "MatrixMixer"; + break; + + default: + info->category = ""; + } + + AUPluginInfo::get_names (temp, info->name, info->creator); + + info->type = ARDOUR::AudioUnit; + info->unique_id = stringify_descriptor (*info->descriptor); + + /* mark the plugin as having flexible i/o */ - AUPluginInfoPtr plug(new AUPluginInfo); - plug->name = AUPluginInfo::get_name (temp); - plug->type = ARDOUR::AudioUnit; - plug->n_inputs = 0; - plug->n_outputs = 0; - // plug->setup_nchannels (temp); - plug->category = "AudioUnit"; - plug->desc = new CAComponentDescription(temp); - - plugs.push_back(plug); + info->n_inputs = -1; + info->n_outputs = -1; + + + plugs.push_back (info); comp = FindNextComponent (comp, &desc); } - - return plugs; } -string -AUPluginInfo::get_name (CAComponentDescription& comp_desc) +void +AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker) { CFStringRef itemName = NULL; - // Marc Poirier -style item name + + // Marc Poirier-style item name CAComponent auComponent (comp_desc); if (auComponent.IsValid()) { CAComponentDescription dummydesc; @@ -379,23 +731,36 @@ AUPluginInfo::get_name (CAComponentDescription& comp_desc) CFRelease(compManufacturerString); } - return CFStringRefToStdString(itemName); -} - -void -AUPluginInfo::setup_nchannels (CAComponentDescription& comp_desc) -{ - CAAudioUnit unit; - - CAAudioUnit::Open (comp_desc, unit); - - if (unit.SupportsNumChannels()) { - n_inputs = n_outputs = 0; + string str = CFStringRefToStdString(itemName); + string::size_type colon = str.find (':'); + + if (colon) { + name = str.substr (colon+1); + maker = str.substr (0, colon); + // strip_whitespace_edges (maker); + // strip_whitespace_edges (name); } else { - AUChannelInfo cinfo; - size_t info_size = sizeof(cinfo); - OSStatus err = AudioUnitGetProperty (unit.AU(), kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, - 0, &cinfo, &info_size); + name = str; + maker = "unknown"; } } +// from CAComponentDescription.cpp (in libs/appleutility in ardour source) +extern char *StringForOSType (OSType t, char *writeLocation); + +std::string +AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc) +{ + char str[24]; + stringstream s; + + s << StringForOSType (desc.Type(), str); + s << " - "; + + s << StringForOSType (desc.SubType(), str); + s << " - "; + + s << StringForOSType (desc.Manu(), str); + + return s.str(); +} diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 91ff8b8d35..08d18c7cab 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -368,6 +369,20 @@ AudioEngine::process_callback (nframes_t nframes) last_monitor_check = next_processed_frames; } + if (session->silent()) { + + boost::shared_ptr p = ports.reader(); + + for (Ports::iterator i = p->begin(); i != p->end(); ++i) { + + Port *port = (*i); + + if (port->sends_output()) { + port->get_buffer().silence(nframes); + } + } + } + _processed_frames = next_processed_frames; return 0; } @@ -510,6 +525,26 @@ AudioEngine::remove_session () remove_all_ports (); } +void +AudioEngine::port_registration_failure (const std::string& portname) +{ + string full_portname = jack_client_name; + full_portname += ':'; + full_portname += portname; + + + jack_port_t* p = jack_port_by_name (_jack, full_portname.c_str()); + string reason; + + if (p) { + reason = _("a port with this name already exists: check for duplicated track/bus names"); + } else { + reason = _("unknown error"); + } + + throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str()); +} + Port * AudioEngine::register_port (DataType dtype, const string& portname, bool input, bool publish) { @@ -533,7 +568,7 @@ AudioEngine::register_port (DataType dtype, const string& portname, bool input, } catch (...) { - throw PortRegistrationFailure(); + throw PortRegistrationFailure("unable to create port (unknown type?)"); } } @@ -563,7 +598,7 @@ AudioEngine::register_output_port (DataType type, const string& portname, bool p return register_port (type, portname, false, publish); } -int +int AudioEngine::unregister_port (Port& port) { /* caller must hold process lock */ @@ -590,6 +625,8 @@ AudioEngine::unregister_port (Port& port) /* writer goes out of scope, forces update */ } + + remove_connections_for (port); return 0; } @@ -1054,6 +1091,23 @@ AudioEngine::remove_all_ports () } } +void +AudioEngine::remove_connections_for (Port& port) +{ + for (PortConnections::iterator i = port_connections.begin(); i != port_connections.end(); ) { + PortConnections::iterator tmp; + + tmp = i; + ++tmp; + + if ((*i).first == port.name()) { + port_connections.erase (i); + } + + i = tmp; + } +} + #ifdef HAVE_JACK_CLIENT_OPEN diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 1284dd343b..81ad45e08a 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -135,18 +135,15 @@ AudioFileSource::removable () const int AudioFileSource::init (ustring pathstr, bool must_exist) { - bool is_new = false; - _length = 0; timeline_position = 0; _peaks_built = false; - file_is_new = false; - if (!find (pathstr, must_exist, is_new, _channel)) { + if (!find (pathstr, must_exist, file_is_new, _channel)) { throw non_existent_source (); } - if (is_new && must_exist) { + if (file_is_new && must_exist) { return -1; } diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 8034f3ddac..301351fe71 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -237,54 +237,6 @@ AudioRegion::listen_to_my_curves () _fade_out->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed)); } -bool -AudioRegion::verify_length (nframes_t len) -{ - boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); - - if (afs && afs->destructive()) { - return true; - } else { - return Region::verify_length(len); - } -} - -bool -AudioRegion::verify_start_and_length (nframes_t new_start, nframes_t new_length) -{ - boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); - - if (afs && afs->destructive()) { - return true; - } else { - return Region::verify_start_and_length(new_start, new_length); - } -} - -bool -AudioRegion::verify_start (nframes_t pos) -{ - boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); - - if (afs && afs->destructive()) { - return true; - } else { - return Region::verify_start(pos); - } -} - -bool -AudioRegion::verify_start_mutable (nframes_t& new_start) -{ - boost::shared_ptr afs = boost::dynamic_pointer_cast(source()); - - if (afs && afs->destructive()) { - return true; - } else { - return Region::verify_start_mutable(new_start); - } -} - void AudioRegion::set_envelope_active (bool yn) { @@ -321,25 +273,26 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra } } -ARDOUR::nframes_t -AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, nframes_t cnt, uint32_t chan_n) const +nframes_t +AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, + nframes_t cnt, + uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const { - return _read_at (_sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n); + return _read_at (_sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames); } -ARDOUR::nframes_t +nframes_t AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position, nframes_t cnt, uint32_t chan_n) const { - return _read_at (_master_sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n); + return _read_at (_master_sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0); } -ARDOUR::nframes_t +nframes_t AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buffer, float *gain_buffer, - nframes_t position, nframes_t cnt, uint32_t chan_n) const + nframes_t position, nframes_t cnt, + uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const { - // cerr << _name << "._read_at(" << position << ") - " << _position << endl; - nframes_t internal_offset; nframes_t buf_offset; nframes_t to_read; @@ -377,13 +330,12 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff _read_data_count = 0; if (chan_n < n_channels()) { - + boost::shared_ptr src = audio_source(chan_n); if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) { - return 0; /* "read nothing" */ } - + _read_data_count += src->read_data_count(); } else { diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index a2ce7209f6..ce8aa95964 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -49,6 +49,8 @@ using Glib::ustring; bool AudioSource::_build_missing_peakfiles = false; bool AudioSource::_build_peakfiles = false; +#define _FPP 256 + AudioSource::AudioSource (Session& s, ustring name) : Source (s, name, DataType::AUDIO) { @@ -135,7 +137,7 @@ AudioSource::peaks_ready (sigc::slot the_slot, sigc::connection& conn) con /* check to see if the peak data is ready. if not connect the slot while still holding the lock. */ - + if (!(ret = _peaks_built)) { conn = PeaksReady.connect (the_slot); } @@ -192,44 +194,36 @@ AudioSource::initialize_peakfile (bool newfile, ustring audio_path) peakpath = find_broken_peakfile (peakpath, audio_path); } - if (newfile) { - - if (!_build_peakfiles) { - return 0; + if (stat (peakpath.c_str(), &statbuf)) { + if (errno != ENOENT) { + /* it exists in the peaks dir, but there is some kind of error */ + + error << string_compose(_("AudioSource: cannot stat peakfile \"%1\""), peakpath) << endmsg; + return -1; } + /* peakfile does not exist */ + _peaks_built = false; - + } else { - - if (stat (peakpath.c_str(), &statbuf)) { - if (errno != ENOENT) { - /* it exists in the peaks dir, but there is some kind of error */ - - error << string_compose(_("AudioSource: cannot stat peakfile \"%1\""), peakpath) << endmsg; - return -1; - } - + + /* we found it in the peaks dir, so check it out */ + + if (statbuf.st_size == 0) { + // empty _peaks_built = false; - } else { + // Check if the audio file has changed since the peakfile was built. + struct stat stat_file; + int err = stat (audio_path.c_str(), &stat_file); - /* we found it in the peaks dir, so check it out */ - - if (statbuf.st_size == 0) { + if (!err && stat_file.st_mtime > statbuf.st_mtime){ _peaks_built = false; + _peak_byte_max = 0; } else { - // Check if the audio file has changed since the peakfile was built. - struct stat stat_file; - int err = stat (audio_path.c_str(), &stat_file); - - if (!err && stat_file.st_mtime > statbuf.st_mtime){ - _peaks_built = false; - _peak_byte_max = 0; - } else { - _peaks_built = true; - _peak_byte_max = statbuf.st_size; - } + _peaks_built = true; + _peak_byte_max = statbuf.st_size; } } } @@ -255,8 +249,15 @@ AudioSource::write (Sample *dst, nframes_t cnt) return write_unlocked (dst, cnt); } -int +int AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const +{ + return read_peaks_with_fpp (peaks, npeaks, start, cnt, samples_per_visual_peak, _FPP); +} + +int +AudioSource::read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, + double samples_per_visual_peak, nframes_t samples_per_file_peak) const { Glib::Mutex::Lock lm (_lock); double scale; @@ -271,7 +272,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr Sample* raw_staging = 0; int _peakfile = -1; - expected_peaks = (cnt / (double) frames_per_peak); + expected_peaks = (cnt / (double) samples_per_file_peak); scale = npeaks/expected_peaks; #undef DEBUG_READ_PEAKS @@ -326,7 +327,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr if (scale == 1.0) { - off_t first_peak_byte = (start / frames_per_peak) * sizeof (PeakData); + off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData); /* open, read, close */ @@ -390,10 +391,10 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* compute the rounded up frame position */ nframes_t current_frame = start; - nframes_t current_stored_peak = (nframes_t) ceil (current_frame / (double) frames_per_peak); + nframes_t current_stored_peak = (nframes_t) ceil (current_frame / (double) samples_per_file_peak); uint32_t next_visual_peak = (uint32_t) ceil (current_frame / samples_per_visual_peak); double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak; - uint32_t stored_peak_before_next_visual_peak = (nframes_t) next_visual_peak_frame / frames_per_peak; + uint32_t stored_peak_before_next_visual_peak = (nframes_t) next_visual_peak_frame / samples_per_file_peak; uint32_t nvisual_peaks = 0; uint32_t stored_peaks_read = 0; uint32_t i = 0; @@ -414,7 +415,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr if (i == stored_peaks_read) { uint32_t start_byte = current_stored_peak * sizeof(PeakData); - tnp = min ((_length/frames_per_peak - current_stored_peak), (nframes_t) expected_peaks); + tnp = min ((_length/samples_per_file_peak - current_stored_peak), (nframes_t) expected_peaks); to_read = min (chunksize, tnp); #ifdef DEBUG_READ_PEAKS @@ -437,7 +438,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr << ')' << " at start_byte = " << start_byte << " _length = " << _length << " versus len = " << fend - << " expected maxpeaks = " << (_length - current_frame)/frames_per_peak + << " expected maxpeaks = " << (_length - current_frame)/samples_per_file_peak << " npeaks was " << npeaks << endl; goto out; @@ -466,7 +467,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr //next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) ); next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) ); - stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / frames_per_peak; + stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak; } if (zero_fill) { @@ -611,7 +612,7 @@ AudioSource::build_peaks_from_scratch () goto out; } - if (compute_and_write_peaks (buf, current_frame, frames_read, true, false)) { + if (compute_and_write_peaks (buf, current_frame, frames_read, true, false, _FPP)) { break; } @@ -662,7 +663,7 @@ void AudioSource::done_with_peakfile_writes (bool done) { if (peak_leftover_cnt) { - compute_and_write_peaks (0, 0, 0, true, false); + compute_and_write_peaks (0, 0, 0, true, false, _FPP); } if (done) { @@ -677,6 +678,13 @@ AudioSource::done_with_peakfile_writes (bool done) int AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready) +{ + return compute_and_write_peaks (buf, first_frame, cnt, force, intermediate_peaks_ready, _FPP); +} + +int +AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, + bool intermediate_peaks_ready, nframes_t fpp) { Sample* buf2 = 0; nframes_t to_do; @@ -707,9 +715,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe x.min = peak_leftovers[0]; x.max = peak_leftovers[0]; - ARDOUR::find_peaks (peak_leftovers + 1, peak_leftover_cnt - 1, &x.min, &x.max); - - off_t byte = (peak_leftover_frame / frames_per_peak) * sizeof (PeakData); + off_t byte = (peak_leftover_frame / fpp) * sizeof (PeakData); if (::pwrite (peakfile, &x, sizeof (PeakData), byte) != sizeof (PeakData)) { error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg; @@ -761,7 +767,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe to_do = cnt; } - peakbuf = new PeakData[(to_do/frames_per_peak)+1]; + peakbuf = new PeakData[(to_do/fpp)+1]; peaks_computed = 0; current_frame = first_frame; frames_done = 0; @@ -769,11 +775,11 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe while (to_do) { /* if some frames were passed in (i.e. we're not flushing leftovers) - and there are less than frames_per_peak to do, save them till + and there are less than fpp to do, save them till next time */ - if (force && (to_do < frames_per_peak)) { + if (force && (to_do < fpp)) { /* keep the left overs around for next time */ if (peak_leftover_size < to_do) { @@ -790,7 +796,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe break; } - nframes_t this_time = min (frames_per_peak, to_do); + nframes_t this_time = min (fpp, to_do); peakbuf[peaks_computed].max = buf[0]; peakbuf[peaks_computed].min = buf[0]; @@ -804,7 +810,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe current_frame += this_time; } - first_peak_byte = (first_frame / frames_per_peak) * sizeof (PeakData); + first_peak_byte = (first_frame / fpp) * sizeof (PeakData); if (can_truncate_peaks()) { @@ -887,7 +893,7 @@ AudioSource::available_peaks (double zoom_factor) const { off_t end; - if (zoom_factor < frames_per_peak) { + if (zoom_factor < _FPP) { return length(); // peak data will come from the audio file } @@ -899,7 +905,7 @@ AudioSource::available_peaks (double zoom_factor) const end = _peak_byte_max; - return (end/sizeof(PeakData)) * frames_per_peak; + return (end/sizeof(PeakData)) * _FPP; } void diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index 6085501470..d6f63c2f9d 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -151,8 +151,19 @@ Auditioner::audition_region (boost::shared_ptr region) reset_panner(); length = the_region->length(); - _diskstream->seek (0); - current_frame = 0; + + int dir; + nframes_t offset = the_region->sync_offset (dir); + + /* can't audition from a negative sync point */ + + if (dir < 0) { + offset = 0; + } + + _diskstream->seek (offset); + current_frame = offset; + g_atomic_int_set (&_active, 1); } diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 508e6515c9..f7711b3224 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -260,7 +260,8 @@ Crossfade::read_raw_internal (Sample* buf, nframes_t start, nframes_t cnt) const nframes_t Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, - float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n) const + float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n, + nframes_t read_frames, nframes_t skip_frames) const { nframes_t offset; nframes_t to_write; @@ -301,9 +302,9 @@ Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, } else if (!(_in->opaque())) { memset (crossfade_buffer_in, 0, sizeof (Sample) * to_write); } - - _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n); - _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n); + + _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); + _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames); float* fiv = new float[to_write]; float* fov = new float[to_write]; diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index f2d2e92169..9f2ce4fab9 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -164,7 +164,6 @@ setup_enum_writer () REGISTER_ENUM (SyncPoint); REGISTER (_RegionPoint); - REGISTER_ENUM (PreFader); REGISTER_ENUM (PostFader); REGISTER (_Placement); @@ -243,6 +242,7 @@ setup_enum_writer () REGISTER_CLASS_ENUM (Session::Event, SetDiskstreamSpeed); REGISTER_CLASS_ENUM (Session::Event, Locate); REGISTER_CLASS_ENUM (Session::Event, LocateRoll); + REGISTER_CLASS_ENUM (Session::Event, LocateRollLocate); REGISTER_CLASS_ENUM (Session::Event, SetLoop); REGISTER_CLASS_ENUM (Session::Event, PunchIn); REGISTER_CLASS_ENUM (Session::Event, PunchOut); diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index a8fb4050f9..91e5b9850c 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -34,11 +34,11 @@ #include #include +#include #include #include #include -#include #include #include #include @@ -52,22 +52,19 @@ using namespace ARDOUR; using namespace PBD; -std::auto_ptr -open_importable_source (const string& path, nframes_t samplerate, - ARDOUR::SrcQuality quality) +static std::auto_ptr +open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQuality quality) { std::auto_ptr source(new ImportableSource(path)); if (source->samplerate() == samplerate) { return source; } - - return std::auto_ptr( - new ResampledImportableSource(path, samplerate, quality) - ); + + return std::auto_ptr(new ResampledImportableSource(path, samplerate, quality)); } -std::string +static std::string get_non_existent_filename (const std::string& basename, uint channel, uint channels) { char buf[PATH_MAX+1]; @@ -86,8 +83,8 @@ get_non_existent_filename (const std::string& basename, uint channel, uint chann } else { snprintf (buf, sizeof(buf), "%s.wav", base.c_str()); } - - if (sys::exists (buf)) { + + if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { /* if the file already exists, we must come up with * a new name for it. for now we just keep appending @@ -106,27 +103,27 @@ get_non_existent_filename (const std::string& basename, uint channel, uint chann return buf; } -vector -get_paths_for_new_sources (const string& import_file_path, const string& session_dir, - uint channels) +static vector +get_paths_for_new_sources (const string& import_file_path, const string& session_dir, uint channels) { vector new_paths; - const string basename = sys::basename (import_file_path); - SessionDirectory sdir(session_dir); + const string basename = basename_nosuffix (import_file_path); for (uint n = 0; n < channels; ++n) { - std::string filename = get_non_existent_filename (basename, n, channels); + std::string filepath; - sys::path filepath = sdir.sound_path() / filename; + filepath = session_dir; + filepath += '/'; + filepath += get_non_existent_filename (basename, n, channels); - new_paths.push_back (filepath.to_string()); + new_paths.push_back (filepath); } return new_paths; } -bool +static bool create_mono_sources_for_writing (const vector& new_paths, Session& sess, uint samplerate, vector >& newfiles) { @@ -138,12 +135,12 @@ create_mono_sources_for_writing (const vector& new_paths, Session& sess, try { source = SourceFactory::createWritable ( - DataType::AUDIO, - sess, - i->c_str(), - false, // destructive - samplerate - ); + DataType::AUDIO, + sess, + i->c_str(), + false, // destructive + samplerate + ); } catch (const failed_constructor& err) { @@ -156,29 +153,29 @@ create_mono_sources_for_writing (const vector& new_paths, Session& sess, return true; } -Glib::ustring +static Glib::ustring compose_status_message (const string& path, - uint file_samplerate, - uint session_samplerate, - uint current_file, - uint total_files) + uint file_samplerate, + uint session_samplerate, + uint current_file, + uint total_files) { if (file_samplerate != session_samplerate) { return string_compose (_("converting %1\n(resample from %2KHz to %3KHz)\n(%4 of %5)"), - sys::path(path).leaf(), - file_samplerate/1000.0f, - session_samplerate/1000.0f, - current_file, total_files); + Glib::path_get_basename (path), + file_samplerate/1000.0f, + session_samplerate/1000.0f, + current_file, total_files); } return string_compose (_("converting %1\n(%2 of %3)"), - sys::path(path).leaf(), - current_file, total_files); + Glib::path_get_basename (path), + current_file, total_files); } -void +static void write_audio_data_to_new_files (ImportableSource* source, Session::import_status& status, - vector >& newfiles) + vector >& newfiles) { const nframes_t nframes = ResampledImportableSource::blocksize; uint channels = source->channels(); @@ -225,10 +222,10 @@ write_audio_data_to_new_files (ImportableSource* source, Session::import_status& } } -void +static void remove_file_source (boost::shared_ptr file_source) { - sys::remove (std::string(file_source->path())); + ::unlink (file_source->path().c_str()); } void @@ -260,7 +257,7 @@ Session::import_audiofiles (import_status& status) vector new_paths = get_paths_for_new_sources (*p, get_best_session_directory_for_new_source (), source->channels()); - + AudioSources newfiles; status.cancel = !create_mono_sources_for_writing (new_paths, *this, frame_rate(), newfiles); @@ -309,3 +306,4 @@ Session::import_audiofiles (import_status& status) status.done = true; } + diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 89c342e9cf..7a2ae2aad9 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -307,12 +307,12 @@ void IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset) { assert(outs.available() >= n_inputs()); - - outs.set_count(n_inputs()); - if (outs.count() == ChanCount::ZERO) + if (n_inputs() == ChanCount::ZERO) return; + outs.set_count(n_inputs()); + for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) { BufferSet::iterator o = outs.begin(*t); @@ -2567,3 +2567,4 @@ IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr b) sigc::mem_fun (*io, &IO::bundle_ports_have_changed) ); } + diff --git a/libs/ardour/jack_audio_port.cc b/libs/ardour/jack_audio_port.cc index 3a0b34ab19..b2ddb6d48e 100644 --- a/libs/ardour/jack_audio_port.cc +++ b/libs/ardour/jack_audio_port.cc @@ -29,10 +29,14 @@ JackAudioPort::JackAudioPort (const std::string& name, Flags flgs, AudioBuffer* { if (buf) { + cout << "jack audio port buffer" << endl; + _buffer = buf; _own_buffer = false; } else { + + cout << "jack audio port no buffer" << endl; /* data space will be provided by JACK */ diff --git a/libs/ardour/jack_midi_port.cc b/libs/ardour/jack_midi_port.cc index 7dbb8655f6..c9471c9d0c 100644 --- a/libs/ardour/jack_midi_port.cc +++ b/libs/ardour/jack_midi_port.cc @@ -71,8 +71,8 @@ JackMidiPort::cycle_start (nframes_t nframes, nframes_t offset_ignored_but_proba assert(_buffer->size() == event_count); - if (_buffer->size() > 0) - cerr << "MIDIPort got " << event_count << " events." << endl; + /*if (_buffer->size() > 0) + cerr << "JackMIDIPort got " << event_count << " events (buf " << _buffer << ")" << endl;*/ } void diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc index 0614e3334d..696ada3b81 100644 --- a/libs/ardour/ladspa_plugin.cc +++ b/libs/ardour/ladspa_plugin.cc @@ -185,6 +185,14 @@ LadspaPlugin::restore_state (PluginState& state) } } +string +LadspaPlugin::unique_id() const +{ + char buf[32]; + snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID); + return string (buf); +} + float LadspaPlugin::default_value (uint32_t port) { diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index d108374ba0..f85fd3ec33 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -41,7 +41,7 @@ PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_f // Meter what we have (midi) for ( ; n < meterable && n < bufs.count().n_midi(); ++n) { - + float val = 0; // GUI needs a better MIDI meter, not much information can be diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc index a629fe458f..1ca6a5db72 100644 --- a/libs/ardour/midi_buffer.cc +++ b/libs/ardour/midi_buffer.cc @@ -104,21 +104,23 @@ void MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset) { assert(src.type() == DataType::MIDI); - const MidiBuffer& msrc = (MidiBuffer&)src; + assert(&src != this); - assert(_capacity >= src.size()); + const MidiBuffer& msrc = (MidiBuffer&)src; + + assert(_capacity >= msrc.size()); clear(); assert(_size == 0); - + // FIXME: slow - for (size_t i=0; i < src.size(); ++i) { + for (size_t i=0; i < msrc.size(); ++i) { const MidiEvent& ev = msrc[i]; if (ev.time() >= offset && ev.time() < offset+nframes) { - //cerr << "MidiBuffer::read_from got event, " << ev.time() << endl; + //cout << "MidiBuffer::read_from got event, " << ev.time() << endl; push_back(ev); } else { - //cerr << "MidiBuffer event out of range, " << ev.time() << endl; + cerr << "MidiBuffer event out of range, " << ev.time() << endl; } } diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index 7590c42e0b..3e8dea4f62 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -26,23 +26,23 @@ using namespace ARDOUR; using namespace std; -MidiPort::MidiPort (const std::string& name, Flags flags, bool external, nframes_t bufsize) +MidiPort::MidiPort (const std::string& name, Flags flags, bool external, nframes_t capacity) : Port (name, flags) , BaseMidiPort (name, flags) , PortFacade (name, flags) { - set_name (name); - - _buffer = new MidiBuffer (bufsize); + _buffer = new MidiBuffer (capacity); - cout << "MIDI port " << name << " external: " << external << endl; - - if (!external) { - _ext_port = 0; - } else { + if (external) { + /* external ports use the same buffer for the jack port (_ext_port) + * and internal ports (this) */ _ext_port = new JackMidiPort (name, flags, _buffer); + } else { + /* internal ports just have a single buffer, no jack port */ + _ext_port = 0; } + set_name (name); reset (); } @@ -70,7 +70,6 @@ MidiPort::cycle_start (nframes_t nframes, nframes_t offset) /* caller must hold process lock */ if (_ext_port) { - // cout << "external\n"; _ext_port->cycle_start (nframes, offset); } @@ -78,11 +77,9 @@ MidiPort::cycle_start (nframes_t nframes, nframes_t offset) if (_ext_port) { - // cout << "external in\n"; - - _buffer->read_from (dynamic_cast(_ext_port)->get_midi_buffer(), nframes, offset); - - // cout << "read " << _buffer->size() << " events." << endl; + BaseMidiPort* mprt = dynamic_cast(_ext_port); + assert(mprt); + assert(&mprt->get_midi_buffer() == _buffer); if (!_connections.empty()) { (*_mixdown) (_connections, _buffer, nframes, offset, false); @@ -90,8 +87,6 @@ MidiPort::cycle_start (nframes_t nframes, nframes_t offset) } else { - // cout << "internal in\n"; - if (_connections.empty()) { _buffer->silence (nframes, offset); } else { @@ -101,10 +96,6 @@ MidiPort::cycle_start (nframes_t nframes, nframes_t offset) } else { - // cout << "out\n"; - _buffer->silence (nframes, offset); } - - // cout << endl; } diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 8d20d8539d..04649a56fe 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -233,6 +234,7 @@ Playlist::init (bool hide) _refcnt = 0; _hidden = hide; _splicing = false; + _shuffling = false; _nudging = false; in_set_state = 0; _edit_mode = Config->get_edit_mode(); @@ -351,7 +353,7 @@ Playlist::notify_region_removed (boost::shared_ptr r) /* this might not be true, but we have to act as though it could be. */ - pending_length = false; + pending_length = false; LengthChanged (); /* EMIT SIGNAL */ pending_modified = false; Modified (); /* EMIT SIGNAL */ @@ -439,7 +441,6 @@ Playlist::flush_notifications () if (n || pending_modified) { if (!in_set_state) { - possibly_splice (); relayer (); } pending_modified = false; @@ -479,12 +480,7 @@ Playlist::add_region (boost::shared_ptr region, nframes_t position, floa --itimes; } - /* later regions will all be spliced anyway */ - if (!holding_state ()) { - possibly_splice_unlocked (); - } - /* note that itimes can be zero if we being asked to just insert a single fraction of the region. */ @@ -495,14 +491,18 @@ Playlist::add_region (boost::shared_ptr region, nframes_t position, floa pos += region->length(); } + nframes_t length = 0; + if (floor (times) != times) { - nframes_t length = (nframes_t) floor (region->length() * (times - floor (times))); + length = (nframes_t) floor (region->length() * (times - floor (times))); string name; _session.region_name (name, region->name(), false); boost::shared_ptr sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags()); add_region_internal (sub, pos); } + + possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr()); release_notifications (); } @@ -540,6 +540,8 @@ Playlist::add_region_internal (boost::shared_ptr region, nframes_t posit regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); all_regions.insert (region); + possibly_splice_unlocked (position, region->length(), region); + if (!holding_state () && !in_set_state) { /* layers get assigned from XML state */ relayer (); @@ -565,12 +567,15 @@ Playlist::replace_region (boost::shared_ptr old, boost::shared_ptrlength() - (nframes64_t) newr->length()); } void @@ -578,14 +583,10 @@ Playlist::remove_region (boost::shared_ptr region) { RegionLock rlock (this); remove_region_internal (region); - - if (!holding_state ()) { - possibly_splice_unlocked (); - } } int -Playlist::remove_region_internal (boost::shared_ptrregion) +Playlist::remove_region_internal (boost::shared_ptr region) { RegionList::iterator i; nframes_t old_length = 0; @@ -602,8 +603,13 @@ Playlist::remove_region_internal (boost::shared_ptrregion) for (i = regions.begin(); i != regions.end(); ++i) { if (*i == region) { + nframes_t pos = (*i)->position(); + nframes64_t distance = (*i)->length(); + regions.erase (i); + possibly_splice_unlocked (pos, -distance); + if (!holding_state ()) { relayer (); remove_dependents (region); @@ -617,6 +623,9 @@ Playlist::remove_region_internal (boost::shared_ptrregion) return 0; } } + + + return -1; } @@ -664,6 +673,7 @@ Playlist::partition (nframes_t start, nframes_t end, bool just_top_level) void Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist) { + RegionLock rlock (this); boost::shared_ptr region; boost::shared_ptr current; string new_name; @@ -671,19 +681,14 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi OverlapType overlap; nframes_t pos1, pos2, pos3, pos4; RegionList new_regions; - RegionList copy; in_partition = true; - delay_notifications(); - /* need to work from a copy, because otherwise the regions we add during the process get operated on as well. */ - { - RegionLock rlock (this); - copy = regions; - } + + RegionList copy = regions; for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) { @@ -691,10 +696,9 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi ++tmp; current = *i; - + if (current->first_frame() == start && current->last_frame() == end) { if (cutting) { - RegionLock rlock (this); remove_region_internal (current); } continue; @@ -703,14 +707,14 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi if ((overlap = current->coverage (start, end)) == OverlapNone) { continue; } - + pos1 = current->position(); pos2 = start; pos3 = end; pos4 = current->last_frame(); if (overlap == OverlapInternal) { - + /* split: we need 3 new regions, the front, middle and end. cut: we need 2 regions, the front and end. */ @@ -730,10 +734,9 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi /* "middle" ++++++ */ - _session.region_name (new_name, current->name(), false); //takes the session-wide region lock + _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit)); - RegionLock rlock (this); add_region_internal (region, start); new_regions.push_back (region); } @@ -743,11 +746,10 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit)); - { - RegionLock rlock (this); - add_region_internal (region, end); - new_regions.push_back (region); - } + + add_region_internal (region, end); + new_regions.push_back (region); + /* "front" ***** */ current->freeze (); @@ -772,9 +774,8 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi /* end +++++ */ _session.region_name (new_name, current->name(), false); - region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, regions.size(), + region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit)); - RegionLock rlock (this); add_region_internal (region, start); new_regions.push_back (region); } @@ -809,7 +810,6 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, 0, pos3 - pos1, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit)); - RegionLock rlock (this); add_region_internal (region, pos1); new_regions.push_back (region); } @@ -839,7 +839,6 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi */ if (cutting) { - RegionLock rlock (this); remove_region_internal (current); } new_regions.push_back (current); @@ -851,8 +850,6 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) { check_dependents (*i, false); } - - release_notifications (); } boost::shared_ptr @@ -919,7 +916,6 @@ Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden) } partition_internal (start, start+cnt-1, true, thawlist); - possibly_splice (); for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) { (*i)->thaw ("playlist cut"); @@ -973,7 +969,6 @@ Playlist::paste (boost::shared_ptr other, nframes_t position, float ti pos += shift; } - possibly_splice_unlocked (); /* XXX shall we handle fractional cases at some point? */ @@ -1033,10 +1028,14 @@ Playlist::split_region (boost::shared_ptr region, nframes_t playlist_pos string before_name; string after_name; + /* split doesn't change anything about length, so don't try to splice */ + + bool old_sp = _splicing; + _splicing = true; + before = playlist_position - region->position(); after = region->length() - before; - _session.region_name (before_name, region->name(), false); left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit)); @@ -1045,7 +1044,7 @@ Playlist::split_region (boost::shared_ptr region, nframes_t playlist_pos add_region_internal (left, region->position()); add_region_internal (right, region->position() + before); - + uint64_t orig_layer_op = region->last_layer_op(); for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { if ((*i)->last_layer_op() > orig_layer_op) { @@ -1060,71 +1059,84 @@ Playlist::split_region (boost::shared_ptr region, nframes_t playlist_pos finalize_split_region (region, left, right); - if (remove_region_internal (region)) { - return; - } + remove_region_internal (region); + + _splicing = old_sp; } void -Playlist::possibly_splice () +Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr exclude) { + if (_splicing || in_set_state) { + /* don't respond to splicing moves or state setting */ + return; + } + if (_edit_mode == Splice) { - splice_locked (); + splice_locked (at, distance, exclude); } } void -Playlist::possibly_splice_unlocked () +Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr exclude) { + if (_splicing || in_set_state) { + /* don't respond to splicing moves or state setting */ + return; + } + if (_edit_mode == Splice) { - splice_unlocked (); + splice_unlocked (at, distance, exclude); } } void -Playlist::splice_locked () +Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr exclude) { { RegionLock rl (this); - core_splice (); + core_splice (at, distance, exclude); } - - notify_length_changed (); } void -Playlist::splice_unlocked () +Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr exclude) { - core_splice (); - notify_length_changed (); + core_splice (at, distance, exclude); } void -Playlist::core_splice () +Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr exclude) { _splicing = true; - + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - - RegionList::iterator next; - - next = i; - ++next; - - if (next == regions.end()) { - break; + + if (exclude && (*i) == exclude) { + continue; + } + + if ((*i)->position() >= at) { + nframes64_t new_pos = (*i)->position() + distance; + if (new_pos < 0) { + new_pos = 0; + } else if (new_pos >= max_frames - (*i)->length()) { + new_pos = max_frames - (*i)->length(); + } + + (*i)->set_position (new_pos, this); } - - (*next)->set_position ((*i)->last_frame() + 1, this); } - + _splicing = false; + + notify_length_changed (); } void Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr region) { - if (in_set_state || _splicing || _nudging) { + if (in_set_state || _splicing || _nudging || _shuffling) { return; } @@ -1147,10 +1159,24 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr regions.erase (i); regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region); - } if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) { + + nframes64_t delta = 0; + + if (what_changed & ARDOUR::PositionChanged) { + delta = (nframes64_t) region->position() - (nframes64_t) region->last_position(); + } + + if (what_changed & ARDOUR::LengthChanged) { + delta += (nframes64_t) region->length() - (nframes64_t) region->last_length(); + } + + if (delta) { + possibly_splice (region->last_position() + region->last_length(), delta, region); + } + if (holding_state ()) { pending_bounds.push_back (region); } else { @@ -1159,7 +1185,6 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr timestamp_layer_op (region); } - possibly_splice (); notify_length_changed (); relayer (); check_dependents (region, false); @@ -1269,9 +1294,114 @@ Playlist::top_region_at (nframes_t frame) return region; } +Playlist::RegionList* +Playlist::regions_to_read (nframes_t start, nframes_t end) +{ + /* Caller must hold lock */ + + RegionList covering; + set to_check; + set > unique; + RegionList here; + + to_check.insert (start); + to_check.insert (end); + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + /* find all/any regions that span start+end */ + + switch ((*i)->coverage (start, end)) { + case OverlapNone: + break; + + case OverlapInternal: + covering.push_back (*i); + break; + + case OverlapStart: + to_check.insert ((*i)->position()); + covering.push_back (*i); + break; + + case OverlapEnd: + to_check.insert ((*i)->last_frame()); + covering.push_back (*i); + break; + + case OverlapExternal: + covering.push_back (*i); + to_check.insert ((*i)->position()); + to_check.insert ((*i)->last_frame()); + break; + } + + /* don't go too far */ + + if ((*i)->position() > end) { + break; + } + } + + RegionList* rlist = new RegionList; + + /* find all the regions that cover each position .... */ + + if (covering.size() == 1) { + + rlist->push_back (covering.front()); + + } else { + + for (set::iterator t = to_check.begin(); t != to_check.end(); ++t) { + + here.clear (); + + for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) { + + if ((*x)->covers (*t)) { + here.push_back (*x); + } + } + + RegionSortByLayer cmp; + here.sort (cmp); + + /* ... and get the top/transparent regions at "here" */ + + for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) { + + unique.insert (*c); + + if ((*c)->opaque()) { + + /* the other regions at this position are hidden by this one */ + + break; + } + } + } + + for (set >::iterator s = unique.begin(); s != unique.end(); ++s) { + rlist->push_back (*s); + } + + if (rlist->size() > 1) { + /* now sort by time order */ + + RegionSortByPosition cmp; + rlist->sort (cmp); + } + } + + return rlist; +} + Playlist::RegionList * Playlist::find_regions_at (nframes_t frame) { + /* Caller must hold lock */ + RegionList *rlist = new RegionList; for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { @@ -1352,6 +1482,92 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir) return ret; } +nframes64_t +Playlist::find_next_region_boundary (nframes64_t frame, int dir) +{ + RegionLock rlock (this); + + nframes64_t closest = max_frames; + nframes64_t ret = -1; + + if (dir > 0) { + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + + boost::shared_ptr r = (*i); + nframes64_t distance; + nframes64_t end = r->position() + r->length(); + bool reset; + + reset = false; + + if (r->first_frame() > frame) { + + distance = r->first_frame() - frame; + + if (distance < closest) { + ret = r->first_frame(); + closest = distance; + reset = true; + } + } + + if (end > frame) { + + distance = end - frame; + + if (distance < closest) { + ret = end; + closest = distance; + reset = true; + } + } + + if (reset) { + break; + } + } + + } else { + + for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) { + + boost::shared_ptr r = (*i); + nframes64_t distance; + bool reset; + + reset = false; + + if (r->last_frame() < frame) { + + distance = frame - r->last_frame(); + + if (distance < closest) { + ret = r->last_frame(); + closest = distance; + reset = true; + } + } + + if (r->first_frame() < frame) { + distance = frame - r->last_frame(); + + if (distance < closest) { + ret = r->first_frame(); + closest = distance; + reset = true; + } + } + + if (reset) { + break; + } + } + } + + return ret; +} + /***********************************************************************/ @@ -1685,6 +1901,33 @@ Playlist::relayer () /* XXX these layer functions are all deprecated */ +void +Playlist::raise_region (boost::shared_ptr region) +{ + uint32_t rsz = regions.size(); + layer_t target = region->layer() + 1U; + + if (target >= rsz) { + /* its already at the effective top */ + return; + } + + move_region_to_layer (target, region, 1); +} + +void +Playlist::lower_region (boost::shared_ptr region) +{ + if (region->layer() == 0) { + /* its already at the bottom */ + return; + } + + layer_t target = region->layer() - 1U; + + move_region_to_layer (target, region, -1); +} + void Playlist::raise_region_to_top (boost::shared_ptr region) { @@ -1707,6 +1950,79 @@ Playlist::lower_region_to_bottom (boost::shared_ptr region) } } +int +Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr region, int dir) +{ + RegionList::iterator i; + typedef pair,layer_t> LayerInfo; + list layerinfo; + layer_t dest; + + { + RegionLock rlock (const_cast (this)); + + for (i = regions.begin(); i != regions.end(); ++i) { + + if (region == *i) { + continue; + } + + if (dir > 0) { + + /* region is moving up, move all regions on intermediate layers + down 1 + */ + + if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) { + dest = (*i)->layer() - 1; + } else { + /* not affected */ + continue; + } + } else { + + /* region is moving down, move all regions on intermediate layers + up 1 + */ + + if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) { + dest = (*i)->layer() + 1; + } else { + /* not affected */ + continue; + } + } + + LayerInfo newpair; + + newpair.first = *i; + newpair.second = dest; + + layerinfo.push_back (newpair); + } + } + + /* now reset the layers without holding the region lock */ + + for (list::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) { + x->first->set_layer (x->second); + } + + region->set_layer (target_layer); + +#if 0 + /* now check all dependents */ + + for (list::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) { + check_dependents (x->first, false); + } + + check_dependents (region, false); +#endif + + return 0; +} + void Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards) { @@ -1817,3 +2133,129 @@ Playlist::timestamp_layer_op (boost::shared_ptr region) region->set_last_layer_op (++layer_op_counter); } + +void +Playlist::shuffle (boost::shared_ptr region, int dir) +{ + bool moved = false; + nframes_t new_pos; + + if (region->locked()) { + return; + } + + _shuffling = true; + + { + RegionLock rlock (const_cast (this)); + + + if (dir > 0) { + + RegionList::iterator next; + + for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + if ((*i) == region) { + next = i; + ++next; + + if (next != regions.end()) { + + if ((*next)->locked()) { + break; + } + + if ((*next)->position() != region->last_frame() + 1) { + /* they didn't used to touch, so after shuffle, + just have them swap positions. + */ + new_pos = (*next)->position(); + } else { + /* they used to touch, so after shuffle, + make sure they still do. put the earlier + region where the later one will end after + it is moved. + */ + new_pos = region->position() + (*next)->length(); + } + + (*next)->set_position (region->position(), this); + region->set_position (new_pos, this); + + /* avoid a full sort */ + + regions.erase (i); // removes the region from the list */ + next++; + regions.insert (next, region); // adds it back after next + + moved = true; + } + break; + } + } + } else { + + RegionList::iterator prev = regions.end(); + + for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) { + if ((*i) == region) { + + if (prev != regions.end()) { + + if ((*prev)->locked()) { + break; + } + + if (region->position() != (*prev)->last_frame() + 1) { + /* they didn't used to touch, so after shuffle, + just have them swap positions. + */ + new_pos = region->position(); + } else { + /* they used to touch, so after shuffle, + make sure they still do. put the earlier + one where the later one will end after + */ + new_pos = (*prev)->position() + region->length(); + } + + region->set_position ((*prev)->position(), this); + (*prev)->set_position (new_pos, this); + + /* avoid a full sort */ + + regions.erase (i); // remove region + regions.insert (prev, region); // insert region before prev + + moved = true; + } + + break; + } + } + } + } + + _shuffling = false; + + if (moved) { + + relayer (); + check_dependents (region, false); + + notify_modified(); + } + +} + +bool +Playlist::region_is_shuffle_constrained (boost::shared_ptr) +{ + RegionLock rlock (const_cast (this)); + + if (regions.size() > 1) { + return true; + } + + return false; +} diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index bc5688c318..7f3f21b1fe 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -40,6 +40,10 @@ #include #include +#ifdef HAVE_AUDIOUNITS +#include +#endif + #include #include "i18n.h" @@ -66,7 +70,21 @@ vector Plugin::get_presets() { vector labels; - lrdf_uris* set_uris = lrdf_get_setting_uris(unique_id()); + uint32_t id; + std::string unique (unique_id()); + + /* XXX problem: AU plugins don't have numeric ID's. + Solution: they have a different method of providing presets. + XXX sub-problem: implement it. + */ + + if (!isdigit (unique[0])) { + return labels; + } + + id = atol (unique.c_str()); + + lrdf_uris* set_uris = lrdf_get_setting_uris(id); if (set_uris) { for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) { @@ -108,6 +126,20 @@ Plugin::save_preset (string name, string domain) { lrdf_portvalue portvalues[parameter_count()]; lrdf_defaults defaults; + uint32_t id; + std::string unique (unique_id()); + + /* XXX problem: AU plugins don't have numeric ID's. + Solution: they have a different method of providing/saving presets. + XXX sub-problem: implement it. + */ + + if (!isdigit (unique[0])) { + return false; + } + + id = atol (unique.c_str()); + defaults.count = parameter_count(); defaults.items = portvalues; @@ -126,7 +158,7 @@ Plugin::save_preset (string name, string domain) string source(string_compose("file:%1/.%2/rdf/ardour-presets.n3", envvar, domain)); - free(lrdf_add_preset(source.c_str(), name.c_str(), unique_id(), &defaults)); + free(lrdf_add_preset(source.c_str(), name.c_str(), id, &defaults)); string path = string_compose("%1/.%2", envvar, domain); if (g_mkdir_with_parents (path.c_str(), 0775)) { @@ -149,7 +181,7 @@ Plugin::save_preset (string name, string domain) } PluginPtr -ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType type) +ARDOUR::find_plugin(Session& session, string identifier, PluginType type) { PluginManager *mgr = PluginManager::the_manager(); PluginInfoList plugs; @@ -162,14 +194,12 @@ ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType ty #ifdef VST_SUPPORT case ARDOUR::VST: plugs = mgr->vst_plugin_info(); - unique_id = 0; // VST plugins don't have a unique id. break; #endif #ifdef HAVE_AUDIOUNITS case ARDOUR::AudioUnit: - plugs = AUPluginInfo::discover (); - unique_id = 0; // Neither do AU. + plugs = mgr->au_plugin_info(); break; #endif @@ -178,10 +208,10 @@ ARDOUR::find_plugin(Session& session, string name, long unique_id, PluginType ty } PluginInfoList::iterator i; + for (i = plugs.begin(); i != plugs.end(); ++i) { - if ((name == "" || (*i)->name == name) && - (unique_id == 0 || (*i)->unique_id == unique_id)) { - return (*i)->load (session); + if (identifier == (*i)->unique_id){ + return (*i)->load (session); } } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 3faebd7179..4153f26e85 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -606,17 +606,10 @@ PluginInsert::get_state(void) XMLNode& PluginInsert::state (bool full) { - char buf[256]; XMLNode& node = Processor::state (full); node.add_property ("type", _plugins[0]->state_node_name()); - snprintf(buf, sizeof(buf), "%s", _plugins[0]->name()); - node.add_property("id", string(buf)); - if (_plugins[0]->state_node_name() == "ladspa") { - char buf[32]; - snprintf (buf, sizeof (buf), "%ld", _plugins[0]->get_info()->unique_id); - node.add_property("unique-id", string(buf)); - } + node.add_property("unique-id", _plugins[0]->unique_id()); node.add_property("count", string_compose("%1", _plugins.size())); node.add_child_nocopy (_plugins[0]->get_state()); @@ -648,7 +641,6 @@ PluginInsert::set_state(const XMLNode& node) XMLNodeIterator niter; XMLPropertyList plist; const XMLProperty *prop; - long unique = 0; ARDOUR::PluginType type; if ((prop = node.property ("type")) == 0) { @@ -666,24 +658,16 @@ PluginInsert::set_state(const XMLNode& node) << endmsg; return -1; } - + prop = node.property ("unique-id"); - if (prop != 0) { - unique = atol(prop->value().c_str()); - } - - if ((prop = node.property ("id")) == 0) { - error << _("XML node describing insert is missing the `id' field") << endmsg; - return -1; + if (prop == 0) { + error << _("Plugin has no unique ID field") << endmsg; + return -1; } boost::shared_ptr plugin; - if (unique != 0) { - plugin = find_plugin (_session, "", unique, type); - } else { - plugin = find_plugin (_session, prop->value(), 0, type); - } + plugin = find_plugin (_session, prop->value(), type); if (plugin == 0) { error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n" diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index dac8a9eead..d866a0d49f 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -43,6 +43,10 @@ #include #endif +#ifdef HAVE_AUDIOUNITS +#include +#endif + #include #include @@ -84,10 +88,23 @@ PluginManager::PluginManager () vst_path = s; } - refresh (); if (_manager == 0) { _manager = this; } + + /* the plugin manager is constructed too early to use Profile */ + + if (getenv ("ARDOUR_SAE")) { + ladspa_plugin_whitelist.push_back (1203); // single band parametric + ladspa_plugin_whitelist.push_back (1772); // caps compressor + ladspa_plugin_whitelist.push_back (1913); // fast lookahead limiter + ladspa_plugin_whitelist.push_back (1075); // simple RMS expander + ladspa_plugin_whitelist.push_back (1061); // feedback delay line (max 5s) + ladspa_plugin_whitelist.push_back (1216); // gverb + ladspa_plugin_whitelist.push_back (2150); // tap pitch shifter + } + + refresh (); } void @@ -99,6 +116,9 @@ PluginManager::refresh () vst_refresh (); } #endif // VST_SUPPORT +#ifdef HAVE_AUDIOUNITS + au_refresh (); +#endif } void @@ -113,6 +133,7 @@ PluginManager::ladspa_refresh () ladspa_discover_from_path (ladspa_path); } + int PluginManager::add_ladspa_directory (string path) { @@ -248,6 +269,12 @@ PluginManager::ladspa_discover (string path) break; } + if (!ladspa_plugin_whitelist.empty()) { + if (find (ladspa_plugin_whitelist.begin(), ladspa_plugin_whitelist.end(), descriptor->UniqueID) == ladspa_plugin_whitelist.end()) { + continue; + } + } + PluginInfoPtr info(new LadspaPluginInfo); info->name = descriptor->Name; info->category = get_ladspa_category(descriptor->UniqueID); @@ -257,7 +284,10 @@ PluginManager::ladspa_discover (string path) info->n_inputs = ChanCount(); info->n_outputs = ChanCount(); info->type = ARDOUR::LADSPA; - info->unique_id = descriptor->UniqueID; + + char buf[32]; + snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID); + info->unique_id = buf; for (uint32_t n=0; n < descriptor->PortCount; ++n) { if ( LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n]) ) { @@ -294,7 +324,7 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) lrdf_statement* matches1 = lrdf_matches (&pattern); if (!matches1) { - return _("Unknown"); + return _(""); } pattern.subject = matches1->object; @@ -306,7 +336,7 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) lrdf_free_statements(matches1); if (!matches2) { - return _("Unknown"); + return _(""); } string label = matches2->object; @@ -315,6 +345,22 @@ PluginManager::get_ladspa_category (uint32_t plugin_id) return label; } +#ifdef HAVE_AUDIOUNITS +void +PluginManager::au_refresh () +{ + au_discover(); +} + +int +PluginManager::au_discover () +{ + _au_plugin_info = AUPluginInfo::discover(); + return 0; +} + +#endif + #ifdef VST_SUPPORT void @@ -373,6 +419,7 @@ int PluginManager::vst_discover (string path) { FSTInfo* finfo; + char buf[32]; if ((finfo = fst_get_info (const_cast (path.c_str()))) == 0) { warning << "Cannot get VST information from " << path << endmsg; @@ -395,6 +442,9 @@ PluginManager::vst_discover (string path) info->name = finfo->name; } + + snprintf (buf, sizeof (buf), "%d", finfo->UniqueID); + info->unique_id = buf; info->category = "VST"; info->path = path; // need to set info->creator but FST doesn't provide it diff --git a/libs/ardour/rb_effect.cc b/libs/ardour/rb_effect.cc new file mode 100644 index 0000000000..0de5a5bf4a --- /dev/null +++ b/libs/ardour/rb_effect.cc @@ -0,0 +1,302 @@ +/* + 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 +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; +using namespace RubberBand; + +Pitch::Pitch (Session& s, TimeFXRequest& req) + : RBEffect (s, req) +{ +} + +Stretch::Stretch (Session& s, TimeFXRequest& req) + : RBEffect (s, req) +{ +} + +RBEffect::RBEffect (Session& s, TimeFXRequest& req) + : Filter (s) + , tsr (req) + +{ + tsr.progress = 0.0f; +} + +RBEffect::~RBEffect () +{ +} + +int +RBEffect::run (boost::shared_ptr region) +{ + SourceList nsrcs; + nframes_t done; + int ret = -1; + const nframes_t bufsize = 256; + gain_t* gain_buffer = 0; + Sample** buffers = 0; + char suffix[32]; + string new_name; + string::size_type at; + nframes_t pos = 0; + int avail = 0; + + RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(), + RubberBandStretcher::DefaultOptions, + tsr.time_fraction, tsr.pitch_fraction); + + stretcher.setExpectedInputDuration(region->length()); + stretcher.setDebugLevel(1); + + tsr.progress = 0.0f; + tsr.done = false; + + uint32_t channels = region->n_channels(); + nframes_t duration = region->length(); + + /* the name doesn't need to be super-precise, but allow for 2 fractional + digits just to disambiguate close but not identical FX + */ + + if (tsr.time_fraction == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.pitch_fraction * 100.0f)); + } else if (tsr.pitch_fraction == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.time_fraction * 100.0f)); + } else { + snprintf (suffix, sizeof (suffix), "@%d-%d", + (int) floor (tsr.time_fraction * 100.0f), + (int) floor (tsr.pitch_fraction * 100.0f)); + } + + /* create new sources */ + + if (make_new_sources (region, nsrcs, suffix)) { + goto out; + } + + gain_buffer = new gain_t[bufsize]; + buffers = new float *[channels]; + + for (uint32_t i = 0; i < channels; ++i) { + buffers[i] = new float[bufsize]; + } + + /* we read from the master (original) sources for the region, + not the ones currently in use, in case it's already been + subject to timefx. */ + + /* study first, process afterwards. */ + + pos = 0; + avail = 0; + done = 0; + + try { + while (pos < duration && !tsr.cancel) { + + nframes_t this_read = 0; + + for (uint32_t i = 0; i < channels; ++i) { + + this_read = 0; + nframes_t this_time; + + this_time = min(bufsize, duration - pos); + + this_read = region->master_read_at + (buffers[i], + buffers[i], + gain_buffer, + pos + region->position(), + this_time, + i); + + if (this_read != 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 / duration) * 0.75; + + stretcher.study(buffers, this_read, pos == duration); + } + + done = 0; + pos = 0; + + while (pos < duration && !tsr.cancel) { + + nframes_t this_read = 0; + + for (uint32_t i = 0; i < channels; ++i) { + + this_read = 0; + nframes_t this_time; + + this_time = min(bufsize, duration - pos); + + this_read = region->master_read_at + (buffers[i], + buffers[i], + gain_buffer, + pos + region->position(), + this_time, + i); + + if (this_read != 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 = 0.75 + ((float) done / duration) * 0.25; + + stretcher.process(buffers, this_read, pos == duration); + + int avail = 0; + + while ((avail = stretcher.available()) > 0) { + + this_read = min(bufsize, uint32_t(avail)); + + stretcher.retrieve(buffers, this_read); + + for (uint32_t i = 0; i < nsrcs.size(); ++i) { + + if (nsrcs[i]->write(buffers[i], this_read) != + this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg; + goto out; + } + } + } + } + + while ((avail = stretcher.available()) >= 0) { + + uint32_t this_read = min(bufsize, uint32_t(avail)); + + stretcher.retrieve(buffers, this_read); + + for (uint32_t i = 0; i < nsrcs.size(); ++i) { + + if (nsrcs[i]->write(buffers[i], this_read) != + this_read) { + 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 >::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.time_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.time_fraction/100.0); + float shift = (*x)->shift() * tsr.pitch_fraction; + + start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch)); + length = (nframes_t) floor (alength / stretch); + + (*x)->set_ancestral_data (start, length, stretch, shift); + } + + out: + + if (gain_buffer) { + delete [] gain_buffer; + } + + if (buffers) { + for (uint32_t i = 0; i < channels; ++i) { + delete buffers[i]; + } + delete [] buffers; + } + + if (ret || tsr.cancel) { + for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) { + (*si)->mark_for_remove (); + } + } + + tsr.done = true; + + return ret; +} + + + + + diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index ecc7b5e305..0505985aea 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -365,6 +365,8 @@ Region::set_length (nframes_t len, void *src) return; } + + _last_length = _length; _length = len; _flags = Region::Flag (_flags & ~WholeFile); @@ -443,6 +445,7 @@ Region::special_set_position (nframes_t pos) a way to store its "natural" or "captured" position. */ + _position = _position; _position = pos; } @@ -454,6 +457,7 @@ Region::set_position (nframes_t pos, void *src) } if (_position != pos) { + _last_position = _position; _position = pos; /* check that the new _position wouldn't make the current @@ -463,6 +467,7 @@ Region::set_position (nframes_t pos, void *src) */ if (max_frames - _length < _position) { + _last_length = _length; _length = max_frames - _position; } } @@ -482,6 +487,7 @@ Region::set_position_on_top (nframes_t pos, void *src) } if (_position != pos) { + _last_position = _position; _position = pos; } @@ -499,7 +505,7 @@ Region::set_position_on_top (nframes_t pos, void *src) } void -Region::nudge_position (long n, void *src) +Region::nudge_position (nframes64_t n, void *src) { if (_flags & Locked) { return; @@ -509,6 +515,8 @@ Region::nudge_position (long n, void *src) return; } + _last_position = _position; + if (n > 0) { if (_position > max_frames - n) { _position = max_frames; @@ -527,11 +535,12 @@ Region::nudge_position (long n, void *src) } void -Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st) +Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh) { _ancestral_length = l; _ancestral_start = s; _stretch = st; + _shift = sh; } void @@ -723,10 +732,16 @@ Region::trim_to_internal (nframes_t position, nframes_t length, void *src) what_changed = Change (what_changed|StartChanged); } if (_length != length) { + if (!_frozen) { + _last_length = _length; + } _length = length; what_changed = Change (what_changed|LengthChanged); } if (_position != position) { + if (!_frozen) { + _last_position = _position; + } _position = position; what_changed = Change (what_changed|PositionChanged); } @@ -867,14 +882,16 @@ Region::adjust_to_sync (nframes_t pos) { int sync_dir; nframes_t offset = sync_offset (sync_dir); + + // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl; if (sync_dir > 0) { if (max_frames - pos > offset) { - pos += offset; + pos -= offset; } } else { if (pos > offset) { - pos -= offset; + pos += offset; } else { pos = 0; } @@ -893,6 +910,24 @@ Region::sync_position() const } } +void +Region::raise () +{ + boost::shared_ptr pl (playlist()); + if (pl) { + pl->raise_region (shared_from_this ()); + } +} + +void +Region::lower () +{ + boost::shared_ptr pl (playlist()); + if (pl) { + pl->lower_region (shared_from_this ()); + } +} + void Region::raise_to_top () @@ -945,6 +980,8 @@ Region::state (bool full_state) node->add_property ("ancestral-length", buf); snprintf (buf, sizeof (buf), "%.12g", _stretch); node->add_property ("stretch", buf); + snprintf (buf, sizeof (buf), "%.12g", _shift); + node->add_property ("shift", buf); switch (_first_edit) { case EditChangesNothing: @@ -1017,9 +1054,11 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _length) { what_changed = Change (what_changed|LengthChanged); + _last_length = _length; _length = val; } } else { + _last_length = _length; _length = 1; } @@ -1027,9 +1066,11 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) sscanf (prop->value().c_str(), "%" PRIu32, &val); if (val != _position) { what_changed = Change (what_changed|PositionChanged); + _last_position = _position; _position = val; } } else { + _last_position = _position; _position = 0; } @@ -1076,6 +1117,12 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) _stretch = 1.0; } + if ((prop = node.property ("shift")) != 0) { + _shift = atof (prop->value()); + } else { + _shift = 1.0; + } + /* note: derived classes set flags */ if (_extra_xml) { @@ -1128,6 +1175,8 @@ void Region::freeze () { _frozen++; + _last_length = _length; + _last_position = _position; } void @@ -1261,27 +1310,46 @@ Region::source_equivalent (boost::shared_ptr other) const bool Region::verify_length (nframes_t len) { + if (source() && source()->destructive()) { + return true; + } + + nframes_t maxlen = 0; + for (uint32_t n=0; n < _sources.size(); ++n) { - if (_start > _sources[n]->length() - len) { - return false; - } + maxlen = max (maxlen, _sources[n]->length() - _start); } + + len = min (len, maxlen); + return true; } bool -Region::verify_start_and_length (nframes_t new_start, nframes_t new_length) +Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length) { + if (source() && source()->destructive()) { + return true; + } + + nframes_t maxlen = 0; + for (uint32_t n=0; n < _sources.size(); ++n) { - if (new_length > _sources[n]->length() - new_start) { - return false; - } + maxlen = max (maxlen, _sources[n]->length() - new_start); } + + new_length = min (new_length, maxlen); + return true; } + bool Region::verify_start (nframes_t pos) { + if (source() && source()->destructive()) { + return true; + } + for (uint32_t n=0; n < _sources.size(); ++n) { if (pos > _sources[n]->length() - _length) { return false; @@ -1293,6 +1361,10 @@ Region::verify_start (nframes_t pos) bool Region::verify_start_mutable (nframes_t& new_start) { + if (source() && source()->destructive()) { + return true; + } + for (uint32_t n=0; n < _sources.size(); ++n) { if (new_start > _sources[n]->length() - _length) { new_start = _sources[n]->length() - _length; diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 8193d4ed77..084c4e58b5 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -80,10 +80,11 @@ Route::init () _muted = false; _soloed = false; _solo_safe = false; + _recordable = true; + _active = true; _phase_invert = false; _denormal_protection = false; order_keys[strdup (N_("signal"))] = order_key_cnt++; - _active = true; _silent = false; _meter_point = MeterPostFader; _initial_delay = 0; @@ -1662,10 +1663,14 @@ Route::add_processor_from_xml (const XMLNode& node) if ((prop = node.property ("type")) != 0) { boost::shared_ptr processor; + bool have_insert = false; - if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "vst") { - + if (prop->value() == "ladspa" || prop->value() == "Ladspa" || + prop->value() == "vst" || + prop->value() == "audiounit") { + processor.reset (new PluginInsert(_session, node)); + have_insert = true; } else if (prop->value() == "port") { @@ -1674,19 +1679,20 @@ Route::add_processor_from_xml (const XMLNode& node) } else if (prop->value() == "send") { processor.reset (new Send (_session, node)); + have_insert = true; } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; } - + add_processor (processor); } else { error << _("Processor XML node has no type property") << endmsg; } } - + catch (failed_constructor &err) { warning << _("processor could not be created. Ignored.") << endmsg; return; @@ -1732,7 +1738,8 @@ Route::_set_state (const XMLNode& node, bool call_base) if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (prop->value()=="yes"?true:false, this); } - + + _active = true; if ((prop = node.property (X_("active"))) != 0) { set_active (prop->value() == "yes"); } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index e155800d23..a7f85a5c84 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -97,6 +97,14 @@ static const int CPU_CACHE_ALIGN = 64; static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */ #endif +bool Session::_disable_all_loaded_plugins = false; + +Session::compute_peak_t Session::compute_peak = 0; +Session::find_peaks_t Session::find_peaks = 0; +Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0; +Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0; +Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0; + sigc::signal Session::AskAboutPendingState; sigc::signal Session::SendFeedback; @@ -105,9 +113,9 @@ sigc::signal Session::StartTimeChanged; sigc::signal Session::EndTimeChanged; Session::Session (AudioEngine &eng, - string fullpath, - string snapshot_name, - string* mix_template) + const string& fullpath, + const string& snapshot_name, + string mix_template) : _engine (eng), _scratch_buffers(new BufferSet()), @@ -127,62 +135,29 @@ Session::Session (AudioEngine &eng, _click_io ((IO*) 0), main_outs (0) { + bool new_session; + if (!eng.connected()) { throw failed_constructor(); } + + cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl; n_physical_outputs = _engine.n_physical_outputs(); n_physical_inputs = _engine.n_physical_inputs(); first_stage_init (fullpath, snapshot_name); - initialize_start_and_end_locations(0, compute_initial_length ()); - - if(mix_template) { - // try and create a new session directory - try - { - if(!_session_dir->create()) { - // an existing session. - // throw a_more_meaningful_exception() - destroy (); - throw failed_constructor (); - } - } - catch(sys::filesystem_error& ex) - { - destroy (); - throw failed_constructor (); - } - - if(!create_session_file_from_template (*mix_template)) { - destroy (); - throw failed_constructor (); - } - - cerr << "Creating session " << fullpath - <<" using template" << *mix_template - << endl; - } else { - // must be an existing session - try - { - // ensure the necessary session subdirectories exist - // in case the directory structure has changed etc. - _session_dir->create(); - } - catch(sys::filesystem_error& ex) - { + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + if (new_session) { + if (create (new_session, mix_template, compute_initial_length())) { + cerr << "create failed\n"; destroy (); throw failed_constructor (); } - - cerr << "Loading session " << fullpath - << " using snapshot " << snapshot_name << " (1)" - << endl; } - - if (second_stage_init (false)) { + + if (second_stage_init (new_session)) { destroy (); throw failed_constructor (); } @@ -228,6 +203,8 @@ Session::Session (AudioEngine &eng, main_outs (0) { + bool new_session; + if (!eng.connected()) { throw failed_constructor(); } @@ -247,11 +224,13 @@ Session::Session (AudioEngine &eng, first_stage_init (fullpath, snapshot_name); - initialize_start_and_end_locations(0, initial_length); - - if (!_session_dir->create () || !create_session_file ()) { - destroy (); - throw failed_constructor (); + new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)); + + if (new_session) { + if (create (new_session, string(), initial_length)) { + destroy (); + throw failed_constructor (); + } } { @@ -286,7 +265,7 @@ Session::Session (AudioEngine &eng, Config->set_input_auto_connect (input_ac); Config->set_output_auto_connect (output_ac); - if (second_stage_init (true)) { + if (second_stage_init (new_session)) { destroy (); throw failed_constructor (); } @@ -3273,7 +3252,7 @@ Session::midi_path_from_name (string name) return spath; } - + boost::shared_ptr Session::create_midi_source_for_session (MidiDiskstream& ds) { diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc index afb284b0f4..148ff92739 100644 --- a/libs/ardour/session_butler.cc +++ b/libs/ardour/session_butler.cc @@ -233,10 +233,6 @@ Session::butler_thread_work () } } - //for (i = diskstreams.begin(); i != diskstreams.end(); ++i) { - // cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl; - //} - if (transport_work_requested()) { butler_transport_work (); } @@ -249,10 +245,23 @@ Session::butler_thread_work () boost::shared_ptr dsl = diskstreams.reader (); +// for (i = dsl->begin(); i != dsl->end(); ++i) { +// cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl; +// } + for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) { boost::shared_ptr ds = *i; + /* don't read inactive tracks */ + + /*IO* io = ds->io(); + + if (ds->io() && !ds->io()->active()) { + cerr << "Skip inactive diskstream " << ds->io()->name() << endl; + continue; + }*/ + switch (ds->do_refill ()) { case 0: bytes += ds->read_data_count(); @@ -294,6 +303,9 @@ Session::butler_thread_work () for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) { // cerr << "write behind for " << (*i)->name () << endl; + + /* note that we still try to flush diskstreams attached to inactive routes + */ switch ((*i)->do_flush (Session::ButlerContext)) { case 0: diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index bf1d9f1a09..f1355b331b 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -41,6 +41,7 @@ static const char* event_names[] = { "SetDiskstreamSpeed", "Locate", "LocateRoll", + "LocateRollLocate", "SetLoop", "PunchIn", "PunchOut", @@ -351,6 +352,13 @@ Session::process_event (Event* ev) _send_smpte_update = true; break; + case Event::LocateRollLocate: + // locate is handled by ::request_roll_at_and_return() + _requested_return_frame = ev->target_frame; + set_transport_speed (ev->speed, true); + break; + + case Event::SetTransportSpeed: set_transport_speed (ev->speed, ev->yes_or_no); break; diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc index b0addd21f6..b1058f2e7e 100644 --- a/libs/ardour/session_export.cc +++ b/libs/ardour/session_export.cc @@ -428,6 +428,10 @@ AudioExportSpecification::process (nframes_t nframes) int Session::start_audio_export (AudioExportSpecification& spec) { + if (!_engine.connected()) { + return -1; + } + if (spec.prepare (current_block_size, frame_rate())) { return -1; } diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index 44b63e0875..6476ab30f7 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -52,6 +52,8 @@ Session::process (nframes_t nframes) { MIDI::Manager::instance()->cycle_start(nframes); + _silent = false; + if (synced_to_jack() && waiting_to_start) { if ( _engine.transport_state() == AudioEngine::TransportRolling) { actually_start_transport (); @@ -302,7 +304,7 @@ Session::process_with_events (nframes_t nframes) } if (!process_can_proceed()) { - no_roll (nframes, 0); + _silent = true; return; } @@ -317,8 +319,8 @@ Session::process_with_events (nframes_t nframes) Event* this_event; Events::iterator the_next_one; - if (post_transport_work & (PostTransportLocate|PostTransportStop)) { - no_roll (nframes, 0); + if (!process_can_proceed()) { + _silent = true; return; } @@ -494,7 +496,7 @@ Session::follow_slave (nframes_t nframes, nframes_t offset) << endl; #endif - if (Config->get_timecode_source_is_synced()) { + if (_slave->is_always_synced() || Config->get_timecode_source_is_synced()) { /* if the TC source is synced, then we assume that its speed is binary: 0.0 or 1.0 @@ -642,7 +644,7 @@ Session::follow_slave (nframes_t nframes, nframes_t offset) slave_state = Stopped; } - if (slave_state == Running && !Config->get_timecode_source_is_synced()) { + if (slave_state == Running && !_slave->is_always_synced() && !Config->get_timecode_source_is_synced()) { if (_transport_speed != 0.0f) { @@ -743,67 +745,64 @@ Session::process_without_events (nframes_t nframes) long frames_moved; nframes_t offset = 0; - { - if (post_transport_work & (PostTransportLocate|PostTransportStop)) { - no_roll (nframes, 0); - return; - } - - if (!_exporting && _slave) { - if (!follow_slave (nframes, 0)) { - return; - } - } + if (!process_can_proceed()) { + _silent = true; + return; + } - if (_transport_speed == 0) { - no_roll (nframes, 0); + if (!_exporting && _slave) { + if (!follow_slave (nframes, 0)) { return; } send_midi_time_code_for_cycle(nframes); + } + + if (_transport_speed == 0) { + no_roll (nframes, 0); + return; + } - if (actively_recording()) { - stop_limit = max_frames; + if (actively_recording()) { + stop_limit = max_frames; + } else { + if (Config->get_stop_at_session_end()) { + stop_limit = current_end_frame(); } else { - if (Config->get_stop_at_session_end()) { - stop_limit = current_end_frame(); - } else { - stop_limit = max_frames; - } + stop_limit = max_frames; } + } - if (maybe_stop (stop_limit)) { - no_roll (nframes, 0); - return; - } + if (maybe_stop (stop_limit)) { + no_roll (nframes, 0); + return; + } - if (maybe_sync_start (nframes, offset)) { - return; - } + if (maybe_sync_start (nframes, offset)) { + return; + } - click (_transport_frame, nframes, offset); + click (_transport_frame, nframes, offset); - prepare_diskstreams (); + prepare_diskstreams (); - frames_moved = (long) floor (_transport_speed * nframes); + frames_moved = (long) floor (_transport_speed * nframes); - if (process_routes (nframes, offset)) { - no_roll (nframes, offset); - return; - } - - commit_diskstreams (nframes, session_needs_butler); + if (process_routes (nframes, offset)) { + no_roll (nframes, offset); + return; + } - if (frames_moved < 0) { - decrement_transport_position (-frames_moved); - } else { - increment_transport_position (frames_moved); - } + commit_diskstreams (nframes, session_needs_butler); - maybe_stop (stop_limit); - check_declick_out (); + if (frames_moved < 0) { + decrement_transport_position (-frames_moved); + } else { + increment_transport_position (frames_moved); + } - } /* implicit release of route lock */ + maybe_stop (stop_limit); + check_declick_out (); if (session_needs_butler) summon_butler (); diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 877047d93a..fa4b103958 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -418,49 +418,106 @@ Session::setup_raid_path (string path) last_rr_session_dir = session_dirs.begin(); } -void -Session::initialize_start_and_end_locations (nframes_t start, nframes_t end) +int +Session::create (bool& new_session, const string& mix_template, nframes_t initial_length) { - start_location->set_end (start); - _locations.add (start_location); + string dir; - end_location->set_end (end); - _locations.add (end_location); -} + if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + return -1; + } -bool -Session::create_session_file () -{ - _state_of_the_state = Clean; + dir = session_directory().peak_path().to_string(); - if (save_state (_current_snapshot_name)) { - error << "Could not create new session file" << endmsg; - return false; + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - return true; -} -bool -Session::create_session_file_from_template (const string& template_path) -{ - sys::path session_file_path(_session_dir->root_path()); + dir = session_directory().sound_path().to_string(); - session_file_path /= _name + statefile_suffix; + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; + } + + dir = session_directory().midi_path().to_string(); - try - { - sys::copy_file (template_path, session_file_path); + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - catch(sys::filesystem_error& ex) - { - error << string_compose (_("Could not use session template %1 to create new session (%2)."), - template_path, ex.what()) - << endmsg; - return false; + + dir = session_directory().dead_sound_path().to_string(); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - return true; + + dir = session_directory().export_path().to_string(); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; + } + + + /* check new_session so we don't overwrite an existing one */ + + if (!mix_template.empty()) { + std::string in_path = mix_template; + + ifstream in(in_path.c_str()); + + if (in){ + string out_path = _path; + out_path += _name; + out_path += statefile_suffix; + + ofstream out(out_path.c_str()); + + if (out){ + out << in.rdbuf(); + + // okay, session is set up. Treat like normal saved + // session from now on. + + new_session = false; + return 0; + + } else { + error << string_compose (_("Could not open %1 for writing mix template"), out_path) + << endmsg; + return -1; + } + + } else { + error << string_compose (_("Could not open mix template %1 for reading"), in_path) + << endmsg; + return -1; + } + + } + + /* set initial start + end point */ + + start_location->set_end (0); + _locations.add (start_location); + + end_location->set_end (initial_length); + _locations.add (end_location); + + _state_of_the_state = Clean; + + + save_state (""); + + return 0; } + int Session::load_diskstreams (const XMLNode& node) { @@ -2767,7 +2824,7 @@ Session::restore_history (string snapshot_name) const string xml_filename = snapshot_name + history_suffix; const sys::path xml_path = _session_dir->root_path() / xml_filename; - info << string_compose(_("Loading history from '%1'."), xml_path.to_string()) << endmsg; + cerr << "Loading history from " << xml_path.to_string() << endmsg; if (!sys::exists (xml_path)) { info << string_compose (_("%1: no history file \"%2\" for this session."), diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 2776fbf41d..1398872b36 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -392,17 +393,35 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) update_latency_compensation (true, abort); } - if ((Config->get_slave_source() == None && Config->get_auto_return()) || (post_transport_work & PostTransportLocate) || synced_to_jack()) { + if ((Config->get_slave_source() == None && Config->get_auto_return()) || + (post_transport_work & PostTransportLocate) || + (_requested_return_frame >= 0) || + synced_to_jack()) { if (pending_locate_flush) { flush_all_inserts (); } - if (((Config->get_slave_source() == None && Config->get_auto_return()) || synced_to_jack()) && !(post_transport_work & PostTransportLocate)) { + if (((Config->get_slave_source() == None && Config->get_auto_return()) || + synced_to_jack() || + _requested_return_frame >= 0) && + !(post_transport_work & PostTransportLocate)) { - _transport_frame = last_stop_frame; + bool do_locate = false; + + if (_requested_return_frame >= 0) { + _transport_frame = _requested_return_frame; + _requested_return_frame = -1; + do_locate = true; + } else { + _transport_frame = last_stop_frame; + } if (synced_to_jack() && !play_loop) { + do_locate = true; + } + + if (do_locate) { // cerr << "non-realtimestop: transport locate to " << _transport_frame << endl; _engine.transport_locate (_transport_frame); } @@ -478,7 +497,9 @@ Session::non_realtime_stop (bool abort, int on_entry, bool& finished) } - PositionChanged (_transport_frame); /* EMIT SIGNAL */ + nframes_t tf = _transport_frame; + + PositionChanged (tf); /* EMIT SIGNAL */ TransportStateChange (); /* EMIT SIGNAL */ /* and start it up again if relevant */ @@ -1202,6 +1223,14 @@ Session::setup_auto_play () merge_event (ev); } +void +Session::request_roll_at_and_return (nframes_t start, nframes_t return_to) +{ + request_locate (start, false); + Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0); + queue_event (ev); +} + void Session::request_bounded_roll (nframes_t start, nframes_t end) { diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index b8d82bdb9d..29f36cc2cd 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -345,7 +345,8 @@ SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, n // FIXME: assumes tempo never changes after start const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat( - _session.engine().frame_rate()); + _session.engine().frame_rate(), + _session.tempo_map().meter_at(_timeline_position)); const uint64_t start_ticks = (uint64_t)((start / frames_per_beat) * _ppqn); @@ -456,8 +457,9 @@ SMFSource::append_event_unlocked(const MidiEvent& ev) assert(ev.time() >= _last_ev_time); // FIXME: assumes tempo never changes after start - const double frames_per_beat = _session.tempo_map().tempo_at - (_timeline_position).frames_per_beat(_session.engine().frame_rate()); + const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat( + _session.engine().frame_rate(), + _session.tempo_map().meter_at(_timeline_position)); const uint32_t delta_time = (uint32_t)((ev.time() - _last_ev_time) / frames_per_beat * _ppqn); @@ -888,7 +890,8 @@ SMFSource::load_model(bool lock, bool force_reload) // FIXME: assumes tempo never changes after start const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat( - _session.engine().frame_rate()); + _session.engine().frame_rate(), + _session.tempo_map().meter_at(_timeline_position)); uint32_t delta_t = 0; int ret; diff --git a/libs/ardour/sndfile_helpers.cc b/libs/ardour/sndfile_helpers.cc index 96dc2c7779..58a51f8bbe 100644 --- a/libs/ardour/sndfile_helpers.cc +++ b/libs/ardour/sndfile_helpers.cc @@ -33,33 +33,27 @@ using namespace std; const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1] = { N_("WAV"), N_("AIFF"), - N_("raw (no header)"), - N_("PAF (Ensoniq Paris)"), - N_("AU (Sun/NeXT)"), - N_("IRCAM"), + N_("CAF"), N_("W64 (64 bit WAV)"), + N_("raw (no header)"), 0 }; const char* const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1] = { N_(".wav"), N_(".aiff"), - N_(".raw"), - N_(".paf"), - N_(".au"), - N_(".ircam"), + N_(".caf"), N_(".w64"), + N_(".raw"), 0 }; int sndfile_header_formats[SNDFILE_HEADER_FORMATS] = { SF_FORMAT_WAV, SF_FORMAT_AIFF, - SF_FORMAT_RAW, - SF_FORMAT_PAF, - SF_FORMAT_AU, - SF_FORMAT_IRCAM, - SF_FORMAT_W64 + SF_FORMAT_CAF, + SF_FORMAT_W64, + SF_FORMAT_RAW }; const char * const sndfile_bitdepth_formats_strings[SNDFILE_BITDEPTH_FORMATS+1] = { diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 89d4e3ed79..661beef40b 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -60,7 +60,7 @@ peak_thread_work () if (SourceFactory::files_with_peaks.empty()) { goto wait; } - + boost::shared_ptr as (SourceFactory::files_with_peaks.front().lock()); SourceFactory::files_with_peaks.pop_front (); SourceFactory::peak_building_lock.unlock (); @@ -68,7 +68,6 @@ peak_thread_work () if (!as) { continue; } - as->setup_peakfile (); } } diff --git a/libs/ardour/st_pitch.cc b/libs/ardour/st_pitch.cc new file mode 100644 index 0000000000..3999c1a746 --- /dev/null +++ b/libs/ardour/st_pitch.cc @@ -0,0 +1,52 @@ +/* + 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 +#include + +#include + +#include +#include +#include +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +Pitch::Pitch (Session& s, TimeFXRequest& req) + : Filter (s) + , tsr (req) + +{ + tsr.progress = 0.0f; +} + +int +Pitch::run (boost::shared_ptr region) +{ + tsr.progress = 1.0f; + tsr.done = true; + + return 1; +} diff --git a/libs/ardour/st_stretch.cc b/libs/ardour/st_stretch.cc new file mode 100644 index 0000000000..e96cd79f2d --- /dev/null +++ b/libs/ardour/st_stretch.cc @@ -0,0 +1,215 @@ +/* + 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 +#include + +#include + +#include +#include +#include +#include +#include + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; +using namespace soundtouch; + +Stretch::Stretch (Session& s, TimeFXRequest& 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.time_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 a_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; + + boost::shared_ptr region = boost::dynamic_pointer_cast(a_region); + + 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.time_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 asrc + = boost::dynamic_pointer_cast(nsrcs[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"), asrc->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 (asrc->write (buffer, this_read) != this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), asrc->name()) << endmsg; + goto out; + } + } + } + + if (!tsr.cancel) { + st.flush (); + } + + while (!tsr.cancel && (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"), asrc->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 >::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.time_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, (*x)->shift()); + } + + 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; +} diff --git a/libs/ardour/stretch.cc b/libs/ardour/stretch.cc deleted file mode 100644 index 64b741d1af..0000000000 --- a/libs/ardour/stretch.cc +++ /dev/null @@ -1,227 +0,0 @@ -/* - 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 -#include - -#include - -#include -#include -#include -#include -#include - -#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 r) -{ - boost::shared_ptr region = boost::dynamic_pointer_cast (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 afs = boost::dynamic_pointer_cast (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 >::iterator x = results.begin(); x != results.end(); ++x) { - - boost::shared_ptr region = boost::dynamic_pointer_cast (*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; -} diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index cd59e93054..780f5c6a5d 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -43,12 +43,17 @@ Tempo TempoMap::_default_tempo (120.0); const double Meter::ticks_per_beat = 1920.0; +double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const +{ + return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type)); +} + /***********************************************************************/ double Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const { - return ((60.0 * sr * _beats_per_bar) / tempo.beats_per_minute()); + return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type())); } /***********************************************************************/ @@ -86,6 +91,16 @@ TempoSection::TempoSection (const XMLNode& node) error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg; throw failed_constructor(); } + + if ((prop = node.property ("note-type")) == 0) { + /* older session, make note type be quarter by default */ + _note_type = 4.0; + } else { + if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) { + error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg; + throw failed_constructor(); + } + } if ((prop = node.property ("movable")) == 0) { error << _("TempoSection XML node has no \"movable\" property") << endmsg; @@ -109,6 +124,8 @@ TempoSection::get_state() const root->add_property ("start", buf); snprintf (buf, sizeof (buf), "%f", _beats_per_minute); root->add_property ("beats-per-minute", buf); + snprintf (buf, sizeof (buf), "%f", _note_type); + root->add_property ("note-type", buf); snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no"); root->add_property ("movable", buf); @@ -210,7 +227,7 @@ TempoMap::TempoMap (nframes_t fr) start.beats = 1; start.ticks = 0; - TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute()); + TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type()); MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor()); t->set_movable (false); @@ -359,7 +376,7 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where) where.ticks = 0; - do_insert (new TempoSection (where, tempo.beats_per_minute())); + do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type())); } StateChanged (Change (0)); @@ -614,7 +631,7 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me const double beats_per_bar = metric.meter().beats_per_bar(); const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate); - const double beat_frames = metric.tempo().frames_per_beat (_frame_rate); + const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter()); /* now compute how far beyond that point we actually are. */ @@ -667,7 +684,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con + start.ticks/Meter::ticks_per_beat; - start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate)); + start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); m = metric_at(end); @@ -676,7 +693,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) + end.ticks/Meter::ticks_per_beat; - end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate)); + end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter())); frames = end_frame - start_frame; @@ -697,7 +714,7 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, double beat_frames = 0; beats_per_bar = meter.beats_per_bar(); - beat_frames = tempo.frames_per_beat (_frame_rate); + beat_frames = tempo.frames_per_beat (_frame_rate,meter); frames = 0; @@ -1088,7 +1105,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beats_per_bar = meter->beats_per_bar (); frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = tempo->frames_per_beat (_frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate, *meter); if (meter->frame() > tempo->frame()) { bar = meter->start().bars; @@ -1198,7 +1215,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const beats_per_bar = meter->beats_per_bar (); frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate); - beat_frames = tempo->frames_per_beat (_frame_rate); + beat_frames = tempo->frames_per_beat (_frame_rate, *meter); ++i; } @@ -1304,7 +1321,7 @@ TempoMap::dump (std::ostream& o) const for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) { if ((t = dynamic_cast(*i)) != 0) { - o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? " + o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? " << t->movable() << ')' << endl; } else if ((m = dynamic_cast(*i)) != 0) { o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc index ac0e6849f9..5969d91982 100644 --- a/libs/ardour/vst_plugin.cc +++ b/libs/ardour/vst_plugin.cc @@ -142,7 +142,7 @@ VSTPlugin::get_state() { XMLNode *root = new XMLNode (state_node_name()); LocaleGuard lg (X_("POSIX")); - + if (_plugin->flags & effFlagsProgramChunks) { /* fetch the current chunk */ @@ -418,10 +418,12 @@ VSTPlugin::activate () _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f); } -uint32_t +string VSTPlugin::unique_id() const { - return _plugin->uniqueID; + char buf[32]; + snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID); + return string (buf); } -- cgit v1.2.3