diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
commit | f7f9d6fdc40248b190ec9c6e1a886261d55777ae (patch) | |
tree | 080723e9dc35a66013b37acbafc67a6afa929302 /libs | |
parent | aa1f736a651376534acaa2268b65d42a3786fff7 (diff) |
merge from 2.0-ongoing by hand, minus key binding editor
git-svn-id: svn://localhost/ardour2/trunk@2539 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
74 files changed, 1792 insertions, 1035 deletions
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 3892af6e9b..465138d90d 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -12,7 +12,7 @@ ardour = env.Copy() # this defines the version number of libardour # -domain = 'libardour' +domain = 'libardour2' ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0) ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") @@ -101,6 +101,7 @@ recent_sessions.cc region.cc region_factory.cc reverse.cc +resampled_source.cc quantize.cc route.cc route_group.cc diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h index 1b9725a04c..6e7b494441 100644 --- a/libs/ardour/ardour/ardour.h +++ b/libs/ardour/ardour/ardour.h @@ -47,8 +47,6 @@ namespace ARDOUR { int init (bool with_vst, bool try_optimization); int cleanup (); - int setup_midi(AudioEngine& engine); - std::string get_ardour_revision (); microseconds_t get_microseconds (); diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index 78d10f9d64..4d80c8ddf5 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -61,7 +61,8 @@ class AudioFileSource : public AudioSource { Glib::ustring path() const { return _path; } Glib::ustring peak_path (Glib::ustring audio_path); - Glib::ustring old_peak_path (Glib::ustring audio_path); + Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path, + Glib::ustring audio_path); uint16_t channel() const { return _channel; } @@ -122,7 +123,7 @@ class AudioFileSource : public AudioSource { to cause issues. */ - virtual void handle_header_position_change (); + virtual void handle_header_position_change () {} protected: @@ -166,6 +167,10 @@ class AudioFileSource : public AudioSource { bool find (Glib::ustring& path, bool must_exist, bool& is_new, uint16_t& chan); bool removable() const; bool writable() const { return _flags & Writable; } + + private: + Glib::ustring old_peak_path (Glib::ustring audio_path); + Glib::ustring broken_peak_path (Glib::ustring audio_path); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 812c30e8c2..7b22528bd1 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -40,7 +40,6 @@ using std::list; using std::vector; -using Glib::ustring; namespace ARDOUR { @@ -49,10 +48,23 @@ const nframes_t frames_per_peak = 256; class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR::AudioSource> { public: - AudioSource (Session&, ustring name); + AudioSource (Session&, Glib::ustring name); AudioSource (Session&, const XMLNode&); virtual ~AudioSource (); + + /* one could argue that this should belong to Source, but other data types + generally do not come with a model of "offset along an audio timeline" + so its here in AudioSource for now. + */ + + virtual nframes_t natural_position() const { return 0; } + /* returns the number of items in this `audio_source' */ + + virtual nframes_t length() const { + return _length; + } + virtual nframes_t available_peaks (double zoom) const; virtual nframes_t read (Sample *dst, nframes_t start, nframes_t cnt) const; @@ -65,8 +77,8 @@ const nframes_t frames_per_peak = 256; virtual bool can_truncate_peaks() const { return true; } - void set_captured_for (ustring str) { _captured_for = str; } - ustring captured_for() const { return _captured_for; } + void set_captured_for (Glib::ustring str) { _captured_for = str; } + Glib::ustring captured_for() const { return _captured_for; } uint32_t read_data_count() const { return _read_data_count; } uint32_t write_data_count() const { return _write_data_count; } @@ -81,7 +93,7 @@ const nframes_t frames_per_peak = 256; XMLNode& get_state (); int set_state (const XMLNode&); - int rename_peakfile (ustring newpath); + int rename_peakfile (Glib::ustring newpath); void touch_peakfile (); static void set_build_missing_peakfiles (bool yn) { @@ -92,35 +104,43 @@ const nframes_t frames_per_peak = 256; _build_peakfiles = yn; } + static bool get_build_peakfiles () { + return _build_peakfiles; + } + virtual int setup_peakfile () { return 0; } int prepare_for_peakfile_writes (); - void done_with_peakfile_writes (); + void done_with_peakfile_writes (bool done = true); protected: static bool _build_missing_peakfiles; static bool _build_peakfiles; - bool _peaks_built; - mutable Glib::Mutex _lock; - ustring peakpath; - ustring _captured_for; + bool _peaks_built; + mutable Glib::Mutex _lock; + mutable Glib::Mutex _peaks_ready_lock; + nframes_t _length; + Glib::ustring peakpath; + Glib::ustring _captured_for; mutable uint32_t _read_data_count; // modified in read() mutable uint32_t _write_data_count; // modified in write() - int initialize_peakfile (bool newfile, ustring path); + int initialize_peakfile (bool newfile, Glib::ustring path); int build_peaks_from_scratch (); - int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force); + int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready_signal); void truncate_peakfile(); mutable off_t _peak_byte_max; // modified in compute_and_write_peak() virtual nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const = 0; virtual nframes_t write_unlocked (Sample *dst, nframes_t cnt) = 0; - virtual ustring peak_path(ustring audio_path) = 0; - virtual ustring old_peak_path(ustring audio_path) = 0; + virtual Glib::ustring peak_path(Glib::ustring audio_path) = 0; + virtual Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path, Glib::ustring audio_path) = 0; + void update_length (nframes_t pos, nframes_t cnt); + private: int peakfile; nframes_t peak_leftover_cnt; @@ -128,7 +148,7 @@ const nframes_t frames_per_peak = 256; Sample* peak_leftovers; nframes_t peak_leftover_frame; - bool file_changed (ustring path); + bool file_changed (Glib::ustring path); }; } diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index fe47614a1f..f96ecc0bd1 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -83,6 +83,14 @@ public: Glib::Mutex& automation_lock() const { return _automation_lock; } + static void set_automation_interval (jack_nframes_t frames) { + _automation_interval = frames; + } + + static jack_nframes_t automation_interval() { + return _automation_interval; + } + protected: void can_automate(Parameter); @@ -102,6 +110,7 @@ protected: std::set<Parameter> _can_automate_list; nframes_t _last_automation_snapshot; + static nframes_t _automation_interval; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/configuration.h b/libs/ardour/ardour/configuration.h index 70b7e166c1..7b890500d8 100644 --- a/libs/ardour/ardour/configuration.h +++ b/libs/ardour/ardour/configuration.h @@ -42,17 +42,7 @@ class Configuration : public PBD::Stateful Configuration(); virtual ~Configuration(); - struct MidiPortDescriptor { - std::string tag; - std::string device; - std::string type; - std::string mode; - - MidiPortDescriptor (const XMLNode&); - XMLNode& get_state(); - }; - - std::map<std::string,MidiPortDescriptor *> midi_ports; + std::map<std::string,XMLNode> midi_ports; void map_parameters (sigc::slot<void,const char*> theSlot); diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index 4d5579a9a0..b592a9f721 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -138,11 +138,15 @@ CONFIG_VARIABLE (bool, verify_remove_last_capture, "verify-remove-last-capture", CONFIG_VARIABLE (bool, no_new_session_dialog, "no-new-session-dialog", false) CONFIG_VARIABLE (bool, use_vst, "use-vst", true) CONFIG_VARIABLE (uint32_t, subframes_per_frame, "subframes-per-frame", 100) -CONFIG_VARIABLE (uint32_t, saved_history_depth, "save-history-depth", 100) +CONFIG_VARIABLE (bool, save_history, "save-history", true) +CONFIG_VARIABLE (int32_t, saved_history_depth, "save-history-depth", 20) +CONFIG_VARIABLE (int32_t, history_depth, "history-depth", 20) CONFIG_VARIABLE (bool, use_overlap_equivalency, "use-overlap-equivalency", false) CONFIG_VARIABLE (bool, periodic_safety_backups, "periodic-safety-backups", true) CONFIG_VARIABLE (uint32_t, periodic_safety_backup_interval, "periodic-safety-backup-interval", 120) -CONFIG_VARIABLE (string, possible_audio_file_regexp, "possible-audio-file-regexp", "\\.(wav|aiff|caf|w64|L|R)$") +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) /* denormal management */ diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 6c040be63e..6e68c01d8c 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -219,7 +219,7 @@ class IO : public Automatable, public Latent public: /* automation */ - + struct GainControl : public AutomationControl { GainControl (std::string name, IO& i, boost::shared_ptr<AutomationList> al) : AutomationControl (i._session, al, name) diff --git a/libs/ardour/ardour/profile.h b/libs/ardour/ardour/profile.h index 3347447915..b016063c4d 100644 --- a/libs/ardour/ardour/profile.h +++ b/libs/ardour/ardour/profile.h @@ -29,6 +29,8 @@ class RuntimeProfile { public: enum Element { SmallScreen, + SAE, + SinglePackage, LastElement }; @@ -38,6 +40,12 @@ class RuntimeProfile { void set_small_screen() { bits[SmallScreen] = true; } bool get_small_screen() const { return bits[SmallScreen]; } + void set_sae () { bits[SAE] = true; } + bool get_sae () const { return bits[SAE]; } + + void set_single_package () { bits[SinglePackage] = true; } + bool get_single_package () const { return bits[SinglePackage]; } + private: boost::dynamic_bitset<uint64_t> bits; diff --git a/libs/ardour/ardour/resampled_source.h b/libs/ardour/ardour/resampled_source.h new file mode 100644 index 0000000000..9a88ca9644 --- /dev/null +++ b/libs/ardour/ardour/resampled_source.h @@ -0,0 +1,50 @@ +/* + 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_resampled_source_h__ +#define __ardour_resampled_source_h__ + +#include <samplerate.h> + +#include <ardour/types.h> +#include <ardour/importable_source.h> + +namespace ARDOUR { + +class ResampledImportableSource : public ImportableSource +{ + public: + ResampledImportableSource (SNDFILE* sf, SF_INFO* info, nframes_t rate, SrcQuality); + ~ResampledImportableSource (); + + nframes_t read (Sample* buffer, nframes_t nframes); + + float ratio() const { return src_data.src_ratio; } + + static const uint32_t blocksize; + + private: + float* input; + SRC_STATE* src_state; + SRC_DATA src_data; +}; + +} + +#endif /* __ardour_resampled_source_h__ */ diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 1fd6eff0f8..fc17af06ee 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -258,6 +258,9 @@ class Route : public IO uint32_t remote_control_id () const; sigc::signal<void> RemoteControlIDChanged; + void sync_order_keys (); + static sigc::signal<void> SyncOrderKeys; + protected: friend class Session; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 94caf8a242..bbcae6e91d 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -261,6 +261,8 @@ class Session : public PBD::StatefulDestructible std::string automation_dir () const; + Glib::ustring peak_path (Glib::ustring) const; + static string change_audio_path_by_name (string oldpath, string oldname, string newname, bool destructive); static string change_midi_path_by_name (string oldpath, string oldname, string newname, bool destructive); @@ -422,6 +424,7 @@ class Session : public PBD::StatefulDestructible int restore_history (string snapshot_name); void remove_state (string snapshot_name); void rename_state (string old_name, string new_name); + void remove_pending_capture_state (); sigc::signal<void,string> StateSaved; sigc::signal<void> StateReady; @@ -568,13 +571,14 @@ class Session : public PBD::StatefulDestructible string doing_what; /* control info */ - bool multichan; bool sample_convert; + SrcQuality quality; volatile bool freeze; std::vector<Glib::ustring> paths; /* result */ - std::vector<boost::shared_ptr<Region> > new_regions; + SourceList sources; + }; int import_audiofile (import_status&); @@ -650,8 +654,6 @@ class Session : public PBD::StatefulDestructible void add_curve(Curve*); void add_automation_list(AutomationList*); - nframes_t automation_interval () const { return _automation_interval; } - /* fade curves */ float get_default_fade_length () const { return default_fade_msecs; } @@ -1650,8 +1652,6 @@ class Session : public PBD::StatefulDestructible void allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force); uint32_t _npan_buffers; - nframes_t _automation_interval; - /* VST support */ long _vst_callback (VSTPlugin*, @@ -1668,7 +1668,6 @@ class Session : public PBD::StatefulDestructible uint32_t n_physical_outputs; uint32_t n_physical_inputs; - void remove_pending_capture_state (); int find_all_sources (std::string path, std::set<std::string>& result); int find_all_sources_across_snapshots (std::set<std::string>& result, bool exclude_this_snapshot); @@ -1688,6 +1687,8 @@ class Session : public PBD::StatefulDestructible XMLNode& get_control_protocol_state (); + void set_history_depth (uint32_t depth); + void sync_order_keys (); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 916e9da49e..4fd71a4c96 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -76,9 +76,6 @@ class SndFileSource : public AudioFileSource { SF_INFO _info; SF_BROADCAST_INFO *_broadcast_info; - mutable float *interleave_buf; - mutable nframes_t interleave_bufsize; - void init (); int open(); void close(); @@ -105,6 +102,7 @@ class SndFileSource : public AudioFileSource { void handle_header_position_change (); static int64_t get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists); + static Sample* get_interleave_buffer (nframes_t size); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index fb591216bf..01f50126a4 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -36,16 +36,23 @@ class Session; class SourceFactory { public: + static void init (); + static sigc::signal<void,boost::shared_ptr<Source> > SourceCreated; - static boost::shared_ptr<Source> create (Session&, const XMLNode& node); + static boost::shared_ptr<Source> create (Session&, const XMLNode& node, bool async = false); static boost::shared_ptr<Source> createSilent (Session&, const XMLNode& node, nframes_t nframes, float sample_rate); - static boost::shared_ptr<Source> createReadable (DataType type, Session&, std::string path, int chn, AudioFileSource::Flag flags, bool announce = true); - static boost::shared_ptr<Source> createWritable (DataType type, Session&, std::string name, bool destructive, nframes_t rate, bool announce = true); + static boost::shared_ptr<Source> createReadable (DataType type, Session&, std::string path, int chn, AudioFileSource::Flag flags, + bool announce = true, bool async = false); + static boost::shared_ptr<Source> createWritable (DataType type, Session&, std::string name, bool destructive, nframes_t rate, + bool announce = true, bool async = true); + + static Glib::Cond* PeaksToBuild; + static Glib::StaticMutex peak_building_lock; + static std::list<boost::weak_ptr<AudioSource> > files_with_peaks; - private: - static int setup_peakfile (boost::shared_ptr<Source>); + static int setup_peakfile (boost::shared_ptr<Source>, bool async); }; } diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index e13bd09d83..5b50713313 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -370,6 +370,15 @@ namespace ARDOUR { }; typedef std::vector<boost::shared_ptr<Source> > SourceList; + + enum SrcQuality { + SrcBest, + SrcGood, + SrcQuick, + SrcFast, + SrcFastest + }; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index cde17d8b49..e52274eb1f 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -50,7 +50,7 @@ int cmp_nocase (const std::string& s, const std::string& s2); int touch_file(Glib::ustring path); Glib::ustring path_expand (Glib::ustring); -Glib::ustring region_name_from_path (Glib::ustring path, bool strip_channels); +Glib::ustring region_name_from_path (Glib::ustring path, bool strip_channels, bool add_channel_suffix = false, uint32_t total = 0, uint32_t this_one = 0); bool path_is_paired (Glib::ustring path, Glib::ustring& pair_base); void compute_equal_power_fades (nframes_t nframes, float* in, float* out); diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index d28d88488e..d5c6120946 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -379,7 +379,9 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh) bottom = region; } - + if (!top->opaque()) { + continue; + } OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index a4ce5f291d..2a4b36f7d8 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -156,7 +156,7 @@ AudioEngine::start () _has_run = true; Running(); /* EMIT SIGNAL */ } else { - error << _("cannot activate JACK client") << endmsg; + // error << _("cannot activate JACK client") << endmsg; } } @@ -1078,7 +1078,7 @@ AudioEngine::connect_to_jack (string client_name) error << _("Unable to connect to JACK server") << endmsg; } - error << string_compose (_("Could not connect to JACK server as \"%1\""), jack_client_name) << endmsg; + // error message is not useful here return -1; } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 1044dd10d2..1284dd343b 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -21,10 +21,13 @@ #include <sys/time.h> #include <sys/stat.h> +#include <stdio.h> // for rename(), sigh #include <unistd.h> #include <fcntl.h> #include <errno.h> +#include <pbd/convert.h> +#include <pbd/basename.h> #include <pbd/mountpoint.h> #include <pbd/stl_delete.h> #include <pbd/strsplit.h> @@ -40,6 +43,7 @@ #include <ardour/sndfile_helpers.h> #include <ardour/sndfilesource.h> #include <ardour/session.h> +#include <ardour/session_directory.h> #include <ardour/source_factory.h> #include <ardour/filename_extensions.h> @@ -153,7 +157,60 @@ AudioFileSource::init (ustring pathstr, bool must_exist) ustring AudioFileSource::peak_path (ustring audio_path) { - return _session.peak_path_from_audio_path (audio_path); + ustring base; + + base = PBD::basename_nosuffix (audio_path); + base += '%'; + base += (char) ('A' + _channel); + + return _session.peak_path (base); +} + +ustring +AudioFileSource::find_broken_peakfile (ustring peak_path, ustring audio_path) +{ + ustring str; + + /* check for the broken location in use by 2.0 for several months */ + + str = broken_peak_path (audio_path); + + if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) { + + if (is_embedded()) { + + /* it would be nice to rename it but the nature of + the bug means that we can't reliably use it. + */ + + peak_path = str; + + } else { + /* all native files are mono, so we can just rename + it. + */ + ::rename (str.c_str(), peak_path.c_str()); + } + + } else { + /* Nasty band-aid for older sessions that were created before we + used libsndfile for all audio files. + */ + + + str = old_peak_path (audio_path); + if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) { + peak_path = str; + } + } + + return peak_path; +} + +ustring +AudioFileSource::broken_peak_path (ustring audio_path) +{ + return _session.peak_path (audio_path); } ustring @@ -171,9 +228,9 @@ AudioFileSource::old_peak_path (ustring audio_path) char buf[32]; #ifdef __APPLE__ - snprintf (buf, sizeof (buf), "%u-%u-%d", stat_mount.st_ino, stat_file.st_ino, _channel); + snprintf (buf, sizeof (buf), "%u-%u-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel); #else - snprintf (buf, sizeof (buf), "%ld-%ld-%d", stat_mount.st_ino, stat_file.st_ino, _channel); + snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel); #endif ustring res = peak_dir; @@ -227,7 +284,7 @@ AudioFileSource::set_state (const XMLNode& node) } if ((prop = node.property (X_("channel"))) != 0) { - _channel = atoi (prop->value().c_str()); + _channel = atoi (prop->value()); } else { _channel = 0; } @@ -265,6 +322,10 @@ AudioFileSource::mark_streaming_write_completed () if (!writable()) { return; } + + /* XXX notice that we're readers of _peaks_built + but we must hold a solid lock on PeaksReady. + */ Glib::Mutex::Lock lm (_lock); @@ -432,7 +493,7 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& fullpath += shorter; if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { - chan = atoi (pathstr.substr (pos+1).c_str()); + chan = atoi (pathstr.substr (pos+1)); pathstr = shorter; keeppath = fullpath; ++cnt; @@ -484,7 +545,7 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& ustring shorter = pathstr.substr (0, pos); if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { - chan = atoi (pathstr.substr (pos+1).c_str()); + chan = atoi (pathstr.substr (pos+1)); pathstr = shorter; } } @@ -542,15 +603,6 @@ AudioFileSource::set_header_position_offset (nframes_t offset) HeaderPositionOffsetChanged (); } -void -AudioFileSource::handle_header_position_change () -{ - if (writable()) { - set_header_timeline_position (); - flush_header (); - } -} - void AudioFileSource::set_timeline_position (int64_t pos) { @@ -603,15 +655,15 @@ AudioFileSource::set_source_name (ustring newname, bool destructive) bool AudioFileSource::is_empty (Session& s, ustring path) { - bool ret = false; + SoundFileInfo info; + string err; - boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, s, path, 0, NoPeakFile, false)); - - if (afs) { - ret = (afs->length() == 0); + if (!get_soundfile_info (path, info, err)) { + /* dangerous: we can't get info, so assume that its not empty */ + return false; } - return ret; + return info.length == 0; } int diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index d09a9b29cd..a2ce7209f6 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <poll.h> #include <float.h> #include <utime.h> #include <cerrno> @@ -29,18 +30,21 @@ #include <algorithm> #include <vector> +#include <glibmm/fileutils.h> + #include <pbd/xml++.h> #include <pbd/pthread_utils.h> #include <ardour/audiosource.h> #include <ardour/cycle_timer.h> -#include <ardour/runtime_functions.h> +#include <ardour/session.h> #include "i18n.h" using namespace std; using namespace ARDOUR; using namespace PBD; +using Glib::ustring; bool AudioSource::_build_missing_peakfiles = false; bool AudioSource::_build_peakfiles = false; @@ -126,12 +130,12 @@ bool AudioSource::peaks_ready (sigc::slot<void> the_slot, sigc::connection& conn) const { bool ret; - Glib::Mutex::Lock lm (_lock); + Glib::Mutex::Lock lm (_peaks_ready_lock); /* 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); } @@ -182,15 +186,10 @@ AudioSource::initialize_peakfile (bool newfile, ustring audio_path) peakpath = peak_path (audio_path); - /* Nasty band-aid for older sessions that were created before we - used libsndfile for all audio files. - */ + /* if the peak file should be there, but isn't .... */ - if (!newfile && access (peakpath.c_str(), R_OK) != 0) { - ustring str = old_peak_path (audio_path); - if (access (str.c_str(), R_OK) == 0) { - peakpath = str; - } + if (!newfile && !Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) { + peakpath = find_broken_peakfile (peakpath, audio_path); } if (newfile) { @@ -332,7 +331,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* open, read, close */ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (a) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return -1; } @@ -406,7 +405,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* open ... close during out: handling */ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (b) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return 0; } @@ -581,9 +580,11 @@ AudioSource::build_peaks_from_scratch () { nframes_t current_frame; nframes_t cnt; - Sample buf[frames_per_peak]; + Sample* buf = 0; nframes_t frames_read; nframes_t frames_to_read; + const nframes_t bufsize = 65536; // 256kB per disk read for mono data is about ideal + int ret = -1; { @@ -598,39 +599,41 @@ AudioSource::build_peaks_from_scratch () current_frame = 0; cnt = _length; _peaks_built = false; + buf = new Sample[bufsize]; while (cnt) { - frames_to_read = min (frames_per_peak, cnt); + frames_to_read = min (bufsize, cnt); if ((frames_read = read_unlocked (buf, current_frame, frames_to_read)) != frames_to_read) { error << string_compose(_("%1: could not write read raw data for peak computation (%2)"), _name, strerror (errno)) << endmsg; - done_with_peakfile_writes (); + done_with_peakfile_writes (false); goto out; } - if (compute_and_write_peaks (buf, current_frame, frames_read, true)) { + if (compute_and_write_peaks (buf, current_frame, frames_read, true, false)) { break; } current_frame += frames_read; cnt -= frames_read; } - + if (cnt == 0) { /* success */ truncate_peakfile(); - _peaks_built = true; } - done_with_peakfile_writes (); + done_with_peakfile_writes ((cnt == 0)); } - - /* lock no longer held, safe to signal */ - - if (_peaks_built) { - PeaksReady (); /* EMIT SIGNAL */ - ret = 0; + + { + Glib::Mutex::Lock lm (_peaks_ready_lock); + + if (_peaks_built) { + PeaksReady (); /* EMIT SIGNAL */ + ret = 0; + } } out: @@ -638,6 +641,10 @@ AudioSource::build_peaks_from_scratch () unlink (peakpath.c_str()); } + if (buf) { + delete [] buf; + } + return ret; } @@ -645,17 +652,21 @@ int AudioSource::prepare_for_peakfile_writes () { if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (c) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return -1; } return 0; } void -AudioSource::done_with_peakfile_writes () +AudioSource::done_with_peakfile_writes (bool done) { if (peak_leftover_cnt) { - compute_and_write_peaks (0, 0, 0, true); + compute_and_write_peaks (0, 0, 0, true, false); + } + + if (done) { + _peaks_built = true; } if (peakfile >= 0) { @@ -665,7 +676,7 @@ AudioSource::done_with_peakfile_writes () } int -AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force) +AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready) { Sample* buf2 = 0; nframes_t to_do; @@ -695,7 +706,8 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe x.min = peak_leftovers[0]; x.max = peak_leftovers[0]; - find_peaks (peak_leftovers + 1, peak_leftover_cnt - 1, &x.min, &x.max); + + 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); @@ -706,8 +718,13 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe _peak_byte_max = max (_peak_byte_max, (off_t) (byte + sizeof(PeakData))); - PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */ - PeaksReady (); /* EMIT SIGNAL */ + { + Glib::Mutex::Lock lm (_peaks_ready_lock); + PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */ + if (intermediate_peaks_ready) { + PeaksReady (); /* EMIT SIGNAL */ + } + } /* left overs are done */ @@ -778,7 +795,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe peakbuf[peaks_computed].max = buf[0]; peakbuf[peaks_computed].min = buf[0]; - find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max); + ARDOUR::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max); peaks_computed++; buf += this_time; @@ -814,8 +831,11 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe _peak_byte_max = max (_peak_byte_max, (off_t) (first_peak_byte + sizeof(PeakData)*peaks_computed)); if (frames_done) { + Glib::Mutex::Lock lm (_peaks_ready_lock); PeakRangeReady (first_frame, frames_done); /* EMIT SIGNAL */ - PeaksReady (); /* EMIT SIGNAL */ + if (intermediate_peaks_ready) { + PeaksReady (); /* EMIT SIGNAL */ + } } ret = 0; @@ -882,3 +902,11 @@ AudioSource::available_peaks (double zoom_factor) const return (end/sizeof(PeakData)) * frames_per_peak; } +void +AudioSource::update_length (nframes_t pos, nframes_t cnt) +{ + if (pos + cnt > _length) { + _length = pos+cnt; + } +} + diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index e344d5f2a6..c509997b2e 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -137,7 +137,7 @@ Auditioner::audition_region (boost::shared_ptr<Region> region) boost::shared_ptr<AudioRegion> the_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); the_region->set_position (0, this); - _diskstream->playlist()->clear (); + _diskstream->playlist()->drop_regions (); _diskstream->playlist()->add_region (the_region, 0, 1); if (_diskstream->n_channels().n_audio() < the_region->n_channels()) { diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 0609b8d380..45b19d1997 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -34,6 +34,8 @@ using namespace std; using namespace ARDOUR; using namespace PBD; +nframes_t Automatable::_automation_interval = 0; + Automatable::Automatable(Session& _session, const string& name) : SessionObject(_session, name) , _last_automation_snapshot(0) @@ -422,7 +424,7 @@ Automatable::protect_automation () void Automatable::automation_snapshot (nframes_t now) { - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { + if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) { if (i->second->list()->automation_write()) { diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc index 5faab7c0ab..1460491180 100644 --- a/libs/ardour/configuration.cc +++ b/libs/ardour/configuration.cc @@ -25,6 +25,9 @@ #include <pbd/filesystem.h> #include <pbd/file_utils.h> +#include <midi++/manager.h> + +#include <ardour/ardour.h> #include <ardour/configuration.h> #include <ardour/audio_diskstream.h> #include <ardour/control_protocol_manager.h> @@ -195,9 +198,12 @@ Configuration::get_state () LocaleGuard lg (X_("POSIX")); root = new XMLNode("Ardour"); - typedef map<string, MidiPortDescriptor*>::const_iterator CI; - for(CI m = midi_ports.begin(); m != midi_ports.end(); ++m){ - root->add_child_nocopy(m->second->get_state()); + + MIDI::Manager::PortMap::const_iterator i; + const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports(); + + for (i = ports.begin(); i != ports.end(); ++i) { + root->add_child_nocopy(i->second->get_state()); } root->add_child_nocopy (get_variables (sigc::mem_fun (*this, &Configuration::save_config_options_predicate), "Config")); @@ -250,10 +256,13 @@ Configuration::set_state (const XMLNode& root) if (node->name() == "MIDI-port") { try { - pair<string,MidiPortDescriptor*> newpair; - newpair.second = new MidiPortDescriptor (*node); - newpair.first = newpair.second->tag; - midi_ports.insert (newpair); + + MIDI::Port::Descriptor desc (*node); + map<string,XMLNode>::iterator x; + if ((x = midi_ports.find (desc.tag)) != midi_ports.end()) { + midi_ports.erase (x); + } + midi_ports.insert (pair<string,XMLNode>(desc.tag,*node)); } catch (failed_constructor& err) { @@ -296,53 +305,6 @@ Configuration::set_variables (const XMLNode& node, ConfigVariableBase::Owner own #undef CONFIG_VARIABLE_SPECIAL } - -Configuration::MidiPortDescriptor::MidiPortDescriptor (const XMLNode& node) -{ - const XMLProperty *prop; - bool have_tag = false; - bool have_device = false; - bool have_type = false; - bool have_mode = false; - - if ((prop = node.property ("tag")) != 0) { - tag = prop->value(); - have_tag = true; - } - - if ((prop = node.property ("device")) != 0) { - device = prop->value(); - have_device = true; - } - - if ((prop = node.property ("type")) != 0) { - type = prop->value(); - have_type = true; - } - - if ((prop = node.property ("mode")) != 0) { - mode = prop->value(); - have_mode = true; - } - - if (!have_tag || !have_device || !have_type || !have_mode) { - throw failed_constructor(); - } -} - -XMLNode& -Configuration::MidiPortDescriptor::get_state() -{ - XMLNode* root = new XMLNode("MIDI-port"); - - root->add_property("tag", tag); - root->add_property("device", device); - root->add_property("type", type); - root->add_property("mode", mode); - - return *root; -} - void Configuration::map_parameters (sigc::slot<void,const char*> theSlot) { diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 556f11125e..d45d5efa9f 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -294,6 +294,13 @@ Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, offset = start - _position; + /* Prevent data from piling up inthe crossfade buffers when reading a transparent region */ + if (!(_out->opaque())) { + memset (crossfade_buffer_out, 0, sizeof (Sample) * to_write); + } 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); @@ -358,6 +365,13 @@ Crossfade::refresh () return false; } + /* Top layer shouldn't be transparent */ + + if (!((layer_relation > 0 ? _in : _out)->opaque())) { + Invalidated (shared_from_this()); + return false; + } + /* layer ordering cannot change */ int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer()); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 52be946f1e..c3eae5ad86 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -39,7 +39,6 @@ #include <pbd/fpu.h> #include <midi++/port.h> -#include <midi++/port_request.h> #include <midi++/manager.h> #include <midi++/mmc.h> @@ -51,6 +50,7 @@ #include <ardour/audiosource.h> #include <ardour/utils.h> #include <ardour/session.h> +#include <ardour/source_factory.h> #include <ardour/control_protocol_manager.h> #include <ardour/audioengine.h> @@ -114,47 +114,25 @@ setup_osc () #endif int -ARDOUR::setup_midi (AudioEngine& engine) +setup_midi () { - std::map<string,Configuration::MidiPortDescriptor*>::iterator i; - int nports; - - if ((nports = Config->midi_ports.size()) == 0) { + if (Config->midi_ports.size() == 0) { warning << _("no MIDI ports specified: no MMC or MTC control possible") << endmsg; return 0; } - MIDI::Manager::instance()->set_api_data(engine.jack()); - - for (i = Config->midi_ports.begin(); i != Config->midi_ports.end(); ++i) { - Configuration::MidiPortDescriptor* port_descriptor; - - port_descriptor = (*i).second; - - MIDI::PortRequest request (port_descriptor->device, - port_descriptor->tag, - port_descriptor->mode, - port_descriptor->type); - - if (request.status != MIDI::PortRequest::OK) { - error << string_compose(_("MIDI port specifications for \"%1\" are not understandable."), port_descriptor->tag) << endmsg; - continue; - } - - MIDI::Manager::instance()->add_port (request); - - nports++; + for (std::map<string,XMLNode>::iterator i = Config->midi_ports.begin(); i != Config->midi_ports.end(); ++i) { + MIDI::Manager::instance()->add_port (i->second); } MIDI::Port* first; const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports(); - first = ports.begin()->second; - if (nports > 1) { + if (ports.size() > 1) { - /* More than one port, so try using specific names for each port */ + first = ports.begin()->second; - map<string,Configuration::MidiPortDescriptor *>::iterator i; + /* More than one port, so try using specific names for each port */ if (Config->get_mmc_port_name() != N_("default")) { default_mmc_port = MIDI::Manager::instance()->port (Config->get_mmc_port_name()); @@ -182,11 +160,13 @@ ARDOUR::setup_midi (AudioEngine& engine) default_midi_port = first; } - } else { + } else if (ports.size() == 1) { + + first = ports.begin()->second; /* Only one port described, so use it for both MTC and MMC */ - default_mmc_port = MIDI::Manager::instance()->port (""); + default_mmc_port = first; default_mtc_port = default_mmc_port; default_midi_port = default_mmc_port; } @@ -209,15 +189,16 @@ ARDOUR::setup_midi (AudioEngine& engine) return 0; } - + void setup_hardware_optimization (bool try_optimization) { bool generic_mix_functions = true; - FPU fpu; if (try_optimization) { + FPU fpu; + #if defined (ARCH_X86) && defined (BUILD_SSE_OPTIMIZATIONS) if (fpu.has_sse()) { @@ -253,6 +234,10 @@ setup_hardware_optimization (bool try_optimization) info << "Apple VecLib H/W specific optimizations in use" << endmsg; } #endif + + /* consider FPU denormal handling to be "h/w optimization" */ + + setup_fpu (); } if (generic_mix_functions) { @@ -265,9 +250,6 @@ setup_hardware_optimization (bool try_optimization) info << "No H/W specific optimizations in use" << endmsg; } - - setup_fpu (); - } int @@ -306,6 +288,8 @@ ARDOUR::init (bool use_vst, bool try_optimization) setup_hardware_optimization (try_optimization); + SourceFactory::init (); + /* singleton - first object is "it" */ new PluginManager (); @@ -387,6 +371,13 @@ ARDOUR::LocaleGuard::~LocaleGuard () void ARDOUR::setup_fpu () { + + if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) { + // valgrind doesn't understand this assembler stuff + // September 10th, 2007 + return; + } + #if defined(ARCH_X86) && defined(USE_XMMINTRIN) int MXCSR; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index bc16cde156..bd6351cf05 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -32,9 +32,9 @@ #include <glibmm.h> #include <pbd/basename.h> +#include <pbd/convert.h> #include <ardour/ardour.h> -#include <ardour/types.h> #include <ardour/session.h> #include <ardour/session_directory.h> #include <ardour/audio_diskstream.h> @@ -43,82 +43,22 @@ #include <ardour/audioregion.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> - +#include <ardour/resampled_source.h> #include "i18n.h" using namespace ARDOUR; using namespace PBD; -#define BLOCKSIZE 4096U - -class ImportableSource { - public: - ImportableSource (SNDFILE* sf, SF_INFO* info) : in (sf), sf_info (info) {} - virtual ~ImportableSource() {} - - virtual nframes_t read (Sample* buffer, nframes_t nframes) { - nframes_t per_channel = nframes / sf_info->channels; - per_channel = sf_readf_float (in, buffer, per_channel); - return per_channel * sf_info->channels; - } - - virtual float ratio() const { return 1.0f; } - -protected: - SNDFILE* in; - SF_INFO* sf_info; -}; - -class ResampledImportableSource : public ImportableSource { - public: - ResampledImportableSource (SNDFILE* sf, SF_INFO* info, nframes_t rate) : ImportableSource (sf, info) { - int err; - - sf_seek (in, 0, SEEK_SET) ; - - /* Initialize the sample rate converter. */ - - if ((src_state = src_new (SRC_SINC_BEST_QUALITY, sf_info->channels, &err)) == 0) { - error << string_compose(_("Import: src_new() failed : %1"), src_strerror (err)) << endmsg ; - throw failed_constructor (); - } - - src_data.end_of_input = 0 ; /* Set this later. */ - - /* Start with zero to force load in while loop. */ - - src_data.input_frames = 0 ; - src_data.data_in = input ; - - src_data.src_ratio = ((float) rate) / sf_info->samplerate ; - - } - - ~ResampledImportableSource () { - src_state = src_delete (src_state) ; - } - - nframes_t read (Sample* buffer, nframes_t nframes); - - float ratio() const { return src_data.src_ratio; } - - private: - float input[BLOCKSIZE]; - SRC_STATE* src_state; - SRC_DATA src_data; -}; - int Session::import_audiofile (import_status& status) { SNDFILE *in; vector<boost::shared_ptr<AudioFileSource> > newfiles; - SourceList sources; SF_INFO info; float *data = 0; Sample **channel_data = 0; - long nfiles = 0; + int nfiles = 0; string basepath; string sounds_dir; nframes_t so_far; @@ -127,178 +67,166 @@ Session::import_audiofile (import_status& status) vector<string> new_paths; struct tm* now; ImportableSource* importable = 0; - const nframes_t nframes = BLOCKSIZE; - - status.new_regions.clear (); - - if ((in = sf_open (status.paths.front().c_str(), SFM_READ, &info)) == 0) { - error << string_compose(_("Import: cannot open input sound file \"%1\""), status.paths.front()) << endmsg; - status.done = 1; - status.cancel = 1; - return -1; - } - - if ((nframes_t) info.samplerate != frame_rate()) { - importable = new ResampledImportableSource (in, &info, frame_rate()); - } else { - importable = new ImportableSource (in, &info); - } - - for (int n = 0; n < info.channels; ++n) { - newfiles.push_back (boost::shared_ptr<AudioFileSource>()); - } + const nframes_t nframes = ResampledImportableSource::blocksize; + uint32_t cnt = 1; - SessionDirectory sdir(get_best_session_directory_for_new_source ()); - sounds_dir = sdir.sound_path().to_string(); - - basepath = PBD::basename_nosuffix (status.paths.front()); + status.sources.clear (); + + for (vector<Glib::ustring>::iterator p = status.paths.begin(); p != status.paths.end(); ++p, ++cnt) { - for (int n = 0; n < info.channels; ++n) { + if ((in = sf_open ((*p).c_str(), SFM_READ, &info)) == 0) { + error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg; + status.done = 1; + status.cancel = 1; + return -1; + } + + if ((nframes_t) info.samplerate != frame_rate()) { + importable = new ResampledImportableSource (in, &info, frame_rate(), status.quality); + } else { + importable = new ImportableSource (in, &info); + } + + newfiles.clear (); - bool goodfile = false; + for (int n = 0; n < info.channels; ++n) { + newfiles.push_back (boost::shared_ptr<AudioFileSource>()); + } + + SessionDirectory sdir(get_best_session_directory_for_new_source ()); + sounds_dir = sdir.sound_path().to_string(); - do { - if (info.channels == 2) { - if (n == 0) { - snprintf (buf, sizeof(buf), "%s/%s-L.wav", sounds_dir.c_str(), basepath.c_str()); + basepath = PBD::basename_nosuffix ((*p)); + + for (int n = 0; n < info.channels; ++n) { + + bool goodfile = false; + + do { + if (info.channels == 2) { + if (n == 0) { + snprintf (buf, sizeof(buf), "%s/%s-L.wav", sounds_dir.c_str(), basepath.c_str()); + } else { + snprintf (buf, sizeof(buf), "%s/%s-R.wav", sounds_dir.c_str(), basepath.c_str()); + } + } else if (info.channels > 1) { + snprintf (buf, sizeof(buf), "%s/%s-c%d.wav", sounds_dir.c_str(), basepath.c_str(), n+1); } else { - snprintf (buf, sizeof(buf), "%s/%s-R.wav", sounds_dir.c_str(), basepath.c_str()); + snprintf (buf, sizeof(buf), "%s/%s.wav", sounds_dir.c_str(), basepath.c_str()); } - } else if (info.channels > 1) { - snprintf (buf, sizeof(buf), "%s/%s-c%d.wav", sounds_dir.c_str(), basepath.c_str(), n+1); - } else { - snprintf (buf, sizeof(buf), "%s/%s.wav", sounds_dir.c_str(), basepath.c_str()); - } - if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { + 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 - * _ to basepath - */ + /* if the file already exists, we must come up with + * a new name for it. for now we just keep appending + * _ to basepath + */ - basepath += "_"; + basepath += "_"; - } else { + } else { - goodfile = true; - } + goodfile = true; + } - } while ( !goodfile); + } while ( !goodfile); - try { - newfiles[n] = boost::dynamic_pointer_cast<AudioFileSource> ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); - } + try { + newfiles[n] = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); + } - catch (failed_constructor& err) { - error << string_compose(_("Session::import_audiofile: cannot open new file source for channel %1"), n+1) << endmsg; - goto out; - } + catch (failed_constructor& err) { + error << string_compose(_("Session::import_audiofile: cannot open new file source for channel %1"), n+1) << endmsg; + goto out; + } - new_paths.push_back (buf); - nfiles++; - } + new_paths.push_back (buf); + newfiles[n]->prepare_for_peakfile_writes (); + nfiles++; + } - data = new float[nframes * info.channels]; - channel_data = new Sample * [ info.channels ]; + data = new float[nframes * info.channels]; + channel_data = new Sample * [ info.channels ]; - for (int n = 0; n < info.channels; ++n) { - channel_data[n] = new Sample[nframes]; - } + for (int n = 0; n < info.channels; ++n) { + channel_data[n] = new Sample[nframes]; + } - so_far = 0; + so_far = 0; - status.doing_what = _("converting audio"); - status.progress = 0.0; + if ((nframes_t) info.samplerate != frame_rate()) { + status.doing_what = string_compose (_("converting %1\n(resample from %2KHz to %3KHz)\n(%4 of %5)"), + basepath, + info.samplerate/1000.0f, + frame_rate()/1000.0f, + cnt, status.paths.size()); + + } else { + status.doing_what = string_compose (_("converting %1\n(%2 of %3)"), + basepath, + cnt, status.paths.size()); - while (!status.cancel) { + } + + status.progress = 0.0; + + while (!status.cancel) { - nframes_t nread, nfread; - long x; - long chn; + nframes_t nread, nfread; + long x; + long chn; - if ((nread = importable->read (data, nframes)) == 0) { - break; - } - nfread = nread / info.channels; + if ((nread = importable->read (data, nframes)) == 0) { + break; + } + nfread = nread / info.channels; - /* de-interleave */ + /* de-interleave */ - for (chn = 0; chn < info.channels; ++chn) { + for (chn = 0; chn < info.channels; ++chn) { - nframes_t n; - for (x = chn, n = 0; n < nfread; x += info.channels, ++n) { - channel_data[chn][n] = (Sample) data[x]; + nframes_t n; + for (x = chn, n = 0; n < nfread; x += info.channels, ++n) { + channel_data[chn][n] = (Sample) data[x]; + } } - } - /* flush to disk */ + /* flush to disk */ - for (chn = 0; chn < info.channels; ++chn) { - newfiles[chn]->write (channel_data[chn], nfread); - } + for (chn = 0; chn < info.channels; ++chn) { + newfiles[chn]->write (channel_data[chn], nfread); + } - so_far += nread; - status.progress = so_far / (importable->ratio () * info.frames * info.channels); - } + so_far += nread; + status.progress = so_far / (importable->ratio () * info.frames * info.channels); + } - if (status.cancel) { - goto out; - } + if (status.cancel) { + goto out; + } + + for (int n = 0; n < info.channels; ++n) { + status.sources.push_back (newfiles[n]); + } - if (status.multichan) { - status.doing_what = _("building region"); - } else { - status.doing_what = _("building regions"); + if (status.cancel) { + goto out; + } } - + status.freeze = true; time_t xnow; time (&xnow); now = localtime (&xnow); - if (status.multichan) { - /* all sources are used in a single multichannel region */ + /* flush the final length(s) to the header(s) */ - for (int n = 0; n < nfiles && !status.cancel; ++n) { - /* flush the final length to the header */ - newfiles[n]->update_header(0, *now, xnow); - sources.push_back(newfiles[n]); - } - - bool strip_paired_suffixes = (newfiles.size() > 1); - - boost::shared_ptr<AudioRegion> r (boost::dynamic_pointer_cast<AudioRegion> - (RegionFactory::create (sources, 0, - newfiles[0]->length(), - region_name_from_path (basepath, strip_paired_suffixes), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); - - status.new_regions.push_back (r); - - } else { - for (int n = 0; n < nfiles && !status.cancel; ++n) { - - /* flush the final length to the header */ - - newfiles[n]->update_header(0, *now, xnow); - - /* The sources had zero-length when created, which means that the Session - did not bother to create whole-file AudioRegions for them. Do it now. - - Note: leave any trailing paired indicators from the file names as part - of the region name. - */ - - status.new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> - (RegionFactory::create (boost::static_pointer_cast<Source> (newfiles[n]), 0, newfiles[n]->length(), - region_name_from_path (newfiles[n]->name(), false), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile | AudioRegion::Import)))); - } + for (SourceList::iterator x = status.sources.begin(); x != status.sources.end() && !status.cancel; ++x) { + boost::dynamic_pointer_cast<AudioFileSource>(*x)->update_header(0, *now, xnow); + boost::dynamic_pointer_cast<AudioSource>(*x)->done_with_peakfile_writes (); } - + /* save state so that we don't lose these new Sources */ if (!status.cancel) { @@ -321,7 +249,8 @@ Session::import_audiofile (import_status& status) } if (status.cancel) { - status.new_regions.clear (); + + status.sources.clear (); for (vector<string>::iterator i = new_paths.begin(); i != new_paths.end(); ++i) { unlink ((*i).c_str()); @@ -337,50 +266,3 @@ Session::import_audiofile (import_status& status) return ret; } - -nframes_t -ResampledImportableSource::read (Sample* output, nframes_t nframes) -{ - int err; - - /* If the input buffer is empty, refill it. */ - - if (src_data.input_frames == 0) { - - src_data.input_frames = ImportableSource::read (input, BLOCKSIZE); - - /* The last read will not be a full buffer, so set end_of_input. */ - - if ((nframes_t) src_data.input_frames < BLOCKSIZE) { - src_data.end_of_input = SF_TRUE ; - } - - src_data.input_frames /= sf_info->channels; - src_data.data_in = input ; - } - - src_data.data_out = output; - - if (!src_data.end_of_input) { - src_data.output_frames = nframes / sf_info->channels ; - } else { - src_data.output_frames = src_data.input_frames; - } - - if ((err = src_process (src_state, &src_data))) { - error << string_compose(_("Import: %1"), src_strerror (err)) << endmsg ; - return 0 ; - } - - /* Terminate if at end */ - - if (src_data.end_of_input && src_data.output_frames_gen == 0) { - return 0; - } - - src_data.data_in += src_data.input_frames_used * sf_info->channels ; - src_data.input_frames -= src_data.input_frames_used ; - - return src_data.output_frames_gen * sf_info->channels; -} - diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a708821c20..bff8c18bc9 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -1307,7 +1307,7 @@ IO::state (bool full_state) int const in_max = _input_maximum == ChanCount::INFINITE ? -1 : _input_maximum.get(_default_type); int const out_max = _output_maximum == ChanCount::INFINITE ? -1 : _output_maximum.get(_default_type); - snprintf (buf, sizeof(buf)-1, "%zd,%d,%zd,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max); + snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max); node->add_property ("iolimits", buf); @@ -2335,7 +2335,7 @@ IO::automation_snapshot (nframes_t now) { Automatable::automation_snapshot (now); - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { + if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { _panner->snapshot (now); } } diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 4af7e2b907..09b54000d8 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -1176,8 +1176,8 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region) save = !(_splicing || _nudging); } - if ((what_changed & Region::MuteChanged) && - !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { + if ((what_changed & our_interests) && + !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { check_dependents (region, false); } diff --git a/libs/ardour/resampled_source.cc b/libs/ardour/resampled_source.cc new file mode 100644 index 0000000000..38aa3832b9 --- /dev/null +++ b/libs/ardour/resampled_source.cc @@ -0,0 +1,128 @@ +/* + 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. + +*/ + +#include <pbd/error.h> +#include <ardour/resampled_source.h> +#include <pbd/failed_constructor.h> + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; + +const uint32_t ResampledImportableSource::blocksize = 4096U; + +ResampledImportableSource::ResampledImportableSource (SNDFILE* sf, SF_INFO* info, nframes_t rate, SrcQuality srcq) + : ImportableSource (sf, info) +{ + int err; + + sf_seek (in, 0, SEEK_SET) ; + + /* Initialize the sample rate converter. */ + + int src_type; + + switch (srcq) { + case SrcBest: + src_type = SRC_SINC_BEST_QUALITY; + break; + case SrcGood: + src_type = SRC_SINC_MEDIUM_QUALITY; + break; + case SrcQuick: + src_type = SRC_SINC_FASTEST; + break; + case SrcFast: + src_type = SRC_ZERO_ORDER_HOLD; + break; + case SrcFastest: + src_type = SRC_LINEAR; + break; + } + + if ((src_state = src_new (src_type, sf_info->channels, &err)) == 0) { + error << string_compose(_("Import: src_new() failed : %1"), src_strerror (err)) << endmsg ; + throw failed_constructor (); + } + + src_data.end_of_input = 0 ; /* Set this later. */ + + /* Start with zero to force load in while loop. */ + + src_data.input_frames = 0 ; + src_data.data_in = input ; + + src_data.src_ratio = ((float) rate) / sf_info->samplerate ; + + input = new float[blocksize]; +} + +ResampledImportableSource::~ResampledImportableSource () +{ + src_state = src_delete (src_state) ; + delete [] input; +} + +nframes_t +ResampledImportableSource::read (Sample* output, nframes_t nframes) +{ + int err; + + /* If the input buffer is empty, refill it. */ + + if (src_data.input_frames == 0) { + + src_data.input_frames = ImportableSource::read (input, blocksize); + + /* The last read will not be a full buffer, so set end_of_input. */ + + if ((nframes_t) src_data.input_frames < blocksize) { + src_data.end_of_input = SF_TRUE ; + } + + src_data.input_frames /= sf_info->channels; + src_data.data_in = input ; + } + + src_data.data_out = output; + + if (!src_data.end_of_input) { + src_data.output_frames = nframes / sf_info->channels ; + } else { + src_data.output_frames = src_data.input_frames; + } + + if ((err = src_process (src_state, &src_data))) { + error << string_compose(_("Import: %1"), src_strerror (err)) << endmsg ; + return 0 ; + } + + /* Terminate if at end */ + + if (src_data.end_of_input && src_data.output_frames_gen == 0) { + return 0; + } + + src_data.data_in += src_data.input_frames_used * sf_info->channels ; + src_data.input_frames -= src_data.input_frames_used ; + + return src_data.output_frames_gen * sf_info->channels; +} + diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 19095425f2..be7dfb8469 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -53,7 +53,7 @@ using namespace ARDOUR; using namespace PBD; uint32_t Route::order_key_cnt = 0; - +sigc::signal<void> Route::SyncOrderKeys; Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type) : IO (sess, name, input_min, input_max, output_min, output_max, default_type), @@ -161,10 +161,35 @@ void Route::set_order_key (const char* name, long n) { order_keys[strdup(name)] = n; + + if (Config->get_sync_all_route_ordering()) { + for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) { + x->second = n; + } + } + _session.set_dirty (); } void +Route::sync_order_keys () +{ + uint32_t key; + + if (order_keys.empty()) { + return; + } + + OrderKeys::iterator x = order_keys.begin(); + key = x->second; + ++x; + + for (; x != order_keys.end(); ++x) { + x->second = key; + } +} + +void Route::inc_gain (gain_t fraction, void *src) { IO::inc_gain (fraction, src); @@ -460,13 +485,9 @@ Route::process_output_buffers (BufferSet& bufs, // OR recording - // h/w monitoring not in use - - (!Config->get_monitoring_model() == HardwareMonitoring && - // AND software monitoring required - Config->get_monitoring_model() == SoftwareMonitoring)) { + Config->get_monitoring_model() == SoftwareMonitoring) { if (apply_gain_automation) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 742b17af2c..e7f2c542e6 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -124,8 +124,7 @@ Session::Session (AudioEngine &eng, routes (new RouteList), auditioner ((Auditioner*) 0), _click_io ((IO*) 0), - main_outs (0), - _automation_interval (0) + main_outs (0) { if (!eng.connected()) { throw failed_constructor(); @@ -224,8 +223,7 @@ Session::Session (AudioEngine &eng, _send_smpte_update (false), diskstreams (new DiskstreamList), routes (new RouteList), - main_outs (0), - _automation_interval (0) + main_outs (0) { if (!eng.connected()) { @@ -1257,8 +1255,10 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - _automation_interval = ((nframes_t) ceil ((double) frames_per_second * 0.25)); + Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval()))); + clear_clicks (); + // XXX we need some equivalent to this, somehow // SndFileSource::setup_standard_crossfades (frames_per_second); @@ -2755,13 +2755,11 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) return boost::shared_ptr<Source>(); } -string -Session::peak_path_from_audio_path (string audio_path) const +Glib::ustring +Session::peak_path (Glib::ustring base) const { sys::path peakfile_path(_session_dir->peak_path()); - - peakfile_path /= basename_nosuffix (audio_path) + peakfile_suffix; - + peakfile_path /= basename_nosuffix (base) + peakfile_suffix; return peakfile_path.to_string(); } @@ -3387,8 +3385,8 @@ Session::remove_empty_sounds () try { sys::remove (audio_file_path); - const string peak_path = peak_path_from_audio_path (audio_file_path.to_string()); - sys::remove (peak_path); + const string peakfile = peak_path (audio_file_path.to_string()); + sys::remove (peakfile); } catch (const sys::filesystem_error& err) { @@ -4124,6 +4122,23 @@ Session::compute_initial_length () } void +Session::sync_order_keys () +{ + if (!Config->get_sync_all_route_ordering()) { + /* leave order keys as they are */ + return; + } + + boost::shared_ptr<RouteList> r = routes.reader (); + + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->sync_order_keys (); + } + + Route::SyncOrderKeys (); // EMIT SIGNAL +} + +void Session::foreach_bundle (sigc::slot<void, boost::shared_ptr<Bundle> > sl) { Glib::Mutex::Lock lm (bundle_lock); diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index 35c1019c0e..aa5a1b87d4 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -410,6 +410,8 @@ Session::process_event (Event* ev) case Event::Audition: set_audition (ev->region); + // drop reference to region + ev->region.reset (); break; case Event::InputConfigurationChange: diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index f2f396cbee..0534da6c89 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -138,10 +138,14 @@ Session::first_stage_init (string fullpath, string snapshot_name) _name = _current_snapshot_name = snapshot_name; + set_history_depth (Config->get_history_depth()); + _current_frame_rate = _engine.frame_rate (); _tempo_map = new TempoMap (_current_frame_rate); _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed)); + + g_atomic_int_set (&processing_prohibited, 0); insert_cnt = 0; _transport_speed = 0; @@ -501,7 +505,7 @@ void Session::remove_pending_capture_state () { sys::path pending_state_file_path(_session_dir->root_path()); - + pending_state_file_path /= _current_snapshot_name + pending_suffix; try @@ -2668,8 +2672,6 @@ Session::save_history (string snapshot_name) { XMLTree tree; - tree.set_root (&_history.get_state (Config->get_saved_history_depth())); - if (snapshot_name.empty()) { snapshot_name = _current_snapshot_name; } @@ -2691,6 +2693,13 @@ Session::save_history (string snapshot_name) } } + + if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) { + return 0; + } + + tree.set_root (&_history.get_state (Config->get_saved_history_depth())); + if (!tree.write (xml_path.to_string())) { error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg; @@ -2977,6 +2986,10 @@ Session::config_changed (const char* parameter_name) set_remote_control_ids (); } else if (PARAM_IS ("denormal-model")) { setup_fpu (); + } else if (PARAM_IS ("history-depth")) { + set_history_depth (Config->get_history_depth()); + } else if (PARAM_IS ("sync-all-route-ordering")) { + sync_order_keys (); } set_dirty (); @@ -2984,3 +2997,9 @@ Session::config_changed (const char* parameter_name) #undef PARAM_IS } + +void +Session::set_history_depth (uint32_t d) +{ + _history.set_depth (d); +} diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 6977eef6bd..7c4859aa55 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -25,7 +25,7 @@ #include <sys/stat.h> #include <glibmm/miscutils.h> - +#include <glibmm/thread.h> #include <ardour/sndfilesource.h> #include <ardour/sndfile_helpers.h> #include <ardour/utils.h> @@ -45,6 +45,21 @@ const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSou AudioFileSource::RemovableIfEmpty| AudioFileSource::CanRename); +struct SizedSampleBuffer { + nframes_t size; + Sample* buf; + + SizedSampleBuffer (nframes_t sz) : size (sz) { + buf = new Sample[size]; + } + + ~SizedSampleBuffer() { + delete [] buf; + } +}; + +Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT; + SndFileSource::SndFileSource (Session& s, const XMLNode& node) : AudioFileSource (s, node) { @@ -186,8 +201,6 @@ SndFileSource::init () // lets try to keep the object initalizations here at the top xfade_buf = 0; - interleave_buf = 0; - interleave_bufsize = 0; sf = 0; _broadcast_info = 0; @@ -272,10 +285,6 @@ SndFileSource::~SndFileSource () touch_peakfile (); } - if (interleave_buf) { - delete [] interleave_buf; - } - if (_broadcast_info) { delete _broadcast_info; } @@ -341,14 +350,7 @@ SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const real_cnt = cnt * _info.channels; - if (interleave_bufsize < real_cnt) { - - if (interleave_buf) { - delete [] interleave_buf; - } - interleave_bufsize = real_cnt; - interleave_buf = new float[interleave_bufsize]; - } + Sample* interleave_buf = get_interleave_buffer (real_cnt); nread = sf_read_float (sf, interleave_buf, real_cnt); ptr = interleave_buf + _channel; @@ -401,7 +403,7 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) update_length (oldlen, cnt); if (_build_peakfiles) { - compute_and_write_peaks (data, frame_pos, cnt, false); + compute_and_write_peaks (data, frame_pos, cnt, false, true); } _write_data_count = cnt; @@ -493,7 +495,7 @@ SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) update_length (file_pos, cnt); if (_build_peakfiles) { - compute_and_write_peaks (data, file_pos, cnt, false); + compute_and_write_peaks (data, file_pos, cnt, false, true); } file_pos += cnt; @@ -904,3 +906,21 @@ SndFileSource::one_of_several_channels () const { return _info.channels > 1; } + +Sample* +SndFileSource::get_interleave_buffer (nframes_t size) +{ + SizedSampleBuffer* ssb; + + if ((ssb = thread_interleave_buffer.get()) == 0) { + ssb = new SizedSampleBuffer (size); + thread_interleave_buffer.set (ssb); + } + + if (ssb->size < size) { + ssb = new SizedSampleBuffer (size); + thread_interleave_buffer.set (ssb); + } + + return ssb->buf; +} diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 148f737551..c6b19c8600 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -19,6 +19,8 @@ */ #include <pbd/error.h> +#include <pbd/convert.h> +#include <pbd/pthread_utils.h> #include <ardour/source_factory.h> #include <ardour/sndfilesource.h> @@ -37,15 +39,69 @@ using namespace std; using namespace PBD; sigc::signal<void,boost::shared_ptr<Source> > SourceFactory::SourceCreated; +Glib::Cond* SourceFactory::PeaksToBuild; +Glib::StaticMutex SourceFactory::peak_building_lock; +std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks; + +static void +peak_thread_work () +{ + PBD::ThreadCreated (pthread_self(), string ("peakbuilder-") + to_string (pthread_self(), std::dec)); + + while (true) { + + SourceFactory::peak_building_lock.lock (); + + wait: + if (SourceFactory::files_with_peaks.empty()) { + SourceFactory::PeaksToBuild->wait (SourceFactory::peak_building_lock); + } + + if (SourceFactory::files_with_peaks.empty()) { + goto wait; + } + + boost::shared_ptr<AudioSource> as (SourceFactory::files_with_peaks.front().lock()); + SourceFactory::files_with_peaks.pop_front (); + SourceFactory::peak_building_lock.unlock (); + + if (!as) { + continue; + } + + as->setup_peakfile (); + } +} + +void +SourceFactory::init () +{ + PeaksToBuild = new Glib::Cond(); + + for (int n = 0; n < 2; ++n) { + Glib::Thread::create (sigc::ptr_fun (::peak_thread_work), false); + } +} int -SourceFactory::setup_peakfile (boost::shared_ptr<Source> s) +SourceFactory::setup_peakfile (boost::shared_ptr<Source> s, bool async) { boost::shared_ptr<AudioSource> as (boost::dynamic_pointer_cast<AudioSource> (s)); + if (as) { - if (as->setup_peakfile ()) { - error << string_compose("SourceFactory: could not set up peakfile for %1", as->name()) << endmsg; - return -1; + + if (async) { + + Glib::Mutex::Lock lm (peak_building_lock); + files_with_peaks.push_back (boost::weak_ptr<AudioSource> (as)); + PeaksToBuild->broadcast (); + + } else { + + if (as->setup_peakfile ()) { + error << string_compose("SourceFactory: could not set up peakfile for %1", as->name()) << endmsg; + return -1; + } } } @@ -60,84 +116,85 @@ SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes, return ret; } +#ifdef USE_COREAUDIO_FOR_FILES boost::shared_ptr<Source> -SourceFactory::create (Session& s, const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) { - /* this is allowed to throw */ - - DataType type = DataType::AUDIO; - const XMLProperty* prop = node.property("type"); - if (prop) { - type = DataType(prop->value()); - } - - if (type == DataType::AUDIO) { - -#ifdef HAVE_COREAUDIO - try { - boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); - if (setup_peakfile (ret)) { + try { + boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { return boost::shared_ptr<Source>(); } - SourceCreated (ret); - return ret; - } + } + SourceCreated (ret); + return ret; + } - catch (failed_constructor& err) { - - /* this is allowed to throw */ - - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (setup_peakfile (ret)) { + + catch (failed_constructor& err) { + + /* this is allowed to throw */ + + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { return boost::shared_ptr<Source>(); } - SourceCreated (ret); - return ret; } -#else - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - SourceCreated (ret); return ret; -#endif + } - } else if (type == DataType::MIDI) { + return boost::shared_ptr<Source>(); +} - boost::shared_ptr<Source> ret (new SMFSource (s, node)); - - SourceCreated (ret); - return ret; +#else + +boost::shared_ptr<Source> +SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) +{ + /* this is allowed to throw */ + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); + + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } - return boost::shared_ptr<Source> (); + SourceCreated (ret); + return ret; } +#endif // USE_COREAUDIO_FOR_FILES + boost::shared_ptr<Source> -SourceFactory::createReadable (DataType type, Session& s, string path, int chn, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string path, int chn, AudioFileSource::Flag flags, bool announce, bool defer_peaks) { if (type == DataType::AUDIO) { #ifdef HAVE_COREAUDIO try { boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); } return ret; } - + catch (failed_constructor& err) { boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); @@ -147,8 +204,10 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, #else boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { @@ -157,7 +216,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, return ret; #endif - + } else if (type == DataType::MIDI) { // FIXME: flags? @@ -174,21 +233,23 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, } boost::shared_ptr<Source> -SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce) +SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce, bool defer_peaks) { /* this might throw failed_constructor(), which is OK */ - + if (type == DataType::AUDIO) { boost::shared_ptr<Source> ret (new SndFileSource - (s, path, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - rate, - (destructive ? AudioFileSource::Flag (SndFileSource::default_writable_flags | AudioFileSource::Destructive) : - SndFileSource::default_writable_flags))); - - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + (s, path, + Config->get_native_file_data_format(), + Config->get_native_file_header_format(), + rate, + (destructive ? AudioFileSource::Flag (SndFileSource::default_writable_flags | AudioFileSource::Destructive) : + SndFileSource::default_writable_flags))); + + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 01e070e920..cfd38c5099 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -143,7 +143,7 @@ touch_file (ustring path) } ustring -region_name_from_path (ustring path, bool strip_channels) +region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one) { path = PBD::basename_nosuffix (path); @@ -160,6 +160,17 @@ region_name_from_path (ustring path, bool strip_channels) } } + if (add_channel_suffix) { + + path += '%'; + + if (total > 2) { + path += (char) ('a' + this_one); + } else { + path += (char) (this_one == 0 ? 'L' : 'R'); + } + } + return path; } diff --git a/libs/clearlooks/SConscript b/libs/clearlooks/SConscript index 110bfe41be..2676f63022 100644 --- a/libs/clearlooks/SConscript +++ b/libs/clearlooks/SConscript @@ -17,6 +17,9 @@ clearlooks = env.Copy() clearlooks.Replace(CCFLAGS = ' `pkg-config --cflags gtk+-2.0` ', LINKFLAGS = ' `pkg-config --libs gtk+-2.0` ') +if env['GTKOSX']: + clearlooks.Append (CCFLAGS = '-DGTKOSX') + libclearlooks = clearlooks.SharedLibrary('clearlooks', libclearlooks_files) usable_libclearlooks = clearlooks.Install ('engines', libclearlooks) diff --git a/libs/clearlooks/clearlooks_style.c b/libs/clearlooks/clearlooks_style.c index 241f14c6e4..074f1604b1 100644 --- a/libs/clearlooks/clearlooks_style.c +++ b/libs/clearlooks/clearlooks_style.c @@ -1677,14 +1677,18 @@ draw_option (DRAW_ARGS) x += (width - RADIO_SIZE)/2; y += (height - RADIO_SIZE)/2; +#ifndef GTKOSX gdk_gc_set_clip_mask (gc, clearlooks_style->radio_pixmap_mask); gdk_gc_set_clip_origin (gc, x, y); +#endif gdk_draw_drawable (window, gc, pixmap, 0, 0, x, y, RADIO_SIZE, RADIO_SIZE); +#ifndef GTKOSX gdk_gc_set_clip_origin (gc, 0, 0); gdk_gc_set_clip_mask (gc, NULL); +#endif if (area) gdk_gc_set_clip_rectangle (gc, NULL); diff --git a/libs/gtkmm2/gtk/gtkmm/toolbar.cc b/libs/gtkmm2/gtk/gtkmm/toolbar.cc index a8d10a7dde..8e51d70501 100644 --- a/libs/gtkmm2/gtk/gtkmm/toolbar.cc +++ b/libs/gtkmm2/gtk/gtkmm/toolbar.cc @@ -30,7 +30,7 @@ //but the GtkToolbar compatibility system is particularly unpleasant, so we just removed it in gtkmm 2.4. murrayc. //In future, this GTK_DISABLE_DEPRECATED might be inappropriate because it might cover extra GTK+ API. Just remove it then. -#define GTK_DISABLE_DEPRECATED +// #define GTK_DISABLE_DEPRECATED #include <glib.h> #include <gtkmm/button.h> diff --git a/libs/midi++2/SConscript b/libs/midi++2/SConscript index 04b027b6a3..769d888903 100644 --- a/libs/midi++2/SConscript +++ b/libs/midi++2/SConscript @@ -7,7 +7,7 @@ import glob Import('env libraries install_prefix') midi2 = env.Copy() -midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['glib2'], libraries['pbd'], libraries['jack'] ]) +midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['glibmm2'], libraries['glib2'], libraries['pbd'], libraries['jack'] ]) domain = 'midipp' @@ -24,7 +24,6 @@ midiparser.cc midiport.cc mmc.cc mtc.cc -port_request.cc version.cc """) diff --git a/libs/midi++2/alsa_sequencer_midiport.cc b/libs/midi++2/alsa_sequencer_midiport.cc index bf891108c6..bae8aff2ab 100644 --- a/libs/midi++2/alsa_sequencer_midiport.cc +++ b/libs/midi++2/alsa_sequencer_midiport.cc @@ -23,10 +23,12 @@ #include <pbd/failed_constructor.h> #include <pbd/error.h> +#include <pbd/xml++.h> #include <midi++/types.h> #include <midi++/alsa_sequencer.h> -#include <midi++/port_request.h> + +#include "i18n.h" //#define DOTRACE 1 @@ -44,31 +46,31 @@ using namespace PBD; snd_seq_t* ALSA_SequencerMidiPort::seq = 0; -ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (PortRequest &req) - : Port (req) +ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (const XMLNode& node) + : Port (node) , decoder (0) , encoder (0) , port_id (-1) { TR_FN(); int err; + Descriptor desc (node); - if (!seq && init_client (req.devname) < 0) { + if (!seq && init_client (desc.device) < 0) { _ok = false; } else { - if (0 <= (err = CreatePorts (req)) && + if (0 <= (err = create_ports (desc)) && 0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read () 0 <= (err = snd_midi_event_new (64, &encoder))) { // Length taken from ARDOUR::Session::mmc_buffer snd_midi_event_init (decoder); snd_midi_event_init (encoder); _ok = true; - req.status = PortRequest::OK; - } else { - req.status = PortRequest::Unknown; - } + } } + + set_state (node); } ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort () @@ -94,7 +96,8 @@ ALSA_SequencerMidiPort::selectable () const return -1; } -int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) +int +ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t ignored) { TR_FN (); int R; @@ -133,7 +136,8 @@ int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t timesta return totwritten; } -int ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t timestamp) +int +ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t ignored) { TR_FN(); int err; @@ -158,17 +162,20 @@ int ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t timestamp) } int -ALSA_SequencerMidiPort::CreatePorts (PortRequest &req) +ALSA_SequencerMidiPort::create_ports (const Port::Descriptor& desc) { int err; unsigned int caps = 0; - if (req.mode == O_WRONLY || req.mode == O_RDWR) + if (desc.mode == O_WRONLY || desc.mode == O_RDWR) caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; - if (req.mode == O_RDONLY || req.mode == O_RDWR) + if (desc.mode == O_RDONLY || desc.mode == O_RDWR) caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; - if (0 <= (err = snd_seq_create_simple_port (seq, req.tagname, caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC))) { + if (0 <= (err = snd_seq_create_simple_port (seq, desc.tag.c_str(), caps, + (SND_SEQ_PORT_TYPE_MIDI_GENERIC| + SND_SEQ_PORT_TYPE_SOFTWARE| + SND_SEQ_PORT_TYPE_APPLICATION)))) { port_id = err; @@ -203,3 +210,216 @@ ALSA_SequencerMidiPort::init_client (std::string name) return -1; } } + +int +ALSA_SequencerMidiPort::discover (vector<PortSet>& ports) +{ + int n = 0; + + snd_seq_client_info_t *client_info; + snd_seq_port_info_t *port_info; + + snd_seq_client_info_alloca (&client_info); + snd_seq_port_info_alloca (&port_info); + snd_seq_client_info_set_client (client_info, -1); + + while (snd_seq_query_next_client(seq, client_info) >= 0) { + + int alsa_client; + + if ((alsa_client = snd_seq_client_info_get_client(client_info)) <= 0) { + break; + } + + snd_seq_port_info_set_client(port_info, alsa_client); + snd_seq_port_info_set_port(port_info, -1); + + char client[256]; + snprintf (client, sizeof (client), "%d:%s", alsa_client, snd_seq_client_info_get_name(client_info)); + + ports.push_back (PortSet (client)); + + while (snd_seq_query_next_port(seq, port_info) >= 0) { + +#if 0 + int type = snd_seq_port_info_get_type(pinfo); + if (!(type & SND_SEQ_PORT_TYPE_PORT)) { + continue; + } +#endif + + unsigned int port_capability = snd_seq_port_info_get_capability(port_info); + + if ((port_capability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) { + + int alsa_port = snd_seq_port_info_get_port(port_info); + + char port[256]; + snprintf (port, sizeof (port), "%d:%s", alsa_port, snd_seq_port_info_get_name(port_info)); + + std::string mode; + + if (port_capability & SND_SEQ_PORT_CAP_READ) { + if (port_capability & SND_SEQ_PORT_CAP_WRITE) { + mode = "duplex"; + } else { + mode = "output"; + } + } else if (port_capability & SND_SEQ_PORT_CAP_WRITE) { + if (port_capability & SND_SEQ_PORT_CAP_READ) { + mode = "duplex"; + } else { + mode = "input"; + } + } + + XMLNode node (X_("MIDI-port")); + node.add_property ("device", client); + node.add_property ("tag", port); + node.add_property ("mode", mode); + node.add_property ("type", "alsa/sequencer"); + + ports.back().ports.push_back (node); + ++n; + } + } + } + + return n; +} + +void +ALSA_SequencerMidiPort::get_connections (vector<SequencerPortAddress>& connections, int dir) const +{ + snd_seq_query_subscribe_t *subs; + snd_seq_addr_t seq_addr; + + snd_seq_query_subscribe_alloca (&subs); + + // Get port connections... + + if (dir) { + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE); + } else { + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_READ); + } + + snd_seq_query_subscribe_set_index(subs, 0); + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_query_subscribe_set_root(subs, &seq_addr); + + while (snd_seq_query_port_subscribers(seq, subs) >= 0) { + + seq_addr = *snd_seq_query_subscribe_get_addr (subs); + + connections.push_back (SequencerPortAddress (seq_addr.client, + seq_addr.port)); + + snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1); + } +} + +XMLNode& +ALSA_SequencerMidiPort::get_state () const +{ + XMLNode& root (Port::get_state ()); + vector<SequencerPortAddress> connections; + XMLNode* sub = 0; + char buf[256]; + + get_connections (connections, 1); + + if (!connections.empty()) { + if (!sub) { + sub = new XMLNode (X_("connections")); + } + for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) { + XMLNode* cnode = new XMLNode (X_("read")); + snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second); + cnode->add_property ("dest", buf); + sub->add_child_nocopy (*cnode); + } + } + + connections.clear (); + get_connections (connections, 0); + + if (!connections.empty()) { + if (!sub) { + sub = new XMLNode (X_("connections")); + } + for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) { + XMLNode* cnode = new XMLNode (X_("write")); + snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second); + cnode->add_property ("dest", buf); + sub->add_child_nocopy (*cnode); + } + } + + if (sub) { + root.add_child_nocopy (*sub); + } + + return root; +} + +void +ALSA_SequencerMidiPort::set_state (const XMLNode& node) +{ + Port::set_state (node); + + XMLNodeList children (node.children()); + XMLNodeIterator iter; + + for (iter = children.begin(); iter != children.end(); ++iter) { + + if ((*iter)->name() == X_("connections")) { + + XMLNodeList gchildren ((*iter)->children()); + XMLNodeIterator gciter; + + for (gciter = gchildren.begin(); gciter != gchildren.end(); ++gciter) { + XMLProperty* prop; + + if ((prop = (*gciter)->property ("dest")) != 0) { + int client; + int port; + + if (sscanf (prop->value().c_str(), "%d:%d", &client, &port) == 2) { + + snd_seq_port_subscribe_t *sub; + snd_seq_addr_t seq_addr; + + snd_seq_port_subscribe_alloca(&sub); + + if ((*gciter)->name() == X_("write")) { + + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_port_subscribe_set_sender(sub, &seq_addr); + + seq_addr.client = client; + seq_addr.port = port; + snd_seq_port_subscribe_set_dest(sub, &seq_addr); + + } else { + + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_port_subscribe_set_dest(sub, &seq_addr); + + seq_addr.client = client; + seq_addr.port = port; + snd_seq_port_subscribe_set_sender(sub, &seq_addr); + } + + snd_seq_subscribe_port (seq, sub); + } + } + } + + break; + } + } +} diff --git a/libs/midi++2/coremidi_midiport.cc b/libs/midi++2/coremidi_midiport.cc index 2cd98239ec..14020a6f35 100644 --- a/libs/midi++2/coremidi_midiport.cc +++ b/libs/midi++2/coremidi_midiport.cc @@ -23,7 +23,6 @@ #include <midi++/coremidi_midiport.h> #include <midi++/types.h> -#include <midi++/port_request.h> #include <mach/mach_time.h> #include <pbd/pthread_utils.h> @@ -36,15 +35,15 @@ MIDITimeStamp CoreMidi_MidiPort::MIDIGetCurrentHostTime() return mach_absolute_time(); } -CoreMidi_MidiPort::CoreMidi_MidiPort (PortRequest &req) : Port (req) +CoreMidi_MidiPort::CoreMidi_MidiPort (const XMLNode& node) : Port (node) { + Descriptor desc (node); + firstrecv = true; int err; - if (0 == (err = Open(req))) { + if (0 == (err = Open(desc))) { _ok = true; - req.status = PortRequest::OK; - } else - req.status = PortRequest::Unknown; + } } CoreMidi_MidiPort::~CoreMidi_MidiPort () {Close();} @@ -56,10 +55,10 @@ void CoreMidi_MidiPort::Close () if (midi_client) MIDIClientDispose(midi_client); } -int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) +int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t ignored) { OSStatus err; - MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer; + MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer; MIDIPacket* packet = MIDIPacketListInit(pktlist); packet = MIDIPacketListAdd(pktlist,sizeof(midi_buffer),packet,MIDIGetCurrentHostTime(),msglen,msg); @@ -77,21 +76,21 @@ int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) } } -int CoreMidi_MidiPort::Open (PortRequest &req) +int CoreMidi_MidiPort::Open (const Descriptor& desc) { OSStatus err; CFStringRef coutputStr; string str; - - coutputStr = CFStringCreateWithCString(0, req.devname, CFStringGetSystemEncoding()); + + coutputStr = CFStringCreateWithCString(0, desc.device.c_str(), CFStringGetSystemEncoding()); err = MIDIClientCreate(coutputStr, 0, 0, &midi_client); CFRelease(coutputStr); - if (!midi_client) { + if (!midi_client) { //error << "Cannot open CoreMidi client : " << err << endmsg. - goto error; - } + goto error; + } - str = req.tagname + string("_in"); + str = desc.tag + string("_in"); coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); err = MIDIDestinationCreate(midi_client, coutputStr, read_proc, this, &midi_destination); CFRelease(coutputStr); @@ -100,7 +99,7 @@ int CoreMidi_MidiPort::Open (PortRequest &req) goto error; } - str = req.tagname + string("_out"); + str = desc.tag + string("_out"); coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); err = MIDISourceCreate(midi_client, coutputStr, &midi_source); CFRelease(coutputStr); @@ -142,3 +141,10 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon, } } +int +CoreMidi_MidiPort::discover (vector<PortSet>& ports) +{ + /* XXX do dynamic port discovery here */ + + return 0; +} diff --git a/libs/midi++2/fd_midiport.cc b/libs/midi++2/fd_midiport.cc index 81d81c8558..17a7eff367 100644 --- a/libs/midi++2/fd_midiport.cc +++ b/libs/midi++2/fd_midiport.cc @@ -34,40 +34,38 @@ using namespace PBD; string *FD_MidiPort::midi_dirpath = 0; string *FD_MidiPort::midi_filename_pattern = 0; -FD_MidiPort::FD_MidiPort (PortRequest &req, +FD_MidiPort::FD_MidiPort (const XMLNode& node, const string &dirpath, const string &pattern) - : Port (req) + : Port (node) { - open (req); + Descriptor desc (node); + + open (desc); if (_fd < 0) { switch (errno) { case EBUSY: error << "MIDI: port device in use" << endmsg; - req.status = PortRequest::Busy; break; case ENOENT: error << "MIDI: no such port device" << endmsg; - req.status = PortRequest::NoSuchFile; break; case EACCES: error << "MIDI: access to port denied" << endmsg; - req.status = PortRequest::NotAllowed; break; default: - req.status = PortRequest::Unknown; + break; } } else { _ok = true; - req.status = PortRequest::OK; if (midi_dirpath == 0) { midi_dirpath = new string (dirpath); midi_filename_pattern = new string (pattern); } - if (req.mode & O_NONBLOCK == 0) { + if (desc.mode & O_NONBLOCK == 0) { /* we unconditionally set O_NONBLOCK during open, but the request didn't ask for it, so remove it. @@ -80,11 +78,11 @@ FD_MidiPort::FD_MidiPort (PortRequest &req, } void -FD_MidiPort::open (PortRequest &req) +FD_MidiPort::open (const Descriptor& desc) { - int mode = req.mode | O_NONBLOCK; - _fd = ::open (req.devname, mode); + int mode = desc.mode | O_NONBLOCK; + _fd = ::open (desc.device.c_str(), mode); } vector<string *> * @@ -152,7 +150,7 @@ FD_MidiPort::do_slow_write (byte *msg, unsigned int msglen) } int -FD_MidiPort::read (byte* buf, size_t max, timestamp_t timestamp) +FD_MidiPort::read (byte* buf, size_t max, timestamp_t ignored) { int nread; diff --git a/libs/midi++2/fifomidi.cc b/libs/midi++2/fifomidi.cc index 7bb126ddeb..a81520bb95 100644 --- a/libs/midi++2/fifomidi.cc +++ b/libs/midi++2/fifomidi.cc @@ -26,19 +26,19 @@ using namespace MIDI; -FIFO_MidiPort::FIFO_MidiPort (PortRequest &req) - : FD_MidiPort (req, ".", "midi") +FIFO_MidiPort::FIFO_MidiPort (const XMLNode& node) + : FD_MidiPort (node, ".", "midi") { } void -FIFO_MidiPort::open (PortRequest &req) +FIFO_MidiPort::open (const Port::Descriptor& desc) { /* This is a placeholder for the fun-and-games I think we will need to do with FIFO's. */ - _fd = ::open (req.devname, req.mode|O_NDELAY); + _fd = ::open (desc.device.c_str(), desc.mode|O_NDELAY); } diff --git a/libs/midi++2/jack_midiport.cc b/libs/midi++2/jack_midiport.cc index 4b2808a698..11cd70a051 100644 --- a/libs/midi++2/jack_midiport.cc +++ b/libs/midi++2/jack_midiport.cc @@ -23,26 +23,22 @@ #include <midi++/types.h> #include <midi++/jack.h> -#include <midi++/port_request.h> using namespace std; using namespace MIDI; -JACK_MidiPort::JACK_MidiPort(PortRequest & req, jack_client_t* jack_client) - : Port(req) +JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client) + : Port(node) , _jack_client(jack_client) , _jack_input_port(NULL) , _jack_output_port(NULL) , _last_read_index(0) { - int err = create_ports(req); + int err = create_ports (node); if (!err) { - req.status = PortRequest::OK; _ok = true; - } else { - req.status = PortRequest::Unknown; - } + } } JACK_MidiPort::~JACK_MidiPort() @@ -94,8 +90,10 @@ JACK_MidiPort::read(byte * buf, size_t max, timestamp_t timestamp) } int -JACK_MidiPort::create_ports(PortRequest & req) +JACK_MidiPort::create_ports(const XMLNode& node) { + Descriptor desc (node); + assert(!_jack_input_port); assert(!_jack_output_port); @@ -103,24 +101,33 @@ JACK_MidiPort::create_ports(PortRequest & req) bool ret = true; - if (req.mode == O_RDWR || req.mode == O_WRONLY) { + if (desc.mode == O_RDWR || desc.mode == O_WRONLY) { _jack_output_port = jack_port_register(_jack_client, - string(req.tagname).append("_out").c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); - jack_midi_clear_buffer( - jack_port_get_buffer(_jack_output_port, nframes)); + string(desc.tag).append("_out").c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes)); ret = ret && (_jack_output_port != NULL); } - - if (req.mode == O_RDWR || req.mode == O_RDONLY) { + + if (desc.mode == O_RDWR || desc.mode == O_RDONLY) { _jack_input_port = jack_port_register(_jack_client, - string(req.tagname).append("_in").c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - jack_midi_clear_buffer( - jack_port_get_buffer(_jack_input_port, nframes)); + string(desc.tag).append("_in").c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes)); ret = ret && (_jack_input_port != NULL); } return ret ? 0 : -1; } +XMLNode& +JACK_MidiPort::get_state () const +{ + XMLNode& root (Port::get_state ()); + return root; +} + +void +JACK_MidiPort::set_state (const XMLNode& node) +{ +} diff --git a/libs/midi++2/midi++/alsa_rawmidi.h b/libs/midi++2/midi++/alsa_rawmidi.h index 54b86edd70..e5abc2832f 100644 --- a/libs/midi++2/midi++/alsa_rawmidi.h +++ b/libs/midi++2/midi++/alsa_rawmidi.h @@ -34,8 +34,8 @@ class ALSA_RawMidiPort : public MIDI::FD_MidiPort { public: - ALSA_RawMidiPort (MIDI::PortRequest &req) - : FD_MidiPort (req, "/dev/snd", "midi") {} + ALSA_RawMidiPort (const XMLNode& node) + : FD_MidiPort (node, "/dev/snd", "midi") {} virtual ~ALSA_RawMidiPort () {} static std::string typestring; @@ -46,7 +46,7 @@ class ALSA_RawMidiPort : public MIDI::FD_MidiPort } }; -} // namespace MIDI +} #endif // __alsa_rawmidi_h__ diff --git a/libs/midi++2/midi++/alsa_sequencer.h b/libs/midi++2/midi++/alsa_sequencer.h index b54486416a..a95f9c476f 100644 --- a/libs/midi++2/midi++/alsa_sequencer.h +++ b/libs/midi++2/midi++/alsa_sequencer.h @@ -30,39 +30,45 @@ namespace MIDI { -class PortRequest; - class ALSA_SequencerMidiPort : public Port + { public: - ALSA_SequencerMidiPort (PortRequest &req); + ALSA_SequencerMidiPort (const XMLNode&); virtual ~ALSA_SequencerMidiPort (); /* select(2)/poll(2)-based I/O */ virtual int selectable() const; - + + static int discover (std::vector<PortSet>&); static std::string typestring; - protected: - std::string get_typestring () const { - return typestring; - } + XMLNode& get_state() const; + void set_state (const XMLNode&); protected: /* Direct I/O */ + int write (byte *msg, size_t msglen, timestamp_t timestamp); int read (byte *buf, size_t max, timestamp_t timestamp); + std::string get_typestring () const { + return typestring; + } + private: snd_midi_event_t *decoder, *encoder; int port_id; snd_seq_event_t SEv; - int CreatePorts(PortRequest &req); + int create_ports (const Port::Descriptor&); static int init_client (std::string name); static snd_seq_t* seq; + + typedef std::pair<int,int> SequencerPortAddress; + void get_connections (std::vector<SequencerPortAddress>&, int dir) const; }; }; /* namespace MIDI */ diff --git a/libs/midi++2/midi++/coremidi_midiport.h b/libs/midi++2/midi++/coremidi_midiport.h index 91eccea4a5..d5d3c23ede 100644 --- a/libs/midi++2/midi++/coremidi_midiport.h +++ b/libs/midi++2/midi++/coremidi_midiport.h @@ -22,6 +22,7 @@ #include <list> #include <string> +#include <vector> #include <fcntl.h> #include <unistd.h> @@ -32,45 +33,47 @@ namespace MIDI { -namespace PortRequest; + class CoreMidi_MidiPort:public Port { + public: + CoreMidi_MidiPort(const XMLNode& node); + virtual ~ CoreMidi_MidiPort(); -class CoreMidi_MidiPort:public Port -{ - public: - CoreMidi_MidiPort(PortRequest & req); - virtual ~ CoreMidi_MidiPort(); - - virtual int selectable() const { - return -1; - } - static std::string typestring; + virtual int selectable() const { + return -1; + } + + static int discover (std::vector<PortSet>&); + static std::string typestring; + + protected: + /* Direct I/O */ + int write (byte * msg, size_t msglen, timestamp_t timestamp); - protected: + int read (byte * buf, size_t max, timestamp_t timestamp) { + return 0; + } + + /* CoreMidi callback */ + static void read_proc(const MIDIPacketList * pktlist, + void *refCon, void *connRefCon); + std::string get_typestring () const { return typestring; } - protected: - /* Direct I/O */ - int write (byte *msg, size_t msglen, timestamp_t timestamp); - int read (byte *buf, size_t max, timestamp_t timestamp); - - /* CoreMidi callback */ - static void read_proc(const MIDIPacketList * pktlist, - void *refCon, void *connRefCon); - - private: - byte midi_buffer[1024]; - MIDIClientRef midi_client; - MIDIEndpointRef midi_destination; - MIDIEndpointRef midi_source; + private: + byte midi_buffer[1024]; + MIDIClientRef midi_client; + MIDIEndpointRef midi_destination; + MIDIEndpointRef midi_source; - int Open(PortRequest & req); - void Close(); - static MIDITimeStamp MIDIGetCurrentHostTime(); + int Open(const Port::Descriptor&); + void Close(); + static MIDITimeStamp MIDIGetCurrentHostTime(); - bool firstrecv; -}; + bool firstrecv; + + }; } // namespace MIDI diff --git a/libs/midi++2/midi++/factory.h b/libs/midi++2/midi++/factory.h index 9954ea72fe..f3402546e9 100644 --- a/libs/midi++2/midi++/factory.h +++ b/libs/midi++2/midi++/factory.h @@ -23,13 +23,12 @@ #include <string> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { class PortFactory { public: - Port *create_port (PortRequest &req, void* data); + Port *create_port (const XMLNode&, void* data); static bool ignore_duplicate_devices (Port::Type); static int get_known_ports (std::vector<PortSet>&); diff --git a/libs/midi++2/midi++/fd_midiport.h b/libs/midi++2/midi++/fd_midiport.h index 34e2e27a1a..ec5a9f8af4 100644 --- a/libs/midi++2/midi++/fd_midiport.h +++ b/libs/midi++2/midi++/fd_midiport.h @@ -29,7 +29,6 @@ #include <unistd.h> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { @@ -37,7 +36,7 @@ class FD_MidiPort : public Port { public: - FD_MidiPort (PortRequest &req, + FD_MidiPort (const XMLNode& node, const std::string &dirpath, const std::string &pattern); @@ -46,23 +45,14 @@ class FD_MidiPort : public Port } virtual int selectable() const; - static std::vector<std::string *> *list_devices (); - - static std::string typestring; - protected: - std::string get_typestring () const { - return typestring; - } + static std::vector<std::string *> *list_devices (); protected: int _fd; - virtual void open (PortRequest &req); + virtual void open (const Port::Descriptor&); - /* Direct I/O */ - - virtual int write (byte *msg, size_t msglen, - timestamp_t timestamp) { + virtual int write (byte *msg, size_t msglen, timestamp_t ignored) { int nwritten; if ((_mode & O_ACCMODE) == O_RDONLY) { @@ -89,8 +79,7 @@ class FD_MidiPort : public Port return nwritten; } - virtual int read (byte *buf, size_t max, - timestamp_t timestamp); + virtual int read (byte *buf, size_t max, timestamp_t ignored); private: static std::string *midi_dirpath; diff --git a/libs/midi++2/midi++/fifomidi.h b/libs/midi++2/midi++/fifomidi.h index ea644dde06..3439c27dcf 100644 --- a/libs/midi++2/midi++/fifomidi.h +++ b/libs/midi++2/midi++/fifomidi.h @@ -25,7 +25,6 @@ #include <unistd.h> #include <midi++/port.h> -#include <midi++/port_request.h> #include <midi++/fd_midiport.h> namespace MIDI { @@ -34,7 +33,7 @@ class FIFO_MidiPort : public MIDI::FD_MidiPort { public: - FIFO_MidiPort (PortRequest &req); + FIFO_MidiPort (const XMLNode&); ~FIFO_MidiPort () {}; static std::string typestring; @@ -45,7 +44,7 @@ class FIFO_MidiPort : public MIDI::FD_MidiPort } private: - void open (PortRequest &req); + void open (const Port::Descriptor&); }; } // namespace MIDI diff --git a/libs/midi++2/midi++/jack.h b/libs/midi++2/midi++/jack.h index 1f25609aac..845dd0c229 100644 --- a/libs/midi++2/midi++/jack.h +++ b/libs/midi++2/midi++/jack.h @@ -39,7 +39,7 @@ namespace MIDI class JACK_MidiPort : public Port { public: - JACK_MidiPort (PortRequest &req, jack_client_t* jack_client); + JACK_MidiPort (const XMLNode& node, jack_client_t* jack_client); virtual ~JACK_MidiPort (); /* No select(2)/poll(2)-based I/O */ @@ -49,6 +49,9 @@ public: static std::string typestring; + virtual XMLNode& get_state () const; + virtual void set_state (const XMLNode&); + protected: std::string get_typestring () const { return typestring; @@ -60,7 +63,7 @@ protected: int read(byte *buf, size_t max, timestamp_t timestamp); private: - int create_ports(PortRequest &req); + int create_ports(const XMLNode&); jack_client_t* _jack_client; jack_port_t* _jack_input_port; diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h index eef52abe52..bb3bf9b999 100644 --- a/libs/midi++2/midi++/manager.h +++ b/libs/midi++2/midi++/manager.h @@ -21,6 +21,8 @@ #define __midi_manager_h__ #include <map> +#include <vector> + #include <string> #include <midi++/types.h> @@ -28,20 +30,18 @@ namespace MIDI { -/** Creates, stores, and manages system MIDI ports. - */ class Manager { public: ~Manager (); void set_api_data(void* data) { api_data = data; } - + /** Signal the start of an audio cycle. * This MUST be called before any reading/writing for this cycle. * Realtime safe. */ void cycle_start(nframes_t nframes); - + /** Signal the end of an audio cycle. * This signifies that the cycle began with @ref cycle_start has ended. * This MUST be called at the end of each cycle. @@ -49,13 +49,25 @@ class Manager { */ void cycle_end(); - Port *add_port (PortRequest &); + Port *add_port (const XMLNode& node); int remove_port (Port*); Port *port (std::string name); size_t nports () { return ports_by_device.size(); } + /* defaults for clients who are not picky */ + + Port *inputPort; + Port *outputPort; + channel_t inputChannelNumber; + channel_t outputChannelNumber; + + int set_input_port (std::string); + int set_output_port (std::string); + int set_input_channel (channel_t); + int set_output_channel (channel_t); + int foreach_port (int (*func)(const Port &, size_t n, void *), void *arg); @@ -70,7 +82,7 @@ class Manager { return theManager; } - static int parse_port_request (std::string str, Port::Type type); + int get_known_ports (std::vector<PortSet>&); private: /* This is a SINGLETON pattern */ @@ -81,7 +93,7 @@ class Manager { PortMap ports_by_device; /* canonical */ PortMap ports_by_tag; /* may contain duplicate Ports */ - void *api_data; + void* api_data; void close_ports (); }; diff --git a/libs/midi++2/midi++/nullmidi.h b/libs/midi++2/midi++/nullmidi.h index 6ed94db71c..8f36e6aed8 100644 --- a/libs/midi++2/midi++/nullmidi.h +++ b/libs/midi++2/midi++/nullmidi.h @@ -24,7 +24,6 @@ #include <string> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index e4338cf952..dcae446c42 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -20,10 +20,11 @@ #define __libmidi_port_h__ #include <string> +#include <iostream> #include <sigc++/sigc++.h> +#include <pbd/xml++.h> -#include <pbd/selectable.h> #include <midi++/types.h> #include <midi++/parser.h> @@ -44,64 +45,82 @@ class Port : public sigc::trackable { FIFO }; - Port (PortRequest &); + + Port (const XMLNode&); virtual ~Port (); + virtual XMLNode& get_state () const; + virtual void set_state (const XMLNode&); + // FIXME: make Manager a friend of port so these can be hidden? - + /* Only for use by MidiManager. Don't ever call this. */ virtual void cycle_start(nframes_t nframes); - /* Only for use by MidiManager. Don't ever call this. */ virtual void cycle_end(); - /* Direct I/O */ - - /** Read a message from port. - * @param buf Raw MIDI message to send - * @param max Max size to write to @a buf - * @param timestamp Time stamp in frames of this message (relative to cycle start) - * @return number of bytes successfully written to \a buf - */ - virtual int read(byte *buf, size_t max, timestamp_t timestamp) = 0; - /** Write a message to port. * @param msg Raw MIDI message to send * @param msglen Size of @a msg * @param timestamp Time stamp in frames of this message (relative to cycle start) * @return number of bytes successfully written */ - virtual int write(byte *msg, size_t msglen, timestamp_t timestamp) = 0; + virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0; + + /** Read a message from port. + * @param buf Raw MIDI message to send + * @param max Max size to write to @a buf + * @param timestamp Time stamp in frames of this message (relative to cycle start) + * @return number of bytes successfully written to \a buf + */ + virtual int read (byte *buf, size_t max, timestamp_t timestamp) = 0; /** Write a message to port. * @return true on success. * FIXME: describe semantics here */ - bool midimsg (byte *msg, size_t len, timestamp_t timestamp) { + int midimsg (byte *msg, size_t len, timestamp_t timestamp) { return !(write (msg, len, timestamp) == (int) len); - } + } + + int three_byte_msg (byte a, byte b, byte c, timestamp_t timestamp) { + byte msg[3]; + msg[0] = a; + msg[1] = b; + msg[2] = c; + + return !(write (msg, 3, timestamp) == 3); + } + bool clock (timestamp_t timestamp); + + /* slowdown i/o to a loop of single byte emissions + interspersed with a busy loop of 10000 * this value. + + This may be ignored by a particular instance + of this virtual class. See FD_MidiPort for an + example of where it used. + */ - /** Slow down I/O to a loop of single byte emissions - * interspersed with a busy loop of 10000 * this value. - * - * This may be ignored by a particular instance of this virtual - * class. See FD_MidiPort for an example of where it used. */ void set_slowdown (size_t n) { slowdown = n; } /* select(2)/poll(2)-based I/O */ /** Get the file descriptor for port. - * @return File descriptor, or -1 if not selectable. */ + * @return File descriptor, or -1 if not selectable. + */ virtual int selectable() const = 0; + static void gtk_read_callback (void *ptr, int fd, int cond); + static void write_callback (byte *msg, unsigned int len, void *); + Channel *channel (channel_t chn) { return _channel[chn&0x7F]; } - Parser *input() { return input_parser; } - Parser *output() { return output_parser; } + Parser *input() { return input_parser; } + Parser *output() { return output_parser; } void iostat (int *written, int *read, const size_t **in_counts, @@ -121,14 +140,21 @@ class Port : public sigc::trackable { } } - bool clock (); - const char *device () const { return _devname.c_str(); } - const char *name () const { return _tagname.c_str(); } - Type type () const { return _type; } - int mode () const { return _mode; } - bool ok () const { return _ok; } - size_t number () const { return _number; } + const char *name () const { return _tagname.c_str(); } + Type type () const { return _type; } + int mode () const { return _mode; } + bool ok () const { return _ok; } + + struct Descriptor { + std::string tag; + std::string device; + int mode; + Port::Type type; + + Descriptor (const XMLNode&); + XMLNode& get_state(); + }; protected: bool _ok; @@ -147,10 +173,21 @@ class Port : public sigc::trackable { Parser *output_parser; size_t slowdown; + virtual std::string get_typestring () const = 0; + private: static size_t nports; }; +struct PortSet { + PortSet (std::string str) : owner (str) { } + + std::string owner; + std::list<XMLNode> ports; +}; + +std::ostream & operator << ( std::ostream & os, const Port & port ); + } // namespace MIDI #endif // __libmidi_port_h__ diff --git a/libs/midi++2/midi++/port_request.h b/libs/midi++2/midi++/port_request.h deleted file mode 100644 index dfde87a63c..0000000000 --- a/libs/midi++2/midi++/port_request.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 1999 Paul Barton-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 __midi_port_request_h__ -#define __midi_port_request_h__ - -#include <string> - -namespace MIDI { - -struct PortRequest { - enum Status { - Unknown, - OK, - Busy, - NoSuchFile, - TypeUnsupported, - NotAllowed - }; - const char *devname; - const char *tagname; - int mode; - Port::Type type; - Status status; - - PortRequest () { - devname = 0; - tagname = 0; - mode = 0; - type = Port::Unknown; - status = Unknown; - } - - PortRequest (const std::string &xdev, - const std::string &xtag, - const std::string &xmode, - const std::string &xtype); -}; - -struct PortSet { - PortSet (std::string str) : owner (str) { } - - std::string owner; - std::list<PortRequest> ports; -}; - -} // namespace MIDI - -#endif // __midi_port_request_h__ - diff --git a/libs/midi++2/midifactory.cc b/libs/midi++2/midifactory.cc index 9d98d9f6a7..d893cc9f47 100644 --- a/libs/midi++2/midifactory.cc +++ b/libs/midi++2/midifactory.cc @@ -24,7 +24,6 @@ #include <midi++/types.h> #include <midi++/factory.h> -#include <midi++/nullmidi.h> #include <midi++/fifomidi.h> #ifdef WITH_JACK_MIDI @@ -33,7 +32,6 @@ std::string MIDI::JACK_MidiPort::typestring = "jack"; #endif // WITH_JACK_MIDI -std::string MIDI::Null_MidiPort::typestring = "null"; std::string MIDI::FIFO_MidiPort::typestring = "fifo"; #ifdef WITH_ALSA @@ -58,50 +56,44 @@ using namespace PBD; // FIXME: void* data pointer, filthy Port * -PortFactory::create_port (PortRequest &req, void* data) +PortFactory::create_port (const XMLNode& node, void* data) { + Port::Descriptor desc (node); Port *port; - switch (req.type) { + switch (desc.type) { #ifdef WITH_JACK_MIDI case Port::JACK_Midi: assert(data != NULL); - port = new JACK_MidiPort (req, (jack_client_t*)data); + port = new JACK_MidiPort (node, (jack_client_t*) data); break; #endif // WITH_JACK_MIDI #ifdef WITH_ALSA case Port::ALSA_RawMidi: - port = new ALSA_RawMidiPort (req); + port = new ALSA_RawMidiPort (node); break; case Port::ALSA_Sequencer: - port = new ALSA_SequencerMidiPort (req); + port = new ALSA_SequencerMidiPort (node); break; #endif // WITH_ALSA #if WITH_COREMIDI case Port::CoreMidi_MidiPort: - port = new CoreMidi_MidiPort (req); + port = new CoreMidi_MidiPort (node); break; #endif // WITH_COREMIDI - case Port::Null: - port = new Null_MidiPort (req); - break; - case Port::FIFO: - port = new FIFO_MidiPort (req); + port = new FIFO_MidiPort (node); break; default: - req.status = PortRequest::TypeUnsupported; return 0; } - req.status = PortRequest::OK; - return port; } @@ -179,8 +171,6 @@ PortFactory::string_to_type (const string& xtype) } else if (strings_equal_ignore_case (xtype, CoreMidi_MidiPort::typestring)) { return Port::CoreMidi_MidiPort; #endif - } else if (strings_equal_ignore_case (xtype, Null_MidiPort::typestring)) { - return Port::Null; } else if (strings_equal_ignore_case (xtype, FIFO_MidiPort::typestring)) { return Port::FIFO; #ifdef WITH_JACK_MIDI diff --git a/libs/midi++2/midimanager.cc b/libs/midi++2/midimanager.cc index ee73bdad86..8a358c3183 100644 --- a/libs/midi++2/midimanager.cc +++ b/libs/midi++2/midimanager.cc @@ -27,21 +27,25 @@ #include <midi++/manager.h> #include <midi++/factory.h> #include <midi++/channel.h> -#include <midi++/port_request.h> using namespace std; using namespace MIDI; using namespace PBD; +/* XXX check for strdup leaks */ + Manager *Manager::theManager = 0; Manager::Manager () - : api_data(NULL) { + inputPort = 0; + outputPort = 0; + inputChannelNumber = 0; + outputChannelNumber = 0; + api_data = 0; } Manager::~Manager () - { PortMap::iterator i; @@ -58,27 +62,27 @@ Manager::~Manager () } Port * -Manager::add_port (PortRequest &req) - +Manager::add_port (const XMLNode& node) { + Port::Descriptor desc (node); PortFactory factory; Port *port; PortMap::iterator existing; pair<string, Port *> newpair; - if (!PortFactory::ignore_duplicate_devices (req.type)) { + if (!PortFactory::ignore_duplicate_devices (desc.type)) { - if ((existing = ports_by_device.find (req.devname)) != ports_by_device.end()) { + if ((existing = ports_by_device.find (desc.device)) != ports_by_device.end()) { port = (*existing).second; - if (port->mode() == req.mode) { + if (port->mode() == desc.mode) { /* Same mode - reuse the port, and just create a new tag entry. */ - newpair.first = req.tagname; + newpair.first = desc.tag; newpair.second = port; ports_by_tag.insert (newpair); @@ -91,10 +95,10 @@ Manager::add_port (PortRequest &req) operation. */ - if ((req.mode == O_RDWR && port->mode() != O_RDWR) || - (req.mode != O_RDWR && port->mode() == O_RDWR)) { + if ((desc.mode == O_RDWR && port->mode() != O_RDWR) || + (desc.mode != O_RDWR && port->mode() == O_RDWR)) { error << "MIDIManager: port tagged \"" - << req.tagname + << desc.tag << "\" cannot be opened duplex and non-duplex" << endmsg; return 0; @@ -103,7 +107,8 @@ Manager::add_port (PortRequest &req) /* modes must be different or complementary */ } } - port = factory.create_port (req, api_data); + + port = factory.create_port (node, api_data); if (port == 0) { return 0; @@ -122,6 +127,18 @@ Manager::add_port (PortRequest &req) newpair.second = port; ports_by_device.insert (newpair); + /* first port added becomes the default input + port. + */ + + if (inputPort == 0) { + inputPort = port; + } + + if (outputPort == 0) { + outputPort = port; + } + return port; } @@ -156,119 +173,88 @@ Manager::remove_port (Port* port) return 0; } -Port * -Manager::port (string name) +int +Manager::set_input_port (string tag) { PortMap::iterator res; + bool found = false; for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { - if (name == (*res).first) { - return (*res).second; + if (tag == (*res).first) { + found = true; + break; } } + + if (!found) { + return -1; + } + + inputPort = (*res).second; return 0; } int -Manager::foreach_port (int (*func)(const Port &, size_t, void *), - void *arg) +Manager::set_output_port (string tag) { - PortMap::const_iterator i; - int retval; - int n; - - for (n = 0, i = ports_by_device.begin(); - i != ports_by_device.end(); i++, n++) { + PortMap::iterator res; + bool found = false; - if ((retval = func (*((*i).second), n, arg)) != 0) { - return retval; + for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { + if (tag == (*res).first) { + found = true; + break; } } - - return 0; -} - -int -Manager::parse_port_request (string str, Port::Type type) -{ - PortRequest *req; - string::size_type colon; - string tag; - - if (str.length() == 0) { - error << "MIDI: missing port specification" << endmsg; + + if (!found) { return -1; } - /* Port specifications look like: + // XXX send a signal to say we're about to change output ports - devicename - devicename:tagname - devicename:tagname:mode + if (outputPort) { + for (channel_t chan = 0; chan < 16; chan++) { + outputPort->channel (chan)->all_notes_off (0); + } + } + outputPort = (*res).second; - where + // XXX send a signal to say we've changed output ports - "devicename" is the full path to the requested file - - "tagname" (optional) is the name used to refer to the - port. If not given, g_path_get_basename (devicename) - will be used. + return 0; +} - "mode" (optional) is either "r" or "w" or something else. - if it is "r", the port will be opened - read-only, if "w", the port will be opened - write-only. Any other value, or no mode - specification at all, will cause the port to - be opened for reading and writing. - */ - - req = new PortRequest; - colon = str.find_first_of (':'); +Port * +Manager::port (string name) +{ + PortMap::iterator res; - if (colon != string::npos) { - req->devname = strdup (str.substr (0, colon).c_str()); - } else { - req->devname = strdup (str.c_str()); + for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { + if (name == (*res).first) { + return (*res).second; + } } - if (colon < str.length()) { - - tag = str.substr (colon+1); + return 0; +} - /* see if there is a mode specification in the tag part */ +int +Manager::foreach_port (int (*func)(const Port &, size_t, void *), + void *arg) +{ + PortMap::const_iterator i; + int retval; + int n; - colon = tag.find_first_of (':'); - - if (colon != string::npos) { - string modestr; - - req->tagname = strdup (tag.substr (0, colon).c_str()); - - modestr = tag.substr (colon+1); - if (modestr == "r") { - req->mode = O_RDONLY; - } else if (modestr == "w") { - req->mode = O_WRONLY; - } else { - req->mode = O_RDWR; - } + for (n = 0, i = ports_by_device.begin(); + i != ports_by_device.end(); i++, n++) { - } else { - req->tagname = strdup (tag.c_str()); - req->mode = O_RDWR; + if ((retval = func (*((*i).second), n, arg)) != 0) { + return retval; } - - } else { - req->tagname = g_path_get_basename (req->devname); - req->mode = O_RDWR; - } - - req->type = type; - - if (MIDI::Manager::instance()->add_port (*req) == 0) { - return -1; } return 0; @@ -277,16 +263,22 @@ Manager::parse_port_request (string str, Port::Type type) void Manager::cycle_start(nframes_t nframes) { - for (PortMap::iterator i = ports_by_device.begin(); - i != ports_by_device.end(); i++) - (*i).second->cycle_start(nframes); + for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) { + (*i).second->cycle_start (nframes); + } } void Manager::cycle_end() { - for (PortMap::iterator i = ports_by_device.begin(); - i != ports_by_device.end(); i++) - (*i).second->cycle_end(); + for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) { + (*i).second->cycle_end (); + } } + +int +Manager::get_known_ports (vector<PortSet>& ports) +{ + return PortFactory::get_known_ports (ports); +} diff --git a/libs/midi++2/midiport.cc b/libs/midi++2/midiport.cc index 7f31b909d3..2e3d36c19c 100644 --- a/libs/midi++2/midiport.cc +++ b/libs/midi++2/midiport.cc @@ -17,24 +17,29 @@ $Id$ */ - +#include <iostream> #include <cstdio> #include <fcntl.h> +#include <pbd/xml++.h> +#include <pbd/failed_constructor.h> + #include <midi++/types.h> #include <midi++/port.h> #include <midi++/channel.h> -#include <midi++/port_request.h> +#include <midi++/factory.h> -using namespace Select; using namespace MIDI; +using namespace std; size_t Port::nports = 0; -Port::Port (PortRequest &req) +Port::Port (const XMLNode& node) : _currently_in_cycle(false) , _nframes_this_cycle(0) { + Descriptor desc (node); + _ok = false; /* derived class must set to true if constructor succeeds. */ @@ -45,10 +50,9 @@ Port::Port (PortRequest &req) output_parser = 0; slowdown = 0; - _devname = req.devname; - _tagname = req.tagname; - _mode = req.mode; - _number = nports++; + _devname = desc.device; + _tagname = desc.tag; + _mode = desc.mode; if (_mode == O_RDONLY || _mode == O_RDWR) { input_parser = new Parser (*this); @@ -77,7 +81,6 @@ Port::Port (PortRequest &req) Port::~Port () - { for (int i = 0; i < 16; i++) { delete _channel[i]; @@ -113,3 +116,87 @@ Port::cycle_end () _nframes_this_cycle = 0; } +XMLNode& +Port::get_state () const +{ + XMLNode* node = new XMLNode ("MIDI-port"); + node->add_property ("tag", _tagname); + node->add_property ("device", _devname); + node->add_property ("mode", PortFactory::mode_to_string (_mode)); + node->add_property ("type", get_typestring()); + + return *node; +} + +void +Port::set_state (const XMLNode& node) +{ + // relax +} + +void +Port::gtk_read_callback (void *ptr, int fd, int cond) +{ + byte buf[64]; + + ((Port *)ptr)->read (buf, sizeof (buf), 0); +} + +void +Port::write_callback (byte *msg, unsigned int len, void *ptr) + +{ + ((Port *)ptr)->write (msg, len, 0); +} + +std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port ) +{ + using namespace std; + os << "MIDI::Port { "; + os << "device: " << port.device(); + os << "; "; + os << "name: " << port.name(); + os << "; "; + os << "type: " << port.type(); + os << "; "; + os << "mode: " << port.mode(); + os << "; "; + os << "ok: " << port.ok(); + os << "; "; + os << " }"; + return os; +} + +Port::Descriptor::Descriptor (const XMLNode& node) +{ + const XMLProperty *prop; + bool have_tag = false; + bool have_device = false; + bool have_type = false; + bool have_mode = false; + + if ((prop = node.property ("tag")) != 0) { + tag = prop->value(); + have_tag = true; + } + + if ((prop = node.property ("device")) != 0) { + device = prop->value(); + have_device = true; + } + + if ((prop = node.property ("type")) != 0) { + type = PortFactory::string_to_type (prop->value()); + have_type = true; + } + + if ((prop = node.property ("mode")) != 0) { + mode = PortFactory::string_to_mode (prop->value()); + have_mode = true; + } + + if (!have_tag || !have_device || !have_type || !have_mode) { + throw failed_constructor(); + } +} + diff --git a/libs/midi++2/miditrace.cc b/libs/midi++2/miditrace.cc index d7c65d9f29..fafe822f82 100644 --- a/libs/midi++2/miditrace.cc +++ b/libs/midi++2/miditrace.cc @@ -11,7 +11,6 @@ Transmitter fatal (Transmitter::Fatal); TextReceiver text_receiver ("mmctest"); #include "midi++/port.h" -#include "midi++/port_request.h" #include "midi++/manager.h" using namespace MIDI; diff --git a/libs/midi++2/mmctest.cc b/libs/midi++2/mmctest.cc index 36fbd61124..062f6e8d32 100644 --- a/libs/midi++2/mmctest.cc +++ b/libs/midi++2/mmctest.cc @@ -11,7 +11,6 @@ Transmitter fatal (Transmitter::Fatal); TextReceiver text_receiver ("mmctest"); #include "midi++/port.h" -#include "midi++/port_request.h" #include "midi++/manager.h" #include "midi++/mmc.h" diff --git a/libs/midi++2/port_request.cc b/libs/midi++2/port_request.cc deleted file mode 100644 index d209f02574..0000000000 --- a/libs/midi++2/port_request.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright (C) 2000 Paul Barton-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. - - $Id$ -*/ - -#include <fcntl.h> -#include <string.h> -#include <midi++/port.h> -#include <midi++/port_request.h> - -using namespace std; -using namespace MIDI; - -PortRequest::PortRequest (const string &xdev, - const string &xtag, - const string &xmode, - const string &xtype) - -{ - status = OK; - - devname = strdup (xdev.c_str()); - tagname = strdup (xtag.c_str()); - - if (xmode == "output" || - xmode == "out" || - xmode == "OUTPUT" || - xmode == "OUT") { - mode = O_WRONLY; - - } else if (xmode == "input" || - xmode == "in" || - xmode == "INPUT" || - xmode == "IN") { - mode = O_RDONLY; - - } else if (xmode == "duplex" || - xmode == "DUPLEX" || - xmode == "inout" || - xmode == "INOUT") { - mode = O_RDWR; - } else { - status = Unknown; - } - - if (xtype == "JACK" || - xtype == "jack") { - type = Port::JACK_Midi; - } else if (xtype == "ALSA/RAW" || - xtype == "alsa/raw") { - type = Port::ALSA_RawMidi; - } else if (xtype == "ALSA/SEQUENCER" || - xtype == "alsa/sequencer") { - type = Port::ALSA_Sequencer; - } else if (xtype == "COREMIDI" || - xtype == "coremidi") { - type = Port::CoreMidi_MidiPort; - } else if (xtype == "NULL" || - xtype == "null") { - type = Port::Null; - } else if (xtype == "FIFO" || - xtype == "fifo") { - type = Port::FIFO; - } else { - status = Unknown; - } -} - diff --git a/libs/pbd/SConscript b/libs/pbd/SConscript index 303ac84552..d513dfc762 100644 --- a/libs/pbd/SConscript +++ b/libs/pbd/SConscript @@ -31,6 +31,7 @@ filesystem_paths.cc file_utils.cc fpu.cc id.cc +misc.c mountpoint.cc pathscanner.cc pool.cc diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc index 07fcc09ace..2ce99ba631 100644 --- a/libs/pbd/convert.cc +++ b/libs/pbd/convert.cc @@ -30,6 +30,7 @@ using std::string; using std::vector; +using Glib::ustring; namespace PBD { @@ -194,6 +195,52 @@ url_decode (string& url) } } +void +url_decode (ustring& url) +{ + ustring::iterator last; + ustring::iterator next; + + for (ustring::iterator i = url.begin(); i != url.end(); ++i) { + if ((*i) == '+') { + next = i; + ++next; + url.replace (i, next, 1, ' '); + } + } + + if (url.length() <= 3) { + return; + } + + last = url.end(); + + --last; /* points at last char */ + --last; /* points at last char - 1 */ + + for (ustring::iterator i = url.begin(); i != last; ) { + + if (*i == '%') { + + next = i; + + url.erase (i); + + i = next; + ++next; + + if (isxdigit (*i) && isxdigit (*next)) { + /* replace first digit with char */ + url.replace (i, next, 1, (gunichar) int_from_hex (*i,*next)); + ++i; /* points at 2nd of 2 digits */ + url.erase (i); + } + } else { + ++i; + } + } +} + #if 0 string length2string (const int32_t frames, const float sample_rate) diff --git a/libs/pbd/file_utils.cc b/libs/pbd/file_utils.cc index efb065fbd4..f8dfe269c5 100644 --- a/libs/pbd/file_utils.cc +++ b/libs/pbd/file_utils.cc @@ -102,14 +102,6 @@ find_file_in_search_path(const SearchPath& search_path, if (tmp.size() == 0) { - info << string_compose - ( - "Found no file named %1 in search path %2", - filename, - search_path.to_string () - ) - << endmsg; - return false; } diff --git a/libs/pbd/misc.c b/libs/pbd/misc.c new file mode 100644 index 0000000000..797be5de45 --- /dev/null +++ b/libs/pbd/misc.c @@ -0,0 +1,21 @@ +#include <pbd/misc.h> + +#ifdef GTKOSX +#include <AppKit/AppKit.h> +#endif + +void +disable_screen_updates () +{ +#ifdef GTKOSX + NSDisableScreenUpdates (); +#endif +} + +void +enable_screen_updates () +{ +#ifdef GTKOSX + NSEnableScreenUpdates(); +#endif +} diff --git a/libs/pbd/pbd/convert.h b/libs/pbd/pbd/convert.h index 00176659cf..83cd285098 100644 --- a/libs/pbd/pbd/convert.h +++ b/libs/pbd/pbd/convert.h @@ -22,6 +22,9 @@ #include <string> #include <vector> +#include <sstream> +#include <iostream> +#include <glibmm/ustring.h> namespace PBD { @@ -30,6 +33,7 @@ std::string short_version (std::string, std::string::size_type target_length); int atoi (const std::string&); double atof (const std::string&); void url_decode (std::string&); +void url_decode (Glib::ustring&); // std::string length2string (const int32_t frames, const float sample_rate); std::string length2string (const int64_t frames, const double sample_rate); @@ -37,6 +41,14 @@ std::string length2string (const int64_t frames, const double sample_rate); std::vector<std::string> internationalize (const char *, const char **); bool strings_equal_ignore_case (const std::string& a, const std::string& b); +template <class T> std::string +to_string (T t, std::ios_base & (*f)(std::ios_base&)) +{ + std::ostringstream oss; + oss << f << t; + return oss.str(); +} + } //namespace PBD #endif /* __pbd_convert_h__ */ diff --git a/libs/pbd/pbd/functor_command.h b/libs/pbd/pbd/functor_command.h new file mode 100644 index 0000000000..e335f4418e --- /dev/null +++ b/libs/pbd/pbd/functor_command.h @@ -0,0 +1,121 @@ +/* + 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 __lib_pbd_functor_command_h__ +#define __lib_pbd_functor_command_h__ + +#include <iostream> +#include <sstream> +#include <string> +#include <map> + +#include <pbd/xml++.h> +#include <pbd/shiva.h> +#include <pbd/command.h> +#include <pbd/failed_constructor.h> + +/** This command class is initialized + */ + +namespace PBD { + +template <class obj_type, class arg_type> +class FunctorCommand : public Command +{ + private: + typedef void (obj_type::*functor_type)(arg_type); + typedef std::map< std::string, functor_type > FunctorMap; + typedef typename FunctorMap::iterator FunctorMapIterator; + + public: + FunctorCommand( + std::string functor, + obj_type object, + arg_type b, + arg_type a + ) : functor_name(functor), + object(object), + before(b), + after(a) + { + method = find_functor(functor); + + /* catch destruction of the object */ + new PBD::Shiva< obj_type, FunctorCommand<obj_type, arg_type> > (object, *this); + } + + ~FunctorCommand() { + GoingAway(); + } + + void operator() () { + (object.*method) (after); + } + + void undo() { + (object.*method) (before); + } + + virtual XMLNode &get_state() { + std::stringstream ss; + + XMLNode *node = new XMLNode("FunctorCommand"); + node->add_property("functor", functor_name); + ss << before; + node->add_property("before", ss.str()); + ss.clear (); + ss << after; + node->add_property("after", ss.str()); + + return *node; + } + + static void register_functor(std::string name, functor_type f) { + functor_map[name] = f; + } + + private: + static functor_type find_functor(std::string name) { + FunctorMapIterator iter; + + if((iter = functor_map.find(name)) == functor_map.end()) { + throw failed_constructor(); + } + + return iter->second; + } + + protected: + std::string functor_name; + obj_type &object; + arg_type before; + arg_type after; + functor_type method; + static FunctorMap functor_map; +}; + +// static initialization of functor_map... +template <class obj_type, class arg_type> +typename FunctorCommand<obj_type, arg_type>::FunctorMap +FunctorCommand<obj_type, arg_type>::functor_map; + +}; + +#endif // __lib_pbd_functor_command_h__ + diff --git a/libs/pbd/pbd/misc.h b/libs/pbd/pbd/misc.h new file mode 100644 index 0000000000..306c00683e --- /dev/null +++ b/libs/pbd/pbd/misc.h @@ -0,0 +1,15 @@ +#ifndef __pbd_misc_h__ +#define __pbd_misc_h__ + +#ifdef __cplusplus +extern "C" { +#endif + + void disable_screen_updates (); + void enable_screen_updates (); + +#ifdef __cplusplus +} +#endif + +#endif /* __pbd_misc_h__ */ diff --git a/libs/pbd/pbd/undo.h b/libs/pbd/pbd/undo.h index 5bfccf5a06..8f1716d09f 100644 --- a/libs/pbd/pbd/undo.h +++ b/libs/pbd/pbd/undo.h @@ -80,20 +80,24 @@ class UndoHistory : public sigc::trackable unsigned long undo_depth() const { return UndoList.size(); } unsigned long redo_depth() const { return RedoList.size(); } - std::string next_undo() const { return (UndoList.empty() ? std::string("") : UndoList.back()->name()); } - std::string next_redo() const { return (RedoList.empty() ? std::string("") : RedoList.back()->name()); } + std::string next_undo() const { return (UndoList.empty() ? std::string() : UndoList.back()->name()); } + std::string next_redo() const { return (RedoList.empty() ? std::string() : RedoList.back()->name()); } void clear (); void clear_undo (); void clear_redo (); - XMLNode &get_state(uint32_t depth = 0); - void save_state(); + XMLNode &get_state(int32_t depth = 0); + void save_state(); - sigc::signal<void> Changed; + void set_depth (int32_t); + int32_t get_depth() const { return _depth; } + sigc::signal<void> Changed; + private: bool _clearing; + int32_t _depth; std::list<UndoTransaction*> UndoList; std::list<UndoTransaction*> RedoList; diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index 6db85e6ab3..aeff37cce7 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -148,12 +148,28 @@ XMLNode &UndoTransaction::get_state() UndoHistory::UndoHistory () { _clearing = false; + _depth = 0; +} + +void +UndoHistory::set_depth (int32_t d) +{ + _depth = d; + + while (_depth > 0 && UndoList.size() > (uint32_t) _depth) { + UndoList.pop_front (); + } } void UndoHistory::add (UndoTransaction* const ut) { ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut)); + + while (_depth > 0 && UndoList.size() > (uint32_t) _depth) { + UndoList.pop_front (); + } + UndoList.push_back (ut); /* we are now owners of the transaction */ @@ -240,17 +256,22 @@ UndoHistory::clear () } XMLNode& -UndoHistory::get_state (uint32_t depth) +UndoHistory::get_state (int32_t depth) { XMLNode *node = new XMLNode ("UndoHistory"); if (depth == 0) { + + return (*node); + + } else if (depth < 0) { + /* everything */ for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) { node->add_child_nocopy((*it)->get_state()); } - + } else { /* just the last "depth" transactions */ @@ -268,3 +289,5 @@ UndoHistory::get_state (uint32_t depth) return *node; } + + diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 3906b53e36..93cbf088c7 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -27,7 +27,6 @@ #include <midi++/port.h> #include <midi++/manager.h> -#include <midi++/port_request.h> #include <ardour/route.h> #include <ardour/session.h> diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc index 05681c0c25..fa7134fa95 100644 --- a/libs/surfaces/mackie/mackie_control_protocol_poll.cc +++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc @@ -9,7 +9,6 @@ #include <midi++/types.h> #include <midi++/port.h> #include <midi++/manager.h> -#include <midi++/port_request.h> #include "i18n.h" #include <unistd.h> diff --git a/libs/surfaces/powermate/powermate.cc b/libs/surfaces/powermate/powermate.cc index 139313f3f8..8b3051af20 100644 --- a/libs/surfaces/powermate/powermate.cc +++ b/libs/surfaces/powermate/powermate.cc @@ -14,12 +14,15 @@ #include <i18n.h> #include <pbd/xml++.h> +#include <pbd/error.h> +#include <glibmm.h> #include "powermate.h" using namespace ARDOUR; using namespace std; using namespace sigc; +using namespace PBD; #define NUM_VALID_PREFIXES 2 @@ -32,17 +35,22 @@ static const char *valid_prefix[NUM_VALID_PREFIXES] = { int open_powermate(const char *dev, int mode) { - int fd = open(dev, mode); - int i; - char name[255]; - - if(fd < 0){ - fprintf(stderr, "Unable to open \"%s\": %s\n", dev, strerror(errno)); - return -1; - } + if (!Glib::file_test (dev, Glib::FILE_TEST_EXISTS)) { + return -1; + } + int fd = open(dev, mode); + int i; + char name[255]; + + if(fd < 0){ + if (errno != EACCES) { + error << string_compose ("Unable to open \"%1\": %2", dev, strerror(errno)) << endmsg; + } + return -1; + } if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){ - fprintf(stderr, "\"%s\": EVIOCGNAME failed: %s\n", dev, strerror(errno)); + error << string_compose ("\"%1\": EVIOCGNAME failed: %2", dev, strerror(errno)) << endmsg; close(fd); return -1; } |