diff options
author | David Robillard <d@drobilla.net> | 2007-01-09 23:24:54 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-01-09 23:24:54 +0000 |
commit | 532f6aad4ac79ca15d69deccd18fca90e444c437 (patch) | |
tree | 0d4ca5449af8eb48ad56e163efcab42c4656e8de /libs | |
parent | ef6b25432d9c46d71b08c0f7d5f2686df428c4e8 (diff) |
Merged with trunk R1283.
NOTE: Compiles, but broken (crash on adding MIDI track).
git-svn-id: svn://localhost/ardour2/branches/midi@1292 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
135 files changed, 5113 insertions, 2567 deletions
diff --git a/libs/appleutility/SConscript b/libs/appleutility/SConscript index c8bab03a87..68b731c78e 100644 --- a/libs/appleutility/SConscript +++ b/libs/appleutility/SConscript @@ -17,6 +17,6 @@ appleutility.Append(LINKFLAGS='-framework CoreServices') libappleutility = appleutility.SharedLibrary('appleutility', appleutility_files) if appleutility['COREAUDIO']: Default(libappleutility) - env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libappleutility)) + env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libappleutility)) env.Alias('tarball', env.Distribute (env['DISTTREE'], ['SConscript'] + appleutility_files + glob.glob('*.h') )) diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 98a1b362bb..68d9112caf 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -65,6 +65,7 @@ crossfade.cc curve.cc cycle_timer.cc default_click.cc +enums.cc gain.cc gdither.cc globals.cc @@ -127,10 +128,10 @@ if ardour['LIBLO']: extra_sources += osc_files ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE") -ardour.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"") -ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\""+final_prefix+"/lib\\\"") -ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"") -ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") +ardour.Append(CXXFLAGS="-DDATA_DIR=\\\"" + os.path.join (final_prefix, 'share') + "\\\"") +ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR']) + "\\\"") +ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\"" + final_config_prefix + "\\\"") +ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\"" + os.path.join (final_prefix, 'share', 'locale') + "\\\"") ardour.Merge ([ libraries['jack'] ]) @@ -180,17 +181,38 @@ int main(int argc, char** argv) return 0; } """ - def CheckJackVideoFrameOffset(context): context.Message('Checking for JackVideoFrameOffset in jack_position_bits_t enum...') result = context.TryLink(jack_video_frame_offset_test, '.c') context.Result(result) return result +# +# See if JACK supports jack_port_ensure_monitor_input() +# +jack_ensure_monitor_input_test = """ +#include <jack/jack.h> +int main(int argc, char** argv) +{ + jack_port_t **port; + + jack_port_ensure_monitor (*port, 1); + return 0; + +} +""" + +def CheckJackEnsureMonitorInput(context): + context.Message('Checking for jack_port_ensure_monitor_input()...') + result = context.TryLink(jack_ensure_monitor_input_test, '.c') + context.Result(result) + return result + conf = Configure(ardour, custom_tests = { 'CheckJackClientOpen' : CheckJackClientOpen, 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies, - 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset + 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset, + 'CheckJackEnsureMonitorInput' : CheckJackEnsureMonitorInput }) if conf.CheckJackClientOpen(): @@ -201,9 +223,9 @@ if conf.CheckJackRecomputeLatencies(): if conf.CheckJackVideoFrameOffset(): ardour.Append(CXXFLAGS="-DHAVE_JACK_VIDEO_SUPPORT") - -if conf.CheckFunc('jack_port_ensure_monitor'): - env.Append(CCFLAGS='-DHAVE_JACK_PORT_ENSURE_MONITOR') + +if conf.CheckJackEnsureMonitorInput(): + ardour.Append(CXXFLAGS='-DHAVE_JACK_PORT_ENSURE_MONITOR') else: print '\nWARNING: You need at least svn revision 985 of jack for hardware monitoring to work correctly.\n' @@ -220,14 +242,19 @@ if conf.CheckCHeader('sys/vfs.h'): if conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'): ardour.Append(LINKFLAGS="-framework CoreMIDI") +if conf.CheckCHeader('/System/Library/Frameworks/AudioToolbox.framework/Headers/ExtendedAudioFile.h'): + ardour.Append(LINKFLAGS="-framework AudioToolbox") + +if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h'): + ardour.Append(CXXFLAGS="-DHAVE_WEAK_COREAUDIO") + if conf.CheckCHeader('/System/Library/Frameworks/AudioUnit.framework/Headers/AudioUnit.h') and ardour['AUDIOUNITS']: ardour.Append(CXXFLAGS="-DHAVE_AUDIOUNITS") ardour.Append(LINKFLAGS="-framework AudioUnit") extra_sources += audiounit_files -if conf.CheckCHeader('/System/Library/Frameworks/AudioToolbox.framework/Headers/ExtendedAudioFile.h') and ardour['COREAUDIO']: +if ardour['COREAUDIO']: ardour.Append(CXXFLAGS="-DHAVE_COREAUDIO") - ardour.Append(LINKFLAGS="-framework AudioToolbox") extra_sources += coreaudio_files if env['CONFIG_ARCH'] == 'apple': @@ -287,7 +314,7 @@ if env['NLS']: i18n (ardour, ardour_files + vst_files + coreaudio_files + audiounit_files, env) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libardour)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libardour)) env.Alias('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], [])) diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h index 4a95e094a9..1da8903a41 100644 --- a/libs/ardour/ardour/audio_diskstream.h +++ b/libs/ardour/ardour/audio_diskstream.h @@ -90,9 +90,9 @@ class AudioDiskstream : public Diskstream } } - AudioPlaylist* audio_playlist () { return dynamic_cast<AudioPlaylist*>(_playlist); } + boost::shared_ptr<AudioPlaylist> audio_playlist () { return boost::dynamic_pointer_cast<AudioPlaylist>(_playlist); } - int use_playlist (Playlist *); + int use_playlist (boost::shared_ptr<Playlist>); int use_new_playlist (); int use_copy_playlist (); @@ -173,33 +173,39 @@ class AudioDiskstream : public Diskstream private: struct ChannelInfo { - - Sample *playback_wrap_buffer; - Sample *capture_wrap_buffer; - Sample *speed_buffer; - - float peak_power; - boost::shared_ptr<AudioFileSource> fades_source; - boost::shared_ptr<AudioFileSource> write_source; - - Port *source; - Sample *current_capture_buffer; - Sample *current_playback_buffer; + ChannelInfo (); + ~ChannelInfo (); - RingBufferNPT<Sample> *playback_buf; - RingBufferNPT<Sample> *capture_buf; + void init (nframes_t buffer_size, nframes_t speed_buffer_size, nframes_t wrap_buffer_size); + void release (); - Sample* scrub_buffer; - Sample* scrub_forward_buffer; - Sample* scrub_reverse_buffer; - - RingBufferNPT<Sample>::rw_vector playback_vector; - RingBufferNPT<Sample>::rw_vector capture_vector; - - RingBufferNPT<CaptureTransition> * capture_transition_buf; - // the following are used in the butler thread only - nframes_t curr_capture_cnt; + Sample *playback_wrap_buffer; + Sample *capture_wrap_buffer; + Sample *speed_buffer; + + float peak_power; + + boost::shared_ptr<AudioFileSource> fades_source; + boost::shared_ptr<AudioFileSource> write_source; + + Port *source; + Sample *current_capture_buffer; + Sample *current_playback_buffer; + + RingBufferNPT<Sample> *playback_buf; + RingBufferNPT<Sample> *capture_buf; + + Sample* scrub_buffer; + Sample* scrub_forward_buffer; + Sample* scrub_reverse_buffer; + + RingBufferNPT<Sample>::rw_vector playback_vector; + RingBufferNPT<Sample>::rw_vector capture_vector; + + RingBufferNPT<CaptureTransition> * capture_transition_buf; + // the following are used in the butler thread only + nframes_t curr_capture_cnt; }; /* The two central butler operations */ diff --git a/libs/ardour/ardour/audio_library.h b/libs/ardour/ardour/audio_library.h index f5ac6da654..8c01f0e3dc 100644 --- a/libs/ardour/ardour/audio_library.h +++ b/libs/ardour/ardour/audio_library.h @@ -1,6 +1,5 @@ /* - Copyright (C) 2003 Paul Davis - Author: Taybin Rutkin + Copyright (C) 2003-2006 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 @@ -16,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #ifndef __ardour_audio_library_h__ @@ -26,64 +24,30 @@ #include <map> #include <vector> -#include <sigc++/signal.h> - -#include <pbd/stateful.h> - using std::vector; using std::string; using std::map; namespace ARDOUR { -class AudioLibrary : public Stateful +class AudioLibrary { public: AudioLibrary (); ~AudioLibrary (); - static string state_node_name; - - XMLNode& get_state (void); - int set_state (const XMLNode&); - - void set_paths (vector<string> paths); - vector<string> get_paths (); - void scan_paths (); - - void add_member (string member); - void remove_member (string uri); + void set_tags (string member, vector<string> tags); + vector<string> get_tags (string member); - void search_members_and (vector<string>& results, - const map<string,string>& fields); - void search_members_or (vector<string>& results, - const map<string,string>& fields); - - void add_field (string field); - void get_fields (vector<string>& fields); - void remove_field (string field); - string get_field (string uri, string field); - void set_field (string uri, string field, string literal); - string get_label (string uri); - void set_label (string uri, string name); + void search_members_and (vector<string>& results, const vector<string> tags); void save_changes(); - - sigc::signal<void> fields_changed; private: - vector<string> sfdb_paths; - - string field_uri (string name); - - bool is_rdf_type (string uri, string type); - void remove_uri (string uri); - string src; - void initialize_db(); - void compact_vector (vector<string>& vec); - bool safe_file_extension (string); + string path2uri (string path); + string uri2path (string uri); }; extern AudioLibrary* Library; diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 207e3c6428..3dbd30f841 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -61,7 +61,7 @@ class AudioEngine : public sigc::trackable bool will_reconnect_at_halt (); void set_reconnect_at_halt (bool); - int stop (); + int stop (bool forever = false); int start (); bool running() const { return _running; } @@ -195,8 +195,7 @@ class AudioEngine : public sigc::trackable jack_client_t *_jack; std::string jack_client_name; Glib::Mutex _process_lock; - Glib::Mutex session_remove_lock; - Glib::Cond session_removed; + Glib::Cond session_removed; bool session_remove_pending; bool _running; bool _has_run; diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index bd609a7d80..c1dc7d1e93 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -59,6 +59,8 @@ class AudioFileSource : public AudioSource { static bool get_soundfile_info (string path, SoundFileInfo& _info, string& error); + static bool safe_file_extension (string path); + void set_allow_remove_if_empty (bool yn); void mark_for_remove(); diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h index 383ec73531..c0d5ed6e75 100644 --- a/libs/ardour/ardour/audioplaylist.h +++ b/libs/ardour/ardour/audioplaylist.h @@ -42,8 +42,10 @@ class AudioPlaylist : public ARDOUR::Playlist public: AudioPlaylist (Session&, const XMLNode&, bool hidden = false); AudioPlaylist (Session&, string name, bool hidden = false); - AudioPlaylist (const AudioPlaylist&, string name, bool hidden = false); - AudioPlaylist (const AudioPlaylist&, nframes_t start, nframes_t cnt, string name, bool hidden = false); + AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, string name, bool hidden = false); + AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, nframes_t start, nframes_t cnt, string name, bool hidden = false); + + ~AudioPlaylist (); void clear (bool with_signals=true); @@ -70,9 +72,6 @@ class AudioPlaylist : public ARDOUR::Playlist void check_dependents (boost::shared_ptr<Region> region, bool norefresh); void remove_dependents (boost::shared_ptr<Region> region); - protected: - ~AudioPlaylist (); /* public should use unref() */ - private: Crossfades _crossfades; /* xfades currently in use */ Crossfades _pending_xfade_adds; diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 2ada255236..0734a66319 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -113,6 +113,9 @@ const nframes_t frames_per_peak = 256; void build_peaks_from_scratch (); int do_build_peak (nframes_t, nframes_t); + void truncate_peakfile(); + + mutable off_t _peak_byte_max; // modified in do_build_peaks() 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; @@ -135,7 +138,7 @@ const nframes_t frames_per_peak = 256; static vector<boost::shared_ptr<AudioSource> > pending_peak_sources; static Glib::Mutex* pending_peak_sources_lock; - static void queue_for_peaks (boost::shared_ptr<AudioSource>); + static void queue_for_peaks (boost::shared_ptr<AudioSource>, bool notify=true); static void clear_queue_for_peaks (); struct PeakBuildRecord { diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h index e5c194e683..a2cfb23e61 100644 --- a/libs/ardour/ardour/automation_event.h +++ b/libs/ardour/ardour/automation_event.h @@ -82,7 +82,8 @@ class AutomationList : public PBD::StatefulDestructible void clear (); void x_scale (double factor); bool extend_to (double); - + void slide (iterator before, double distance); + void reposition_for_rt_add (double when); void rt_add (double when, double value); void add (double when, double value); @@ -190,7 +191,7 @@ class AutomationList : public PBD::StatefulDestructible AutomationEventList events; mutable Glib::Mutex lock; - bool _frozen; + int8_t _frozen; bool changed_when_thawed; bool _dirty; @@ -209,6 +210,7 @@ class AutomationList : public PBD::StatefulDestructible double min_yval; double max_yval; double default_value; + bool sort_pending; iterator rt_insertion_point; double rt_pos; diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index 8044190066..c520477c55 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -3,13 +3,8 @@ CONFIG_VARIABLE (AutoConnectOption, output_auto_connect, "output-auto-connect", AutoConnectOption (0)) CONFIG_VARIABLE (AutoConnectOption, input_auto_connect, "input-auto-connect", AutoConnectOption (0)) -#ifdef __APPLE__ -CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "coreaudio:Built-in Audio:in1") -CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "coreaudio:Built-in Audio:in2") -#else -CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "alsa_pcm:playback_1") -CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "alsa_pcm:playback_2") -#endif +CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default") +CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default") /* MIDI and MIDI related */ @@ -100,16 +95,15 @@ CONFIG_VARIABLE (bool, quieten_at_speed, "quieten-at-speed", true) /* timecode and sync */ CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true) +CONFIG_VARIABLE (SmpteFormat, smpte_format, "smpte-format", smpte_30) CONFIG_VARIABLE (bool, use_video_sync, "use-video-sync", false) CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true) -CONFIG_VARIABLE (float, smpte_frames_per_second, "smpte-frames-per-second", 30.0f) CONFIG_VARIABLE (float, video_pullup, "video-pullup", 0.0f) -CONFIG_VARIABLE (bool, smpte_drop_frames, "smpte-drop-frames", false) /* metering */ CONFIG_VARIABLE (float, meter_hold, "meter-hold", 100.0f) -CONFIG_VARIABLE (float, meter_falloff, "meter-falloff", 0.375f) +CONFIG_VARIABLE (float, meter_falloff, "meter-falloff", 27.0f) CONFIG_VARIABLE (nframes_t, over_length_short, "over-length-short", 2) CONFIG_VARIABLE (nframes_t, over_length_long, "over-length-long", 10) @@ -119,6 +113,8 @@ CONFIG_VARIABLE (bool, hiding_groups_deactivates_groups, "hiding-groups-deactiva CONFIG_VARIABLE (bool, verify_remove_last_capture, "verify-remove-last-capture", true) 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) /* BWAV */ @@ -127,4 +123,4 @@ CONFIG_VARIABLE (string, bwf_organization_code, "bwf-organization-code", "US") /* these variables have custom set() methods (e.g. path globbing) */ -CONFIG_VARIABLE_SPECIAL(std::string, raid_path, "raid-path", "", path_expand) +CONFIG_VARIABLE_SPECIAL(Glib::ustring, raid_path, "raid-path", "", path_expand) diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h index d29ba47056..0422698c5e 100644 --- a/libs/ardour/ardour/crossfade.h +++ b/libs/ardour/ardour/crossfade.h @@ -64,12 +64,13 @@ class Crossfade : public PBD::StatefulDestructible /* copy constructor to copy a crossfade with new regions. used (for example) - when a playlist copy is made */ + when a playlist copy is made + */ Crossfade (const Crossfade &, boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>); /* the usual XML constructor */ - Crossfade (const ARDOUR::Playlist&, XMLNode&); + Crossfade (const Playlist&, XMLNode&); virtual ~Crossfade(); bool operator== (const ARDOUR::Crossfade&); diff --git a/libs/ardour/ardour/cycles.h b/libs/ardour/ardour/cycles.h index ad3e512669..a6f34d59be 100644 --- a/libs/ardour/ardour/cycles.h +++ b/libs/ardour/ardour/cycles.h @@ -187,7 +187,7 @@ static inline cycles_t get_cycles (void) /* begin mach */ #elif defined(__APPLE__) -#ifdef HAVE_COREAUDIO +#ifdef HAVE_WEAK_COREAUDIO #include <CoreAudio/HostTime.h> #else // Due to MacTypes.h and libgnomecanvasmm Rect conflict typedef unsigned long long UInt64; diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h index eb6d936222..a81921b9f1 100644 --- a/libs/ardour/ardour/diskstream.h +++ b/libs/ardour/ardour/diskstream.h @@ -76,8 +76,8 @@ class IO; virtual float playback_buffer_load() const = 0; virtual float capture_buffer_load() const = 0; - void set_flag (Flag f) { _flags |= f; } - void unset_flag (Flag f) { _flags &= ~f; } + void set_flag (Flag f) { _flags = Flag (_flags | f); } + void unset_flag (Flag f) { _flags = Flag (_flags & ~f); } AlignStyle alignment_style() const { return _alignment_style; } void set_align_style (AlignStyle); @@ -104,9 +104,9 @@ class IO; void set_speed (double); void non_realtime_set_speed (); - Playlist* playlist () { return _playlist; } + boost::shared_ptr<Playlist> playlist () { return _playlist; } - virtual int use_playlist (Playlist *); + virtual int use_playlist (boost::shared_ptr<Playlist>); virtual int use_new_playlist () = 0; virtual int use_copy_playlist () = 0; @@ -205,7 +205,7 @@ class IO; virtual void playlist_changed (Change); virtual void playlist_modified (); - virtual void playlist_deleted (Playlist*); + virtual void playlist_deleted (boost::weak_ptr<Playlist>); virtual void finish_capture (bool rec_monitors_input) = 0; virtual void transport_stopped (struct tm&, time_t, bool abort) = 0; @@ -245,7 +245,8 @@ class IO; ARDOUR::Session& _session; ARDOUR::IO* _io; ChanCount _n_channels; - Playlist* _playlist; + + boost::shared_ptr<Playlist> _playlist; mutable gint _record_enabled; double _visible_speed; @@ -299,10 +300,9 @@ class IO; sigc::connection ports_created_c; sigc::connection plmod_connection; - sigc::connection plstate_connection; sigc::connection plgone_connection; - unsigned char _flags; + Flag _flags; }; }; /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/insert.h b/libs/ardour/ardour/insert.h index 217fd89885..31e59e6704 100644 --- a/libs/ardour/ardour/insert.h +++ b/libs/ardour/ardour/insert.h @@ -46,10 +46,8 @@ class Plugin; class Insert : public Redirect { public: - Insert(Session& s, Placement p); - Insert(Session& s, string name, Placement p); - - Insert(Session& s, Placement p, int imin, int imax, int omin, int omax); + Insert(Session& s, std::string name, Placement p); + Insert(Session& s, std::string name, Placement p, int imin, int imax, int omin, int omax); virtual ~Insert() { } @@ -87,6 +85,11 @@ class PortInsert : public Insert int32_t can_support_input_configuration (int32_t) const; int32_t configure_io (int32_t magic, int32_t in, int32_t out); int32_t compute_output_streams (int32_t cnt) const; + + uint32_t bit_slot() const { return bitslot; } + + private: + uint32_t bitslot; }; class PluginInsert : public Insert diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index f7e1993bb2..c4df46415b 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -203,16 +203,16 @@ class IO : public PBD::StatefulDestructible PBD::Controllable& gain_control() { return _gain_control; } - + static void update_meters(); -private: + private: static sigc::signal<void> Meter; static Glib::StaticMutex m_meter_signal_lock; sigc::connection m_meter_connection; -public: + public: /* automation */ diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index 94f70bb4e8..57e13de5af 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -68,7 +68,7 @@ class Location : public PBD::StatefulDestructible Location () { _start = 0; _end = 0; - _flags = 0; + _flags = Flags (0); } Location (const Location& other); @@ -124,7 +124,7 @@ class Location : public PBD::StatefulDestructible string _name; nframes_t _start; nframes_t _end; - uint32_t _flags; + Flags _flags; void set_mark (bool yn); bool set_flag_internal (bool yn, Flags flag); @@ -153,6 +153,7 @@ class Locations : public PBD::StatefulDestructible Location* end_location() const; Location* start_location() const; + int next_available_name(string& result,string base); uint32_t num_range_markers() const; int set_current (Location *, bool want_lock = true); diff --git a/libs/ardour/ardour/meter.h b/libs/ardour/ardour/meter.h index f18a2e6de9..17379c3baa 100644 --- a/libs/ardour/ardour/meter.h +++ b/libs/ardour/ardour/meter.h @@ -38,6 +38,7 @@ public: void setup (const ChanCount& in); void reset (); + void reset_max (); /** Compute peaks */ void run (BufferSet& bufs, jack_nframes_t nframes, jack_nframes_t offset=0); @@ -49,6 +50,14 @@ public: return minus_infinity(); } } + + float max_peak_power (uint32_t n) { + if (n < _max_peak_power.size()) { + return _max_peak_power[n]; + } else { + return minus_infinity(); + } + } private: @@ -58,6 +67,7 @@ private: Session& _session; std::vector<float> _peak_power; std::vector<float> _visible_peak_power; + std::vector<float> _max_peak_power; }; diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h index 583cc23de5..ee11b5e133 100644 --- a/libs/ardour/ardour/midi_diskstream.h +++ b/libs/ardour/ardour/midi_diskstream.h @@ -72,9 +72,9 @@ class MidiDiskstream : public Diskstream void set_record_enabled (bool yn); - MidiPlaylist* midi_playlist () { return dynamic_cast<MidiPlaylist*>(_playlist); } + boost::shared_ptr<MidiPlaylist> midi_playlist () { return boost::dynamic_pointer_cast<MidiPlaylist>(_playlist); } - int use_playlist (Playlist *); + int use_playlist (boost::shared_ptr<Playlist>); int use_new_playlist (); int use_copy_playlist (); diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h index 6f89d23404..492241d6b5 100644 --- a/libs/ardour/ardour/midi_playlist.h +++ b/libs/ardour/ardour/midi_playlist.h @@ -40,10 +40,12 @@ class MidiPlaylist : public ARDOUR::Playlist public: MidiPlaylist (Session&, const XMLNode&, bool hidden = false); MidiPlaylist (Session&, string name, bool hidden = false); - MidiPlaylist (const MidiPlaylist&, string name, bool hidden = false); - MidiPlaylist (const MidiPlaylist&, jack_nframes_t start, jack_nframes_t cnt, + MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden = false); + MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, jack_nframes_t start, jack_nframes_t cnt, string name, bool hidden = false); + ~MidiPlaylist (); + nframes_t read (MidiRingBuffer& buf, jack_nframes_t start, jack_nframes_t cnt, uint32_t chan_n=0); @@ -63,9 +65,6 @@ protected: void refresh_dependents (boost::shared_ptr<Region> region); void remove_dependents (boost::shared_ptr<Region> region); -protected: - ~MidiPlaylist (); /* public should use unref() */ - private: XMLNode& state (bool full_state); void dump () const; diff --git a/libs/ardour/ardour/named_selection.h b/libs/ardour/ardour/named_selection.h index 87b71e73ff..fd5777ccf6 100644 --- a/libs/ardour/ardour/named_selection.h +++ b/libs/ardour/ardour/named_selection.h @@ -23,6 +23,7 @@ #include <string> #include <list> +#include <boost/shared_ptr.hpp> #include <pbd/stateful.h> @@ -35,12 +36,12 @@ class Playlist; struct NamedSelection : public Stateful { - NamedSelection (std::string, std::list<Playlist*>&); + NamedSelection (std::string, std::list<boost::shared_ptr<Playlist> >&); NamedSelection (Session&, const XMLNode&); virtual ~NamedSelection (); std::string name; - std::list<Playlist*> playlists; + std::list<boost::shared_ptr<Playlist> > playlists; XMLNode& get_state (void); diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 831c9b3905..9893f7391a 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -26,6 +26,7 @@ #include <map> #include <list> #include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> #include <sys/stat.h> @@ -47,21 +48,25 @@ namespace ARDOUR { class Session; class Region; -class Playlist : public PBD::StatefulDestructible { +class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Playlist> { public: typedef list<boost::shared_ptr<Region> > RegionList; Playlist (Session&, const XMLNode&, DataType type, bool hidden = false); Playlist (Session&, string name, DataType type, bool hidden = false); - Playlist (const Playlist&, string name, bool hidden = false); - Playlist (const Playlist&, nframes_t start, nframes_t cnt, string name, bool hidden = false); + Playlist (boost::shared_ptr<const Playlist>, string name, bool hidden = false); + Playlist (boost::shared_ptr<const Playlist>, nframes_t start, nframes_t cnt, string name, bool hidden = false); + + virtual ~Playlist (); + + void set_region_ownership (); virtual void clear (bool with_signals=true); virtual void dump () const; - void ref(); - void unref(); - uint32_t refcnt() const { return _refcnt; } + void use(); + void release(); + bool used () const { return _refcnt != 0; } std::string name() const { return _name; } void set_name (std::string str); @@ -92,9 +97,9 @@ class Playlist : public PBD::StatefulDestructible { void duplicate (boost::shared_ptr<Region>, nframes_t position, float times); void nudge_after (nframes_t start, nframes_t distance, bool forwards); - Playlist* cut (list<AudioRange>&, bool result_is_hidden = true); - Playlist* copy (list<AudioRange>&, bool result_is_hidden = true); - int paste (Playlist&, nframes_t position, float times); + boost::shared_ptr<Playlist> cut (list<AudioRange>&, bool result_is_hidden = true); + boost::shared_ptr<Playlist> copy (list<AudioRange>&, bool result_is_hidden = true); + int paste (boost::shared_ptr<Playlist>, nframes_t position, float times); RegionList* regions_at (nframes_t frame); RegionList* regions_touched (nframes_t start, nframes_t end); @@ -109,14 +114,11 @@ class Playlist : public PBD::StatefulDestructible { int set_state (const XMLNode&); XMLNode& get_template (); - sigc::signal<void,Playlist*,bool> InUse; - sigc::signal<void> Modified; - sigc::signal<void> NameChanged; - sigc::signal<void> LengthChanged; - sigc::signal<void> LayeringChanged; - sigc::signal<void> StatePushed; - - static sigc::signal<void,Playlist*> PlaylistCreated; + sigc::signal<void,bool> InUse; + sigc::signal<void> Modified; + sigc::signal<void> NameChanged; + sigc::signal<void> LengthChanged; + sigc::signal<void> LayeringChanged; static string bump_name (string old_name, Session&); static string bump_name_once (string old_name); @@ -142,7 +144,6 @@ class Playlist : public PBD::StatefulDestructible { protected: friend class Session; - virtual ~Playlist (); /* members of the public use unref() */ protected: struct RegionLock { @@ -244,26 +245,23 @@ class Playlist : public PBD::StatefulDestructible { boost::shared_ptr<Region> region_by_id (PBD::ID); - void add_region_internal (boost::shared_ptr<Region>, nframes_t position, bool delay_sort = false); - - int remove_region_internal (boost::shared_ptr<Region>, bool delay_sort = false); + void add_region_internal (boost::shared_ptr<Region>, nframes_t position); + + int remove_region_internal (boost::shared_ptr<Region>); RegionList *find_regions_at (nframes_t frame); void copy_regions (RegionList&) const; void partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist); nframes_t _get_maximum_extent() const; - Playlist* cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t, bool), - list<AudioRange>& ranges, bool result_is_hidden); - Playlist *cut (nframes_t start, nframes_t cnt, bool result_is_hidden); - Playlist *copy (nframes_t start, nframes_t cnt, bool result_is_hidden); + boost::shared_ptr<Playlist> cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t, bool), + list<AudioRange>& ranges, bool result_is_hidden); + boost::shared_ptr<Playlist> cut (nframes_t start, nframes_t cnt, bool result_is_hidden); + boost::shared_ptr<Playlist> copy (nframes_t start, nframes_t cnt, bool result_is_hidden); int move_region_to_layer (layer_t, boost::shared_ptr<Region> r, int dir); void relayer (); - - static Playlist* copyPlaylist (const Playlist&, nframes_t start, nframes_t length, - string name, bool result_is_hidden); void unset_freeze_parent (Playlist*); void unset_freeze_child (Playlist*); diff --git a/libs/ardour/ardour/playlist_factory.h b/libs/ardour/ardour/playlist_factory.h new file mode 100644 index 0000000000..23aad3cd78 --- /dev/null +++ b/libs/ardour/ardour/playlist_factory.h @@ -0,0 +1,25 @@ +#ifndef __ardour_playlist_factory_h__ +#define __ardour_playlist_factory_h__ + +#include <ardour/playlist.h> + +class XMLNode; + +namespace ARDOUR { + +class Session; + +class PlaylistFactory { + + public: + static sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistCreated; + + static boost::shared_ptr<Playlist> create (Session&, const XMLNode&, bool hidden = false); + static boost::shared_ptr<Playlist> create (DataType type, Session&, string name, bool hidden = false); + static boost::shared_ptr<Playlist> create (boost::shared_ptr<const Playlist>, string name, bool hidden = false); + static boost::shared_ptr<Playlist> create (boost::shared_ptr<const Playlist>, nframes_t start, nframes_t cnt, string name, bool hidden = false); +}; + +} + +#endif /* __ardour_playlist_factory_h__ */ diff --git a/libs/ardour/ardour/redirect.h b/libs/ardour/ardour/redirect.h index 79ae4516c5..8c3de09c10 100644 --- a/libs/ardour/ardour/redirect.h +++ b/libs/ardour/ardour/redirect.h @@ -111,8 +111,6 @@ class Redirect : public IO virtual void transport_stopped (nframes_t frame) {}; protected: - void set_placement (const string&, void *src); - /* children may use this stuff as they see fit */ map<uint32_t,AutomationList*> parameter_automation; diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 88bb294e5d..46865d8357 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -167,9 +167,8 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro virtual uint32_t read_data_count() const { return _read_data_count; } - ARDOUR::Playlist* playlist() const { return _playlist; } - - void set_playlist (ARDOUR::Playlist*); + boost::shared_ptr<ARDOUR::Playlist> playlist() const { return _playlist.lock(); } + virtual void set_playlist (boost::weak_ptr<ARDOUR::Playlist>); void source_deleted (boost::shared_ptr<Source>); @@ -238,7 +237,7 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro Change _pending_changed; uint64_t _last_layer_op; ///< timestamp Glib::Mutex _lock; - ARDOUR::Playlist* _playlist; + boost::weak_ptr<ARDOUR::Playlist> _playlist; SourceList _sources; /** Used when timefx are applied, so we can always use the original source */ SourceList _master_sources; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 6bc37ee51e..2f94e0c80a 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -248,7 +248,7 @@ class Route : public IO void curve_reallocate (); protected: - unsigned char _flags; + Flag _flags; /* tight cache-line access here is more important than sheer speed of access. diff --git a/libs/ardour/ardour/route_group.h b/libs/ardour/ardour/route_group.h index e9fad1aa2b..d87d3fa3a4 100644 --- a/libs/ardour/ardour/route_group.h +++ b/libs/ardour/ardour/route_group.h @@ -115,7 +115,7 @@ class RouteGroup : public Stateful, public sigc::trackable { Session& _session; list<Route *> routes; string _name; - uint32_t _flags; + Flag _flags; void remove_when_going_away (Route*); }; diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h index 12a0cba3ec..eceb301bf8 100644 --- a/libs/ardour/ardour/send.h +++ b/libs/ardour/ardour/send.h @@ -33,12 +33,15 @@ namespace ARDOUR { -class Send : public Redirect { +class Send : public Redirect +{ public: Send (Session&, Placement); Send (Session&, const XMLNode&); Send (const Send&); ~Send (); + + uint32_t bit_slot() const { return bitslot; } void run (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset); @@ -54,9 +57,12 @@ class Send : public Redirect { uint32_t pans_required() const { return _expected_inputs.get(DataType::AUDIO); } void expect_inputs (const ChanCount&); + static uint32_t how_many_sends(); + private: bool _metering; ChanCount _expected_inputs; + uint32_t bitslot; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 039bf92362..1c1ea9e62c 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -29,6 +29,7 @@ #include <stack> #include <boost/weak_ptr.hpp> +#include <boost/dynamic_bitset.hpp> #include <stdint.h> @@ -117,9 +118,9 @@ using std::set; class Session : public PBD::StatefulDestructible { private: - typedef std::pair<boost::shared_ptr<Route>,bool> RouteBooleanState; + typedef std::pair<boost::weak_ptr<Route>,bool> RouteBooleanState; typedef vector<RouteBooleanState> GlobalRouteBooleanState; - typedef std::pair<boost::shared_ptr<Route>,MeterPoint> RouteMeterState; + typedef std::pair<boost::weak_ptr<Route>,MeterPoint> RouteMeterState; typedef vector<RouteMeterState> GlobalRouteMeterState; public: @@ -253,6 +254,7 @@ class Session : public PBD::StatefulDestructible void set_dirty (); void set_clean (); bool dirty() const { return _state_of_the_state & Dirty; } + void set_deletion_in_progress (); bool deletion_in_progress() const { return _state_of_the_state & Deletion; } sigc::signal<void> DirtyChanged; @@ -391,6 +393,9 @@ class Session : public PBD::StatefulDestructible double frames_per_smpte_frame() const { return _frames_per_smpte_frame; } nframes_t smpte_frames_per_hour() const { return _smpte_frames_per_hour; } + float smpte_frames_per_second() const; + bool smpte_drop_frames() const; + /* Locations */ Locations *locations() { return &_locations; } @@ -401,6 +406,7 @@ class Session : public PBD::StatefulDestructible void set_auto_punch_location (Location *); void set_auto_loop_location (Location *); + int location_name(string& result, string base = string("")); void reset_input_monitor_state (); @@ -495,19 +501,6 @@ class Session : public PBD::StatefulDestructible nframes_t transport_frame () const {return _transport_frame; } nframes_t audible_frame () const; - enum SmpteFormat { - smpte_23976, - smpte_24, - smpte_24976, - smpte_25, - smpte_2997, - smpte_2997drop, - smpte_30, - smpte_30drop, - smpte_5994, - smpte_60, - }; - enum PullupFormat { pullup_Plus4Plus1, pullup_Plus4, @@ -517,11 +510,10 @@ class Session : public PBD::StatefulDestructible pullup_Minus1, pullup_Minus4Plus1, pullup_Minus4, - pullup_Minus4Minus1, + pullup_Minus4Minus1 }; - int set_smpte_type (float fps, bool drop_frames); - + int set_smpte_format (SmpteFormat); void sync_time_vars(); void bbt_time (nframes_t when, BBT_Time&); @@ -584,7 +576,7 @@ class Session : public PBD::StatefulDestructible bool multichan; bool sample_convert; volatile bool freeze; - string pathname; + std::vector<Glib::ustring> paths; /* result */ std::vector<boost::shared_ptr<Region> > new_regions; @@ -602,7 +594,6 @@ class Session : public PBD::StatefulDestructible void add_source (boost::shared_ptr<Source>); void remove_source (boost::weak_ptr<Source>); - int cleanup_audio_file_source (boost::shared_ptr<AudioFileSource>); struct cleanup_report { vector<string> paths; @@ -622,8 +613,7 @@ class Session : public PBD::StatefulDestructible this playlist. */ - sigc::signal<int,ARDOUR::Playlist*> AskAboutPlaylistDeletion; - + sigc::signal<int,boost::shared_ptr<ARDOUR::Playlist> > AskAboutPlaylistDeletion; /* handlers should return !0 for use pending state, 0 for ignore it. @@ -631,9 +621,6 @@ class Session : public PBD::StatefulDestructible static sigc::signal<int> AskAboutPendingState; - sigc::signal<void,boost::shared_ptr<Source> > SourceAdded; - sigc::signal<void,boost::shared_ptr<Source> > SourceRemoved; - boost::shared_ptr<AudioFileSource> create_audio_source_for_session (ARDOUR::AudioDiskstream&, uint32_t which_channel, bool destructive); boost::shared_ptr<MidiSource> create_midi_source_for_session (ARDOUR::MidiDiskstream&); @@ -642,15 +629,15 @@ class Session : public PBD::StatefulDestructible /* playlist management */ - Playlist* playlist_by_name (string name); - void add_playlist (Playlist *); - sigc::signal<void,Playlist*> PlaylistAdded; - sigc::signal<void,Playlist*> PlaylistRemoved; + boost::shared_ptr<Playlist> playlist_by_name (string name); + void add_playlist (boost::shared_ptr<Playlist>); + sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistAdded; + sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistRemoved; uint32_t n_playlists() const; - template<class T> void foreach_playlist (T *obj, void (T::*func)(Playlist *)); - void get_playlists (std::vector<Playlist*>&); + template<class T> void foreach_playlist (T *obj, void (T::*func)(boost::shared_ptr<Playlist>)); + void get_playlists (std::vector<boost::shared_ptr<Playlist> >&); /* named selections */ @@ -711,9 +698,11 @@ class Session : public PBD::StatefulDestructible uint32_t n_plugin_inserts() const { return _plugin_inserts.size(); } uint32_t n_sends() const { return _sends.size(); } - string next_send_name(); - string next_insert_name(); - + uint32_t next_send_id(); + uint32_t next_insert_id(); + void mark_send_id (uint32_t); + void mark_insert_id (uint32_t); + /* s/w "RAID" management */ nframes_t available_capture_duration(); @@ -786,59 +775,75 @@ class Session : public PBD::StatefulDestructible std::map<PBD::ID, PBD::StatefulThingWithGoingAway*> registry; // these commands are implemented in libs/ardour/session_command.cc - Command *memento_command_factory(XMLNode *n); - void register_with_memento_command_factory(PBD::ID, PBD::StatefulThingWithGoingAway *); + Command* memento_command_factory(XMLNode* n); + void register_with_memento_command_factory(PBD::ID, PBD::StatefulThingWithGoingAway*); + + Command* global_state_command_factory (const XMLNode& n); + + class GlobalRouteStateCommand : public Command + { + public: + GlobalRouteStateCommand (Session&, void*); + GlobalRouteStateCommand (Session&, const XMLNode& node); + int set_state (const XMLNode&); + XMLNode& get_state (); + + protected: + GlobalRouteBooleanState before, after; + Session& sess; + void* src; + + }; - class GlobalSoloStateCommand : public Command + class GlobalSoloStateCommand : public GlobalRouteStateCommand { - GlobalRouteBooleanState before, after; - Session &sess; - void *src; - public: - GlobalSoloStateCommand(Session &, void *src); - void operator()(); - void undo(); - XMLNode &get_state(); - void mark(); + public: + GlobalSoloStateCommand (Session &, void *src); + GlobalSoloStateCommand (Session&, const XMLNode&); + void operator()(); //redo + void undo(); + XMLNode &get_state(); + void mark(); }; - class GlobalMuteStateCommand : public Command + class GlobalMuteStateCommand : public GlobalRouteStateCommand { - GlobalRouteBooleanState before, after; - Session &sess; - void *src; - public: - GlobalMuteStateCommand(Session &, void *src); - void operator()(); - void undo(); - XMLNode &get_state(); - void mark(); + public: + GlobalMuteStateCommand(Session &, void *src); + GlobalMuteStateCommand (Session&, const XMLNode&); + void operator()(); // redo + void undo(); + XMLNode &get_state(); + void mark(); }; - class GlobalRecordEnableStateCommand : public Command + class GlobalRecordEnableStateCommand : public GlobalRouteStateCommand { - GlobalRouteBooleanState before, after; - Session &sess; - void *src; - public: - GlobalRecordEnableStateCommand(Session &, void *src); - void operator()(); - void undo(); - XMLNode &get_state(); - void mark(); + public: + GlobalRecordEnableStateCommand(Session &, void *src); + GlobalRecordEnableStateCommand (Session&, const XMLNode&); + void operator()(); // redo + void undo(); + XMLNode &get_state(); + void mark(); }; class GlobalMeteringStateCommand : public Command { - GlobalRouteMeterState before, after; - Session &sess; - void *src; - public: - GlobalMeteringStateCommand(Session &, void *src); - void operator()(); - void undo(); - XMLNode &get_state(); - void mark(); + public: + GlobalMeteringStateCommand(Session &, void *src); + GlobalMeteringStateCommand (Session&, const XMLNode&); + void operator()(); + void undo(); + XMLNode &get_state(); + int set_state (const XMLNode&); + void mark(); + + protected: + Session& sess; + void* src; + GlobalRouteMeterState before; + GlobalRouteMeterState after; }; /* clicking */ @@ -951,6 +956,7 @@ class Session : public PBD::StatefulDestructible private: int create (bool& new_session, string* mix_template, nframes_t initial_length); + void destroy (); nframes_t compute_initial_length (); @@ -1102,7 +1108,6 @@ class Session : public PBD::StatefulDestructible void hookup_io (); void when_engine_running (); - sigc::connection first_time_running; void graph_reordered (); string _current_snapshot_name; @@ -1134,6 +1139,8 @@ class Session : public PBD::StatefulDestructible bool butler_should_run; mutable gint butler_should_do_transport_work; int butler_request_pipe[2]; + + inline bool transport_work_requested() const { return g_atomic_int_get(&butler_should_do_transport_work); } struct ButlerRequest { enum Type { @@ -1306,7 +1313,7 @@ class Session : public PBD::StatefulDestructible nframes_t _smpte_frames_per_hour; nframes_t _smpte_offset; bool _smpte_offset_negative; - + /* cache the most-recently requested time conversions. This helps when we * have multiple clocks showing the same time (e.g. the transport frame) */ bool last_smpte_valid; @@ -1392,9 +1399,8 @@ class Session : public PBD::StatefulDestructible void realtime_stop (bool abort); void non_realtime_start_scrub (); void non_realtime_set_speed (); - void non_realtime_stop (bool abort); - void non_realtime_overwrite (); - void non_realtime_buffer_fill (); + void non_realtime_stop (bool abort, int entry_request_count, bool& finished); + void non_realtime_overwrite (int entry_request_count, bool& finished); void butler_transport_work (); void post_transport (); void engine_halted (); @@ -1469,19 +1475,19 @@ class Session : public PBD::StatefulDestructible /* PLAYLISTS */ mutable Glib::Mutex playlist_lock; - typedef set<Playlist *> PlaylistList; + typedef set<boost::shared_ptr<Playlist> > PlaylistList; PlaylistList playlists; PlaylistList unused_playlists; int load_playlists (const XMLNode&); int load_unused_playlists (const XMLNode&); - void remove_playlist (Playlist *); - void track_playlist (Playlist *, bool); + void remove_playlist (boost::weak_ptr<Playlist>); + void track_playlist (bool, boost::weak_ptr<Playlist>); - Playlist *playlist_factory (string name); - Playlist *XMLPlaylistFactory (const XMLNode&); + boost::shared_ptr<Playlist> playlist_factory (string name); + boost::shared_ptr<Playlist> XMLPlaylistFactory (const XMLNode&); - void playlist_length_changed (Playlist *); + void playlist_length_changed (); void diskstream_playlist_changed (boost::shared_ptr<Diskstream>); /* NAMED SELECTIONS */ @@ -1522,9 +1528,12 @@ class Session : public PBD::StatefulDestructible list<PortInsert *> _port_inserts; list<PluginInsert *> _plugin_inserts; list<Send *> _sends; + boost::dynamic_bitset<uint32_t> send_bitset; + boost::dynamic_bitset<uint32_t> insert_bitset; uint32_t send_cnt; uint32_t insert_cnt; + void add_redirect (Redirect *); void remove_redirect (Redirect *); diff --git a/libs/ardour/ardour/session_playlist.h b/libs/ardour/ardour/session_playlist.h index 6f1b8dbd12..20cf4d8f2e 100644 --- a/libs/ardour/ardour/session_playlist.h +++ b/libs/ardour/ardour/session_playlist.h @@ -27,7 +27,7 @@ namespace ARDOUR { template<class T> void -Session::foreach_playlist (T *obj, void (T::*func)(Playlist *)) +Session::foreach_playlist (T *obj, void (T::*func)(boost::shared_ptr<Playlist>)) { Glib::Mutex::Lock lm (playlist_lock); for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); i++) { diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h index e94b1af54f..8eaab14ec5 100644 --- a/libs/ardour/ardour/source.h +++ b/libs/ardour/ardour/source.h @@ -66,8 +66,8 @@ class Source : public PBD::StatefulDestructible void use () { _in_use++; } void disuse () { if (_in_use) { _in_use--; } } - void add_playlist (ARDOUR::Playlist*); - void remove_playlist (ARDOUR::Playlist*); + void add_playlist (boost::shared_ptr<ARDOUR::Playlist>); + void remove_playlist (boost::weak_ptr<ARDOUR::Playlist>); uint32_t used() const; @@ -83,7 +83,7 @@ class Source : public PBD::StatefulDestructible time_t _timestamp; jack_nframes_t _length; - std::set<ARDOUR::Playlist*> _playlists; + std::set<boost::shared_ptr<ARDOUR::Playlist> > _playlists; private: uint32_t _in_use; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index d7a2da2f46..eee04d9bfa 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -115,13 +115,12 @@ class Track : public Route struct FreezeRecord { FreezeRecord() - : playlist(0) - , have_mementos(false) + : have_mementos(false) {} ~FreezeRecord(); - Playlist* playlist; + boost::shared_ptr<Playlist> playlist; vector<FreezeRecordInsertInfo*> insert_info; bool have_mementos; FreezeState state; diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 1138b5208f..30cdcd8232 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -143,6 +143,18 @@ namespace ARDOUR { } }; + enum SmpteFormat { + smpte_23976, + smpte_24, + smpte_24976, + smpte_25, + smpte_2997, + smpte_2997drop, + smpte_30, + smpte_30drop, + smpte_5994, + smpte_60 + }; struct AnyTime { enum Type { @@ -322,7 +334,7 @@ namespace ARDOUR { }; enum ShuttleUnits { - Percentage, + Percentage, Semitones }; @@ -340,6 +352,7 @@ std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf); std::istream& operator>>(std::istream& o, ARDOUR::SlaveSource& sf); std::istream& operator>>(std::istream& o, ARDOUR::ShuttleBehaviour& sf); std::istream& operator>>(std::istream& o, ARDOUR::ShuttleUnits& sf); +std::istream& operator>>(std::istream& o, ARDOUR::SmpteFormat& sf); using ARDOUR::nframes_t; diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index d926f52f82..de97a5c150 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -34,10 +34,9 @@ class XMLNode; void elapsed_time_to_str (char *buf, uint32_t seconds); -std::string legalize_for_path (std::string str); +Glib::ustring legalize_for_path (Glib::ustring str); std::ostream& operator<< (std::ostream& o, const ARDOUR::BBT_Time& bbt); XMLNode* find_named_node (const XMLNode& node, std::string name); -std::string placement_as_string (ARDOUR::Placement); static inline float f_max(float x, float a) { x -= a; @@ -52,10 +51,11 @@ int cmp_nocase (const std::string& s, const std::string& s2); int tokenize_fullpath (std::string fullpath, std::string& path, std::string& name); -int touch_file(std::string path); +int touch_file(Glib::ustring path); -std::string region_name_from_path (std::string path); -std::string path_expand (std::string); +Glib::ustring path_expand (Glib::ustring); +Glib::ustring region_name_from_path (Glib::ustring path, bool strip_channels); +bool path_is_paired (Glib::ustring path, Glib::ustring& pair_base); void compute_equal_power_fades (nframes_t nframes, float* in, float* out); @@ -66,6 +66,8 @@ const char* edit_mode_to_string (ARDOUR::EditMode); ARDOUR::EditMode string_to_edit_mode (std::string); float meter_falloff_to_float (ARDOUR::MeterFalloff); +ARDOUR::MeterFalloff meter_falloff_from_float (float); +float meter_falloff_to_db_per_sec (float); float meter_hold_to_float (ARDOUR::MeterHold); #if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS) diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 9c5f5233b7..7165a58b27 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -35,6 +35,7 @@ #include <glibmm/thread.h> #include <pbd/xml++.h> #include <pbd/memento_command.h> +#include <pbd/enumwriter.h> #include <ardour/ardour.h> #include <ardour/audioengine.h> @@ -46,6 +47,7 @@ #include <ardour/send.h> #include <ardour/region_factory.h> #include <ardour/audioplaylist.h> +#include <ardour/playlist_factory.h> #include <ardour/cycle_timer.h> #include <ardour/audioregion.h> #include <ardour/audio_port.h> @@ -96,34 +98,6 @@ AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node) } void -AudioDiskstream::init_channel (ChannelInfo &chan) -{ - chan.playback_wrap_buffer = 0; - chan.capture_wrap_buffer = 0; - chan.speed_buffer = 0; - chan.peak_power = 0.0f; - chan.source = 0; - chan.current_capture_buffer = 0; - chan.current_playback_buffer = 0; - chan.curr_capture_cnt = 0; - - chan.playback_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size()); - chan.capture_buf = new RingBufferNPT<Sample> (_session.diskstream_buffer_size()); - chan.capture_transition_buf = new RingBufferNPT<CaptureTransition> (128); - - - /* touch the ringbuffer buffers, which will cause - them to be mapped into locked physical RAM if - we're running with mlockall(). this doesn't do - much if we're not. - */ - memset (chan.playback_buf->buffer(), 0, sizeof (Sample) * chan.playback_buf->bufsize()); - memset (chan.capture_buf->buffer(), 0, sizeof (Sample) * chan.capture_buf->bufsize()); - memset (chan.capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * chan.capture_transition_buf->bufsize()); -} - - -void AudioDiskstream::init (Diskstream::Flag f) { Diskstream::init(f); @@ -140,40 +114,21 @@ AudioDiskstream::init (Diskstream::Flag f) assert(_n_channels == ChanCount(DataType::AUDIO, 1)); } -void -AudioDiskstream::destroy_channel (ChannelInfo &chan) -{ - if (chan.write_source) { - chan.write_source.reset (); - } - - if (chan.speed_buffer) { - delete [] chan.speed_buffer; - } - - if (chan.playback_wrap_buffer) { - delete [] chan.playback_wrap_buffer; - } - if (chan.capture_wrap_buffer) { - delete [] chan.capture_wrap_buffer; - } - - delete chan.playback_buf; - delete chan.capture_buf; - delete chan.capture_transition_buf; - - chan.playback_buf = 0; - chan.capture_buf = 0; -} - AudioDiskstream::~AudioDiskstream () { - Glib::Mutex::Lock lm (state_lock); + notify_callbacks (); - for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) - destroy_channel((*chan)); - - channels.clear(); + { + /* don't be holding this lock as we exit the destructor, glib will wince + visibly since the mutex gets destroyed before we release it. + */ + + Glib::Mutex::Lock lm (state_lock); + for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) { + (*chan).release (); + } + channels.clear(); + } } void @@ -288,15 +243,13 @@ AudioDiskstream::get_input_sources () int AudioDiskstream::find_and_use_playlist (const string& name) { - Playlist* pl; - AudioPlaylist* playlist; + boost::shared_ptr<AudioPlaylist> playlist; - if ((pl = _session.playlist_by_name (name)) == 0) { - playlist = new AudioPlaylist(_session, name); - pl = playlist; + if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlist_by_name (name))) == 0) { + playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, name)); } - if ((playlist = dynamic_cast<AudioPlaylist*> (pl)) == 0) { + if (!playlist) { error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg; return -1; } @@ -305,9 +258,9 @@ AudioDiskstream::find_and_use_playlist (const string& name) } int -AudioDiskstream::use_playlist (Playlist* playlist) +AudioDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist) { - assert(dynamic_cast<AudioPlaylist*>(playlist)); + assert(boost::dynamic_pointer_cast<AudioPlaylist>(playlist)); Diskstream::use_playlist(playlist); @@ -318,7 +271,7 @@ int AudioDiskstream::use_new_playlist () { string newname; - AudioPlaylist* playlist; + boost::shared_ptr<AudioPlaylist> playlist; if (!in_set_state && destructive()) { return 0; @@ -330,9 +283,11 @@ AudioDiskstream::use_new_playlist () newname = Playlist::bump_name (_name, _session); } - if ((playlist = new AudioPlaylist (_session, newname, hidden())) != 0) { + if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (_session, newname, hidden()))) != 0) { + playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); + } else { return -1; } @@ -353,11 +308,11 @@ AudioDiskstream::use_copy_playlist () } string newname; - AudioPlaylist* playlist; + boost::shared_ptr<AudioPlaylist> playlist; newname = Playlist::bump_name (_playlist->name(), _session); - if ((playlist = new AudioPlaylist (*audio_playlist(), newname)) != 0) { + if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) { playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); } else { @@ -1553,7 +1508,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca } else { string whole_file_region_name; - whole_file_region_name = region_name_from_path (channels[0].write_source->name()); + whole_file_region_name = region_name_from_path (channels[0].write_source->name(), true); /* Register a new region with the Session that describes the entire source. Do this first @@ -1684,7 +1639,7 @@ AudioDiskstream::finish_capture (bool rec_monitors_input) void AudioDiskstream::set_record_enabled (bool yn) { - if (!recordable() || !_session.record_enabling_legal()) { + if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs().get(DataType::AUDIO) == 0) { return; } @@ -1762,8 +1717,7 @@ AudioDiskstream::get_state () char buf[64] = ""; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof(buf), "0x%x", _flags); - node->add_property ("flags", buf); + node->add_property ("flags", enum_2_string (_flags)); snprintf (buf, sizeof(buf), "%zd", channels.size()); node->add_property ("channels", buf); @@ -1850,7 +1804,7 @@ AudioDiskstream::set_state (const XMLNode& node) } if ((prop = node.property ("flags")) != 0) { - _flags = strtol (prop->value().c_str(), 0, 0); + _flags = Flag (string_2_enum (prop->value(), _flags)); } if ((prop = node.property ("channels")) != 0) { @@ -1982,7 +1936,7 @@ AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force) } capturing_sources.clear (); - + for (chan = channels.begin(), n = 0; chan != channels.end(); ++chan, ++n) { if (!destructive()) { @@ -2109,15 +2063,19 @@ AudioDiskstream::add_channel () { /* XXX need to take lock??? */ - ChannelInfo chan; + /* this copies the ChannelInfo, which currently has no buffers. kind + of pointless really, but we want the channels list to contain + actual objects, not pointers to objects. mostly for convenience, + which isn't much in evidence. + */ - init_channel (chan); + channels.push_back (ChannelInfo()); - chan.speed_buffer = new Sample[speed_buffer_size]; - chan.playback_wrap_buffer = new Sample[wrap_buffer_size]; - chan.capture_wrap_buffer = new Sample[wrap_buffer_size]; + /* now allocate the buffers */ - channels.push_back (chan); + channels.back().init (_session.diskstream_buffer_size(), + speed_buffer_size, + wrap_buffer_size); _n_channels.set(DataType::AUDIO, channels.size()); @@ -2129,10 +2087,8 @@ AudioDiskstream::remove_channel () { if (channels.size() > 1) { /* XXX need to take lock??? */ - ChannelInfo & chan = channels.back(); - destroy_channel (chan); + channels.back().release (); channels.pop_back(); - _n_channels.set(DataType::AUDIO, channels.size()); return 0; } @@ -2217,7 +2173,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) try { region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), - region_name_from_path (first_fs->name()), + region_name_from_path (first_fs->name(), true), 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile))); region->special_set_position (0); } @@ -2231,7 +2187,7 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node) } try { - region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name()))); + region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true))); } catch (failed_constructor& err) { @@ -2261,10 +2217,10 @@ AudioDiskstream::set_destructive (bool yn) if (!can_become_destructive (bounce_ignored)) { return -1; } - _flags |= Destructive; + _flags = Flag (_flags | Destructive); use_destructive_playlist (); } else { - _flags &= ~Destructive; + _flags = Flag (_flags & ~Destructive); reset_write_sources (true, true); } } @@ -2313,3 +2269,82 @@ AudioDiskstream::can_become_destructive (bool& requires_bounce) const requires_bounce = false; return true; } + +AudioDiskstream::ChannelInfo::ChannelInfo () +{ + playback_wrap_buffer = 0; + capture_wrap_buffer = 0; + speed_buffer = 0; + peak_power = 0.0f; + source = 0; + current_capture_buffer = 0; + current_playback_buffer = 0; + curr_capture_cnt = 0; + playback_buf = 0; + capture_buf = 0; + capture_transition_buf = 0; +} + +void +AudioDiskstream::ChannelInfo::init (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size) +{ + speed_buffer = new Sample[speed_size]; + playback_wrap_buffer = new Sample[wrap_size]; + capture_wrap_buffer = new Sample[wrap_size]; + + playback_buf = new RingBufferNPT<Sample> (bufsize); + capture_buf = new RingBufferNPT<Sample> (bufsize); + capture_transition_buf = new RingBufferNPT<CaptureTransition> (128); + + /* touch the ringbuffer buffers, which will cause + them to be mapped into locked physical RAM if + we're running with mlockall(). this doesn't do + much if we're not. + */ + + memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize()); + memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize()); + memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize()); +} + +AudioDiskstream::ChannelInfo::~ChannelInfo () +{ +} + +void +AudioDiskstream::ChannelInfo::release () +{ + if (write_source) { + write_source.reset (); + } + + if (speed_buffer) { + delete [] speed_buffer; + speed_buffer = 0; + } + + if (playback_wrap_buffer) { + delete [] playback_wrap_buffer; + playback_wrap_buffer = 0; + } + + if (capture_wrap_buffer) { + delete [] capture_wrap_buffer; + capture_wrap_buffer = 0; + } + + if (playback_buf) { + delete playback_buf; + playback_buf = 0; + } + + if (capture_buf) { + delete capture_buf; + capture_buf = 0; + } + + if (capture_transition_buf) { + delete capture_transition_buf; + capture_transition_buf = 0; + } +} diff --git a/libs/ardour/audio_library.cc b/libs/ardour/audio_library.cc index 3aa6d05be1..2ed4739a96 100644 --- a/libs/ardour/audio_library.cc +++ b/libs/ardour/audio_library.cc @@ -1,6 +1,5 @@ /* - Copyright (C) 2003 Paul Davis - Author: Taybin Rutkin + Copyright (C) 2003-2006 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 @@ -16,25 +15,16 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ -#include <cstdio> // Needed so that libraptor (included in lrdf) won't complain -#include <cerrno> -#include <iostream> #include <sstream> -#include <cctype> -#include <sys/types.h> -#include <sys/stat.h> -#include <fts.h> +#include <libxml/uri.h> #include <lrdf.h> #include <pbd/compose.h> -#include <ardour/ardour.h> -#include <ardour/configuration.h> #include <ardour/audio_library.h> #include <ardour/utils.h> @@ -42,16 +32,11 @@ using namespace std; using namespace ARDOUR; -using namespace PBD; -static char* SOUNDFILE = "http://ardour.org/ontology/Soundfile"; - -string AudioLibrary::state_node_name = "AudioLibrary"; +static char* TAG = "http://ardour.org/ontology/Tag"; AudioLibrary::AudioLibrary () { -// sfdb_paths.push_back("/Users/taybin/sounds"); - src = "file:" + get_user_ardour_path() + "sfdb"; // workaround for possible bug in raptor that crashes when saving to a @@ -59,23 +44,6 @@ AudioLibrary::AudioLibrary () touch_file(get_user_ardour_path() + "sfdb"); lrdf_read_file(src.c_str()); - - lrdf_statement pattern; - - pattern.subject = SOUNDFILE; - pattern.predicate = RDF_TYPE; - pattern.object = RDFS_CLASS; - pattern.object_type = lrdf_uri; - - lrdf_statement* matches = lrdf_matches(&pattern); - - // if empty DB, create basic schema - if (matches == 0) { - initialize_db (); - save_changes(); - } - - lrdf_free_statements(matches); } AudioLibrary::~AudioLibrary () @@ -83,56 +51,96 @@ AudioLibrary::~AudioLibrary () } void -AudioLibrary::initialize_db () -{ - // define ardour:Soundfile - lrdf_add_triple(src.c_str(), SOUNDFILE, RDF_TYPE, RDFS_CLASS, lrdf_uri); - - // add intergral fields - add_field(_("channels")); - add_field(_("samplerate")); - add_field(_("resolution")); - add_field(_("format")); -} - -void AudioLibrary::save_changes () { if (lrdf_export_by_source(src.c_str(), src.substr(5).c_str())) { - warning << string_compose(_("Could not open %1. Audio Library not saved"), src) << endmsg; + PBD::warning << string_compose(_("Could not open %1. Audio Library not saved"), src) << endmsg; } } -void -AudioLibrary::add_member (string member) +string +AudioLibrary::path2uri (string path) { - string file_uri(string_compose("file:%1", member)); + xmlURI temp; + memset(&temp, 0, sizeof(temp)); + + xmlChar *cal = xmlCanonicPath((xmlChar*) path.c_str()); + temp.path = (char *) cal; + xmlChar *ret = xmlSaveUri(&temp); + xmlFree(cal); + + stringstream uri; + uri << "file:" << (const char*) ret; + + xmlFree (ret); + + return uri.str(); +} - lrdf_add_triple(src.c_str(), file_uri.c_str(), RDF_TYPE, - SOUNDFILE, lrdf_uri); +string +AudioLibrary::uri2path (string uri) +{ + string path = xmlURIUnescapeString(uri.c_str(), 0, 0); + return path.substr(5); } void -AudioLibrary::remove_member (string uri) +AudioLibrary::set_tags (string member, vector<string> tags) +{ + sort (tags.begin(), tags.end()); + tags.erase (unique(tags.begin(), tags.end()), tags.end()); + + string file_uri(path2uri(member)); + + lrdf_remove_uri_matches (file_uri.c_str()); + + for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) { + lrdf_add_triple (src.c_str(), file_uri.c_str(), TAG, (*i).c_str(), lrdf_literal); + } +} + +vector<string> +AudioLibrary::get_tags (string member) { - lrdf_remove_uri_matches (uri.c_str()); + vector<string> tags; + + lrdf_statement pattern; + pattern.subject = strdup(path2uri(member).c_str()); + pattern.predicate = TAG; + pattern.object = 0; + pattern.object_type = lrdf_literal; + + lrdf_statement* matches = lrdf_matches (&pattern); + free (pattern.subject); + + lrdf_statement* current = matches; + while (current != 0) { + tags.push_back (current->object); + + current = current->next; + } + + lrdf_free_statements (matches); + + sort (tags.begin(), tags.end()); + + return tags; } void -AudioLibrary::search_members_and (vector<string>& members, - const map<string,string>& fields) +AudioLibrary::search_members_and (vector<string>& members, const vector<string> tags) { lrdf_statement **head; lrdf_statement* pattern = 0; lrdf_statement* old = 0; head = &pattern; - map<string,string>::const_iterator i; - for (i = fields.begin(); i != fields.end(); ++i){ + vector<string>::const_iterator i; + for (i = tags.begin(); i != tags.end(); ++i){ pattern = new lrdf_statement; pattern->subject = "?"; - pattern->predicate = strdup(field_uri(i->first).c_str()); - pattern->object = strdup((i->second).c_str()); + pattern->predicate = TAG; + pattern->object = strdup((*i).c_str()); pattern->next = old; old = pattern; @@ -141,340 +149,21 @@ AudioLibrary::search_members_and (vector<string>& members, if (*head != 0) { lrdf_uris* ulist = lrdf_match_multi(*head); for (uint32_t j = 0; ulist && j < ulist->count; ++j) { -// printf("AND: %s\n", ulist->items[j]); - members.push_back(ulist->items[j]); +// cerr << "AND: " << uri2path(ulist->items[j]) << endl; + members.push_back(uri2path(ulist->items[j])); } lrdf_free_uris(ulist); - compact_vector(members); + sort(members.begin(), members.end()); + unique(members.begin(), members.end()); } // memory clean up pattern = *head; while(pattern){ - free(pattern->predicate); free(pattern->object); old = pattern; pattern = pattern->next; delete old; } } - -void -AudioLibrary::search_members_or (vector<string>& members, - const map<string,string>& fields) -{ - map<string,string>::const_iterator i; - - lrdf_statement pattern; - for (i = fields.begin(); i != fields.end(); ++i) { - pattern.subject = 0; - pattern.predicate = strdup(field_uri(i->first).c_str()); - pattern.object = strdup((i->second).c_str()); - pattern.object_type = lrdf_literal; - - lrdf_statement* matched = lrdf_matches(&pattern); - - lrdf_statement* old = matched; - while(matched) { -// printf ("OR: %s\n", matched->subject); - members.push_back(matched->subject); - matched = matched->next; - } - - free(pattern.predicate); - free(pattern.object); - lrdf_free_statements (old); - } - - compact_vector(members); -} - -void -AudioLibrary::add_field (string name) -{ - string local_field = field_uri(name); - lrdf_statement pattern; - pattern.subject = strdup(local_field.c_str()); - pattern.predicate = RDF_TYPE; - pattern.object = RDF_BASE "Property"; - pattern.object_type = lrdf_uri; - - if(lrdf_exists_match(&pattern)) { - return; - } - - // of type rdf:Property - lrdf_add_triple(src.c_str(), local_field.c_str(), RDF_TYPE, - RDF_BASE "Property", lrdf_uri); - // of range ardour:Soundfile - lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "range", - SOUNDFILE, lrdf_uri); - // of domain rdf:Literal - lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "domain", - RDF_BASE "Literal", lrdf_uri); - - set_label (local_field, name); - - fields_changed(); /* EMIT SIGNAL */ -} - -void -AudioLibrary::get_fields (vector<string>& fields) -{ - lrdf_statement pattern; - - pattern.subject = 0; - pattern.predicate = RDFS_BASE "range"; - pattern.object = SOUNDFILE; - pattern.object_type = lrdf_uri; - - lrdf_statement* matches = lrdf_matches(&pattern); - - lrdf_statement* current = matches; - while (current != 0) { - fields.push_back(get_label(current->subject)); - - current = current->next; - } - - lrdf_free_statements(matches); - - compact_vector(fields); -} - -void -AudioLibrary::remove_field (string name) -{ - lrdf_remove_uri_matches(field_uri(name).c_str()); - fields_changed (); /* EMIT SIGNAL */ -} - -string -AudioLibrary::get_field (string uri, string field) -{ - lrdf_statement pattern; - - pattern.subject = strdup(uri.c_str()); - - pattern.predicate = strdup(field_uri(field).c_str()); - - pattern.object = 0; - pattern.object_type = lrdf_literal; - - lrdf_statement* matches = lrdf_matches(&pattern); - free(pattern.subject); - free(pattern.predicate); - - stringstream object; - if (matches != 0){ - object << matches->object; - } - - lrdf_free_statements(matches); - return object.str(); -} - -void -AudioLibrary::set_field (string uri, string field, string literal) -{ - lrdf_statement pattern; - - pattern.subject = strdup(uri.c_str()); - - string local_field = field_uri(field); - pattern.predicate = strdup(local_field.c_str()); - - pattern.object = 0; - pattern.object_type = lrdf_literal; - - lrdf_remove_matches(&pattern); - free(pattern.subject); - free(pattern.predicate); - - lrdf_add_triple(src.c_str(), uri.c_str(), local_field.c_str(), - literal.c_str(), lrdf_literal); - - fields_changed(); /* EMIT SIGNAL */ -} - -string -AudioLibrary::field_uri (string name) -{ - stringstream local_field; - local_field << "file:sfdb/fields/" << name; - - return local_field.str(); -} - -string -AudioLibrary::get_label (string uri) -{ - lrdf_statement pattern; - pattern.subject = strdup(uri.c_str()); - pattern.predicate = RDFS_BASE "label"; - pattern.object = 0; - pattern.object_type = lrdf_literal; - - lrdf_statement* matches = lrdf_matches (&pattern); - free(pattern.subject); - - stringstream label; - if (matches != 0){ - label << matches->object; - } - - lrdf_free_statements(matches); - - return label.str(); -} - -void -AudioLibrary::set_label (string uri, string label) -{ - lrdf_statement pattern; - pattern.subject = strdup(uri.c_str()); - pattern.predicate = RDFS_BASE "label"; - pattern.object = 0; - pattern.object_type = lrdf_literal; - - lrdf_remove_matches(&pattern); - free(pattern.subject); - - lrdf_add_triple(src.c_str(), uri.c_str(), RDFS_BASE "label", - label.c_str(), lrdf_literal); -} - -void -AudioLibrary::compact_vector(vector<string>& vec) -{ - sort(vec.begin(), vec.end()); - unique(vec.begin(), vec.end()); -} - -void -AudioLibrary::set_paths (vector<string> paths) -{ - sfdb_paths = paths; - - scan_paths (); -} - -vector<string> -AudioLibrary::get_paths () -{ - return sfdb_paths; -} - -void -AudioLibrary::scan_paths () -{ - if (sfdb_paths.size() < 1) { - return; - } - - vector<char *> pathv(sfdb_paths.size()); - unsigned int i; - for (i = 0; i < sfdb_paths.size(); ++i) { - pathv[i] = new char[sfdb_paths[i].length() +1]; - sfdb_paths[i].copy(pathv[i], string::npos); - pathv[i][sfdb_paths[i].length()] = 0; - } - pathv[i] = 0; - - FTS* ft = fts_open(&pathv[0], FTS_LOGICAL|FTS_NOSTAT|FTS_PHYSICAL|FTS_XDEV, 0); - if (errno) { - error << strerror(errno) << endmsg; - return; - } - - lrdf_statement s; - s.predicate = RDF_TYPE; - s.object = SOUNDFILE; - s.object_type = lrdf_uri; - string filename; - while (FTSENT* file = fts_read(ft)) { - if ((file->fts_info & FTS_F) && (safe_file_extension(file->fts_name))) { - filename = "file:"; - filename.append(file->fts_accpath); - s.subject = strdup(filename.c_str()); - if (lrdf_exists_match(&s)) { - continue; - } else { - add_member(file->fts_accpath); - cout << file->fts_accpath << endl; - } - free(s.subject); - } - } - fts_close(ft); - - for (i = 0; i < pathv.size(); ++i) { - delete[] pathv[i]; - } - - save_changes(); -} - -bool -AudioLibrary::safe_file_extension(string file) -{ - return !(file.rfind(".wav") == string::npos && - file.rfind(".aiff")== string::npos && - file.rfind(".aif") == string::npos && - file.rfind(".snd") == string::npos && - file.rfind(".au") == string::npos && - file.rfind(".raw") == string::npos && - file.rfind(".sf") == string::npos && - file.rfind(".cdr") == string::npos && - file.rfind(".smp") == string::npos && - file.rfind(".maud")== string::npos && - file.rfind(".vwe") == string::npos && - file.rfind(".paf") == string::npos && -#ifdef HAVE_COREAUDIO - file.rfind(".mp3") == string::npos && - file.rfind(".aac") == string::npos && - file.rfind(".mp4") == string::npos && -#endif // HAVE_COREAUDIO - file.rfind(".voc") == string::npos); -} - -XMLNode& -AudioLibrary::get_state () -{ - XMLNode* root = new XMLNode(X_("AudioLibrary")); - - for (vector<string>::iterator i = sfdb_paths.begin(); i != sfdb_paths.end(); ++i) { - XMLNode* node = new XMLNode(X_("Path")); - node->add_property("value", *i); - root->add_child_nocopy(*node); - } - - return *root; -} - -int -AudioLibrary::set_state (const XMLNode& node) -{ - if (node.name() != X_("AudioLibrary")) { - fatal << "programming error: AudioLibrary: incorrect XML node sent to set_state()" << endmsg; - return -1; - } - - XMLNodeList nodes = node.children(X_("Path")); - - vector<string> paths; - XMLProperty* prop; - XMLNode* child; - for (XMLNodeConstIterator iter = nodes.begin(); iter != nodes.end(); ++iter) { - child = *iter; - - if ((prop = child->property(X_("value"))) != 0) { - paths.push_back(prop->value()); - } - } - - set_paths (paths); - - return 0; -} diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 335cb020ae..91bed1a4fa 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -48,39 +48,31 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden in_set_state++; set_state (node); in_set_state--; - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) : Playlist (session, name, DataType::AUDIO, hidden) { - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } - } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden) : Playlist (other, name, hidden) { - RegionList::const_iterator in_o = other.regions.begin(); + RegionList::const_iterator in_o = other->regions.begin(); RegionList::iterator in_n = regions.begin(); - while (in_o != other.regions.end()) { + while (in_o != other->regions.end()) { boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o); // We look only for crossfades which begin with the current region, so we don't get doubles - for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) { + for (list<Crossfade *>::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) { if ((*xfades)->in() == ar) { // We found one! Now copy it! - RegionList::const_iterator out_o = other.regions.begin(); + RegionList::const_iterator out_o = other->regions.begin(); RegionList::const_iterator out_n = regions.begin(); - while (out_o != other.regions.end()) { + while (out_o != other->regions.end()) { boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o); @@ -102,13 +94,9 @@ AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidd in_o++; in_n++; } - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, nframes_t start, nframes_t cnt, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) { /* this constructor does NOT notify others (session) */ @@ -437,8 +425,15 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh) /* in, out */ xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn); add_crossfade (*xfade); - xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut); - add_crossfade (*xfade); + + if (top_region_at (top->last_frame() - 1) == top) { + /* + only add a fade out if there is no region on top of the end of 'top' (which + would cover it). + */ + xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut); + add_crossfade (*xfade); + } } else { @@ -666,7 +661,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr<Region> region) x = xtmp; } - region->set_playlist (0); + region->set_playlist (boost::shared_ptr<Playlist>()); } for (c = _crossfades.begin(); c != _crossfades.end(); ) { diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index f2681aceba..b0ac7a4bd7 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -17,11 +17,14 @@ $Id$ */ -#include <pbd/error.h> + #include <sigc++/retype.h> #include <sigc++/retype_return.h> #include <sigc++/bind.h> +#include <pbd/error.h> +#include <pbd/enumwriter.h> + #include <ardour/audio_track.h> #include <ardour/audio_diskstream.h> #include <ardour/session.h> @@ -32,6 +35,7 @@ #include <ardour/route_group_specialized.h> #include <ardour/insert.h> #include <ardour/audioplaylist.h> +#include <ardour/playlist_factory.h> #include <ardour/panner.h> #include <ardour/utils.h> #include <ardour/buffer_set.h> @@ -235,14 +239,7 @@ AudioTrack::_set_state (const XMLNode& node, bool call_base) } if ((prop = node.property (X_("mode"))) != 0) { - if (prop->value() == X_("normal")) { - _mode = Normal; - } else if (prop->value() == X_("destructive")) { - _mode = Destructive; - } else { - warning << string_compose ("unknown audio track mode \"%1\" seen and ignored", prop->value()) << endmsg; - _mode = Normal; - } + _mode = TrackMode (string_2_enum (prop->value(), _mode)); } else { _mode = Normal; } @@ -311,8 +308,7 @@ AudioTrack::state(bool full_state) freeze_node = new XMLNode (X_("freeze-info")); freeze_node->add_property ("playlist", _freeze_record.playlist->name()); - snprintf (buf, sizeof (buf), "%d", (int) _freeze_record.state); - freeze_node->add_property ("state", buf); + freeze_node->add_property ("state", enum_2_string (_freeze_record.state)); for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) { inode = new XMLNode (X_("insert")); @@ -329,15 +325,8 @@ AudioTrack::state(bool full_state) /* Alignment: act as a proxy for the diskstream */ XMLNode* align_node = new XMLNode (X_("alignment")); - switch (_diskstream->alignment_style()) { - case ExistingMaterial: - snprintf (buf, sizeof (buf), X_("existing")); - break; - case CaptureTime: - snprintf (buf, sizeof (buf), X_("capture")); - break; - } - align_node->add_property (X_("style"), buf); + AlignStyle as = _diskstream->alignment_style (); + align_node->add_property (X_("style"), enum_2_string (as)); root.add_child_nocopy (*align_node); XMLNode* remote_control_node = new XMLNode (X_("remote_control")); @@ -345,14 +334,7 @@ AudioTrack::state(bool full_state) remote_control_node->add_property (X_("id"), buf); root.add_child_nocopy (*remote_control_node); - switch (_mode) { - case Normal: - root.add_property (X_("mode"), X_("normal")); - break; - case Destructive: - root.add_property (X_("mode"), X_("destructive")); - break; - } + root.add_property (X_("mode"), enum_2_string (_mode)); /* we don't return diskstream state because we don't own the diskstream exclusively. control of the diskstream @@ -395,18 +377,18 @@ AudioTrack::set_state_part_two () _freeze_record.insert_info.clear (); if ((prop = fnode->property (X_("playlist"))) != 0) { - Playlist* pl = _session.playlist_by_name (prop->value()); + boost::shared_ptr<Playlist> pl = _session.playlist_by_name (prop->value()); if (pl) { - _freeze_record.playlist = dynamic_cast<AudioPlaylist*> (pl); + _freeze_record.playlist = boost::dynamic_pointer_cast<AudioPlaylist> (pl); } else { - _freeze_record.playlist = 0; + _freeze_record.playlist.reset (); _freeze_record.state = NoFreeze; return; } } if ((prop = fnode->property (X_("state"))) != 0) { - _freeze_record.state = (FreezeState) atoi (prop->value().c_str()); + _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state)); } XMLNodeConstIterator citer; @@ -433,11 +415,21 @@ AudioTrack::set_state_part_two () if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) { if ((prop = fnode->property (X_("style"))) != 0) { - if (prop->value() == "existing") { - _diskstream->set_persistent_align_style (ExistingMaterial); - } else if (prop->value() == "capture") { - _diskstream->set_persistent_align_style (CaptureTime); + + /* fix for older sessions from before EnumWriter */ + + string pstr; + + if (prop->value() == "capture") { + pstr = "CaptureTime"; + } else if (prop->value() == "existing") { + pstr = "ExistingMaterial"; + } else { + pstr = prop->value(); } + + AlignStyle as = AlignStyle (string_2_enum (pstr, as)); + _diskstream->set_persistent_align_style (as); } } return; @@ -669,8 +661,7 @@ AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes Glib::RWLock::ReaderLock rlock (redirect_lock); - // FIXME - AudioPlaylist* const apl = dynamic_cast<AudioPlaylist*>(diskstream->playlist()); + boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist>(diskstream->playlist()); assert(apl); if (apl->read (buffers.get_audio(nframes).data(nframes), @@ -777,12 +768,12 @@ AudioTrack::freeze (InterThreadInfo& itt) { vector<boost::shared_ptr<Source> > srcs; string new_playlist_name; - Playlist* new_playlist; + boost::shared_ptr<Playlist> new_playlist; string dir; string region_name; boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream(); - if ((_freeze_record.playlist = dynamic_cast<AudioPlaylist*>(diskstream->playlist())) == 0) { + if ((_freeze_record.playlist = boost::dynamic_pointer_cast<AudioPlaylist>(diskstream->playlist())) == 0) { return; } @@ -839,7 +830,7 @@ AudioTrack::freeze (InterThreadInfo& itt) } } - new_playlist = new AudioPlaylist (_session, new_playlist_name, false); + new_playlist = PlaylistFactory::create (_session, new_playlist_name, false); region_name = new_playlist_name; /* create a new region from all filesources, keep it private */ @@ -854,7 +845,7 @@ AudioTrack::freeze (InterThreadInfo& itt) new_playlist->set_frozen (true); region->set_locked (true); - diskstream->use_playlist (dynamic_cast<AudioPlaylist*>(new_playlist)); + diskstream->use_playlist (boost::dynamic_pointer_cast<AudioPlaylist>(new_playlist)); diskstream->set_record_enabled (false); _freeze_record.state = Frozen; @@ -886,7 +877,7 @@ AudioTrack::unfreeze () } } - _freeze_record.playlist = 0; + _freeze_record.playlist.reset (); } _freeze_record.state = UnFrozen; diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index b0cd64c8d1..be070c74e6 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -49,6 +49,12 @@ using namespace PBD; gint AudioEngine::m_meter_exit; +static void +ardour_jack_error (const char* msg) +{ + error << "JACK: " << msg << endmsg; +} + AudioEngine::AudioEngine (string client_name) : ports (new Ports) { @@ -80,11 +86,16 @@ AudioEngine::AudioEngine (string client_name) AudioEngine::~AudioEngine () { - if (_running) { - jack_client_close (_jack); + { + Glib::Mutex::Lock tm (_process_lock); + session_removed.signal (); + + if (_running) { + jack_client_close (_jack); + } + + stop_metering_thread (); } - - stop_metering_thread (); } void @@ -152,11 +163,18 @@ AudioEngine::start () } int -AudioEngine::stop () +AudioEngine::stop (bool forever) { if (_running) { _running = false; - jack_deactivate (_jack); + if (forever) { + jack_client_t* foo = _jack; + _jack = 0; + jack_client_close (foo); + stop_metering_thread (); + } else { + jack_deactivate (_jack); + } Stopped(); /* EMIT SIGNAL */ } @@ -164,7 +182,6 @@ AudioEngine::stop () } - bool AudioEngine::get_sync_offset (nframes_t& offset) const { @@ -196,7 +213,7 @@ void AudioEngine::jack_timebase_callback (jack_transport_state_t state, nframes_t nframes, jack_position_t* pos, int new_position) { - if (session && session->synced_to_jack()) { + if (_jack && session && session->synced_to_jack()) { session->jack_timebase_callback (state, nframes, pos, new_position); } } @@ -210,7 +227,7 @@ AudioEngine::_jack_sync_callback (jack_transport_state_t state, jack_position_t* int AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos) { - if (session) { + if (_jack && session) { return session->jack_sync_callback (state, pos); } else { return true; @@ -220,14 +237,20 @@ AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* int AudioEngine::_xrun_callback (void *arg) { - static_cast<AudioEngine *>(arg)->Xrun (); /* EMIT SIGNAL */ + AudioEngine* ae = static_cast<AudioEngine*> (arg); + if (ae->jack()) { + ae->Xrun (); /* EMIT SIGNAL */ + } return 0; } int AudioEngine::_graph_order_callback (void *arg) { - static_cast<AudioEngine *>(arg)->GraphReordered (); /* EMIT SIGNAL */ + AudioEngine* ae = static_cast<AudioEngine*> (arg); + if (ae->jack()) { + ae->GraphReordered (); /* EMIT SIGNAL */ + } return 0; } @@ -381,9 +404,9 @@ AudioEngine::stop_metering_thread () { if (m_meter_thread) { g_atomic_int_set (&m_meter_exit, 1); + m_meter_thread->join (); + m_meter_thread = 0; } - m_meter_thread->join (); - m_meter_thread = 0; } void @@ -397,8 +420,11 @@ AudioEngine::start_metering_thread () void AudioEngine::meter_thread () { - while (g_atomic_int_get(&m_meter_exit) != true) { + while (true) { Glib::usleep (10000); /* 1/100th sec interval */ + if (g_atomic_int_get(&m_meter_exit)) { + break; + } IO::update_meters (); } } @@ -406,8 +432,26 @@ AudioEngine::meter_thread () void AudioEngine::set_session (Session *s) { + Glib::Mutex::Lock pl (_process_lock); + if (!session) { + session = s; + + nframes_t blocksize = jack_get_buffer_size (_jack); + + /* page in as much of the session process code as we + can before we really start running. + */ + + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); + session->process (blocksize); } } @@ -421,16 +465,13 @@ AudioEngine::remove_session () if (session) { session_remove_pending = true; session_removed.wait(_process_lock); - } + } } else { - session = 0; - } remove_all_ports (); - } Port * @@ -468,8 +509,6 @@ AudioEngine::register_input_port (DataType type, const string& portname) return newport; } else { - - _process_lock.unlock(); throw PortRegistrationFailure(); } @@ -512,8 +551,6 @@ AudioEngine::register_output_port (DataType type, const string& portname) return newport; } else { - - _process_lock.unlock(); throw PortRegistrationFailure (); } @@ -719,11 +756,9 @@ AudioEngine::get_ports (const string& port_name_pattern, const string& type_name void AudioEngine::halted (void *arg) { - AudioEngine *ae = reinterpret_cast<AudioEngine *> (arg); + AudioEngine* ae = static_cast<AudioEngine *> (arg); ae->_running = false; - ae->_jack = 0; - ae->_buffer_size = 0; ae->_frame_rate = 0; @@ -1006,6 +1041,8 @@ AudioEngine::connect_to_jack (string client_name) if (status & JackNameNotUnique) { jack_client_name = jack_get_client_name (_jack); } + + jack_set_error_function (ardour_jack_error); return 0; } @@ -1033,9 +1070,7 @@ AudioEngine::disconnect_from_jack () return 0; } - if (jack_client_close (_jack)) { - error << _("cannot shutdown connection to JACK") << endmsg; - } + jack_client_close (_jack); _buffer_size = 0; _frame_rate = 0; diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 16cb990ec2..af41094748 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -22,12 +22,14 @@ #include <sys/time.h> #include <sys/stat.h> #include <unistd.h> +#include <fcntl.h> #include <errno.h> #include <pbd/mountpoint.h> #include <pbd/pathscanner.h> #include <pbd/stl_delete.h> #include <pbd/strsplit.h> +#include <pbd/enumwriter.h> #include <sndfile.h> @@ -193,9 +195,7 @@ XMLNode& AudioFileSource::get_state () { XMLNode& root (AudioSource::get_state()); - char buf[16]; - snprintf (buf, sizeof (buf), "0x%x", (int)_flags); - root.add_property ("flags", buf); + root.add_property ("flags", enum_2_string (_flags)); return root; } @@ -210,9 +210,7 @@ AudioFileSource::set_state (const XMLNode& node) if ((prop = node.property (X_("flags"))) != 0) { - int ival; - sscanf (prop->value().c_str(), "0x%x", &ival); - _flags = Flag (ival); + _flags = Flag (string_2_enum (prop->value(), _flags)); } else { @@ -257,7 +255,7 @@ AudioFileSource::mark_streaming_write_completed () if (_peaks_built || pending_peak_builds.empty()) { _peaks_built = true; - PeaksReady (); /* EMIT SIGNAL */ + PeaksReady (); /* EMIT SIGNAL */ } } @@ -287,9 +285,11 @@ AudioFileSource::move_to_trash (const string trash_dir_name) stick it in the `trash_dir_name' directory on whichever filesystem it was already on. */ - + newpath = Glib::path_get_dirname (_path); - newpath = Glib::path_get_dirname (newpath); + newpath = Glib::path_get_dirname (newpath); + + cerr << "from " << _path << " dead dir looks like " << newpath << endl; newpath += '/'; newpath += trash_dir_name; @@ -522,7 +522,7 @@ AudioFileSource::set_name (string newname, bool destructive) } if (rename (oldpath.c_str(), newpath.c_str()) != 0) { - error << string_compose (_("cannot rename audio file for %1 to %2"), _name, newpath) << endmsg; + error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg; return -1; } @@ -556,3 +556,26 @@ AudioFileSource::setup_peakfile () return 0; } } + +bool +AudioFileSource::safe_file_extension(string file) +{ + return !(file.rfind(".wav") == string::npos && + file.rfind(".aiff")== string::npos && + file.rfind(".aif") == string::npos && + file.rfind(".snd") == string::npos && + file.rfind(".au") == string::npos && + file.rfind(".raw") == string::npos && + file.rfind(".sf") == string::npos && + file.rfind(".cdr") == string::npos && + file.rfind(".smp") == string::npos && + file.rfind(".maud")== string::npos && + file.rfind(".vwe") == string::npos && + file.rfind(".paf") == string::npos && +#ifdef HAVE_COREAUDIO + file.rfind(".mp3") == string::npos && + file.rfind(".aac") == string::npos && + file.rfind(".mp4") == string::npos && +#endif // HAVE_COREAUDIO + file.rfind(".voc") == string::npos); +} diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 939f9c02dd..8b533deda3 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -32,6 +32,7 @@ #include <pbd/basename.h> #include <pbd/xml++.h> #include <pbd/stacktrace.h> +#include <pbd/enumwriter.h> #include <ardour/audioregion.h> #include <ardour/session.h> @@ -482,8 +483,8 @@ AudioRegion::state (bool full) char buf2[64]; LocaleGuard lg (X_("POSIX")); - snprintf (buf, sizeof (buf), "0x%x", (int) _flags); - node.add_property ("flags", buf); + node.add_property ("flags", enum_2_string (_flags)); + snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude); node.add_property ("scale-gain", buf); @@ -564,7 +565,9 @@ AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool sen uint32_t old_flags = _flags; if ((prop = node.property ("flags")) != 0) { - _flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16)); + _flags = Flag (string_2_enum (prop->value(), _flags)); + + //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16)); _flags = Flag (_flags & ~Region::LeftOfSplit); _flags = Flag (_flags & ~Region::RightOfSplit); @@ -662,49 +665,49 @@ AudioRegion::set_fade_in (FadeShape shape, nframes_t len) switch (shape) { case Linear: - _fade_in.add (0.0, 0.0); - _fade_in.add (len, 1.0); + _fade_in.fast_simple_add (0.0, 0.0); + _fade_in.fast_simple_add (len, 1.0); break; case Fast: - _fade_in.add (0, 0); - _fade_in.add (len * 0.389401, 0.0333333); - _fade_in.add (len * 0.629032, 0.0861111); - _fade_in.add (len * 0.829493, 0.233333); - _fade_in.add (len * 0.9447, 0.483333); - _fade_in.add (len * 0.976959, 0.697222); - _fade_in.add (len, 1); + _fade_in.fast_simple_add (0, 0); + _fade_in.fast_simple_add (len * 0.389401, 0.0333333); + _fade_in.fast_simple_add (len * 0.629032, 0.0861111); + _fade_in.fast_simple_add (len * 0.829493, 0.233333); + _fade_in.fast_simple_add (len * 0.9447, 0.483333); + _fade_in.fast_simple_add (len * 0.976959, 0.697222); + _fade_in.fast_simple_add (len, 1); break; case Slow: - _fade_in.add (0, 0); - _fade_in.add (len * 0.0207373, 0.197222); - _fade_in.add (len * 0.0645161, 0.525); - _fade_in.add (len * 0.152074, 0.802778); - _fade_in.add (len * 0.276498, 0.919444); - _fade_in.add (len * 0.481567, 0.980556); - _fade_in.add (len * 0.767281, 1); - _fade_in.add (len, 1); + _fade_in.fast_simple_add (0, 0); + _fade_in.fast_simple_add (len * 0.0207373, 0.197222); + _fade_in.fast_simple_add (len * 0.0645161, 0.525); + _fade_in.fast_simple_add (len * 0.152074, 0.802778); + _fade_in.fast_simple_add (len * 0.276498, 0.919444); + _fade_in.fast_simple_add (len * 0.481567, 0.980556); + _fade_in.fast_simple_add (len * 0.767281, 1); + _fade_in.fast_simple_add (len, 1); break; case LogA: - _fade_in.add (0, 0); - _fade_in.add (len * 0.0737327, 0.308333); - _fade_in.add (len * 0.246544, 0.658333); - _fade_in.add (len * 0.470046, 0.886111); - _fade_in.add (len * 0.652074, 0.972222); - _fade_in.add (len * 0.771889, 0.988889); - _fade_in.add (len, 1); + _fade_in.fast_simple_add (0, 0); + _fade_in.fast_simple_add (len * 0.0737327, 0.308333); + _fade_in.fast_simple_add (len * 0.246544, 0.658333); + _fade_in.fast_simple_add (len * 0.470046, 0.886111); + _fade_in.fast_simple_add (len * 0.652074, 0.972222); + _fade_in.fast_simple_add (len * 0.771889, 0.988889); + _fade_in.fast_simple_add (len, 1); break; case LogB: - _fade_in.add (0, 0); - _fade_in.add (len * 0.304147, 0.0694444); - _fade_in.add (len * 0.529954, 0.152778); - _fade_in.add (len * 0.725806, 0.333333); - _fade_in.add (len * 0.847926, 0.558333); - _fade_in.add (len * 0.919355, 0.730556); - _fade_in.add (len, 1); + _fade_in.fast_simple_add (0, 0); + _fade_in.fast_simple_add (len * 0.304147, 0.0694444); + _fade_in.fast_simple_add (len * 0.529954, 0.152778); + _fade_in.fast_simple_add (len * 0.725806, 0.333333); + _fade_in.fast_simple_add (len * 0.847926, 0.558333); + _fade_in.fast_simple_add (len * 0.919355, 0.730556); + _fade_in.fast_simple_add (len, 1); break; } @@ -722,47 +725,47 @@ AudioRegion::set_fade_out (FadeShape shape, nframes_t len) switch (shape) { case Fast: - _fade_out.add (len * 0, 1); - _fade_out.add (len * 0.023041, 0.697222); - _fade_out.add (len * 0.0553, 0.483333); - _fade_out.add (len * 0.170507, 0.233333); - _fade_out.add (len * 0.370968, 0.0861111); - _fade_out.add (len * 0.610599, 0.0333333); - _fade_out.add (len * 1, 0); + _fade_out.fast_simple_add (len * 0, 1); + _fade_out.fast_simple_add (len * 0.023041, 0.697222); + _fade_out.fast_simple_add (len * 0.0553, 0.483333); + _fade_out.fast_simple_add (len * 0.170507, 0.233333); + _fade_out.fast_simple_add (len * 0.370968, 0.0861111); + _fade_out.fast_simple_add (len * 0.610599, 0.0333333); + _fade_out.fast_simple_add (len * 1, 0); break; case LogA: - _fade_out.add (len * 0, 1); - _fade_out.add (len * 0.228111, 0.988889); - _fade_out.add (len * 0.347926, 0.972222); - _fade_out.add (len * 0.529954, 0.886111); - _fade_out.add (len * 0.753456, 0.658333); - _fade_out.add (len * 0.9262673, 0.308333); - _fade_out.add (len * 1, 0); + _fade_out.fast_simple_add (len * 0, 1); + _fade_out.fast_simple_add (len * 0.228111, 0.988889); + _fade_out.fast_simple_add (len * 0.347926, 0.972222); + _fade_out.fast_simple_add (len * 0.529954, 0.886111); + _fade_out.fast_simple_add (len * 0.753456, 0.658333); + _fade_out.fast_simple_add (len * 0.9262673, 0.308333); + _fade_out.fast_simple_add (len * 1, 0); break; case Slow: - _fade_out.add (len * 0, 1); - _fade_out.add (len * 0.305556, 1); - _fade_out.add (len * 0.548611, 0.991736); - _fade_out.add (len * 0.759259, 0.931129); - _fade_out.add (len * 0.918981, 0.68595); - _fade_out.add (len * 0.976852, 0.22865); - _fade_out.add (len * 1, 0); + _fade_out.fast_simple_add (len * 0, 1); + _fade_out.fast_simple_add (len * 0.305556, 1); + _fade_out.fast_simple_add (len * 0.548611, 0.991736); + _fade_out.fast_simple_add (len * 0.759259, 0.931129); + _fade_out.fast_simple_add (len * 0.918981, 0.68595); + _fade_out.fast_simple_add (len * 0.976852, 0.22865); + _fade_out.fast_simple_add (len * 1, 0); break; case LogB: - _fade_out.add (len * 0, 1); - _fade_out.add (len * 0.080645, 0.730556); - _fade_out.add (len * 0.277778, 0.289256); - _fade_out.add (len * 0.470046, 0.152778); - _fade_out.add (len * 0.695853, 0.0694444); - _fade_out.add (len * 1, 0); + _fade_out.fast_simple_add (len * 0, 1); + _fade_out.fast_simple_add (len * 0.080645, 0.730556); + _fade_out.fast_simple_add (len * 0.277778, 0.289256); + _fade_out.fast_simple_add (len * 0.470046, 0.152778); + _fade_out.fast_simple_add (len * 0.695853, 0.0694444); + _fade_out.fast_simple_add (len * 1, 0); break; case Linear: - _fade_out.add (len * 0, 1); - _fade_out.add (len * 1, 0); + _fade_out.fast_simple_add (len * 0, 1); + _fade_out.fast_simple_add (len * 1, 0); break; } @@ -851,8 +854,8 @@ AudioRegion::set_default_envelope () { _envelope.freeze (); _envelope.clear (); - _envelope.add (0, 1.0f); - _envelope.add (_length, 1.0f); + _envelope.fast_simple_add (0, 1.0f); + _envelope.fast_simple_add (_length, 1.0f); _envelope.thaw (); } @@ -996,12 +999,14 @@ AudioRegion::exportme (Session& session, AudioExportSpecification& spec) void AudioRegion::set_scale_amplitude (gain_t g) { + boost::shared_ptr<Playlist> pl (playlist()); + _scale_amplitude = g; /* tell the diskstream we're in */ - - if (_playlist) { - _playlist->Modified(); + + if (pl) { + pl->Modified(); } /* tell everybody else */ @@ -1068,8 +1073,10 @@ AudioRegion::normalize_to (float target_dB) /* tell the diskstream we're in */ - if (_playlist) { - _playlist->Modified(); + boost::shared_ptr<Playlist> pl (playlist()); + + if (pl) { + pl->Modified(); } /* tell everybody else */ diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index 93165b7fe4..203590a4e1 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -35,6 +35,7 @@ #include <pbd/pthread_utils.h> #include <ardour/audiosource.h> +#include <ardour/cycle_timer.h> #include "i18n.h" @@ -59,6 +60,7 @@ AudioSource::AudioSource (Session& s, string name) } _peaks_built = false; + _peak_byte_max = 0; next_peak_clear_should_notify = true; _read_data_count = 0; _write_data_count = 0; @@ -72,6 +74,7 @@ AudioSource::AudioSource (Session& s, const XMLNode& node) } _peaks_built = false; + _peak_byte_max = 0; next_peak_clear_should_notify = true; _read_data_count = 0; _write_data_count = 0; @@ -251,13 +254,13 @@ AudioSource::stop_peak_thread () } void -AudioSource::queue_for_peaks (boost::shared_ptr<AudioSource> source) +AudioSource::queue_for_peaks (boost::shared_ptr<AudioSource> source, bool notify) { if (have_peak_thread) { Glib::Mutex::Lock lm (*pending_peak_sources_lock); - source->next_peak_clear_should_notify = true; + source->next_peak_clear_should_notify = notify; if (find (pending_peak_sources.begin(), pending_peak_sources.end(), @@ -384,8 +387,10 @@ AudioSource::initialize_peakfile (bool newfile, string audio_path) if (!err && stat_file.st_mtime > statbuf.st_mtime){ _peaks_built = false; + _peak_byte_max = 0; } else { _peaks_built = true; + _peak_byte_max = statbuf.st_size; } } } @@ -431,7 +436,8 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr expected_peaks = (cnt / (double) frames_per_peak); scale = npeaks/expected_peaks; -#if 0 +#undef DEBUG_READ_PEAKS +#ifdef DEBUG_READ_PEAKS cerr << "======>RP: npeaks = " << npeaks << " start = " << start << " cnt = " << cnt @@ -457,8 +463,9 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr if (npeaks == cnt) { +#ifdef DEBUG_READ_PEAKS cerr << "RAW DATA\n"; - +#endif /* no scaling at all, just get the sample data and duplicate it for both max and min peak values. */ @@ -485,12 +492,14 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* open, read, close */ - if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) { + if ((peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return -1; } - // cerr << "DIRECT PEAKS\n"; +#ifdef DEBUG_READ_PEAKS + cerr << "DIRECT PEAKS\n"; +#endif nread = ::pread (peakfile, peaks, sizeof (PeakData)* npeaks, first_peak_byte); close (peakfile); @@ -523,8 +532,9 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr if (scale < 1.0) { - // cerr << "DOWNSAMPLE\n"; - +#ifdef DEBUG_READ_PEAKS + cerr << "DOWNSAMPLE\n"; +#endif /* the caller wants: - more frames-per-peak (lower resolution) than the peakfile, or to put it another way, @@ -535,7 +545,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks */ - const uint32_t chunksize = (uint32_t) min (expected_peaks, 4096.0); + const uint32_t chunksize = (uint32_t) min (expected_peaks, 65536.0); staging = new PeakData[chunksize]; @@ -556,7 +566,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_RDWR|O_CREAT, 0664)) < 0) { + if ((peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return 0; } @@ -569,10 +579,15 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr tnp = min ((_length/frames_per_peak - current_stored_peak), (nframes_t) expected_peaks); to_read = min (chunksize, tnp); - off_t fend = lseek (peakfile, 0, SEEK_END); +#ifdef DEBUG_READ_PEAKS + cerr << "read " << sizeof (PeakData) * to_read << " from peakfile @ " << start_byte << endl; +#endif if ((nread = ::pread (peakfile, staging, sizeof (PeakData) * to_read, start_byte)) != sizeof (PeakData) * to_read) { + + off_t fend = lseek (peakfile, 0, SEEK_END); + cerr << "AudioSource[" << _name << "]: cannot read peak data from peakfile (" @@ -589,11 +604,11 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr << endl; goto out; } - + i = 0; stored_peaks_read = nread / sizeof(PeakData); } - + xmax = -1.0; xmin = 1.0; @@ -624,8 +639,9 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr } else { - // cerr << "UPSAMPLE\n"; - +#ifdef DEBUG_READ_PEAKS + cerr << "UPSAMPLE\n"; +#endif /* the caller wants - less frames-per-peak (more resolution) @@ -704,6 +720,10 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr delete [] raw_staging; } +#ifdef DEBUG_READ_PEAKS + cerr << "RP DONE\n"; +#endif + return ret; } @@ -757,6 +777,7 @@ AudioSource::build_peaks () } if (pr_signal) { + truncate_peakfile(); PeaksReady (); /* EMIT SIGNAL */ } } @@ -777,6 +798,8 @@ AudioSource::do_build_peak (nframes_t first_frame, nframes_t cnt) off_t first_peak_byte; int peakfile = -1; int ret = -1; + off_t target_length; + off_t endpos; #ifdef DEBUG_PEAK_BUILD cerr << pthread_self() << ": " << _name << ": building peaks for " << first_frame << " to " << first_frame + cnt - 1 << endl; @@ -828,11 +851,32 @@ AudioSource::do_build_peak (nframes_t first_frame, nframes_t cnt) cnt -= frames_read; } +#define BLOCKSIZE (128 * 1024) + + /* on some filesystems (ext3, at least) this helps to reduce fragmentation of + the peakfiles. its not guaranteed to do so, and even on ext3 (as of december 2006) + it does not cause single-extent allocation even for peakfiles of + less than BLOCKSIZE bytes. only call ftruncate if we'll make the file larger. + */ + endpos = lseek (peakfile, 0, SEEK_END); + + target_length = BLOCKSIZE * ((first_peak_byte + BLOCKSIZE + 1) / BLOCKSIZE); + + if (endpos < target_length) { + // XXX - we really shouldn't be doing this for destructive source peaks + ftruncate (peakfile, target_length); + //cerr << "do build TRUNC: " << peakpath << " " << target_length << endl; + + /* error doesn't actually matter though, so continue on without testing */ + } + if (::pwrite (peakfile, peakbuf, sizeof (PeakData) * peaki, first_peak_byte) != (ssize_t) (sizeof (PeakData) * peaki)) { error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg; goto out; } + _peak_byte_max = max(_peak_byte_max, (off_t) (first_peak_byte + sizeof(PeakData)*peaki)); + ret = 0; out: @@ -850,7 +894,28 @@ AudioSource::build_peaks_from_scratch () next_peak_clear_should_notify = true; pending_peak_builds.push_back (new PeakBuildRecord (0, _length)); - queue_for_peaks (shared_from_this()); + queue_for_peaks (shared_from_this(), true); +} + +void +AudioSource::truncate_peakfile () +{ + int peakfile = -1; + + /* truncate the peakfile down to its natural length if necessary */ + + if ((peakfile = ::open (peakpath.c_str(), O_RDWR)) >= 0) { + off_t end = lseek (peakfile, 0, SEEK_END); + + if (end > _peak_byte_max) { + ftruncate(peakfile, _peak_byte_max); + //cerr << "truncated " << peakpath << " to " << _peak_byte_max << " bytes" << endl; + } + else { + //cerr << "NOT truncated " << peakpath << " to " << _peak_byte_max << " end " << end << endl; + } + close (peakfile); + } } bool @@ -872,26 +937,19 @@ AudioSource::file_changed (string path) nframes_t AudioSource::available_peaks (double zoom_factor) const { - int peakfile; off_t end; if (zoom_factor < frames_per_peak) { return length(); // peak data will come from the audio file } - /* peak data comes from peakfile */ - - if ((peakfile = ::open (peakpath.c_str(), O_RDONLY)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; - return 0; - } - - { - Glib::Mutex::Lock lm (_lock); - end = lseek (peakfile, 0, SEEK_END); - } + /* peak data comes from peakfile, but the filesize might not represent + the valid data due to ftruncate optimizations, so use _peak_byte_max state. + XXX - there might be some atomicity issues here, we should probably add a lock, + but _peak_byte_max only monotonically increases after initialization. + */ - close (peakfile); + end = _peak_byte_max; return (end/sizeof(PeakData)) * frames_per_peak; } diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc index 34cf5637b6..7bbc4cd0ba 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -24,6 +24,7 @@ #include <ardour/audio_diskstream.h> #include <ardour/audioregion.h> +#include <ardour/audioengine.h> #include <ardour/route.h> #include <ardour/session.h> #include <ardour/auditioner.h> @@ -43,8 +44,17 @@ Auditioner::Auditioner (Session& s) { string left = Config->get_auditioner_output_left(); string right = Config->get_auditioner_output_right(); + + if (left == "default") { + left = _session.engine().get_nth_physical_output (DataType::AUDIO, 0); + } + + if (right == "default") { + right = _session.engine().get_nth_physical_output (DataType::AUDIO, 1); + } if ((left.length() == 0) && (right.length() == 0)) { + warning << _("no outputs available for auditioner - manual connection required") << endmsg; return; } @@ -77,7 +87,7 @@ AudioPlaylist& Auditioner::prepare_playlist () { // FIXME auditioner is still audio-only - AudioPlaylist* const apl = dynamic_cast<AudioPlaylist*>(_diskstream->playlist()); + boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist>(_diskstream->playlist()); assert(apl); apl->clear (); @@ -184,18 +194,34 @@ Auditioner::play_audition (nframes_t nframes) void Auditioner::output_changed (IOChange change, void* src) { + string phys; + if (change & ConnectionsChanged) { const char ** connections; connections = output (0)->get_connections (); if (connections) { - Config->set_auditioner_output_left (connections[0]); + phys = _session.engine().get_nth_physical_output (DataType::AUDIO, 0); + if (phys != connections[0]) { + Config->set_auditioner_output_left (connections[0]); + } else { + Config->set_auditioner_output_left ("default"); + } free (connections); + } else { + Config->set_auditioner_output_left (""); } connections = output (1)->get_connections (); if (connections) { - Config->set_auditioner_output_right (connections[0]); + phys = _session.engine().get_nth_physical_output (DataType::AUDIO, 1); + if (phys != connections[0]) { + Config->set_auditioner_output_right (connections[0]); + } else { + Config->set_auditioner_output_right ("default"); + } free (connections); + } else { + Config->set_auditioner_output_right (""); } } } diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc index 5cc2f50e38..af6fffdeb9 100644 --- a/libs/ardour/automation_event.cc +++ b/libs/ardour/automation_event.cc @@ -26,7 +26,7 @@ #include <algorithm> #include <sigc++/bind.h> #include <ardour/automation_event.h> -#include <pbd/convert.h> +#include <pbd/stacktrace.h> #include "i18n.h" @@ -37,6 +37,11 @@ using namespace PBD; sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated; +static bool sort_events_by_time (ControlEvent* a, ControlEvent* b) +{ + return a->when < b->when; +} + #if 0 static void dumpit (const AutomationList& al, string prefix = "") { @@ -50,7 +55,7 @@ static void dumpit (const AutomationList& al, string prefix = "") AutomationList::AutomationList (double defval) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _state = Off; _style = Absolute; @@ -63,13 +68,14 @@ AutomationList::AutomationList (double defval) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; AutomationListCreated(this); } AutomationList::AutomationList (const AutomationList& other) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _style = other._style; min_yval = other.min_yval; @@ -82,6 +88,7 @@ AutomationList::AutomationList (const AutomationList& other) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) { /* we have to use other point_factory() because @@ -96,7 +103,7 @@ AutomationList::AutomationList (const AutomationList& other) AutomationList::AutomationList (const AutomationList& other, double start, double end) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _style = other._style; min_yval = other.min_yval; @@ -109,6 +116,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; /* now grab the relevant points, and shift them back if necessary */ @@ -129,7 +137,7 @@ AutomationList::AutomationList (const AutomationList& other, double start, doubl AutomationList::AutomationList (const XMLNode& node) { - _frozen = false; + _frozen = 0; changed_when_thawed = false; _touching = false; min_yval = FLT_MIN; @@ -141,6 +149,7 @@ AutomationList::AutomationList (const XMLNode& node) rt_insertion_point = events.end(); lookup_cache.left = -1; lookup_cache.range.first = events.end(); + sort_pending = false; set_state (node); @@ -402,7 +411,7 @@ AutomationList::add (double when, double value) } if (insert) { - + events.insert (insertion_point, point_factory (when, value)); reposition_for_rt_add (0); @@ -512,9 +521,18 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double while (start != end) { (*start)->when += xdelta; (*start)->value += ydelta; + if (isnan ((*start)->value)) { + abort (); + } ++start; } + if (!_frozen) { + events.sort (sort_events_by_time); + } else { + sort_pending = true; + } + mark_dirty (); } @@ -522,6 +540,25 @@ AutomationList::move_range (iterator start, iterator end, double xdelta, double } void +AutomationList::slide (iterator before, double distance) +{ + { + Glib::Mutex::Lock lm (lock); + + if (before == events.end()) { + return; + } + + while (before != events.end()) { + (*before)->when += distance; + ++before; + } + } + + maybe_signal_changed (); +} + +void AutomationList::modify (iterator iter, double when, double val) { /* note: we assume higher level logic is in place to avoid this @@ -531,11 +568,23 @@ AutomationList::modify (iterator iter, double when, double val) { Glib::Mutex::Lock lm (lock); + (*iter)->when = when; (*iter)->value = val; + + if (isnan (val)) { + abort (); + } + + if (!_frozen) { + events.sort (sort_events_by_time); + } else { + sort_pending = true; + } + mark_dirty (); } - + maybe_signal_changed (); } @@ -576,13 +625,31 @@ AutomationList::control_points_adjacent (double xval) void AutomationList::freeze () { - _frozen = true; + _frozen++; } void AutomationList::thaw () { - _frozen = false; + if (_frozen == 0) { + PBD::stacktrace (cerr); + fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg; + /*NOTREACHED*/ + } + + if (--_frozen > 0) { + return; + } + + { + Glib::Mutex::Lock lm (lock); + + if (sort_pending) { + events.sort (sort_events_by_time); + sort_pending = false; + } + } + if (changed_when_thawed) { StateChanged(); /* EMIT SIGNAL */ } @@ -1240,6 +1307,7 @@ AutomationList::deserialize_events (const XMLNode& node) } thaw (); + return 0; } @@ -1271,6 +1339,7 @@ AutomationList::set_state (const XMLNode& node) jack_nframes_t x; double y; + freeze (); clear (); for (i = elist.begin(); i != elist.end(); ++i) { @@ -1287,9 +1356,11 @@ AutomationList::set_state (const XMLNode& node) } y = atof (prop->value().c_str()); - add (x, y); + fast_simple_add (x, y); } + thaw (); + return 0; } @@ -1345,7 +1416,7 @@ AutomationList::set_state (const XMLNode& node) deserialize_events (*(*niter)); } } - + return 0; } diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc index e84e92fa26..eb3d879447 100644 --- a/libs/ardour/configuration.cc +++ b/libs/ardour/configuration.cc @@ -25,7 +25,6 @@ #include <pbd/xml++.h> #include <ardour/ardour.h> -#include <ardour/audio_library.h> #include <ardour/configuration.h> #include <ardour/audio_diskstream.h> #include <ardour/destructive_filesource.h> @@ -172,7 +171,6 @@ Configuration::get_state () } root->add_child_nocopy (ControlProtocolManager::instance().get_state()); - root->add_child_nocopy (Library->get_state()); return *root; } @@ -235,8 +233,6 @@ Configuration::set_state (const XMLNode& root) } else if (node->name() == ControlProtocolManager::state_node_name) { _control_protocol_state = new XMLNode (*node); - } else if (node->name() == AudioLibrary::state_node_name) { - Library->set_state (*node); } } diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc index a715254747..de177d0e3d 100644 --- a/libs/ardour/control_protocol_manager.cc +++ b/libs/ardour/control_protocol_manager.cc @@ -154,9 +154,11 @@ ControlProtocolManager::teardown (ControlProtocolInfo& cpi) static bool protocol_filter (const string& str, void *arg) { - /* Not a dotfile, has a prefix before a period, suffix is "so" */ + /* Not a dotfile, has a prefix before a period, suffix is "so", or "dylib" */ - return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3)); + return str[0] != '.' + && ((str.length() > 3 && str.find (".so") == (str.length() - 3)) + || (str.length() > 6 && str.find (".dylib") == (str.length() - 6))); } void diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 739ea1cc0e..fd2fced83c 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -759,7 +759,7 @@ Crossfade::set_state (const XMLNode& node) /* fade out */ - _fade_in.freeze (); + _fade_out.freeze (); _fade_out.clear (); children = fo->children(); diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc index 5f6f3956cf..09c5b75b86 100644 --- a/libs/ardour/diskstream.cc +++ b/libs/ardour/diskstream.cc @@ -71,14 +71,12 @@ sigc::signal<void> Diskstream::DiskUnderrun; Diskstream::Diskstream (Session &sess, const string &name, Flag flag) : _name (name) , _session (sess) - , _playlist(NULL) { init (flag); } Diskstream::Diskstream (Session& sess, const XMLNode& node) : _session (sess) - , _playlist(NULL) { init (Recordable); } @@ -131,7 +129,7 @@ Diskstream::~Diskstream () //Glib::Mutex::Lock lm (state_lock); if (_playlist) - _playlist->unref (); + _playlist->release (); } void @@ -305,7 +303,7 @@ Diskstream::set_speed (double sp) } int -Diskstream::use_playlist (Playlist* playlist) +Diskstream::use_playlist (boost::shared_ptr<Playlist> playlist) { { Glib::Mutex::Lock lm (state_lock); @@ -314,26 +312,30 @@ Diskstream::use_playlist (Playlist* playlist) return 0; } - plstate_connection.disconnect(); plmod_connection.disconnect (); plgone_connection.disconnect (); if (_playlist) { - _playlist->unref(); + _playlist->release(); } _playlist = playlist; - _playlist->ref(); + _playlist->use(); if (!in_set_state && recordable()) { reset_write_sources (false); } plmod_connection = _playlist->Modified.connect (mem_fun (*this, &Diskstream::playlist_modified)); - plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), _playlist)); + plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), boost::weak_ptr<Playlist>(_playlist))); } - if (!overwrite_queued) { + /* don't do this if we've already asked for it *or* if we are setting up + the diskstream for the very first time - the input changed handling will + take care of the buffer refill. + */ + + if (!overwrite_queued && !(_session.state_of_the_state() & Session::CannotSave)) { _session.request_overwrite_buffer (this); overwrite_queued = true; } @@ -360,14 +362,21 @@ Diskstream::playlist_modified () } void -Diskstream::playlist_deleted (Playlist* pl) +Diskstream::playlist_deleted (boost::weak_ptr<Playlist> wpl) { - /* this catches an ordering issue with session destruction. playlists - are destroyed before diskstreams. we have to invalidate any handles - we have to the playlist. - */ + boost::shared_ptr<Playlist> pl (wpl.lock()); + + if (pl == _playlist) { - _playlist = 0; + /* this catches an ordering issue with session destruction. playlists + are destroyed before diskstreams. we have to invalidate any handles + we have to the playlist. + */ + + if (_playlist) { + _playlist.reset (); + } + } } int diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc new file mode 100644 index 0000000000..0460df43d8 --- /dev/null +++ b/libs/ardour/enums.cc @@ -0,0 +1,327 @@ +#include <pbd/enumwriter.h> + +#include <ardour/types.h> +#include <ardour/session.h> +#include <ardour/location.h> +#include <ardour/audiofilesource.h> +#include <ardour/diskstream.h> +#include <ardour/audioregion.h> +#include <ardour/route_group.h> +#include <ardour/panner.h> +#include <ardour/track.h> + +using namespace std; +using namespace PBD; +using namespace ARDOUR; + +void +setup_enum_writer () +{ + EnumWriter* enum_writer = new EnumWriter(); + vector<int> i; + vector<string> s; + + OverlapType _OverlapType; + AlignStyle _AlignStyle; + MeterPoint _MeterPoint; + TrackMode _TrackMode; + MeterFalloff _MeterFalloff; + MeterHold _MeterHold; + EditMode _EditMode; + RegionPoint _RegionPoint; + Placement _Placement; + MonitorModel _MonitorModel; + CrossfadeModel _CrossfadeModel; + LayerModel _LayerModel; + SoloModel _SoloModel; + SampleFormat _SampleFormat; + HeaderFormat _HeaderFormat; + PluginType _PluginType; + SlaveSource _SlaveSource; + ShuttleBehaviour _ShuttleBehaviour; + ShuttleUnits _ShuttleUnits; + mute_type _mute_type; + Session::RecordState _Session_RecordState; + Session::Event::Type _Session_Event_Type; + SmpteFormat _Session_SmpteFormat; + Session::PullupFormat _Session_PullupFormat; + AudioRegion::FadeShape _AudioRegion_FadeShape; + Panner::LinkDirection _Panner_LinkDirection; + IOChange _IOChange; + AutomationType _AutomationType; + AutoState _AutoState; + AutoStyle _AutoStyle; + AutoConnectOption _AutoConnectOption; + Session::StateOfTheState _Session_StateOfTheState; + Route::Flag _Route_Flag; + AudioFileSource::Flag _AudioFileSource_Flag; + Diskstream::Flag _Diskstream_Flag; + Location::Flags _Location_Flags; + RouteGroup::Flag _RouteGroup_Flag; + Region::Flag _Region_Flag; + Track::FreezeState _Track_FreezeState; + +#define REGISTER(e) enum_writer->register_distinct (typeid(e).name(), i, s); i.clear(); s.clear() +#define REGISTER_BITS(e) enum_writer->register_bits (typeid(e).name(), i, s); i.clear(); s.clear() +#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e) +#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) + + REGISTER_ENUM (NoChange); + REGISTER_ENUM (ConfigurationChanged); + REGISTER_ENUM (ConnectionsChanged); + REGISTER_BITS (_IOChange); + + REGISTER_ENUM (OverlapNone); + REGISTER_ENUM (OverlapInternal); + REGISTER_ENUM (OverlapStart); + REGISTER_ENUM (OverlapEnd); + REGISTER_ENUM (OverlapExternal); + REGISTER (_OverlapType); + + REGISTER_ENUM (GainAutomation); + REGISTER_ENUM (PanAutomation); + REGISTER_ENUM (PluginAutomation); + REGISTER_ENUM (SoloAutomation); + REGISTER_ENUM (MuteAutomation); + REGISTER_BITS (_AutomationType); + + REGISTER_ENUM (Off); + REGISTER_ENUM (Write); + REGISTER_ENUM (Touch); + REGISTER_ENUM (Play); + REGISTER_BITS (_AutoState); + + REGISTER_ENUM (Absolute); + REGISTER_ENUM (Trim); + REGISTER_BITS (_AutoStyle); + + REGISTER_ENUM (CaptureTime); + REGISTER_ENUM (ExistingMaterial); + REGISTER (_AlignStyle); + + REGISTER_ENUM (MeterInput); + REGISTER_ENUM (MeterPreFader); + REGISTER_ENUM (MeterPostFader); + REGISTER (_MeterPoint); + + REGISTER_ENUM (Normal); + REGISTER_ENUM (Destructive); + REGISTER (_TrackMode); + + REGISTER_ENUM (MeterFalloffOff); + REGISTER_ENUM (MeterFalloffSlowest); + REGISTER_ENUM (MeterFalloffSlow); + REGISTER_ENUM (MeterFalloffMedium); + REGISTER_ENUM (MeterFalloffFast); + REGISTER_ENUM (MeterFalloffFaster); + REGISTER_ENUM (MeterFalloffFastest); + REGISTER (_MeterFalloff); + + REGISTER_ENUM (MeterHoldOff); + REGISTER_ENUM (MeterHoldShort); + REGISTER_ENUM (MeterHoldMedium); + REGISTER_ENUM (MeterHoldLong); + REGISTER (_MeterHold); + + REGISTER_ENUM (Slide); + REGISTER_ENUM (Splice); + REGISTER (_EditMode); + + REGISTER_ENUM (Start); + REGISTER_ENUM (End); + REGISTER_ENUM (SyncPoint); + REGISTER (_RegionPoint); + + + REGISTER_ENUM (PreFader); + REGISTER_ENUM (PostFader); + REGISTER (_Placement); + + REGISTER_ENUM (HardwareMonitoring); + REGISTER_ENUM (SoftwareMonitoring); + REGISTER_ENUM (ExternalMonitoring); + REGISTER (_MonitorModel); + + REGISTER_ENUM (FullCrossfade); + REGISTER_ENUM (ShortCrossfade); + REGISTER (_CrossfadeModel); + + REGISTER_ENUM (LaterHigher); + REGISTER_ENUM (MoveAddHigher); + REGISTER_ENUM (AddHigher); + REGISTER (_LayerModel); + + REGISTER_ENUM (InverseMute); + REGISTER_ENUM (SoloBus); + REGISTER (_SoloModel); + + REGISTER_ENUM (AutoConnectPhysical); + REGISTER_ENUM (AutoConnectMaster); + REGISTER_BITS (_AutoConnectOption); + + REGISTER_ENUM (FormatFloat); + REGISTER_ENUM (FormatInt24); + REGISTER (_SampleFormat); + + REGISTER_ENUM (BWF); + REGISTER_ENUM (WAVE); + REGISTER_ENUM (WAVE64); + REGISTER_ENUM (CAF); + REGISTER_ENUM (AIFF); + REGISTER_ENUM (iXML); + REGISTER_ENUM (RF64); + REGISTER (_HeaderFormat); + + REGISTER_ENUM (AudioUnit); + REGISTER_ENUM (LADSPA); + REGISTER_ENUM (VST); + REGISTER (_PluginType); + + REGISTER_ENUM (None); + REGISTER_ENUM (MTC); + REGISTER_ENUM (JACK); + REGISTER (_SlaveSource); + + REGISTER_ENUM (Sprung); + REGISTER_ENUM (Wheel); + REGISTER (_ShuttleBehaviour); + + REGISTER_ENUM (Percentage); + REGISTER_ENUM (Semitones); + REGISTER (_ShuttleUnits); + + REGISTER_CLASS_ENUM (Session, Disabled); + REGISTER_CLASS_ENUM (Session, Enabled); + REGISTER_CLASS_ENUM (Session, Recording); + REGISTER (_Session_RecordState); + + REGISTER_CLASS_ENUM (Session::Event, SetTransportSpeed); + REGISTER_CLASS_ENUM (Session::Event, SetDiskstreamSpeed); + REGISTER_CLASS_ENUM (Session::Event, Locate); + REGISTER_CLASS_ENUM (Session::Event, LocateRoll); + REGISTER_CLASS_ENUM (Session::Event, SetLoop); + REGISTER_CLASS_ENUM (Session::Event, PunchIn); + REGISTER_CLASS_ENUM (Session::Event, PunchOut); + REGISTER_CLASS_ENUM (Session::Event, RangeStop); + REGISTER_CLASS_ENUM (Session::Event, RangeLocate); + REGISTER_CLASS_ENUM (Session::Event, Overwrite); + REGISTER_CLASS_ENUM (Session::Event, SetSlaveSource); + REGISTER_CLASS_ENUM (Session::Event, Audition); + REGISTER_CLASS_ENUM (Session::Event, InputConfigurationChange); + REGISTER_CLASS_ENUM (Session::Event, SetAudioRange); + REGISTER_CLASS_ENUM (Session::Event, SetPlayRange); + REGISTER_CLASS_ENUM (Session::Event, StopOnce); + REGISTER_CLASS_ENUM (Session::Event, AutoLoop); + REGISTER (_Session_Event_Type); + + REGISTER_CLASS_ENUM (Session, Clean); + REGISTER_CLASS_ENUM (Session, Dirty); + REGISTER_CLASS_ENUM (Session, CannotSave); + REGISTER_CLASS_ENUM (Session, Deletion); + REGISTER_CLASS_ENUM (Session, InitialConnecting); + REGISTER_CLASS_ENUM (Session, Loading); + REGISTER_CLASS_ENUM (Session, InCleanup); + REGISTER_BITS (_Session_StateOfTheState); + + REGISTER_ENUM (smpte_23976); + REGISTER_ENUM (smpte_24); + REGISTER_ENUM (smpte_24976); + REGISTER_ENUM (smpte_25); + REGISTER_ENUM (smpte_2997); + REGISTER_ENUM (smpte_2997drop); + REGISTER_ENUM (smpte_30); + REGISTER_ENUM (smpte_30drop); + REGISTER_ENUM (smpte_5994); + REGISTER_ENUM (smpte_60); + REGISTER (_Session_SmpteFormat); + + REGISTER_CLASS_ENUM (Session, pullup_Plus4Plus1); + REGISTER_CLASS_ENUM (Session, pullup_Plus4); + REGISTER_CLASS_ENUM (Session, pullup_Plus4Minus1); + REGISTER_CLASS_ENUM (Session, pullup_Plus1); + REGISTER_CLASS_ENUM (Session, pullup_None); + REGISTER_CLASS_ENUM (Session, pullup_Minus1); + REGISTER_CLASS_ENUM (Session, pullup_Minus4Plus1); + REGISTER_CLASS_ENUM (Session, pullup_Minus4); + REGISTER_CLASS_ENUM (Session, pullup_Minus4Minus1); + REGISTER (_Session_PullupFormat); + + REGISTER_ENUM (PRE_FADER); + REGISTER_ENUM (POST_FADER); + REGISTER_ENUM (CONTROL_OUTS); + REGISTER_ENUM (MAIN_OUTS); + REGISTER (_mute_type); + + REGISTER_CLASS_ENUM (Route, Hidden); + REGISTER_CLASS_ENUM (Route, MasterOut); + REGISTER_CLASS_ENUM (Route, ControlOut); + REGISTER_BITS (_Route_Flag); + + REGISTER_CLASS_ENUM (AudioFileSource, Writable); + REGISTER_CLASS_ENUM (AudioFileSource, CanRename); + REGISTER_CLASS_ENUM (AudioFileSource, Broadcast); + REGISTER_CLASS_ENUM (AudioFileSource, Removable); + REGISTER_CLASS_ENUM (AudioFileSource, RemovableIfEmpty); + REGISTER_CLASS_ENUM (AudioFileSource, RemoveAtDestroy); + REGISTER_CLASS_ENUM (AudioFileSource, NoPeakFile); + REGISTER_CLASS_ENUM (AudioFileSource, Destructive); + REGISTER_BITS (_AudioFileSource_Flag); + + REGISTER_CLASS_ENUM (AudioRegion, Linear); + REGISTER_CLASS_ENUM (AudioRegion, Fast); + REGISTER_CLASS_ENUM (AudioRegion, Slow); + REGISTER_CLASS_ENUM (AudioRegion, LogA); + REGISTER_CLASS_ENUM (AudioRegion, LogB); + REGISTER (_AudioRegion_FadeShape); + + REGISTER_CLASS_ENUM (Diskstream, Recordable); + REGISTER_CLASS_ENUM (Diskstream, Hidden); + REGISTER_CLASS_ENUM (Diskstream, Destructive); + REGISTER_BITS (_Diskstream_Flag); + + REGISTER_CLASS_ENUM (Location, IsMark); + REGISTER_CLASS_ENUM (Location, IsAutoPunch); + REGISTER_CLASS_ENUM (Location, IsAutoLoop); + REGISTER_CLASS_ENUM (Location, IsHidden); + REGISTER_CLASS_ENUM (Location, IsCDMarker); + REGISTER_CLASS_ENUM (Location, IsEnd); + REGISTER_CLASS_ENUM (Location, IsRangeMarker); + REGISTER_CLASS_ENUM (Location, IsStart); + REGISTER_BITS (_Location_Flags); + + + REGISTER_CLASS_ENUM (RouteGroup, Relative); + REGISTER_CLASS_ENUM (RouteGroup, Active); + REGISTER_CLASS_ENUM (RouteGroup, Hidden); + REGISTER_BITS (_RouteGroup_Flag); + + REGISTER_CLASS_ENUM (Panner, SameDirection); + REGISTER_CLASS_ENUM (Panner, OppositeDirection); + REGISTER (_Panner_LinkDirection); + + REGISTER_CLASS_ENUM (Region, Muted); + REGISTER_CLASS_ENUM (Region, Opaque); + REGISTER_CLASS_ENUM (Region, EnvelopeActive); + REGISTER_CLASS_ENUM (Region, DefaultFadeIn); + REGISTER_CLASS_ENUM (Region, DefaultFadeOut); + REGISTER_CLASS_ENUM (Region, Locked); + REGISTER_CLASS_ENUM (Region, Automatic); + REGISTER_CLASS_ENUM (Region, WholeFile); + REGISTER_CLASS_ENUM (Region, FadeIn); + REGISTER_CLASS_ENUM (Region, FadeOut); + REGISTER_CLASS_ENUM (Region, Copied); + REGISTER_CLASS_ENUM (Region, Import); + REGISTER_CLASS_ENUM (Region, External); + REGISTER_CLASS_ENUM (Region, SyncMarked); + REGISTER_CLASS_ENUM (Region, LeftOfSplit); + REGISTER_CLASS_ENUM (Region, RightOfSplit); + REGISTER_CLASS_ENUM (Region, Hidden); + REGISTER_CLASS_ENUM (Region, DoNotSaveState); + REGISTER_BITS (_Region_Flag); + + REGISTER_CLASS_ENUM (Track, NoFreeze); + REGISTER_CLASS_ENUM (Track, Frozen); + REGISTER_CLASS_ENUM (Track, UnFrozen); + REGISTER (_Track_FreezeState); + +} diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index f92660470c..515d9e1c2f 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -191,42 +191,11 @@ setup_midi (AudioEngine& engine ) return 0; } - -int -ARDOUR::init (ARDOUR::AudioEngine& engine, bool use_vst, bool try_optimization) + +void +setup_hardware_optimization (bool try_optimization) { - bool generic_mix_functions = true; - - (void) bindtextdomain(PACKAGE, LOCALEDIR); - - PBD::ID::init (); - - lrdf_init(); - Library = new AudioLibrary; - - Config = new Configuration; - - if (Config->load_state ()) { - return -1; - } - - Config->set_use_vst (use_vst); - - if (setup_midi (engine)) { - return -1; - } - -#ifdef HAVE_LIBLO - if (setup_osc ()) { - return -1; - } -#endif - -#ifdef VST_SUPPORT - if (Config->get_use_vst() && fst_init ()) { - return -1; - } -#endif + bool generic_mix_functions = true; if (try_optimization) { @@ -261,7 +230,7 @@ ARDOUR::init (ARDOUR::AudioEngine& engine, bool use_vst, bool try_optimization) #endif /* USE_X86_64_ASM */ if (use_sse) { - cerr << "Enabling SSE optimized routines" << endl; + info << "Using SSE optimized routines" << endmsg; // SSE SET Session::compute_peak = x86_sse_compute_peak; @@ -301,6 +270,47 @@ ARDOUR::init (ARDOUR::AudioEngine& engine, bool use_vst, bool try_optimization) info << "No H/W specific optimizations in use" << endmsg; } +} + +int +ARDOUR::init (ARDOUR::AudioEngine& engine, bool use_vst, bool try_optimization) +{ + extern void setup_enum_writer (); + + (void) bindtextdomain(PACKAGE, LOCALEDIR); + + PBD::ID::init (); + + setup_enum_writer (); + + lrdf_init(); + Library = new AudioLibrary; + + Config = new Configuration; + + if (Config->load_state ()) { + return -1; + } + + Config->set_use_vst (use_vst); + + if (setup_midi (engine)) { + return -1; + } + +#ifdef HAVE_LIBLO + if (setup_osc ()) { + return -1; + } +#endif + +#ifdef VST_SUPPORT + if (Config->get_use_vst() && fst_init ()) { + return -1; + } +#endif + + setup_hardware_optimization (try_optimization); /* singleton - first object is "it" */ new PluginManager (); @@ -393,8 +403,14 @@ ARDOUR::get_system_data_path () { string path; - path += DATA_DIR; - path += "/ardour2/"; + char *envvar; + + if ((envvar = getenv ("ARDOUR_DATA_PATH")) != 0) { + path = envvar; + } else { + path += DATA_DIR; + path += "/ardour2/"; + } return path; } @@ -403,9 +419,14 @@ string ARDOUR::get_system_module_path () { string path; + char *envvar; - path += MODULE_DIR; - path += "/ardour2/"; + if ((envvar = getenv ("ARDOUR_MODULE_PATH")) != 0) { + path = envvar; + } else { + path += MODULE_DIR; + path += "/ardour2/"; + } return path; } @@ -602,4 +623,5 @@ std::istream& operator>>(std::istream& o, CrossfadeModel& var) { return int_to_t std::istream& operator>>(std::istream& o, SlaveSource& var) { return int_to_type<SlaveSource> (o, var); } std::istream& operator>>(std::istream& o, ShuttleBehaviour& var) { return int_to_type<ShuttleBehaviour> (o, var); } std::istream& operator>>(std::istream& o, ShuttleUnits& var) { return int_to_type<ShuttleUnits> (o, var); } +std::istream& operator>>(std::istream& o, SmpteFormat& var) { return int_to_type<SmpteFormat> (o, var); } diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index f16a6e7d8c..4466c40a32 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -74,22 +74,22 @@ Session::import_audiofile (import_status& status) status.new_regions.clear (); - if ((in = sf_open (status.pathname.c_str(), SFM_READ, &info)) == 0) { - error << string_compose(_("Import: cannot open input sound file \"%1\""), status.pathname) << endmsg; + 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; return -1; } else { if ((uint32_t) info.samplerate != frame_rate()) { sf_close(in); status.doing_what = _("resampling audio"); // resample to session frame_rate - if (sample_rate_convert(status, status.pathname, tmp_convert_file)) { + if (sample_rate_convert(status, status.paths.front(), tmp_convert_file)) { if ((in = sf_open (tmp_convert_file.c_str(), SFM_READ, &info)) == 0) { error << string_compose(_("Import: cannot open converted sound file \"%1\""), tmp_convert_file) << endmsg; return -1; } } else if (!status.cancel){ // error - error << string_compose(_("Import: error while resampling sound file \"%1\""), status.pathname) << endmsg; + error << string_compose(_("Import: error while resampling sound file \"%1\""), status.paths.front()) << endmsg; return -1; } else { // canceled @@ -103,7 +103,7 @@ Session::import_audiofile (import_status& status) } sounds_dir = discover_best_sound_dir (); - basepath = PBD::basename_nosuffix (status.pathname); + basepath = PBD::basename_nosuffix (status.paths.front()); for (n = 0; n < info.channels; ++n) { @@ -112,14 +112,14 @@ Session::import_audiofile (import_status& status) do { if (info.channels == 2) { if (n == 0) { - snprintf (buf, sizeof(buf), "%s%s-L.wav", sounds_dir.c_str(), basepath.c_str()); + 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()); + 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%lu.wav", sounds_dir.c_str(), basepath.c_str(), n+1); + snprintf (buf, sizeof(buf), "%s/%s-c%lu.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()); + snprintf (buf, sizeof(buf), "%s/%s.wav", sounds_dir.c_str(), basepath.c_str()); } if (::access (buf, F_OK) == 0) { @@ -217,8 +217,13 @@ Session::import_audiofile (import_status& status) sources.push_back(newfiles[n]); } - boost::shared_ptr<AudioRegion> r (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, newfiles[0]->length(), region_name_from_path (Glib::path_get_basename (basepath)), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); + 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); @@ -231,11 +236,14 @@ Session::import_audiofile (import_status& status) /* 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 (Glib::path_get_basename (newfiles[n]->name())), + region_name_from_path (newfiles[n]->name(), false), 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile | AudioRegion::Import)))); } } diff --git a/libs/ardour/insert.cc b/libs/ardour/insert.cc index 034b043763..d109642fd4 100644 --- a/libs/ardour/insert.cc +++ b/libs/ardour/insert.cc @@ -50,21 +50,15 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -/* ********** FIXME: TYPE ************** */ -/* Inserts are still definitely audio only */ - -Insert::Insert(Session& s, Placement p) - : Redirect (s, s.next_insert_name(), p) -{ -} - -Insert::Insert(Session& s, Placement p, int imin, int imax, int omin, int omax) - : Redirect (s, s.next_insert_name(), p, imin, imax, omin, omax) +/* ********** FIXME: TYPE ************** + * Inserts are still definitely audio only */ +Insert::Insert(Session& s, string name, Placement p) + : Redirect (s, name, p) { } -Insert::Insert(Session& s, string name, Placement p) - : Redirect (s, name, p) +Insert::Insert(Session& s, string name, Placement p, int imin, int imax, int omin, int omax) + : Redirect (s, name, p, imin, imax, omin, omax) { } @@ -624,14 +618,8 @@ PluginInsert::state (bool full) XMLNode* child = new XMLNode("port"); snprintf(buf, sizeof(buf), "%" PRIu32, *x); child->add_property("number", string(buf)); - - if (full) { - snprintf(buf, sizeof(buf), "0x%x", automation_list (*x).automation_state ()); - } else { - snprintf(buf, sizeof(buf), "0x%x", ARDOUR::Off); - } - child->add_property("auto", string(buf)); - + + child->add_child_nocopy (automation_list (*x).state (full)); autonode->add_child_nocopy (*child); } @@ -735,43 +723,62 @@ PluginInsert::set_state(const XMLNode& node) /* look for port automation node */ for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == port_automation_node_name) { - XMLNodeList cnodes; - XMLProperty *cprop; - XMLNodeConstIterator iter; - XMLNode *child; - const char *port; - uint32_t port_id; - - cnodes = (*niter)->children ("port"); - - for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){ - - child = *iter; - - if ((cprop = child->property("number")) != 0) { - port = cprop->value().c_str(); - } else { - warning << _("PluginInsert: Auto: no ladspa port number") << endmsg; - continue; - } - sscanf (port, "%" PRIu32, &port_id); + if ((*niter)->name() != port_automation_node_name) { + continue; + } - if (port_id >= _plugins[0]->parameter_count()) { - warning << _("PluginInsert: Auto: port id out of range") << endmsg; - continue; - } - + XMLNodeList cnodes; + XMLProperty *cprop; + XMLNodeConstIterator iter; + XMLNode *child; + const char *port; + uint32_t port_id; + + cnodes = (*niter)->children ("port"); + + for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){ + + child = *iter; + + if ((cprop = child->property("number")) != 0) { + port = cprop->value().c_str(); + } else { + warning << _("PluginInsert: Auto: no ladspa port number") << endmsg; + continue; + } + + sscanf (port, "%" PRIu32, &port_id); + + if (port_id >= _plugins[0]->parameter_count()) { + warning << _("PluginInsert: Auto: port id out of range") << endmsg; + continue; + } + + if (!child->children().empty()) { + automation_list (port_id).set_state (*child->children().front()); + } else { if ((cprop = child->property("auto")) != 0) { + + /* old school */ + int x; sscanf (cprop->value().c_str(), "0x%x", &x); automation_list (port_id).set_automation_state (AutoState (x)); + + } else { + + /* missing */ + + automation_list (port_id).set_automation_state (Off); } } - - break; + } + + /* done */ + + break; } if (niter == nlist.end()) { @@ -830,14 +837,14 @@ PluginInsert::type () ***************************************************************/ PortInsert::PortInsert (Session& s, Placement p) - : Insert (s, p, 1, -1, 1, -1) + : Insert (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1) { init (); RedirectCreated (this); /* EMIT SIGNAL */ } PortInsert::PortInsert (const PortInsert& other) - : Insert (other._session, other.placement(), 1, -1, 1, -1) + : Insert (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1) { init (); RedirectCreated (this); /* EMIT SIGNAL */ @@ -900,9 +907,11 @@ XMLNode& PortInsert::state (bool full) { XMLNode *node = new XMLNode("Insert"); - + char buf[32]; node->add_child_nocopy (Redirect::state(full)); - node->add_property("type", "port"); + node->add_property ("type", "port"); + snprintf (buf, sizeof (buf), "%" PRIu32, bitslot); + node->add_property ("bitslot", buf); return *node; } @@ -925,6 +934,13 @@ PortInsert::set_state(const XMLNode& node) return -1; } + if ((prop = node.property ("bitslot")) == 0) { + bitslot = _session.next_insert_id(); + } else { + sscanf (prop->value().c_str(), "%" PRIu32, &bitslot); + _session.mark_insert_id (bitslot); + } + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((*niter)->name() == Redirect::state_node_name) { Redirect::set_state (**niter); diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index 60e7ec3f42..7ed6158b85 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -55,6 +55,7 @@ extern "C" int isnan (double); extern "C" int isinf (double); #endif +#define BLOCK_PROCESS_CALLBACK() Glib::Mutex::Lock em (_session.engine().process_lock()) using namespace std; using namespace ARDOUR; @@ -339,7 +340,7 @@ IO::disconnect_input (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -375,7 +376,7 @@ IO::connect_input (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -409,7 +410,7 @@ IO::disconnect_output (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -444,7 +445,8 @@ IO::connect_output (Port* our_port, string other_port, void* src) } { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -503,7 +505,8 @@ IO::remove_output_port (Port* port, void* src) IOChange change (NoChange); { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -554,7 +557,8 @@ IO::add_output_port (string destination, void* src, DataType type) type = _default_type; { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -605,7 +609,8 @@ IO::remove_input_port (Port* port, void* src) IOChange change (NoChange); { - Glib::Mutex::Lock em(_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); + { Glib::Mutex::Lock lm (io_lock); @@ -657,7 +662,7 @@ IO::add_input_port (string source, void* src, DataType type) type = _default_type; { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -707,7 +712,7 @@ int IO::disconnect_inputs (void* src) { { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -729,7 +734,7 @@ int IO::disconnect_outputs (void* src) { { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); { Glib::Mutex::Lock lm (io_lock); @@ -793,7 +798,7 @@ IO::ensure_inputs_locked (ChanCount count, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure(); } _inputs.add (input_port); @@ -845,7 +850,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) } { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm (io_lock); Port* port; @@ -904,12 +909,12 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) return -1; } } - + catch (AudioEngine::PortRegistrationFailure& err) { setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure(); } _inputs.add (port); @@ -941,7 +946,7 @@ IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src) setup_peak_meters (); reset_panner (); /* pass it on */ - throw err; + throw AudioEngine::PortRegistrationFailure (); } _outputs.add (port); @@ -998,7 +1003,7 @@ IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src) } if (lockit) { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock im (io_lock); changed = ensure_inputs_locked (count, clear, src); } else { @@ -1095,7 +1100,7 @@ IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src) /* XXX caller should hold io_lock, but generally doesn't */ if (lockit) { - Glib::Mutex::Lock em (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock im (io_lock); changed = ensure_outputs_locked (count, clear, src); } else { @@ -1906,7 +1911,7 @@ IO::use_input_connection (Connection& c, void* src) uint32_t limit; { - Glib::Mutex::Lock lm (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); limit = c.nports(); @@ -1985,7 +1990,7 @@ IO::use_output_connection (Connection& c, void* src) uint32_t limit; { - Glib::Mutex::Lock lm (_session.engine().process_lock()); + BLOCK_PROCESS_CALLBACK (); Glib::Mutex::Lock lm2 (io_lock); limit = c.nports(); diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index e09a59d42f..bec87e5dd6 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -28,6 +28,7 @@ #include <pbd/stl_delete.h> #include <pbd/xml++.h> +#include <pbd/enumwriter.h> #include <ardour/location.h> #include <ardour/session.h> @@ -35,6 +36,8 @@ #include "i18n.h" +#define SUFFIX_MAX 32 + using namespace std; using namespace ARDOUR; using namespace sigc; @@ -217,12 +220,12 @@ Location::set_flag_internal (bool yn, Flags flag) { if (yn) { if (!(_flags & flag)) { - _flags |= flag; + _flags = Flags (_flags | flag); return true; } } else { if (_flags & flag) { - _flags &= ~flag; + _flags = Flags (_flags & ~flag); return true; } } @@ -273,8 +276,7 @@ Location::get_state (void) node->add_property ("start", buf); snprintf (buf, sizeof (buf), "%u", end()); node->add_property ("end", buf); - snprintf (buf, sizeof (buf), "%" PRIu32, (uint32_t) _flags); - node->add_property ("flags", buf); + node->add_property ("flags", enum_2_string (_flags)); return *node; } @@ -327,14 +329,12 @@ Location::set_state (const XMLNode& node) _end = atoi (prop->value().c_str()); - _flags = 0; - if ((prop = node.property ("flags")) == 0) { error << _("XML node for Location has no flags information") << endmsg; return -1; } - _flags = Flags (atoi (prop->value().c_str())); + _flags = Flags (string_2_enum (prop->value(), _flags)); for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) { @@ -404,6 +404,40 @@ Locations::set_current (Location *loc, bool want_lock) } int +Locations::next_available_name(string& result,string base) +{ + LocationList::iterator i; + Location* location; + string temp; + string::size_type l; + int suffix; + char buf[32]; + bool available[SUFFIX_MAX+1]; + + result = base; + for (int k=1; k<SUFFIX_MAX; k++) { + available[k] = true; + } + l = base.length(); + for (i = locations.begin(); i != locations.end(); ++i) { + location =* i; + temp = location->name(); + if (l && !temp.find(base,0)) { + suffix = atoi(temp.substr(l,3)); + if (suffix) available[suffix] = false; + } + } + for (int k=1; k<=SUFFIX_MAX; k++) { + if (available[k]) { + snprintf (buf, 31, "%d", k); + result += buf; + return 1; + } + } + return 0; +} + +int Locations::set_current_unlocked (Location *loc) { if (find (locations.begin(), locations.end(), loc) == locations.end()) { diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc index 1ce610d13c..66e0a98dd3 100644 --- a/libs/ardour/meter.cc +++ b/libs/ardour/meter.cc @@ -57,6 +57,14 @@ PeakMeter::reset () } void +PeakMeter::reset_max () +{ + for (size_t i = 0; i < _max_peak_power.size(); ++i) { + _max_peak_power[i] = -INFINITY; + } +} + +void PeakMeter::setup (const ChanCount& in) { uint32_t limit = in.get(DataType::AUDIO); @@ -68,11 +76,13 @@ PeakMeter::setup (const ChanCount& in) while (_peak_power.size() < limit) { _peak_power.push_back (0); - _visible_peak_power.push_back (0); + _visible_peak_power.push_back (minus_infinity()); + _max_peak_power.push_back (minus_infinity()); } assert(_peak_power.size() == limit); assert(_visible_peak_power.size() == limit); + assert(_max_peak_power.size() == limit); } /** To be driven by the Meter signal from IO. @@ -102,6 +112,10 @@ PeakMeter::meter () new_peak = minus_infinity(); } + /* update max peak */ + + _max_peak_power[n] = std::max (new_peak, _max_peak_power[n]); + if (Config->get_meter_falloff() == 0.0f || new_peak > _visible_peak_power[n]) { _visible_peak_power[n] = new_peak; } else { diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc index b1ec7da965..f3de214791 100644 --- a/libs/ardour/midi_diskstream.cc +++ b/libs/ardour/midi_diskstream.cc @@ -14,8 +14,6 @@ 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: diskstream.cc 567 2006-06-07 14:54:12Z trutkin $ */ #include <fstream> @@ -36,6 +34,7 @@ #include <glibmm/thread.h> #include <pbd/xml++.h> #include <pbd/memento_command.h> +#include <pbd/enumwriter.h> #include <ardour/ardour.h> #include <ardour/audioengine.h> @@ -47,6 +46,7 @@ #include <ardour/send.h> #include <ardour/region_factory.h> #include <ardour/midi_playlist.h> +#include <ardour/playlist_factory.h> #include <ardour/cycle_timer.h> #include <ardour/midi_region.h> #include <ardour/midi_port.h> @@ -220,16 +220,14 @@ MidiDiskstream::get_input_sources () int MidiDiskstream::find_and_use_playlist (const string& name) { - Playlist* pl; - MidiPlaylist* playlist; + boost::shared_ptr<MidiPlaylist> playlist; - if ((pl = _session.playlist_by_name (name)) == 0) { - playlist = new MidiPlaylist(_session, name); - pl = playlist; + if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (_session.playlist_by_name (name))) == 0) { + playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (_session, name)); } - if ((playlist = dynamic_cast<MidiPlaylist*> (pl)) == 0) { - error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't a midi playlist"), name) << endmsg; + if (!playlist) { + error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't an midi playlist"), name) << endmsg; return -1; } @@ -237,18 +235,20 @@ MidiDiskstream::find_and_use_playlist (const string& name) } int -MidiDiskstream::use_playlist (Playlist* playlist) -{ - assert(dynamic_cast<MidiPlaylist*>(playlist)); +MidiDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist) +{ + assert(boost::dynamic_pointer_cast<MidiPlaylist>(playlist)); - return Diskstream::use_playlist(playlist); + Diskstream::use_playlist(playlist); + + return 0; } int MidiDiskstream::use_new_playlist () -{ +{ string newname; - MidiPlaylist* playlist; + boost::shared_ptr<MidiPlaylist> playlist; if (!in_set_state && destructive()) { return 0; @@ -260,9 +260,11 @@ MidiDiskstream::use_new_playlist () newname = Playlist::bump_name (_name, _session); } - if ((playlist = new MidiPlaylist (_session, newname, hidden())) != 0) { + if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (_session, newname, hidden()))) != 0) { + playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); + } else { return -1; } @@ -271,6 +273,8 @@ MidiDiskstream::use_new_playlist () int MidiDiskstream::use_copy_playlist () { + assert(midi_playlist()); + if (destructive()) { return 0; } @@ -281,11 +285,11 @@ MidiDiskstream::use_copy_playlist () } string newname; - MidiPlaylist* playlist; + boost::shared_ptr<MidiPlaylist> playlist; newname = Playlist::bump_name (_playlist->name(), _session); - if ((playlist = new MidiPlaylist (*midi_playlist(), newname)) != 0) { + if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist>(PlaylistFactory::create (midi_playlist(), newname))) != 0) { playlist->set_orig_diskstream_id (id()); return use_playlist (playlist); } else { @@ -1039,6 +1043,8 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap } + string whole_file_region_name; + whole_file_region_name = region_name_from_path (_write_source->name(), true); /* Register a new region with the Session that describes the entire source. Do this first so that any sub-regions will obviously be @@ -1048,7 +1054,7 @@ MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_cap assert(_write_source); boost::shared_ptr<Region> rx (RegionFactory::create (srcs, _write_source->last_capture_start_frame(), total_capture, - region_name_from_path (_write_source->name()), + whole_file_region_name, 0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile))); region = boost::dynamic_pointer_cast<MidiRegion> (rx); @@ -1289,7 +1295,7 @@ MidiDiskstream::set_state (const XMLNode& node) } if ((prop = node.property ("flags")) != 0) { - _flags = strtol (prop->value().c_str(), 0, 0); + _flags = Flag (string_2_enum (prop->value(), _flags)); } if ((prop = node.property ("channels")) != 0) { diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc index b2318bbe96..0352e1e910 100644 --- a/libs/ardour/midi_playlist.cc +++ b/libs/ardour/midi_playlist.cc @@ -49,22 +49,14 @@ MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden) in_set_state = true; set_state (node); in_set_state = false; - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden) : Playlist (session, name, DataType::MIDI, hidden) { - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } - } -MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden) +MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden) : Playlist (other, name, hidden) { throw; // nope @@ -106,12 +98,9 @@ MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, string name, bool hidden) in_n++; } */ - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } -MidiPlaylist::MidiPlaylist (const MidiPlaylist& other, jack_nframes_t start, jack_nframes_t dur, string name, bool hidden) +MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, jack_nframes_t start, jack_nframes_t dur, string name, bool hidden) : Playlist (other, start, dur, name, hidden) { /* this constructor does NOT notify others (session) */ diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc index f4d1912061..d7ce12d411 100644 --- a/libs/ardour/midi_port.cc +++ b/libs/ardour/midi_port.cc @@ -61,7 +61,7 @@ MidiPort::cycle_start (jack_nframes_t nframes) void* jack_buffer = jack_port_get_buffer(_port, nframes); const jack_nframes_t event_count - = jack_midi_port_get_info(jack_buffer, nframes)->event_count; + = jack_midi_get_event_count(jack_buffer, nframes); assert(event_count < _buffer.capacity()); diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index a18d0c20ce..26441203a3 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -303,11 +303,11 @@ MidiTrack::set_state_part_two () _freeze_record.insert_info.clear (); if ((prop = fnode->property (X_("playlist"))) != 0) { - Playlist* pl = _session.playlist_by_name (prop->value()); + boost::shared_ptr<Playlist> pl = _session.playlist_by_name (prop->value()); if (pl) { - _freeze_record.playlist = dynamic_cast<MidiPlaylist*> (pl); + _freeze_record.playlist = boost::dynamic_pointer_cast<MidiPlaylist> (pl); } else { - _freeze_record.playlist = 0; + _freeze_record.playlist.reset(); _freeze_record.state = NoFreeze; return; } diff --git a/libs/ardour/named_selection.cc b/libs/ardour/named_selection.cc index 605d7cae13..ecce09692f 100644 --- a/libs/ardour/named_selection.cc +++ b/libs/ardour/named_selection.cc @@ -33,12 +33,14 @@ using namespace PBD; sigc::signal<void,NamedSelection*> NamedSelection::NamedSelectionCreated; -NamedSelection::NamedSelection (string n, list<Playlist*>& l) +typedef std::list<boost::shared_ptr<Playlist> > PlaylistList; + +NamedSelection::NamedSelection (string n, PlaylistList& l) : name (n) { playlists = l; - for (list<Playlist*>::iterator i = playlists.begin(); i != playlists.end(); ++i) { - (*i)->ref(); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + (*i)->use(); } NamedSelectionCreated (this); } @@ -65,13 +67,13 @@ NamedSelection::NamedSelection (Session& session, const XMLNode& node) const XMLNode* plnode; string playlist_name; - Playlist* playlist; + boost::shared_ptr<Playlist> playlist; plnode = *niter; if ((property = plnode->property ("name")) != 0) { if ((playlist = session.playlist_by_name (property->value())) != 0) { - playlist->ref(); + playlist->use(); playlists.push_back (playlist); } else { warning << string_compose (_("Chunk %1 uses an unknown playlist \"%2\""), name, property->value()) << endmsg; @@ -87,8 +89,8 @@ NamedSelection::NamedSelection (Session& session, const XMLNode& node) NamedSelection::~NamedSelection () { - for (list<Playlist*>::iterator i = playlists.begin(); i != playlists.end(); ++i) { - (*i)->unref(); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + (*i)->release(); } } @@ -107,7 +109,7 @@ NamedSelection::get_state () root->add_property ("name", name); child = root->add_child ("Playlists"); - for (list<Playlist*>::iterator i = playlists.begin(); i != playlists.end(); ++i) { + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { XMLNode* plnode = new XMLNode ("Playlist"); plnode->add_property ("name", (*i)->name()); diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc index ee8e100e4a..c69bd84402 100644 --- a/libs/ardour/panner.cc +++ b/libs/ardour/panner.cc @@ -36,6 +36,7 @@ #include <pbd/error.h> #include <pbd/failed_constructor.h> #include <pbd/xml++.h> +#include <pbd/enumwriter.h> #include <ardour/session.h> #include <ardour/panner.h> @@ -1068,8 +1069,7 @@ Panner::state (bool full) char buf[32]; root->add_property (X_("linked"), (_linked ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _link_direction); - root->add_property (X_("link_direction"), buf); + root->add_property (X_("link_direction"), enum_2_string (_link_direction)); root->add_property (X_("bypassed"), (bypassed() ? "yes" : "no")); /* add each output */ @@ -1113,8 +1113,8 @@ Panner::set_state (const XMLNode& node) } if ((prop = node.property (X_("link_direction"))) != 0) { - sscanf (prop->value().c_str(), "%d", &i); - set_link_direction ((LinkDirection) (i)); + LinkDirection ld; /* here to provide type information */ + set_link_direction (LinkDirection (string_2_enum (prop->value(), ld))); } nlist = node.children(); diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index d439cf1265..2becdc19b4 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -36,6 +36,7 @@ #include <ardour/session.h> #include <ardour/region.h> #include <ardour/region_factory.h> +#include <ardour/playlist_factory.h> #include "i18n.h" @@ -43,14 +44,12 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -sigc::signal<void,Playlist*> Playlist::PlaylistCreated; - struct ShowMeTheList { - ShowMeTheList (Playlist *pl, const string& n) : playlist (pl), name (n) {} + ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {} ~ShowMeTheList () { cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl; }; - Playlist *playlist; + boost::shared_ptr<Playlist> playlist; string name; }; @@ -91,51 +90,52 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide init (hide); _name = "unnamed"; /* reset by set_state */ - /* derived class calls set_state() */ + /* set state called by derived class */ } -Playlist::Playlist (const Playlist& other, string namestr, bool hide) - : _name (namestr), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id) +Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide) + : _name (namestr), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id) { init (hide); RegionList tmp; - other.copy_regions (tmp); + other->copy_regions (tmp); in_set_state++; for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) { - add_region_internal( (*x), (*x)->position() ); + add_region_internal( (*x), (*x)->position()); } in_set_state--; - _splicing = other._splicing; - _nudging = other._nudging; - _edit_mode = other._edit_mode; + _splicing = other->_splicing; + _nudging = other->_nudging; + _edit_mode = other->_edit_mode; in_set_state = 0; in_flush = false; in_partition = false; subcnt = 0; _read_data_count = 0; - _frozen = other._frozen; - - layer_op_counter = other.layer_op_counter; - freeze_length = other.freeze_length; + _frozen = other->_frozen; + layer_op_counter = other->layer_op_counter; + freeze_length = other->freeze_length; } -Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide) - : _name (str), _session (other._session), _type(other._type), _orig_diskstream_id(other._orig_diskstream_id) +Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide) + : _name (str), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id) { - RegionLock rlock2 (&((Playlist&)other)); - + RegionLock rlock2 (const_cast<Playlist*> (other.get())); + nframes_t end = start + cnt - 1; init (hide); - for (RegionList::const_iterator i = other.regions.begin(); i != other.regions.end(); i++) { + in_set_state++; + + for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) { boost::shared_ptr<Region> region; boost::shared_ptr<Region> new_region; @@ -182,32 +182,30 @@ Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, strin new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags()); - add_region_internal (new_region, position, true); + add_region_internal (new_region, position); } + in_set_state--; + /* this constructor does NOT notify others (session) */ } void -Playlist::ref () +Playlist::use () { ++_refcnt; - InUse (this, true); /* EMIT SIGNAL */ + InUse (true); /* EMIT SIGNAL */ } void -Playlist::unref () +Playlist::release () { if (_refcnt > 0) { _refcnt--; } + if (_refcnt == 0) { - InUse (this, false); /* EMIT SIGNAL */ - - if (_hidden) { - /* nobody knows we exist */ - delete this; - } + InUse (false); /* EMIT SIGNAL */ } } @@ -266,7 +264,7 @@ Playlist::~Playlist () RegionLock rl (this); for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) { - (*i)->set_playlist (0); + (*i)->set_playlist (boost::shared_ptr<Playlist>()); } } @@ -411,12 +409,8 @@ Playlist::flush_notifications () timestamp_layer_op (*r); } pending_length = true; - n++; - } - - for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) { dependent_checks_needed.insert (*r); - /* don't increment n again - its the same list */ + n++; } for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { @@ -424,10 +418,6 @@ Playlist::flush_notifications () n++; } - for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { - check_dependents (*s, false); - } - for (s = pending_removes.begin(); s != pending_removes.end(); ++s) { remove_dependents (*s); n++; @@ -448,6 +438,10 @@ Playlist::flush_notifications () Modified (); /* EMIT SIGNAL */ } + for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { + check_dependents (*s, false); + } + pending_adds.clear (); pending_removes.clear (); pending_bounds.clear (); @@ -471,7 +465,7 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa nframes_t pos = position; if (itimes >= 1) { - add_region_internal (region, pos, true); + add_region_internal (region, pos); pos += region->length(); --itimes; } @@ -488,7 +482,7 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa for (int i = 0; i < itimes; ++i) { boost::shared_ptr<Region> copy = RegionFactory::create (region); - add_region_internal (copy, pos, true); + add_region_internal (copy, pos); pos += region->length(); } @@ -497,12 +491,24 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa string name; _session.region_name (name, region->name(), false); boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags()); - add_region_internal (sub, pos, true); + add_region_internal (sub, pos); + } +} + +void +Playlist::set_region_ownership () +{ + RegionLock rl (this); + RegionList::iterator i; + boost::weak_ptr<Playlist> pl (shared_from_this()); + + for (i = regions.begin(); i != regions.end(); ++i) { + (*i)->set_playlist (pl); } } void -Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort) +Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position) { RegionSortByPosition cmp; nframes_t old_length = 0; @@ -511,7 +517,11 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit old_length = _get_maximum_extent(); } - region->set_playlist (this); + if (!in_set_state) { + boost::shared_ptr<Playlist> foo (shared_from_this()); + region->set_playlist (boost::weak_ptr<Playlist>(foo)); + } + region->set_position (position, this); timestamp_layer_op (region); @@ -564,7 +574,7 @@ Playlist::remove_region (boost::shared_ptr<Region> region) } int -Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort) +Playlist::remove_region_internal (boost::shared_ptr<Region>region) { RegionList::iterator i; nframes_t old_length = 0; @@ -698,7 +708,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit)); - add_region_internal (region, start, true); + add_region_internal (region, start); new_regions.push_back (region); } @@ -708,7 +718,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit)); - add_region_internal (region, end, true); + add_region_internal (region, end); new_regions.push_back (region); /* "front" ***** */ @@ -737,7 +747,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit)); - add_region_internal (region, start, true); + add_region_internal (region, start); new_regions.push_back (region); } @@ -771,7 +781,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi _session.region_name (new_name, current->name(), false); region = RegionFactory::create (current, 0, pos3 - pos1, new_name, regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit)); - add_region_internal (region, pos1, true); + add_region_internal (region, pos1); new_regions.push_back (region); } @@ -813,20 +823,19 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi } } -Playlist* -Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden) +boost::shared_ptr<Playlist> +Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden) { - Playlist* ret; - Playlist* pl; + boost::shared_ptr<Playlist> ret; + boost::shared_ptr<Playlist> pl; nframes_t start; if (ranges.empty()) { - return 0; + return boost::shared_ptr<Playlist>(); } start = ranges.front().start; - for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) { pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden); @@ -840,39 +849,31 @@ Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list< chopped. */ - ret->paste (*pl, (*i).start - start, 1.0f); - delete pl; + ret->paste (pl, (*i).start - start, 1.0f); } } - if (ret) { - /* manually notify session of new playlist here - because the playlists were constructed without notifying - */ - PlaylistCreated (ret); - } - return ret; } -Playlist* +boost::shared_ptr<Playlist> Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden) { - Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut; + boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut; return cut_copy (pmf, ranges, result_is_hidden); } -Playlist* +boost::shared_ptr<Playlist> Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden) { - Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy; + boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy; return cut_copy (pmf, ranges, result_is_hidden); } -Playlist * +boost::shared_ptr<Playlist> Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden) { - Playlist *the_copy; + boost::shared_ptr<Playlist> the_copy; RegionList thawlist; char buf[32]; @@ -881,8 +882,8 @@ Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden) new_name += '.'; new_name += buf; - if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) { - return 0; + if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) { + return boost::shared_ptr<Playlist>(); } partition_internal (start, start+cnt-1, true, thawlist); @@ -895,7 +896,7 @@ Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden) return the_copy; } -Playlist * +boost::shared_ptr<Playlist> Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden) { char buf[32]; @@ -906,28 +907,28 @@ Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden) new_name += buf; cnt = min (_get_maximum_extent() - start, cnt); - return copyPlaylist (*this, start, cnt, new_name, result_is_hidden); + return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden); } int -Playlist::paste (Playlist& other, nframes_t position, float times) +Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times) { times = fabs (times); nframes_t old_length; { RegionLock rl1 (this); - RegionLock rl2 (&other); + RegionLock rl2 (other.get()); old_length = _get_maximum_extent(); int itimes = (int) floor (times); nframes_t pos = position; - nframes_t shift = other._get_maximum_extent(); + nframes_t shift = other->_get_maximum_extent(); layer_t top_layer = regions.size(); while (itimes--) { - for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) { + for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) { boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i); /* put these new regions on top of all existing ones, but preserve @@ -966,7 +967,7 @@ Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float while (itimes--) { boost::shared_ptr<Region> copy = RegionFactory::create (region); - add_region_internal (copy, pos, true); + add_region_internal (copy, pos); pos += region->length(); } @@ -975,7 +976,7 @@ Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float string name; _session.region_name (name, region->name(), false); boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags()); - add_region_internal (sub, pos, true); + add_region_internal (sub, pos); } } @@ -1010,7 +1011,7 @@ Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_pos _session.region_name (after_name, region->name(), false); right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit)); - add_region_internal (left, region->position(), true); + add_region_internal (left, region->position()); add_region_internal (right, region->position() + before); uint64_t orig_layer_op = region->last_layer_op(); @@ -1027,7 +1028,7 @@ Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_pos finalize_split_region (region, left, right); - if (remove_region_internal (region, true)) { + if (remove_region_internal (region)) { return; } } @@ -1118,7 +1119,6 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> } if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) { - if (holding_state ()) { pending_bounds.push_back (region); } else { @@ -1128,9 +1128,9 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> } possibly_splice (); - check_dependents (region, false); notify_length_changed (); relayer (); + check_dependents (region, false); } } } @@ -1397,6 +1397,10 @@ Playlist::set_state (const XMLNode& node) } } + notify_modified (); + + thaw (); + /* update dependents, which was not done during add_region_internal due to in_set_state being true */ @@ -1405,10 +1409,6 @@ Playlist::set_state (const XMLNode& node) check_dependents (*r, false); } - notify_modified (); - - thaw (); - in_set_state--; return 0; diff --git a/libs/ardour/playlist_factory.cc b/libs/ardour/playlist_factory.cc index 4461783874..862f85a402 100644 --- a/libs/ardour/playlist_factory.cc +++ b/libs/ardour/playlist_factory.cc @@ -22,24 +22,91 @@ #include <ardour/playlist.h> #include <ardour/audioplaylist.h> +#include <ardour/midi_playlist.h> +#include <ardour/playlist_factory.h> #include "i18n.h" using namespace ARDOUR; using namespace PBD; -Playlist* -Playlist::copyPlaylist (const Playlist& playlist, nframes_t start, nframes_t length, - string name, bool result_is_hidden) +sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistFactory::PlaylistCreated; + +boost::shared_ptr<Playlist> +PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden) +{ + const XMLProperty* type = node.property("type"); + + boost::shared_ptr<Playlist> pl; + + if ( !type || type->value() == "audio" ) + pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, node, hidden)); + else if ( type->value() == "midi" ) + pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, node, hidden)); + + pl->set_region_ownership (); + + if (pl && !hidden) { + PlaylistCreated (pl); + } + return pl; +} + +boost::shared_ptr<Playlist> +PlaylistFactory::create (DataType type, Session& s, string name, bool hidden) +{ + boost::shared_ptr<Playlist> pl; + + if (type == DataType::AUDIO) + pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, name, hidden)); + else if (type == DataType::MIDI) + pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, name, hidden)); + + if (pl && !hidden) { + PlaylistCreated (pl); + } + + return pl; +} + +boost::shared_ptr<Playlist> +PlaylistFactory::create (boost::shared_ptr<const Playlist> old, string name, bool hidden) { - const AudioPlaylist* apl; - - if ((apl = dynamic_cast<const AudioPlaylist*> (&playlist)) != 0) { - return new AudioPlaylist (*apl, start, length, name, result_is_hidden); - } else { - fatal << _("programming error: Playlist::copyPlaylist called with unknown Playlist type") - << endmsg; - /*NOTREACHED*/ - return 0; + boost::shared_ptr<Playlist> pl; + boost::shared_ptr<const AudioPlaylist> apl; + boost::shared_ptr<const MidiPlaylist> mpl; + + if ((apl = boost::dynamic_pointer_cast<const AudioPlaylist> (old)) != 0) { + pl = boost::shared_ptr<Playlist> (new AudioPlaylist (apl, name, hidden)); + pl->set_region_ownership (); + } else if ((mpl = boost::dynamic_pointer_cast<const MidiPlaylist> (old)) != 0) { + pl = boost::shared_ptr<Playlist> (new MidiPlaylist (mpl, name, hidden)); + pl->set_region_ownership (); } + + if (pl && !hidden) { + PlaylistCreated (pl); + } + + return pl; +} + +boost::shared_ptr<Playlist> +PlaylistFactory::create (boost::shared_ptr<const Playlist> old, nframes_t start, nframes_t cnt, string name, bool hidden) +{ + boost::shared_ptr<Playlist> pl; + boost::shared_ptr<const AudioPlaylist> apl; + boost::shared_ptr<const MidiPlaylist> mpl; + + if ((apl = boost::dynamic_pointer_cast<const AudioPlaylist> (old)) != 0) { + pl = boost::shared_ptr<Playlist> (new AudioPlaylist (apl, start, cnt, name, hidden)); + pl->set_region_ownership (); + } else if ((mpl = boost::dynamic_pointer_cast<const MidiPlaylist> (old)) != 0) { + pl = boost::shared_ptr<Playlist> (new MidiPlaylist (mpl, start, cnt, name, hidden)); + pl->set_region_ownership (); + } + + /* this factory method does NOT notify others */ + + return pl; } diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index b24b2619d3..09da4c9ca7 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -343,7 +343,7 @@ PluginManager::add_vst_directory (string path) static bool vst_filter (const string& str, void *arg) { /* Not a dotfile, has a prefix before a period, suffix is "dll" */ - + return str[0] != '.' && (str.length() > 4 && str.find (".dll") == (str.length() - 4)); } @@ -375,6 +375,7 @@ PluginManager::vst_discover (string path) FSTInfo* finfo; if ((finfo = fst_get_info (const_cast<char *> (path.c_str()))) == 0) { + warning << "Cannot get VST information from " << path << endmsg; return -1; } diff --git a/libs/ardour/redirect.cc b/libs/ardour/redirect.cc index adad79e2a3..dbdd3d1ddd 100644 --- a/libs/ardour/redirect.cc +++ b/libs/ardour/redirect.cc @@ -28,6 +28,7 @@ #include <sigc++/bind.h> #include <pbd/xml++.h> +#include <pbd/enumwriter.h> #include <ardour/redirect.h> #include <ardour/session.h> @@ -58,6 +59,7 @@ Redirect::Redirect (Session& s, const string& name, Placement p, Redirect::~Redirect () { + notify_callbacks (); } boost::shared_ptr<Redirect> @@ -96,18 +98,6 @@ Redirect::set_placement (Placement p, void *src) } } -void -Redirect::set_placement (const string& str, void *src) -{ - if (str == _("pre")) { - set_placement (PreFader, this); - } else if (str == _("post")) { - set_placement (PostFader, this); - } else { - error << string_compose(_("Redirect: unknown placement string \"%1\" (ignored)"), str) << endmsg; - } -} - /* NODE STRUCTURE <Automation [optionally with visible="...." ]> @@ -194,7 +184,7 @@ Redirect::state (bool full_state) stringstream sstr; node->add_property("active", active() ? "yes" : "no"); - node->add_property("placement", placement_as_string (placement())); + node->add_property("placement", enum_2_string (_placement)); node->add_child_nocopy (IO::state (full_state)); if (_extra_xml){ @@ -294,7 +284,20 @@ Redirect::set_state (const XMLNode& node) return -1; } - set_placement (prop->value(), this); + /* hack to handle older sessions before we only used EnumWriter */ + + string pstr; + + if (prop->value() == "pre") { + pstr = "PreFader"; + } else if (prop->value() == "post") { + pstr = "PostFader"; + } else { + pstr = prop->value(); + } + + Placement p = Placement (string_2_enum (pstr, p)); + set_placement (p, this); return 0; } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 6d8c71b563..973e14fd31 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -65,7 +65,6 @@ Region::Region (boost::shared_ptr<Source> src, jack_nframes_t start, jack_nframe , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) - , _playlist(0) { _sources.push_back (src); _master_sources.push_back (src); @@ -89,7 +88,6 @@ Region::Region (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, c , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) - , _playlist(0) { set<boost::shared_ptr<Source> > unique_srcs; @@ -125,7 +123,6 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes , _read_data_count(0) , _pending_changed(Change (0)) , _last_layer_op(0) - , _playlist(0) { if (other->_sync_position < offset) _sync_position = other->_sync_position; @@ -167,7 +164,6 @@ Region::Region (boost::shared_ptr<const Region> other) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(other->_last_layer_op) - , _playlist(0) { other->_first_edit = EditChangesName; @@ -209,10 +205,7 @@ Region::Region (SourceList& srcs, const XMLNode& node) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(0) - , _playlist(0) - { - set<boost::shared_ptr<Source> > unique_srcs; for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) { @@ -250,7 +243,6 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node) , _read_data_count(0) , _pending_changed(Change(0)) , _last_layer_op(0) - , _playlist(0) { _sources.push_back (src); @@ -265,9 +257,11 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node) Region::~Region () { - if (_playlist) { + boost::shared_ptr<Playlist> pl (playlist()); + + if (pl) { for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (_playlist); + (*i)->remove_playlist (pl); } } @@ -276,29 +270,30 @@ Region::~Region () } void -Region::set_playlist (Playlist* pl) +Region::set_playlist (boost::weak_ptr<Playlist> wpl) { - if (pl == _playlist) { + boost::shared_ptr<Playlist> old_playlist = (_playlist.lock()); + boost::shared_ptr<Playlist> pl (wpl.lock()); + + if (old_playlist == pl) { return; } - Playlist* old_playlist = _playlist; - if (pl) { if (old_playlist) { for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (old_playlist); - (*i)->add_playlist (_playlist); + (*i)->remove_playlist (_playlist); + (*i)->add_playlist (pl); } } else { for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->add_playlist (_playlist); + (*i)->add_playlist (pl); } } } else { if (old_playlist) { for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) { - (*i)->remove_playlist (old_playlist); + (*i)->remove_playlist (_playlist); } } } @@ -357,9 +352,11 @@ Region::maybe_uncopy () void Region::first_edit () { - if (_first_edit != EditChangesNothing && _playlist) { + boost::shared_ptr<Playlist> pl (playlist()); + + if (_first_edit != EditChangesNothing && pl) { - _name = _playlist->session().new_region_name (_name); + _name = pl->session().new_region_name (_name); _first_edit = EditChangesNothing; send_change (NameChanged); @@ -370,7 +367,9 @@ Region::first_edit () bool Region::at_natural_position () const { - if (!_playlist) { + boost::shared_ptr<Playlist> pl (playlist()); + + if (!pl) { return false; } @@ -388,7 +387,9 @@ Region::at_natural_position () const void Region::move_to_natural_position (void *src) { - if (!_playlist) { + boost::shared_ptr<Playlist> pl (playlist()); + + if (!pl) { return; } @@ -448,7 +449,11 @@ Region::set_position_on_top (nframes_t pos, void *src) _position = pos; } - _playlist->raise_region_to_top (shared_from_this ()); + boost::shared_ptr<Playlist> pl (playlist()); + + if (pl) { + pl->raise_region_to_top (shared_from_this ()); + } /* do this even if the position is the same. this helps out a GUI that has moved its representation already. @@ -835,42 +840,37 @@ Region::sync_position() const void Region::raise () { - if (_playlist == 0) { - return; + boost::shared_ptr<Playlist> pl (playlist()); + if (pl) { + pl->raise_region (shared_from_this ()); } - - _playlist->raise_region (shared_from_this ()); } void Region::lower () { - if (_playlist == 0) { - return; + boost::shared_ptr<Playlist> pl (playlist()); + if (pl) { + pl->lower_region (shared_from_this ()); } - - _playlist->lower_region (shared_from_this ()); } void Region::raise_to_top () { - - if (_playlist == 0) { - return; + boost::shared_ptr<Playlist> pl (playlist()); + if (pl) { + pl->raise_region_to_top (shared_from_this()); } - - _playlist->raise_region_to_top (shared_from_this()); } void Region::lower_to_bottom () { - if (_playlist == 0) { - return; + boost::shared_ptr<Playlist> pl (playlist()); + if (pl) { + pl->lower_region_to_bottom (shared_from_this()); } - - _playlist->lower_region_to_bottom (shared_from_this()); } void @@ -919,7 +919,7 @@ Region::state (bool full_state) snprintf (buf, sizeof (buf), "%d", (int) _layer); node->add_property ("layer", buf); - snprintf (buf, sizeof (buf), "%u", _sync_position); + snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position); node->add_property ("sync-position", buf); return *node; @@ -1230,11 +1230,13 @@ Region::verify_start_mutable (jack_nframes_t& new_start) boost::shared_ptr<Region> Region::get_parent() const { - if (_playlist) { + boost::shared_ptr<Playlist> pl (playlist()); + + if (pl) { boost::shared_ptr<Region> r; boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this()); - if (grrr2 && (r = _playlist->session().find_whole_file_parent (grrr2))) { + if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) { return boost::static_pointer_cast<Region> (r); } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 5314c99632..a2bc65407c 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -24,6 +24,7 @@ #include <sigc++/bind.h> #include <pbd/xml++.h> +#include <pbd/enumwriter.h> #include <ardour/timestamps.h> #include <ardour/audioengine.h> @@ -889,6 +890,10 @@ Route::clear_redirects (void *src) { Glib::RWLock::WriterLock lm (redirect_lock); + RedirectList::iterator i; + for (i = _redirects.begin(); i != _redirects.end(); ++i) { + (*i)->drop_references (); + } _redirects.clear (); } @@ -985,6 +990,8 @@ Route::remove_redirect (boost::shared_ptr<Redirect> redirect, void *src, uint32_ reset_panner (); } + redirect->drop_references (); + redirects_changed (src); /* EMIT SIGNAL */ return 0; } @@ -1317,8 +1324,7 @@ Route::state(bool full_state) char buf[32]; if (_flags) { - snprintf (buf, sizeof (buf), "0x%x", _flags); - node->add_property("flags", buf); + node->add_property("flags", enum_2_string (_flags)); } node->add_property("default-type", _default_type.to_string()); @@ -1478,9 +1484,7 @@ Route::_set_state (const XMLNode& node, bool call_base) } if ((prop = node.property (X_("flags"))) != 0) { - int x; - sscanf (prop->value().c_str(), "0x%x", &x); - _flags = Flag (x); + _flags = Flag (string_2_enum (prop->value(), _flags)); } else { _flags = Flag (0); } diff --git a/libs/ardour/route_group.cc b/libs/ardour/route_group.cc index 5730623742..c2aa59ed8b 100644 --- a/libs/ardour/route_group.cc +++ b/libs/ardour/route_group.cc @@ -26,6 +26,7 @@ #include <sigc++/bind.h> #include <pbd/error.h> +#include <pbd/enumwriter.h> #include <ardour/route_group.h> #include <ardour/audio_track.h> @@ -125,11 +126,9 @@ RouteGroup::get_max_factor(gain_t factor) XMLNode& RouteGroup::get_state (void) { - char buf[32]; XMLNode *node = new XMLNode ("RouteGroup"); node->add_property ("name", _name); - snprintf (buf, sizeof (buf), "%" PRIu32, (uint32_t) _flags); - node->add_property ("flags", buf); + node->add_property ("flags", enum_2_string (_flags)); return *node; } @@ -143,7 +142,7 @@ RouteGroup::set_state (const XMLNode& node) } if ((prop = node.property ("flags")) != 0) { - _flags = atoi (prop->value().c_str()); + _flags = Flag (string_2_enum (prop->value(), _flags)); } return 0; @@ -157,9 +156,9 @@ RouteGroup::set_active (bool yn, void *src) return; } if (yn) { - _flags |= Active; + _flags = Flag (_flags | Active); } else { - _flags &= ~Active; + _flags = Flag (_flags & ~Active); } _session.set_dirty (); FlagsChanged (src); /* EMIT SIGNAL */ @@ -173,9 +172,9 @@ RouteGroup::set_relative (bool yn, void *src) return; } if (yn) { - _flags |= Relative; + _flags = Flag (_flags | Relative); } else { - _flags &= ~Relative; + _flags = Flag (_flags & ~Relative); } _session.set_dirty (); FlagsChanged (src); /* EMIT SIGNAL */ @@ -189,14 +188,14 @@ RouteGroup::set_hidden (bool yn, void *src) return; } if (yn) { - _flags |= Hidden; + _flags = Flag (_flags | Hidden); if (Config->get_hiding_groups_deactivates_groups()) { - _flags &= ~Active; + _flags = Flag (_flags & ~Active); } } else { - _flags &= ~Hidden; + _flags = Flag (_flags & ~Hidden); if (Config->get_hiding_groups_deactivates_groups()) { - _flags |= Active; + _flags = Flag (_flags | Active); } } _session.set_dirty (); diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index 73dbf11ad5..0141007803 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -34,7 +34,7 @@ using namespace ARDOUR; using namespace PBD; Send::Send (Session& s, Placement p) - : Redirect (s, s.next_send_name(), p) + : Redirect (s, string_compose (_("send %1"), (bitslot = s.next_send_id()) + 1), p) { _metering = false; RedirectCreated (this); /* EMIT SIGNAL */ @@ -53,7 +53,7 @@ Send::Send (Session& s, const XMLNode& node) } Send::Send (const Send& other) - : Redirect (other._session, other._session.next_send_name(), other.placement()) + : Redirect (other._session, string_compose (_("send %1"), (bitslot = other._session.next_send_id()) + 1), other.placement()) { _metering = false; RedirectCreated (this); /* EMIT SIGNAL */ @@ -74,7 +74,10 @@ XMLNode& Send::state(bool full) { XMLNode *node = new XMLNode("Send"); + char buf[32]; node->add_child_nocopy (Redirect::state (full)); + snprintf (buf, sizeof (buf), "%" PRIu32, bitslot); + node->add_property ("bitslot", buf); return *node; } @@ -83,6 +86,14 @@ Send::set_state(const XMLNode& node) { XMLNodeList nlist = node.children(); XMLNodeIterator niter; + const XMLProperty* prop; + + if ((prop = node.property ("bitslot")) == 0) { + bitslot = _session.next_send_id(); + } else { + sscanf (prop->value().c_str(), "%" PRIu32, &bitslot); + _session.mark_send_id (bitslot); + } /* Send has regular IO automation (gain, pan) */ diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 1b7c3be6dd..6ad8f7dbd2 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -40,6 +40,7 @@ #include <pbd/pathscanner.h> #include <pbd/stl_delete.h> #include <pbd/basename.h> +#include <pbd/stacktrace.h> #include <ardour/audioengine.h> #include <ardour/configuration.h> @@ -290,12 +291,13 @@ Session::Session (AudioEngine &eng, if (new_session) { if (create (new_session, mix_template, compute_initial_length())) { cerr << "create failed\n"; + destroy (); throw failed_constructor (); } } if (second_stage_init (new_session)) { - cerr << "2nd state failed\n"; + destroy (); throw failed_constructor (); } @@ -359,6 +361,7 @@ Session::Session (AudioEngine &eng, if (new_session) { if (create (new_session, 0, initial_length)) { + destroy (); throw failed_constructor (); } } @@ -386,6 +389,7 @@ Session::Session (AudioEngine &eng, Config->set_output_auto_connect (output_ac); if (second_stage_init (new_session)) { + destroy (); throw failed_constructor (); } @@ -402,6 +406,12 @@ Session::Session (AudioEngine &eng, Session::~Session () { + destroy (); +} + +void +Session::destroy () +{ /* if we got to here, leaving pending capture state around is a mistake. */ @@ -469,10 +479,24 @@ Session::~Session () tmp = i; ++tmp; - delete *i; + (*i)->drop_references (); + + i = tmp; + } + + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) { + PlaylistList::iterator tmp; + + tmp = i; + ++tmp; + + (*i)->drop_references (); i = tmp; } + + playlists.clear (); + unused_playlists.clear (); #ifdef TRACK_DESTRUCTION cerr << "delete regions\n"; @@ -619,8 +643,6 @@ Session::when_engine_running () /* we don't want to run execute this again */ - first_time_running.disconnect (); - set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); @@ -685,23 +707,6 @@ Session::when_engine_running () // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO? } - if (auditioner == 0) { - - /* we delay creating the auditioner till now because - it makes its own connections to ports named - in the ARDOUR_RC config file. the engine has - to be running for this to work. - */ - - try { - auditioner.reset (new Auditioner (*this)); - } - - catch (failed_constructor& err) { - warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; - } - } - /* Create a set of Connection objects that map to the physical outputs currently available */ @@ -841,6 +846,7 @@ Session::when_engine_running () } } + _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); /* hook us up to the engine */ @@ -867,6 +873,22 @@ Session::hookup_io () _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting); + if (auditioner == 0) { + + /* we delay creating the auditioner till now because + it makes its own connections to ports. + the engine has to be running for this to work. + */ + + try { + auditioner.reset (new Auditioner (*this)); + } + + catch (failed_constructor& err) { + warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg; + } + } + /* Tell all IO objects to create their ports */ IO::enable_ports (); @@ -918,7 +940,7 @@ Session::hookup_io () } void -Session::playlist_length_changed (Playlist* pl) +Session::playlist_length_changed () { /* we can't just increase end_location->end() if pl->get_maximum_extent() if larger. if the playlist used to be the longest playlist, @@ -932,10 +954,10 @@ Session::playlist_length_changed (Playlist* pl) void Session::diskstream_playlist_changed (boost::shared_ptr<Diskstream> dstream) { - Playlist *playlist; + boost::shared_ptr<Playlist> playlist; if ((playlist = dstream->playlist()) != 0) { - playlist->LengthChanged.connect (sigc::bind (mem_fun (this, &Session::playlist_length_changed), playlist)); + playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed)); } /* see comment in playlist_length_changed () */ @@ -2317,7 +2339,7 @@ Session::get_maximum_extent () const boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader(); for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) { - Playlist* pl = (*i)->playlist(); + boost::shared_ptr<Playlist> pl = (*i)->playlist(); if ((me = pl->get_maximum_extent()) > max) { max = me; } @@ -2697,14 +2719,10 @@ Session::add_source (boost::shared_ptr<Source> source) result = sources.insert (entry); } - if (!result.second) { - cerr << "\tNOT inserted ? " << result.second << endl; + if (result.second) { + source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source))); + set_dirty(); } - - source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source))); - set_dirty(); - - SourceAdded (source); /* EMIT SIGNAL */ } void @@ -2737,8 +2755,6 @@ Session::remove_source (boost::weak_ptr<Source> src) save_state (_current_snapshot_name); } - - SourceRemoved(source); /* EMIT SIGNAL */ } boost::shared_ptr<Source> @@ -2924,6 +2940,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool } else { snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str()); } + } else { spath += '/'; @@ -2956,6 +2973,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool if (cnt > limit) { error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg; + destroy (); throw failed_constructor(); } } @@ -2967,6 +2985,7 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool string foo = buf; spath = discover_best_sound_dir (); + spath += '/'; string::size_type pos = foo.find_last_of ('/'); @@ -3176,7 +3195,7 @@ Session::create_midi_source_for_session (MidiDiskstream& ds) /* Playlist management */ -Playlist * +boost::shared_ptr<Playlist> Session::playlist_by_name (string name) { Glib::Mutex::Lock lm (playlist_lock); @@ -3190,11 +3209,12 @@ Session::playlist_by_name (string name) return* i; } } - return 0; + + return boost::shared_ptr<Playlist>(); } void -Session::add_playlist (Playlist* playlist) +Session::add_playlist (boost::shared_ptr<Playlist> playlist) { if (playlist->hidden()) { return; @@ -3204,9 +3224,8 @@ Session::add_playlist (Playlist* playlist) Glib::Mutex::Lock lm (playlist_lock); if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) { playlists.insert (playlists.begin(), playlist); - // playlist->ref(); - playlist->InUse.connect (mem_fun (*this, &Session::track_playlist)); - playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), playlist)); + playlist->InUse.connect (sigc::bind (mem_fun (*this, &Session::track_playlist), boost::weak_ptr<Playlist>(playlist))); + playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), boost::weak_ptr<Playlist>(playlist))); } } @@ -3216,7 +3235,7 @@ Session::add_playlist (Playlist* playlist) } void -Session::get_playlists (vector<Playlist*>& s) +Session::get_playlists (vector<boost::shared_ptr<Playlist> >& s) { { Glib::Mutex::Lock lm (playlist_lock); @@ -3230,15 +3249,25 @@ Session::get_playlists (vector<Playlist*>& s) } void -Session::track_playlist (Playlist* pl, bool inuse) +Session::track_playlist (bool inuse, boost::weak_ptr<Playlist> wpl) { + boost::shared_ptr<Playlist> pl(wpl.lock()); + + if (!pl) { + return; + } + PlaylistList::iterator x; + if (pl->hidden()) { + /* its not supposed to be visible */ + return; + } + { Glib::Mutex::Lock lm (playlist_lock); if (!inuse) { - //cerr << "shifting playlist to unused: " << pl->name() << endl; unused_playlists.insert (pl); @@ -3248,8 +3277,7 @@ Session::track_playlist (Playlist* pl, bool inuse) } else { - //cerr << "shifting playlist to used: " << pl->name() << endl; - + playlists.insert (pl); if ((x = unused_playlists.find (pl)) != unused_playlists.end()) { @@ -3260,20 +3288,24 @@ Session::track_playlist (Playlist* pl, bool inuse) } void -Session::remove_playlist (Playlist* playlist) +Session::remove_playlist (boost::weak_ptr<Playlist> weak_playlist) { if (_state_of_the_state & Deletion) { return; } + boost::shared_ptr<Playlist> playlist (weak_playlist.lock()); + + if (!playlist) { + return; + } + { Glib::Mutex::Lock lm (playlist_lock); - // cerr << "removing playlist: " << playlist->name() << endl; PlaylistList::iterator i; i = find (playlists.begin(), playlists.end(), playlist); - if (i != playlists.end()) { playlists.erase (i); } @@ -3451,6 +3483,12 @@ Session::graph_reordered () return; } + /* every track/bus asked for this to be handled but it was deferred because + we were connecting. do it now. + */ + + request_input_change_handling (); + resort_routes (); /* force all diskstreams to update their capture offset values to @@ -3528,18 +3566,28 @@ Session::remove_redirect (Redirect* redirect) Insert* insert; PortInsert* port_insert; PluginInsert* plugin_insert; - + if ((insert = dynamic_cast<Insert *> (redirect)) != 0) { if ((port_insert = dynamic_cast<PortInsert *> (insert)) != 0) { - _port_inserts.remove (port_insert); + list<PortInsert*>::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert); + if (x != _port_inserts.end()) { + insert_bitset[port_insert->bit_slot()] = false; + _port_inserts.erase (x); + } } else if ((plugin_insert = dynamic_cast<PluginInsert *> (insert)) != 0) { _plugin_inserts.remove (plugin_insert); } else { - fatal << _("programming error: unknown type of Insert deleted!") << endmsg; + fatal << string_compose (_("programming error: %1"), + X_("unknown type of Insert deleted!")) + << endmsg; /*NOTREACHED*/ } } else if ((send = dynamic_cast<Send *> (redirect)) != 0) { - _sends.remove (send); + list<Send*>::iterator x = find (_sends.begin(), _sends.end(), send); + if (x != _sends.end()) { + send_bitset[send->bit_slot()] = false; + _sends.erase (x); + } } else { fatal << _("programming error: unknown type of Redirect deleted!") << endmsg; /*NOTREACHED*/ @@ -3561,6 +3609,13 @@ Session::available_capture_duration () case FormatInt24: sample_bytes_on_disk = 3; break; + + default: + /* impossible, but keep some gcc versions happy */ + fatal << string_compose (_("programming error: %1"), + X_("illegal native file data format")) + << endmsg; + /*NOTREACHED*/ } double scale = 4096.0 / sample_bytes_on_disk; @@ -3642,20 +3697,70 @@ Session::ensure_buffers (ChanCount howmany) allocate_pan_automation_buffers (current_block_size, howmany.get(DataType::AUDIO), false); } -string -Session::next_send_name () +uint32_t +Session::next_insert_id () { - char buf[32]; - snprintf (buf, sizeof (buf), "send %" PRIu32, ++send_cnt); - return buf; + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < insert_bitset.size(); ++n) { + if (!insert_bitset[n]) { + insert_bitset[n] = true; + cerr << "Returning " << n << " as insert ID\n"; + return n; + + } + } + + /* none available, so resize and try again */ + + insert_bitset.resize (insert_bitset.size() + 16, false); + } } -string -Session::next_insert_name () +uint32_t +Session::next_send_id () { - char buf[32]; - snprintf (buf, sizeof (buf), "insert %" PRIu32, ++insert_cnt); - return buf; + /* this doesn't really loop forever. just think about it */ + + while (true) { + for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < send_bitset.size(); ++n) { + if (!send_bitset[n]) { + send_bitset[n] = true; + cerr << "Returning " << n << " as send ID\n"; + return n; + + } + } + + /* none available, so resize and try again */ + + send_bitset.resize (send_bitset.size() + 16, false); + } +} + +void +Session::mark_send_id (uint32_t id) +{ + if (id >= send_bitset.size()) { + send_bitset.resize (id+16, false); + } + if (send_bitset[id]) { + warning << string_compose (_("send ID %1 appears to be in use already"), id) << endmsg; + } + send_bitset[id] = true; +} + +void +Session::mark_insert_id (uint32_t id) +{ + if (id >= insert_bitset.size()) { + insert_bitset.resize (id+16, false); + } + if (insert_bitset[id]) { + warning << string_compose (_("insert ID %1 appears to be in use already"), id) << endmsg; + } + insert_bitset[id] = true; } /* Named Selection management */ @@ -3732,12 +3837,6 @@ Session::route_name_unique (string n) const return true; } -int -Session::cleanup_audio_file_source (boost::shared_ptr<AudioFileSource> fs) -{ - return fs->move_to_trash (dead_sound_dir_name); -} - uint32_t Session::n_playlists () const { @@ -3794,18 +3893,17 @@ int Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len, bool overwrite, vector<boost::shared_ptr<Source> >& srcs, InterThreadInfo& itt) { + int ret = -1; + boost::shared_ptr<Playlist> playlist; boost::shared_ptr<AudioFileSource> fsource; - - int ret = -1; - Playlist* playlist = 0; - uint32_t x; - char buf[PATH_MAX+1]; - string dir; - ChanCount nchans(track.audio_diskstream()->n_channels()); - jack_nframes_t position; - jack_nframes_t this_chunk; - jack_nframes_t to_do; - BufferSet buffers; + uint32_t x; + char buf[PATH_MAX+1]; + string dir; + ChanCount nchans(track.audio_diskstream()->n_channels()); + nframes_t position; + nframes_t this_chunk; + nframes_t to_do; + BufferSet buffers; // any bigger than this seems to cause stack overflows in called functions const nframes_t chunk_size = (128 * 1024)/4; @@ -3915,7 +4013,7 @@ Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t le /* construct a region to represent the bounced material */ boost::shared_ptr<Region> aregion = RegionFactory::create (srcs, 0, srcs.front()->length(), - region_name_from_path (srcs.front()->name())); + region_name_from_path (srcs.front()->name(), true)); ret = 0; } diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc index 832284901c..bb02eede91 100644 --- a/libs/ardour/session_butler.cc +++ b/libs/ardour/session_butler.cc @@ -157,8 +157,6 @@ Session::_butler_thread_work (void* arg) return 0; } -#define transport_work_requested() g_atomic_int_get(&butler_should_do_transport_work) - void * Session::butler_thread_work () { diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc index 5816f1c6b7..9d054c22fd 100644 --- a/libs/ardour/session_command.cc +++ b/libs/ardour/session_command.cc @@ -11,18 +11,22 @@ #include <ardour/midi_source.h> #include <ardour/midi_region.h> #include <pbd/error.h> -using namespace PBD; -#include "i18n.h" +#include <pbd/id.h> +#include <pbd/statefuldestructible.h> +#include <pbd/failed_constructor.h> +using namespace PBD; +using namespace ARDOUR; -namespace ARDOUR { +#include "i18n.h" void Session::register_with_memento_command_factory(PBD::ID id, PBD::StatefulThingWithGoingAway *ptr) { registry[id] = ptr; } -Command *Session::memento_command_factory(XMLNode *n) +Command * +Session::memento_command_factory(XMLNode *n) { PBD::ID id; XMLNode *before = 0, *after = 0; @@ -70,8 +74,9 @@ Command *Session::memento_command_factory(XMLNode *n) } else if (obj_T == typeid (TempoMap).name()) { return new MementoCommand<TempoMap>(*_tempo_map, before, after); } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name()) { - if (Playlist *pl = playlist_by_name(child->property("name")->value())) - return new MementoCommand<Playlist>(*pl, before, after); + if (boost::shared_ptr<Playlist> pl = playlist_by_name(child->property("name")->value())) { + return new MementoCommand<Playlist>(*(pl.get()), before, after); + } } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name()) { return new MementoCommand<Route>(*route_by_id(id), before, after); } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) { @@ -86,100 +91,425 @@ Command *Session::memento_command_factory(XMLNode *n) return 0 ; } +Command * +Session::global_state_command_factory (const XMLNode& node) +{ + const XMLProperty* prop; + Command* command = 0; + + if ((prop = node.property ("type")) == 0) { + error << _("GlobalRouteStateCommand has no \"type\" node, ignoring") << endmsg; + return 0; + } + + try { + + if (prop->value() == "solo") { + command = new GlobalSoloStateCommand (*this, node); + } else if (prop->value() == "mute") { + command = new GlobalMuteStateCommand (*this, node); + } else if (prop->value() == "rec-enable") { + command = new GlobalRecordEnableStateCommand (*this, node); + } else if (prop->value() == "metering") { + command = new GlobalMeteringStateCommand (*this, node); + } else { + error << string_compose (_("unknown type of GlobalRouteStateCommand (%1), ignored"), prop->value()) << endmsg; + } + } + + catch (failed_constructor& err) { + return 0; + } + + return command; +} + +Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, void* p) + : sess (s), src (p) +{ +} + +Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, const XMLNode& node) + : sess (s), src (this) +{ + if (set_state (node)) { + throw failed_constructor (); + } +} + +int +Session::GlobalRouteStateCommand::set_state (const XMLNode& node) +{ + GlobalRouteBooleanState states; + XMLNodeList nlist; + const XMLProperty* prop; + XMLNode* child; + XMLNodeConstIterator niter; + int loop; + + before.clear (); + after.clear (); + + for (loop = 0; loop < 2; ++loop) { + + const char *str; + + if (loop) { + str = "after"; + } else { + str = "before"; + } + + if ((child = node.child (str)) == 0) { + warning << string_compose (_("global route state command has no \"%1\" node, ignoring entire command"), str) << endmsg; + return -1; + } + + nlist = child->children(); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + RouteBooleanState rbs; + boost::shared_ptr<Route> route; + ID id; + + prop = (*niter)->property ("id"); + id = prop->value (); + + if ((route = sess.route_by_id (id)) == 0) { + warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg; + continue; + } + + rbs.first = boost::weak_ptr<Route> (route); + + prop = (*niter)->property ("yn"); + rbs.second = (prop->value() == "1"); + + if (loop) { + after.push_back (rbs); + } else { + before.push_back (rbs); + } + } + } + + return 0; +} + +XMLNode& +Session::GlobalRouteStateCommand::get_state () +{ + XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand")); + XMLNode* nbefore = new XMLNode (X_("before")); + XMLNode* nafter = new XMLNode (X_("after")); + + for (Session::GlobalRouteBooleanState::iterator x = before.begin(); x != before.end(); ++x) { + XMLNode* child = new XMLNode ("s"); + boost::shared_ptr<Route> r = x->first.lock(); + + if (r) { + child->add_property (X_("id"), r->id().to_s()); + child->add_property (X_("yn"), (x->second ? "1" : "0")); + nbefore->add_child_nocopy (*child); + } + } + + for (Session::GlobalRouteBooleanState::iterator x = after.begin(); x != after.end(); ++x) { + XMLNode* child = new XMLNode ("s"); + boost::shared_ptr<Route> r = x->first.lock(); + + if (r) { + child->add_property (X_("id"), r->id().to_s()); + child->add_property (X_("yn"), (x->second ? "1" : "0")); + nafter->add_child_nocopy (*child); + } + } + + node->add_child_nocopy (*nbefore); + node->add_child_nocopy (*nafter); + + return *node; +} + // solo + Session::GlobalSoloStateCommand::GlobalSoloStateCommand(Session &sess, void *src) - : sess(sess), src(src) + : GlobalRouteStateCommand (sess, src) { after = before = sess.get_global_route_boolean(&Route::soloed); } -void Session::GlobalSoloStateCommand::mark() + +Session::GlobalSoloStateCommand::GlobalSoloStateCommand (Session& sess, const XMLNode& node) + : Session::GlobalRouteStateCommand (sess, node) +{ +} + +void +Session::GlobalSoloStateCommand::mark() { after = sess.get_global_route_boolean(&Route::soloed); } -void Session::GlobalSoloStateCommand::operator()() + +void +Session::GlobalSoloStateCommand::operator()() { sess.set_global_solo(after, src); } -void Session::GlobalSoloStateCommand::undo() + +void +Session::GlobalSoloStateCommand::undo() { sess.set_global_solo(before, src); } -XMLNode &Session::GlobalSoloStateCommand::get_state() + +XMLNode& +Session::GlobalSoloStateCommand::get_state() { - XMLNode *node = new XMLNode("GlobalSoloStateCommand"); - return *node; + XMLNode& node = GlobalRouteStateCommand::get_state(); + node.add_property ("type", "solo"); + return node; } // mute Session::GlobalMuteStateCommand::GlobalMuteStateCommand(Session &sess, void *src) - : sess(sess), src(src) + : GlobalRouteStateCommand (sess, src) { after = before = sess.get_global_route_boolean(&Route::muted); } -void Session::GlobalMuteStateCommand::mark() + +Session::GlobalMuteStateCommand::GlobalMuteStateCommand (Session& sess, const XMLNode& node) + : Session::GlobalRouteStateCommand (sess, node) { - after = sess.get_global_route_boolean(&Route::muted); } -void Session::GlobalMuteStateCommand::operator()() + +void +Session::GlobalMuteStateCommand::mark() { - sess.set_global_mute(after, src); + after = sess.get_global_route_boolean(&Route::muted); } -void Session::GlobalMuteStateCommand::undo() + +void +Session::GlobalMuteStateCommand::operator()() { - sess.set_global_mute(before, src); + sess.set_global_mute(after, src); } -XMLNode &Session::GlobalMuteStateCommand::get_state() + +void +Session::GlobalMuteStateCommand::undo() { - XMLNode *node = new XMLNode("GlobalMuteStateCommand"); - return *node; + sess.set_global_mute(before, src); +} + +XMLNode& +Session::GlobalMuteStateCommand::get_state() +{ + XMLNode& node = GlobalRouteStateCommand::get_state(); + node.add_property ("type", "mute"); + return node; } // record enable Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand(Session &sess, void *src) - : sess(sess), src(src) + : GlobalRouteStateCommand (sess, src) +{ + after = before = sess.get_global_route_boolean(&Route::record_enabled); +} + +Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand (Session& sess, const XMLNode& node) + : Session::GlobalRouteStateCommand (sess, node) { - after = before = sess.get_global_route_boolean(&Route::record_enabled); } -void Session::GlobalRecordEnableStateCommand::mark() + +void +Session::GlobalRecordEnableStateCommand::mark() { - after = sess.get_global_route_boolean(&Route::record_enabled); + after = sess.get_global_route_boolean(&Route::record_enabled); } -void Session::GlobalRecordEnableStateCommand::operator()() + +void +Session::GlobalRecordEnableStateCommand::operator()() { - sess.set_global_record_enable(after, src); + sess.set_global_record_enable(after, src); } -void Session::GlobalRecordEnableStateCommand::undo() + +void +Session::GlobalRecordEnableStateCommand::undo() { - sess.set_global_record_enable(before, src); + sess.set_global_record_enable(before, src); } -XMLNode &Session::GlobalRecordEnableStateCommand::get_state() + +XMLNode& +Session::GlobalRecordEnableStateCommand::get_state() { - XMLNode *node = new XMLNode("GlobalRecordEnableStateCommand"); - return *node; + XMLNode& node = GlobalRouteStateCommand::get_state(); + node.add_property ("type", "rec-enable"); + return node; } // metering -Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand(Session &sess, void *src) - : sess(sess), src(src) +Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand(Session &s, void *p) + : sess (s), src (p) { - after = before = sess.get_global_route_metering(); + after = before = sess.get_global_route_metering(); } -void Session::GlobalMeteringStateCommand::mark() + +Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand (Session& s, const XMLNode& node) + : sess (s), src (this) { - after = sess.get_global_route_metering(); + if (set_state (node)) { + throw failed_constructor(); + } } -void Session::GlobalMeteringStateCommand::operator()() + +void +Session::GlobalMeteringStateCommand::mark() { - sess.set_global_route_metering(after, src); + after = sess.get_global_route_metering(); } -void Session::GlobalMeteringStateCommand::undo() + +void +Session::GlobalMeteringStateCommand::operator()() { - sess.set_global_route_metering(before, src); + sess.set_global_route_metering(after, src); } -XMLNode &Session::GlobalMeteringStateCommand::get_state() + +void +Session::GlobalMeteringStateCommand::undo() { - XMLNode *node = new XMLNode("GlobalMeteringStateCommand"); - return *node; + sess.set_global_route_metering(before, src); } -} // namespace ARDOUR +XMLNode& +Session::GlobalMeteringStateCommand::get_state() +{ + XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand")); + XMLNode* nbefore = new XMLNode (X_("before")); + XMLNode* nafter = new XMLNode (X_("after")); + + for (Session::GlobalRouteMeterState::iterator x = before.begin(); x != before.end(); ++x) { + XMLNode* child = new XMLNode ("s"); + boost::shared_ptr<Route> r = x->first.lock(); + + if (r) { + child->add_property (X_("id"), r->id().to_s()); + + const char* meterstr; + + switch (x->second) { + case MeterInput: + meterstr = X_("input"); + break; + case MeterPreFader: + meterstr = X_("pre"); + break; + case MeterPostFader: + meterstr = X_("post"); + break; + } + + child->add_property (X_("meter"), meterstr); + nbefore->add_child_nocopy (*child); + } + } + + for (Session::GlobalRouteMeterState::iterator x = after.begin(); x != after.end(); ++x) { + XMLNode* child = new XMLNode ("s"); + boost::shared_ptr<Route> r = x->first.lock(); + + if (r) { + child->add_property (X_("id"), r->id().to_s()); + + const char* meterstr; + + switch (x->second) { + case MeterInput: + meterstr = X_("input"); + break; + case MeterPreFader: + meterstr = X_("pre"); + break; + case MeterPostFader: + meterstr = X_("post"); + break; + } + + child->add_property (X_("meter"), meterstr); + nafter->add_child_nocopy (*child); + } + } + + node->add_child_nocopy (*nbefore); + node->add_child_nocopy (*nafter); + + node->add_property ("type", "metering"); + + return *node; +} + +int +Session::GlobalMeteringStateCommand::set_state (const XMLNode& node) +{ + GlobalRouteBooleanState states; + XMLNodeList nlist; + const XMLProperty* prop; + XMLNode* child; + XMLNodeConstIterator niter; + int loop; + + before.clear (); + after.clear (); + + for (loop = 0; loop < 2; ++loop) { + + const char *str; + + if (loop) { + str = "after"; + } else { + str = "before"; + } + + if ((child = node.child (str)) == 0) { + warning << string_compose (_("global route meter state command has no \"%1\" node, ignoring entire command"), str) << endmsg; + return -1; + } + + nlist = child->children(); + + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + + RouteMeterState rms; + boost::shared_ptr<Route> route; + ID id; + + prop = (*niter)->property ("id"); + id = prop->value (); + + if ((route = sess.route_by_id (id)) == 0) { + warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg; + continue; + } + + rms.first = boost::weak_ptr<Route> (route); + + prop = (*niter)->property ("meter"); + + if (prop->value() == X_("pre")) { + rms.second = MeterPreFader; + } else if (prop->value() == X_("post")) { + rms.second = MeterPostFader; + } else { + rms.second = MeterInput; + } + + if (loop) { + after.push_back (rms); + } else { + before.push_back (rms); + } + } + } + + return 0; +} diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index dc7eabf40f..981f9505d9 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -305,9 +305,14 @@ Session::process_event (Event* ev) */ if (non_realtime_work_pending()) { - immediate_events.insert (immediate_events.end(), ev); - _remove_event (ev); - return; + + /* except locates, which we have the capability to handle */ + + if (ev->type != Event::Locate) { + immediate_events.insert (immediate_events.end(), ev); + _remove_event (ev); + return; + } } //printf("Processing event: %s\n", event_names[ev->type]); diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index b3a5ec6e01..18bf9b83e1 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -581,7 +581,7 @@ Session::mmc_step (MIDI::MachineControl &mmc, int steps) } double diff_secs = diff.tv_sec + (diff.tv_usec / 1000000.0); - double cur_speed = (((steps * 0.5) * Config->get_smpte_frames_per_second()) / diff_secs) / Config->get_smpte_frames_per_second(); + double cur_speed = (((steps * 0.5) * smpte_frames_per_second()) / diff_secs) / smpte_frames_per_second(); if (_transport_speed == 0 || cur_speed * _transport_speed < 0) { /* change direction */ @@ -640,6 +640,8 @@ Session::mmc_locate (MIDI::MachineControl &mmc, const MIDI::byte* mmc_tc) smpte.minutes = mmc_tc[1]; smpte.seconds = mmc_tc[2]; smpte.frames = mmc_tc[3]; + smpte.rate = smpte_frames_per_second(); + smpte.drop = smpte_drop_frames(); // Also takes smpte offset into account: smpte_to_sample( smpte, target_frame, true /* use_offset */, false /* use_subframes */ ); diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc index c877abdca4..2de3448cd1 100644 --- a/libs/ardour/session_process.cc +++ b/libs/ardour/session_process.cc @@ -57,7 +57,7 @@ Session::process (nframes_t nframes) } if (non_realtime_work_pending()) { - if (g_atomic_int_get (&butler_should_do_transport_work) == 0) { + if (!transport_work_requested ()) { post_transport (); } } diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index bcc9b730ba..097a9e2381 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -43,8 +43,8 @@ #ifdef HAVE_SYS_VFS_H #include <sys/vfs.h> #else -#include <sys/mount.h> #include <sys/param.h> +#include <sys/mount.h> #endif #include <glibmm.h> @@ -57,6 +57,8 @@ #include <pbd/pathscanner.h> #include <pbd/pthread_utils.h> #include <pbd/strsplit.h> +#include <pbd/stacktrace.h> +#include <pbd/copyfile.h> #include <ardour/audioengine.h> #include <ardour/configuration.h> @@ -93,6 +95,7 @@ #include <ardour/control_protocol_manager.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> +#include <ardour/playlist_factory.h> #include <control_protocol/control_protocol.h> @@ -107,12 +110,14 @@ void Session::first_stage_init (string fullpath, string snapshot_name) { if (fullpath.length() == 0) { + destroy (); throw failed_constructor(); } char buf[PATH_MAX+1]; if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) { error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg; + destroy (); throw failed_constructor(); } @@ -133,7 +138,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed)); g_atomic_int_set (&processing_prohibited, 0); - send_cnt = 0; insert_cnt = 0; _transport_speed = 0; _last_transport_speed = 0; @@ -164,7 +168,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) _worst_output_latency = 0; _worst_input_latency = 0; _worst_track_latency = 0; - _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading); + _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading|Deletion); _slave = 0; butler_mixdown_buffer = 0; butler_gain_buffer = 0; @@ -247,7 +251,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) RegionFactory::CheckNewRegion.connect (mem_fun (*this, &Session::add_region)); SourceFactory::SourceCreated.connect (mem_fun (*this, &Session::add_source)); - Playlist::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); + PlaylistFactory::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); @@ -316,10 +320,20 @@ Session::second_stage_init (bool new_session) _engine.Halted.connect (mem_fun (*this, &Session::engine_halted)); _engine.Xrun.connect (mem_fun (*this, &Session::xrun_recovery)); - if (_engine.running()) { + try { when_engine_running(); - } else { - first_time_running = _engine.Running.connect (mem_fun (*this, &Session::when_engine_running)); + } + + /* handle this one in a different way than all others, so that its clear what happened */ + + catch (AudioEngine::PortRegistrationFailure& err) { + error << _("Unable to create all required ports") + << endmsg; + return -1; + } + + catch (...) { + return -1; } //send_full_time_code (); @@ -391,6 +405,7 @@ Session::setup_raid_path (string path) if (fspath[fspath.length()-1] != '/') { fspath += '/'; } + fspath += sound_dir (false); AudioFileSource::set_search_path (fspath); @@ -604,15 +619,12 @@ Session::save_state (string snapshot_name, bool pending) xml_path = _path; xml_path += snapshot_name; xml_path += _statefile_suffix; + bak_path = xml_path; bak_path += ".bak"; - // Make backup of state file - - if ((access (xml_path.c_str(), F_OK) == 0) && - (rename(xml_path.c_str(), bak_path.c_str()))) { - error << _("could not backup old state file, current state not saved.") << endmsg; - return -1; + if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) { + copy_file (xml_path, bak_path); } } else { @@ -623,30 +635,31 @@ Session::save_state (string snapshot_name, bool pending) } + string tmp_path; + + tmp_path = _path; + tmp_path += snapshot_name; + tmp_path += ".tmp"; + cerr << "actually writing state\n"; - if (!tree.write (xml_path)) { - error << string_compose (_("state could not be saved to %1"), xml_path) << endmsg; + if (!tree.write (tmp_path)) { + error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg; + unlink (tmp_path.c_str()); + return -1; - /* don't leave a corrupt file lying around if it is - possible to fix. - */ + } else { - if (unlink (xml_path.c_str())) { - error << string_compose (_("could not remove corrupt state file %1"), xml_path) << endmsg; - } else { - if (!pending) { - if (rename (bak_path.c_str(), xml_path.c_str())) { - error << string_compose (_("could not restore state file from backup %1"), bak_path) << endmsg; - } - } + if (rename (tmp_path.c_str(), xml_path.c_str()) != 0) { + error << string_compose (_("could not rename temporary session file %1 to %2"), tmp_path, xml_path) << endmsg; + unlink (tmp_path.c_str()); + return -1; } - - return -1; } if (!pending) { - save_history(snapshot_name); + + save_history (snapshot_name); bool was_dirty = dirty(); @@ -715,15 +728,52 @@ Session::load_state (string snapshot_name) set_dirty(); - if (state_tree->read (xmlpath)) { - return 0; - } else { + if (!state_tree->read (xmlpath)) { error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg; + delete state_tree; + state_tree = 0; + return -1; } - delete state_tree; - state_tree = 0; - return -1; + XMLNode& root (*state_tree->root()); + + if (root.name() != X_("Session")) { + error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath) << endmsg; + delete state_tree; + state_tree = 0; + return -1; + } + + const XMLProperty* prop; + bool is_old = false; + + if ((prop = root.property ("version")) == 0) { + /* no version implies very old version of Ardour */ + is_old = true; + } else { + int major_version; + major_version = atoi (prop->value()); // grab just the first number before the period + if (major_version < 2) { + is_old = true; + } + } + + if (is_old) { + string backup_path; + + backup_path = xmlpath; + backup_path += ".1"; + + info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"), + xmlpath, backup_path) + << endmsg; + + copy_file (xmlpath, backup_path); + + /* if it fails, don't worry. right? */ + } + + return 0; } int @@ -1520,6 +1570,7 @@ Session::load_sources (const XMLNode& node) if ((source = XMLSourceFactory (**niter)) == 0) { error << _("Session: cannot create Source from XML description.") << endmsg; } + } return 0; @@ -1791,7 +1842,7 @@ Session::load_playlists (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Playlist *playlist; + boost::shared_ptr<Playlist> playlist; nlist = node.children(); @@ -1812,7 +1863,7 @@ Session::load_unused_playlists (const XMLNode& node) { XMLNodeList nlist; XMLNodeConstIterator niter; - Playlist *playlist; + boost::shared_ptr<Playlist> playlist; nlist = node.children(); @@ -1827,34 +1878,22 @@ Session::load_unused_playlists (const XMLNode& node) // now manually untrack it - track_playlist (playlist, false); + track_playlist (false, boost::weak_ptr<Playlist> (playlist)); } return 0; } -Playlist * +boost::shared_ptr<Playlist> Session::XMLPlaylistFactory (const XMLNode& node) { - const XMLProperty* type = node.property("type"); - try { - - if ( !type || type->value() == "audio" ) { - - return new AudioPlaylist (*this, node); - - } else if (type->value() == "midi") { - - return new MidiPlaylist (*this, node); - - } - - } catch (failed_constructor& err) { - return 0; + return PlaylistFactory::create (*this, node); } - return 0; + catch (failed_constructor& err) { + return boost::shared_ptr<Playlist>(); + } } int @@ -1913,7 +1952,6 @@ Session::sound_dir (bool with_path) const old_withpath = _path; old_withpath += old_sound_dir_name; - old_withpath += '/'; if (stat (old_withpath.c_str(), &statbuf) == 0) { if (with_path) @@ -1933,7 +1971,6 @@ Session::sound_dir (bool with_path) const res += legalize_for_path (_name); res += '/'; res += sound_dir_name; - res += '/'; return res; } @@ -2265,7 +2302,12 @@ void Session::set_global_route_metering (GlobalRouteMeterState s, void* arg) { for (GlobalRouteMeterState::iterator i = s.begin(); i != s.end(); ++i) { - i->first->set_meter_point (i->second, arg); + + boost::shared_ptr<Route> r = (i->first.lock()); + + if (r) { + r->set_meter_point (i->second, arg); + } } } @@ -2273,8 +2315,13 @@ void Session::set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void* arg) { for (GlobalRouteBooleanState::iterator i = s.begin(); i != s.end(); ++i) { - Route* r = i->first.get(); - (r->*method) (i->second, arg); + + boost::shared_ptr<Route> r = (i->first.lock()); + + if (r) { + Route* rp = r.get(); + (rp->*method) (i->second, arg); + } } } @@ -2506,11 +2553,20 @@ Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_th return 0; } +struct RegionCounter { + typedef std::map<PBD::ID,boost::shared_ptr<AudioSource> > AudioSourceList; + AudioSourceList::iterator iter; + boost::shared_ptr<Region> region; + uint32_t count; + + RegionCounter() : count (0) {} +}; + int Session::cleanup_sources (Session::cleanup_report& rep) { vector<boost::shared_ptr<Source> > dead_sources; - vector<Playlist*> playlists_tbd; + vector<boost::shared_ptr<Playlist> > playlists_tbd; PathScanner scanner; string sound_path; vector<space_and_path>::iterator i; @@ -2549,73 +2605,36 @@ Session::cleanup_sources (Session::cleanup_report& rep) /* now delete any that were marked for deletion */ - for (vector<Playlist*>::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) { - PlaylistList::iterator foo; - - if ((foo = unused_playlists.find (*x)) != unused_playlists.end()) { - unused_playlists.erase (foo); - } - delete *x; + for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) { + (*x)->drop_references (); } - /* step 2: find all un-referenced sources */ + playlists_tbd.clear (); + + /* step 2: find all un-used sources */ rep.paths.clear (); rep.space = 0; for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) { - + SourceMap::iterator tmp; tmp = i; ++tmp; - /* only remove files that are not in use and have some size - to them. otherwise we remove the current "nascent" + /* do not bother with files that are zero size, otherwise we remove the current "nascent" capture files. */ - cerr << "checking out source " << i->second->name() << " use_count = " << i->second.use_count() << endl; - - if (i->second.use_count() == 1 && i->second->length() > 0) { + if (!i->second->used() && i->second->length() > 0) { dead_sources.push_back (i->second); - - /* remove this source from our own list to avoid us - adding it to the list of all sources below - */ - - sources.erase (i); - } + i->second->GoingAway(); + } i = tmp; } - /* Step 3: get rid of all regions in the region list that use any dead sources - in case the sources themselves don't go away (they might be referenced in - other snapshots). - */ - - for (vector<boost::shared_ptr<Source> >::iterator i = dead_sources.begin(); i != dead_sources.end();++i) { - - for (RegionList::iterator r = regions.begin(); r != regions.end(); ) { - RegionList::iterator tmp; - - tmp = r; - ++tmp; - - boost::shared_ptr<Region> reg = r->second; - - for (uint32_t n = 0; n < reg->n_channels(); ++n) { - if (reg->source (n) == (*i)) { - /* this region is dead */ - remove_region (reg); - } - } - - r = tmp; - } - } - /* build a list of all the possible sound directories for the session */ for (i = session_dirs.begin(); i != session_dirs.end(); ) { @@ -2624,7 +2643,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) ++nexti; sound_path += (*i).path; - sound_path += sound_dir_name; + sound_path += sound_dir (false); if (nexti != session_dirs.end()) { sound_path += ':'; @@ -2632,7 +2651,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) i = nexti; } - + /* now do the same thing for the files that ended up in the sounds dir(s) but are not referenced as sources in any snapshot. */ @@ -2672,7 +2691,6 @@ Session::cleanup_sources (Session::cleanup_report& rep) used = true; break; } - } if (!used) { @@ -2697,11 +2715,31 @@ Session::cleanup_sources (Session::cleanup_report& rep) on whichever filesystem it was already on. */ - newpath = Glib::path_get_dirname (*x); - newpath = Glib::path_get_dirname (newpath); + if (_path.find ("/sounds/")) { + + /* old school, go up 1 level */ + + newpath = Glib::path_get_dirname (*x); // "sounds" + newpath = Glib::path_get_dirname (newpath); // "session-name" + + } else { + + /* new school, go up 4 levels */ + + newpath = Glib::path_get_dirname (*x); // "audiofiles" + newpath = Glib::path_get_dirname (newpath); // "session-name" + newpath = Glib::path_get_dirname (newpath); // "interchange" + newpath = Glib::path_get_dirname (newpath); // "session-dir" + } newpath += '/'; newpath += dead_sound_dir_name; + + if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), newpath, strerror (errno)) << endmsg; + return -1; + } + newpath += '/'; newpath += Glib::path_get_basename ((*x)); @@ -2741,7 +2779,6 @@ Session::cleanup_sources (Session::cleanup_report& rep) << endmsg; goto out; } - /* see if there an easy to find peakfile for this file, and remove it. */ @@ -2759,7 +2796,6 @@ Session::cleanup_sources (Session::cleanup_report& rep) goto out; } } - } ret = 0; @@ -2866,6 +2902,12 @@ Session::set_clean () } void +Session::set_deletion_in_progress () +{ + _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion); +} + +void Session::add_controllable (Controllable* c) { Glib::Mutex::Lock lm (controllables_lock); @@ -2916,8 +2958,8 @@ Session::save_history (string snapshot_name) XMLTree tree; string xml_path; string bak_path; - - tree.set_root (&_history.get_state()); + + tree.set_root (&_history.get_state (Config->get_saved_history_depth())); if (snapshot_name.empty()) { snapshot_name = _current_snapshot_name; @@ -2934,8 +2976,6 @@ Session::save_history (string snapshot_name) return -1; } - cerr << "actually writing history\n"; - if (!tree.write (xml_path)) { error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; @@ -2965,18 +3005,22 @@ Session::restore_history (string snapshot_name) XMLTree tree; string xmlpath; + if (snapshot_name.empty()) { + snapshot_name = _current_snapshot_name; + } + /* read xml */ xmlpath = _path + snapshot_name + ".history"; cerr << string_compose(_("Loading history from '%1'."), xmlpath) << endmsg; if (access (xmlpath.c_str(), F_OK)) { - error << string_compose(_("%1: session history file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg; - return 1; + info << string_compose (_("%1: no history file \"%2\" for this session."), _name, xmlpath) << endmsg; + return 1; } if (!tree.read (xmlpath)) { - error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg; - return -1; + error << string_compose (_("Could not understand session history file \"%1\""), xmlpath) << endmsg; + return -1; } /* replace history */ @@ -3005,10 +3049,19 @@ Session::restore_history (string snapshot_name) if (n->name() == "MementoCommand" || n->name() == "MementoUndoCommand" || n->name() == "MementoRedoCommand") { + if ((c = memento_command_factory(n))) { ut->add_command(c); } + + } else if (n->name() == X_("GlobalRouteStateCommand")) { + + if ((c = global_state_command_factory (*n))) { + ut->add_command (c); + } + } else { + error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg; } } @@ -3039,7 +3092,6 @@ Session::config_changed (const char* parameter_name) for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { if ((*i)->record_enabled ()) { - //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; (*i)->monitor_input (!Config->get_auto_input()); } } @@ -3099,7 +3151,7 @@ Session::config_changed (const char* parameter_name) setup_raid_path (Config->get_raid_path()); - } else if (PARAM_IS ("smpte-frames-per-second") || PARAM_IS ("smpte-drop-frames")) { + } else if (PARAM_IS ("smpte-format")) { sync_time_vars (); diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc index d24bab5ff8..585dad6b1a 100644 --- a/libs/ardour/session_time.cc +++ b/libs/ardour/session_time.cc @@ -48,43 +48,141 @@ Session::bbt_time (nframes_t when, BBT_Time& bbt) } /* SMPTE TIME */ +float +Session::smpte_frames_per_second() const +{ + switch (Config->get_smpte_format()) { + case smpte_23976: + return 23.976; + + break; + case smpte_24: + return 24; + + break; + case smpte_24976: + return 24.976; + + break; + case smpte_25: + return 25; + + break; + case smpte_2997: + return 29.97; + + break; + case smpte_2997drop: + return 29.97; + + break; + case smpte_30: + return 30; + + break; + case smpte_30drop: + return 30; + + break; + case smpte_5994: + return 59.94; + + break; + case smpte_60: + return 60; + + break; + default: + cerr << "Editor received unexpected smpte type" << endl; + } + return 30.0; +} +bool +Session::smpte_drop_frames() const +{ + switch (Config->get_smpte_format()) { + case smpte_23976: + return false; + + break; + case smpte_24: + return false; + + break; + case smpte_24976: + return false; + + break; + case smpte_25: + return false; + + break; + case smpte_2997: + return false; + break; + case smpte_2997drop: + return true; + + break; + case smpte_30: + return false; + + break; + case smpte_30drop: + return true; + + break; + case smpte_5994: + return false; + + break; + case smpte_60: + return false; + + break; + default: + cerr << "Editor received unexpected smpte type" << endl; + } + return false; +} void Session::sync_time_vars () { _current_frame_rate = (nframes_t) round (_base_frame_rate * (1.0 + (Config->get_video_pullup()/100.0))); - _frames_per_hour = _current_frame_rate * 3600; - _frames_per_smpte_frame = (double) _current_frame_rate / (double) Config->get_smpte_frames_per_second(); - _smpte_frames_per_hour = (unsigned long) (Config->get_smpte_frames_per_second() * 3600.0); + _frames_per_smpte_frame = (double) _current_frame_rate / (double) smpte_frames_per_second(); + if (smpte_drop_frames()) { + _frames_per_hour = (long)(107892 * _frames_per_smpte_frame); + } else { + _frames_per_hour = (long)(3600 * rint(smpte_frames_per_second()) * _frames_per_smpte_frame); + } + _smpte_frames_per_hour = (nframes_t)rint(smpte_frames_per_second() * 3600.0); + } int -Session::set_smpte_type (float fps, bool drop_frames) +Session::set_smpte_format (SmpteFormat format) { - Config->set_smpte_frames_per_second (fps); - Config->set_smpte_drop_frames (drop_frames); + + Config->set_smpte_format (format); last_smpte_valid = false; // smpte type bits are the middle two in the upper nibble - switch ((int) ceil (fps)) { + switch ((int) ceil (smpte_frames_per_second())) { case 24: mtc_smpte_bits = 0; - SMPTE::Time::default_rate = SMPTE::MTC_24_FPS; break; case 25: mtc_smpte_bits = 0x20; - SMPTE::Time::default_rate = SMPTE::MTC_25_FPS; break; case 30: default: - if (drop_frames) { + if (smpte_drop_frames()) { mtc_smpte_bits = 0x40; - SMPTE::Time::default_rate = SMPTE::MTC_30_FPS_DROP; } else { mtc_smpte_bits = 0x60; - SMPTE::Time::default_rate = SMPTE::MTC_30_FPS; } break; }; @@ -113,7 +211,8 @@ Session::set_smpte_offset_negative (bool neg) void Session::smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset, bool use_subframes ) const { - if (Config->get_smpte_drop_frames()) { + + if (smpte.drop) { // The drop frame format was created to better approximate the 30000/1001 = 29.97002997002997.... // framerate of NTSC color TV. The used frame rate of drop frame is 29.97, which drifts by about // 0.108 frame per hour, or about 1.3 frames per 12 hours. This is not perfect, but a lot better @@ -152,9 +251,10 @@ Session::smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset // 0:10:00:00 0.0 0 600.000 26460000 (accurate) // // Per Sigmond <per@sigmond.no> - + // Samples inside time dividable by 10 minutes (real time accurate) - nframes_t base_samples = ((smpte.hours * 60 * 60) + ((smpte.minutes / 10) * 10 * 60)) * frame_rate(); + nframes_t base_samples = (nframes_t) (((smpte.hours * 107892) + ((smpte.minutes / 10) * 17982)) * _frames_per_smpte_frame); + // Samples inside time exceeding the nearest 10 minutes (always offset, see above) long exceeding_df_minutes = smpte.minutes % 10; long exceeding_df_seconds = (exceeding_df_minutes * 60) + smpte.seconds; @@ -162,12 +262,18 @@ Session::smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset nframes_t exceeding_samples = (nframes_t) rint(exceeding_df_frames * _frames_per_smpte_frame); sample = base_samples + exceeding_samples; } else { - // Non drop is easy: - sample = (((smpte.hours * 60 * 60) + (smpte.minutes * 60) + smpte.seconds) * frame_rate()) + (nframes_t)rint(smpte.frames * _frames_per_smpte_frame); + /* + Non drop is easy.. just note the use of + rint(smpte.rate) * _frames_per_smpte_frame + (frames per SMPTE second), which is larger than + frame_rate() in the non-integer SMPTE rate case. + */ + + sample = (nframes_t)rint((((smpte.hours * 60 * 60) + (smpte.minutes * 60) + smpte.seconds) * (rint(smpte.rate) * _frames_per_smpte_frame)) + (smpte.frames * _frames_per_smpte_frame)); } if (use_subframes) { - sample += (long) (((double)smpte.subframes * _frames_per_smpte_frame) / 80.0); + sample += (long) (((double)smpte.subframes * _frames_per_smpte_frame) / Config->get_subframes_per_frame()); } if (use_offset) { @@ -190,6 +296,7 @@ Session::smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset } } } + } @@ -224,14 +331,14 @@ Session::sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, // high sample numbers in the calculations that follow. smpte.hours = offset_sample / _frames_per_hour; offset_sample = offset_sample % _frames_per_hour; - + // Calculate exact number of (exceeding) smpte frames and fractional frames smpte_frames_left_exact = (double) offset_sample / _frames_per_smpte_frame; smpte_frames_fraction = smpte_frames_left_exact - floor( smpte_frames_left_exact ); - smpte.subframes = (long) rint(smpte_frames_fraction * 80.0); + smpte.subframes = (long) rint(smpte_frames_fraction * Config->get_subframes_per_frame()); // XXX Not sure if this is necessary anymore... - if (smpte.subframes == 80) { + if (smpte.subframes == Config->get_subframes_per_frame()) { // This can happen with 24 fps (and 29.97 fps ?) smpte_frames_left_exact = ceil( smpte_frames_left_exact ); smpte.subframes = 0; @@ -240,7 +347,7 @@ Session::sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, // Extract hour-exceeding frames for minute, second and frame calculations smpte_frames_left = ((long) floor( smpte_frames_left_exact )); - if (Config->get_smpte_drop_frames()) { + if (smpte_drop_frames()) { // See long explanation in smpte_to_sample()... // Number of 10 minute chunks @@ -276,15 +383,18 @@ Session::sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, } } else { // Non drop is easy - smpte.minutes = smpte_frames_left / ((long) Config->get_smpte_frames_per_second () * 60); - smpte_frames_left = smpte_frames_left % ((long) Config->get_smpte_frames_per_second () * 60); - smpte.seconds = smpte_frames_left / (long) Config->get_smpte_frames_per_second (); - smpte.frames = smpte_frames_left % (long) Config->get_smpte_frames_per_second (); + smpte.minutes = smpte_frames_left / ((long) rint (smpte_frames_per_second ()) * 60); + smpte_frames_left = smpte_frames_left % ((long) rint (smpte_frames_per_second ()) * 60); + smpte.seconds = smpte_frames_left / (long) rint(smpte_frames_per_second ()); + smpte.frames = smpte_frames_left % (long) rint(smpte_frames_per_second ()); } if (!use_subframes) { smpte.subframes = 0; } + /* set frame rate and drop frame */ + smpte.rate = smpte_frames_per_second (); + smpte.drop = smpte_drop_frames(); } void @@ -415,7 +525,7 @@ Session::jack_timebase_callback (jack_transport_state_t state, #ifdef HAVE_JACK_VIDEO_SUPPORT //poke audio video ratio so Ardour can track Video Sync - pos->audio_frames_per_video_frame = frame_rate() / Config->get_smpte_frames_per_second (); + pos->audio_frames_per_video_frame = frame_rate() / smpte_frames_per_second(); pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio); #endif @@ -423,7 +533,7 @@ Session::jack_timebase_callback (jack_transport_state_t state, /* SMPTE info */ t.smpte_offset = _smpte_offset; - t.smpte_frame_rate = Config->get_smpte_frames_per_second (); + t.smpte_frame_rate = smpte_frames_per_second(); if (_transport_speed) { @@ -474,7 +584,7 @@ Session::convert_to_frames_at (nframes_t position, AnyTime& any) secs = any.smpte.hours * 60 * 60; secs += any.smpte.minutes * 60; secs += any.smpte.seconds; - secs += any.smpte.frames / Config->get_smpte_frames_per_second (); + secs += any.smpte.frames / smpte_frames_per_second(); if (_smpte_offset_negative) { return (nframes_t) floor (secs * frame_rate()) - _smpte_offset; diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index e9c4e3785f..ad3573a72e 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -52,8 +52,10 @@ using namespace PBD; void Session::request_input_change_handling () { - Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0); - queue_event (ev); + if (!(_state_of_the_state & (InitialConnecting|Deletion))) { + Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0); + queue_event (ev); + } } void @@ -185,9 +187,14 @@ Session::realtime_stop (bool abort) void Session::butler_transport_work () { + restart: + bool finished; boost::shared_ptr<RouteList> r = routes.reader (); boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader(); + int on_entry = g_atomic_int_get (&butler_should_do_transport_work); + finished = true; + if (post_transport_work & PostTransportCurveRealloc) { for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { (*i)->curve_reallocate(); @@ -211,30 +218,48 @@ Session::butler_transport_work () cumulative_rf_motion = 0; reset_rf_scale (0); - for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { - if (!(*i)->hidden()) { - if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) { - (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed())); + /* don't seek if locate will take care of that in non_realtime_stop() */ + + if (!(post_transport_work & PostTransportLocate)) { + + for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) { + if (!(*i)->hidden()) { + if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) { + (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed())); + } + else { + (*i)->seek (_transport_frame); + } } - else { - (*i)->seek (_transport_frame); + if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) { + /* new request, stop seeking, and start again */ + g_atomic_int_dec_and_test (&butler_should_do_transport_work); + goto restart; } } } } if (post_transport_work & (PostTransportStop|PostTransportLocate)) { - non_realtime_stop (post_transport_work & PostTransportAbort); + non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished); + if (!finished) { + g_atomic_int_dec_and_test (&butler_should_do_transport_work); + goto restart; + } } if (post_transport_work & PostTransportOverWrite) { - non_realtime_overwrite (); + non_realtime_overwrite (on_entry, finished); + if (!finished) { + g_atomic_int_dec_and_test (&butler_should_do_transport_work); + goto restart; + } } if (post_transport_work & PostTransportAudition) { non_realtime_set_audition (); } - + g_atomic_int_dec_and_test (&butler_should_do_transport_work); } @@ -249,7 +274,7 @@ Session::non_realtime_set_speed () } void -Session::non_realtime_overwrite () +Session::non_realtime_overwrite (int on_entry, bool& finished) { boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader(); @@ -257,11 +282,15 @@ Session::non_realtime_overwrite () if ((*i)->pending_overwrite) { (*i)->overwrite_existing_buffers (); } + if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) { + finished = false; + return; + } } } void -Session::non_realtime_stop (bool abort) +Session::non_realtime_stop (bool abort, int on_entry, bool& finished) { struct tm* now; time_t xnow; @@ -376,6 +405,11 @@ Session::non_realtime_stop (bool abort) (*i)->seek (_transport_frame); } } + if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) { + finished = false; + /* we will be back */ + return; + } } #ifdef LEAVE_TRANSPORT_UNADJUSTED @@ -629,6 +663,8 @@ Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool w } else { + cerr << "butler not requested\n"; + /* this is functionally what clear_clicks() does but with a tentative lock */ Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK); @@ -904,7 +940,6 @@ Session::post_transport () if (post_transport_work & PostTransportLocate) { if ((Config->get_auto_play() && !_exporting) || (post_transport_work & PostTransportRoll)) { - start_transport (); } else { @@ -1127,7 +1162,7 @@ void Session::request_bounded_roll (nframes_t start, nframes_t end) { request_stop (); - Event *ev = new Event (Event::StopOnce, Event::Replace, Event::Immediate, end, 0.0); + Event *ev = new Event (Event::StopOnce, Event::Replace, end, Event::Immediate, 0.0); queue_event (ev); request_locate (start, true); } @@ -1135,6 +1170,8 @@ Session::request_bounded_roll (nframes_t start, nframes_t end) void Session::engine_halted () { + bool ignored; + /* there will be no more calls to process(), so we'd better clean up for ourselves, right now. @@ -1147,7 +1184,7 @@ Session::engine_halted () stop_butler (); realtime_stop (false); - non_realtime_stop (false); + non_realtime_stop (false, 0, ignored); transport_sub_state = 0; TransportStateChange (); /* EMIT SIGNAL */ diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc index 6fb71e9596..aca8cf5a25 100644 --- a/libs/ardour/smf_source.cc +++ b/libs/ardour/smf_source.cc @@ -49,7 +49,7 @@ uint64_t SMFSource::header_position_offset; */ SMFSource::SMFSource (Session& s, std::string path, Flag flags) - : MidiSource (s, region_name_from_path(path)) + : MidiSource (s, region_name_from_path(path, false)) , _channel(0) , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now , _allow_remove_if_empty(true) diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index a30bfcf49b..8e90eac6ab 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -408,26 +408,25 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) PeakBuildRecord *pbr = 0; if (pending_peak_builds.size()) { - pbr = pending_peak_builds.back(); - } + pbr = pending_peak_builds.back(); + } - if (pbr && pbr->frame + pbr->cnt == oldlen) { - - /* the last PBR extended to the start of the current write, - so just extend it again. - */ - - pbr->cnt += cnt; - } else { - pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt)); - } + if (pbr && pbr->frame + pbr->cnt == oldlen) { - _peaks_built = false; + /* the last PBR extended to the start of the current write, + so just extend it again. + */ + pbr->cnt += cnt; + } else { + pending_peak_builds.push_back (new PeakBuildRecord (oldlen, cnt)); + } + + _peaks_built = false; } if (_build_peakfiles) { - queue_for_peaks (shared_from_this ()); + queue_for_peaks (shared_from_this (), false); } _write_data_count = cnt; @@ -540,7 +539,7 @@ SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) } if (_build_peakfiles) { - queue_for_peaks (shared_from_this ()); + queue_for_peaks (shared_from_this (), true); } return cnt; diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 8f0afd3507..db2147493a 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -131,15 +131,22 @@ Source::update_length (jack_nframes_t pos, jack_nframes_t cnt) } void -Source::add_playlist (Playlist* pl) +Source::add_playlist (boost::shared_ptr<Playlist> pl) { _playlists.insert (pl); + pl->GoingAway.connect (bind (mem_fun (*this, &Source::remove_playlist), boost::weak_ptr<Playlist> (pl))); } void -Source::remove_playlist (Playlist* pl) +Source::remove_playlist (boost::weak_ptr<Playlist> wpl) { - std::set<Playlist*>::iterator x; + boost::shared_ptr<Playlist> pl (wpl.lock()); + + if (!pl) { + return; + } + + std::set<boost::shared_ptr<Playlist> >::iterator x; if ((x = _playlists.find (pl)) != _playlists.end()) { _playlists.erase (x); diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc index 001af609dc..ad01bf6171 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -64,47 +64,26 @@ SourceFactory::create (Session& s, const XMLNode& node) if (type == DataType::AUDIO) { - if (node.property (X_("destructive")) != 0) { - - boost::shared_ptr<Source> ret (new DestructiveFileSource (s, node)); + try { + boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); if (setup_peakfile (ret)) { return boost::shared_ptr<Source>(); } SourceCreated (ret); return ret; + } - } else { - - try { - boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); + catch (failed_constructor& err) { + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); if (setup_peakfile (ret)) { return boost::shared_ptr<Source>(); } SourceCreated (ret); return ret; - - } catch (failed_constructor& err) { - - try { - boost::shared_ptr<Source> ret (new CoreAudioSource (node)); - SourceCreated (ret); - return ret; - } - - - catch (failed_constructor& err) { - - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - SourceCreated (ret); - return ret; - } } } else if (type == DataType::MIDI) { - + boost::shared_ptr<Source> ret (new SMFSource (node)); SourceCreated (ret); return ret; @@ -127,33 +106,24 @@ SourceFactory::create (Session& s, const XMLNode& node) if (type == DataType::AUDIO) { - if (node.property (X_("destructive")) != 0) { - - boost::shared_ptr<Source> ret (new DestructiveFileSource (s, node)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - SourceCreated (ret); - return ret; + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - } else { - - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - SourceCreated (ret); - return ret; + if (setup_peakfile (ret)) { + return boost::shared_ptr<Source>(); } + + SourceCreated (ret); + return ret; } else if (type == DataType::MIDI) { boost::shared_ptr<Source> ret (new SMFSource (s, node)); + SourceCreated (ret); return ret; } - + return boost::shared_ptr<Source> (); } @@ -164,39 +134,53 @@ boost::shared_ptr<Source> SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { - if (flags & Destructive) { - boost::shared_ptr<Source> ret (new DestructiveFileSource (s, idstr, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - if (announce) { - SourceCreated (ret); - } - return ret; - + + if (!(flags & Destructive)) { + try { boost::shared_ptr<Source> ret (new CoreAudioSource (s, idstr, flags)); + if (setup_peakfile (ret)) { + return boost::shared_ptr<Source>(); + } if (announce) { SourceCreated (ret); } return ret; - - } catch (failed_constructor& err) { + } + + catch (failed_constructor& err) { boost::shared_ptr<Source> ret (new SndFileSource (s, idstr, flags)); + if (setup_peakfile (ret)) { + return boost::shared_ptr<Source>(); + } if (announce) { SourceCreated (ret); } return ret; } + } else { + + boost::shared_ptr<Source> ret (new SndFileSource (s, idstr, flags)); + if (setup_peakfile (ret)) { + return boost::shared_ptr<Source>(); + } + if (announce) { + SourceCreated (ret); + } + return ret; + } + } else if (type == DataType::MIDI) { boost::shared_ptr<Source> ret (new SMFSource (s, node)); - SourceCreated (ret); + if (announce) { + SourceCreated (ret); + } return ret; } - + return boost::shared_ptr<Source>(); } @@ -206,27 +190,32 @@ boost::shared_ptr<Source> SourceFactory::createReadable (DataType type, Session& s, string idstr, AudioFileSource::Flag flags, bool announce) { if (type == DataType::AUDIO) { - + boost::shared_ptr<Source> ret (new SndFileSource (s, idstr, flags)); + if (setup_peakfile (ret)) { return boost::shared_ptr<Source>(); } + if (announce) { SourceCreated (ret); } + return ret; } else if (type == DataType::MIDI) { - boost::shared_ptr<Source> ret (new SMFSource (s, idstr, SMFSource::Flag(0))); // FIXME: flags? + // FIXME: flags? + boost::shared_ptr<Source> ret (new SMFSource (s, idstr, SMFSource::Flag(0))); + if (announce) { SourceCreated (ret); } - return ret; + return ret; } - - return boost::shared_ptr<Source> (); + + return boost::shared_ptr<Source>(); } #endif // HAVE_COREAUDIO @@ -235,39 +224,33 @@ boost::shared_ptr<Source> SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce) { /* this might throw failed_constructor(), which is OK */ + if (type == DataType::AUDIO) { - - if (destructive) { - boost::shared_ptr<Source> ret (new DestructiveFileSource (s, path, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - rate)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - if (announce) { - SourceCreated (ret); - } - } else { - boost::shared_ptr<Source> ret (new SndFileSource (s, path, - Config->get_native_file_data_format(), - Config->get_native_file_header_format(), - rate)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - if (announce) { - SourceCreated (ret); - } - return ret; + 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>(); + } + if (announce) { + SourceCreated (ret); } + return ret; } else if (type == DataType::MIDI) { boost::shared_ptr<Source> ret (new SMFSource (s, path)); - SourceCreated (ret); + + if (announce) { + SourceCreated (ret); + } return ret; - + } return boost::shared_ptr<Source> (); diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index 0ff94324bb..e86c30dd4d 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -924,7 +924,10 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) return frame_time (the_beat); - /* XXX just keeping this for reference + + + /***************************** + XXX just keeping this for reference TempoMap::BBTPointList::iterator i; TempoMap::BBTPointList *more_zoomed_bbt_points; @@ -978,7 +981,8 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num) delete more_zoomed_bbt_points; return fr ; - */ + ******************************/ + } diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 9c94d32241..e0ef8fd0b3 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -40,6 +40,7 @@ #include <pbd/error.h> #include <pbd/stacktrace.h> #include <pbd/xml++.h> +#include <pbd/basename.h> #include <ardour/utils.h> #include "i18n.h" @@ -47,6 +48,7 @@ using namespace ARDOUR; using namespace std; using namespace PBD; +using Glib::ustring; void elapsed_time_to_str (char *buf, uint32_t seconds) @@ -90,6 +92,24 @@ elapsed_time_to_str (char *buf, uint32_t seconds) } } +ustring +legalize_for_path (ustring str) +{ + ustring::size_type pos; + ustring legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; + ustring legal; + + legal = str; + pos = 0; + + while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) { + legal.replace (pos, 1, "_"); + pos += 1; + } + + return legal; +} +#if 0 string legalize_for_path (string str) { @@ -107,6 +127,7 @@ legalize_for_path (string str) return legal; } +#endif ostream& operator<< (ostream& o, const BBT_Time& bbt) @@ -180,7 +201,7 @@ tokenize_fullpath (string fullpath, string& path, string& name) } int -touch_file (string path) +touch_file (ustring path) { int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660); if (fd >= 0) { @@ -190,22 +211,31 @@ touch_file (string path) return 1; } -string -placement_as_string (Placement p) +ustring +region_name_from_path (ustring path, bool strip_channels) { - switch (p) { - case PreFader: - return _("pre"); - default: /* to get g++ to realize we have all the cases covered */ - case PostFader: - return _("post"); + path = PBD::basename_nosuffix (path); + + if (strip_channels) { + + /* remove any "?R", "?L" or "?[a-z]" channel identifier */ + + ustring::size_type len = path.length(); + + if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && + (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) { + + path = path.substr (0, path.length() - 2); + } } -} -string -region_name_from_path (string path) + return path; +} + +bool +path_is_paired (ustring path, ustring& pair_base) { - string::size_type pos; + ustring::size_type pos; /* remove any leading path */ @@ -219,21 +249,23 @@ region_name_from_path (string path) path = path.substr (0, pos); } - /* remove any "?R", "?L" or "?[a-z]" channel identifier */ - - string::size_type len = path.length(); + ustring::size_type len = path.length(); + + /* look for possible channel identifier: "?R", "%R", ".L" etc. */ - if (len > 3 && (path[len-2] == '%' || path[len-2] == '?') && + if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) { - path = path.substr (0, path.length() - 2); - } + pair_base = path.substr (0, len-2); + return true; - return path; -} + } -string -path_expand (string path) + return false; +} + +ustring +path_expand (ustring path) { #ifdef HAVE_WORDEXP /* Handle tilde and environment variable expansion in session path */ @@ -371,25 +403,65 @@ slave_source_to_string (SlaveSource src) } } +/* I don't really like hard-coding these falloff rates here + * Probably should use a map of some kind that could be configured + * These rates are db/sec. +*/ + +#define METER_FALLOFF_OFF 0.0f +#define METER_FALLOFF_SLOWEST 6.6f // BBC standard +#define METER_FALLOFF_SLOW 8.6f // BBC standard +#define METER_FALLOFF_MEDIUM 20.0f +#define METER_FALLOFF_FAST 32.0f +#define METER_FALLOFF_FASTER 46.0f +#define METER_FALLOFF_FASTEST 70.0f + float meter_falloff_to_float (MeterFalloff falloff) { switch (falloff) { case MeterFalloffOff: - return 0.0f; + return METER_FALLOFF_OFF; case MeterFalloffSlowest: - return 1.0f; + return METER_FALLOFF_SLOWEST; case MeterFalloffSlow: - return 2.0f; + return METER_FALLOFF_SLOW; case MeterFalloffMedium: - return 3.0f; + return METER_FALLOFF_MEDIUM; case MeterFalloffFast: - return 4.0f; + return METER_FALLOFF_FAST; case MeterFalloffFaster: - return 5.0f; + return METER_FALLOFF_FASTER; case MeterFalloffFastest: + return METER_FALLOFF_FASTEST; default: - return 6.0f; + return METER_FALLOFF_FAST; + } +} + +MeterFalloff +meter_falloff_from_float (float val) +{ + if (val == METER_FALLOFF_OFF) { + return MeterFalloffOff; + } + else if (val <= METER_FALLOFF_SLOWEST) { + return MeterFalloffSlowest; + } + else if (val <= METER_FALLOFF_SLOW) { + return MeterFalloffSlow; + } + else if (val <= METER_FALLOFF_MEDIUM) { + return MeterFalloffMedium; + } + else if (val <= METER_FALLOFF_FAST) { + return MeterFalloffFast; + } + else if (val <= METER_FALLOFF_FASTER) { + return MeterFalloffFaster; + } + else { + return MeterFalloffFastest; } } diff --git a/libs/clearlooks/SConscript b/libs/clearlooks/SConscript index 0df20efe56..110bfe41be 100644 --- a/libs/clearlooks/SConscript +++ b/libs/clearlooks/SConscript @@ -1,4 +1,14 @@ +# -*- python -*- + import os.path +import glob + +libclearlooks_files = [ + 'clearlooks_draw.c', + 'clearlooks_rc_style.c', + 'clearlooks_style.c', + 'clearlooks_theme_main.c', + 'support.c' ] Import ('env install_prefix') @@ -7,17 +17,17 @@ clearlooks = env.Copy() clearlooks.Replace(CCFLAGS = ' `pkg-config --cflags gtk+-2.0` ', LINKFLAGS = ' `pkg-config --libs gtk+-2.0` ') -libclearlooks = clearlooks.SharedLibrary('clearlooks', [ - 'clearlooks_draw.c', - 'clearlooks_rc_style.c', - 'clearlooks_style.c', - 'clearlooks_theme_main.c', - 'support.c' -]) +libclearlooks = clearlooks.SharedLibrary('clearlooks', libclearlooks_files) usable_libclearlooks = clearlooks.Install ('engines', libclearlooks) Default (usable_libclearlooks) env.Alias('install', - env.Install(os.path.join(install_prefix,'lib/ardour2/engines'), + env.Install(os.path.join(install_prefix,env['LIBDIR'], 'ardour2', 'engines'), libclearlooks)) + +env.Alias('tarball', env.Distribute (env['DISTTREE'], + [ 'SConscript', 'bits.c'] + + libclearlooks_files + + glob.glob('*.h') + )) diff --git a/libs/flowcanvas/SConscript b/libs/flowcanvas/SConscript index 6394982a20..18b061ef82 100644 --- a/libs/flowcanvas/SConscript +++ b/libs/flowcanvas/SConscript @@ -39,7 +39,7 @@ libflowcanvas = flowcanvas.SharedLibrary('flowcanvas', flowcanvas_files) Default(libflowcanvas) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libflowcanvas)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libflowcanvas)) env.Alias('tarball', env.Distribute (env['DISTTREE'], ['SConscript'] + diff --git a/libs/fst/SConscript b/libs/fst/SConscript index 771de86dc8..bdffd959b5 100644 --- a/libs/fst/SConscript +++ b/libs/fst/SConscript @@ -11,24 +11,42 @@ Import('env install_prefix') fst = env.Copy(CC="winegcc") fst.Append (CPPPATH=".") -hackSDK = fst.Command('vst/aeffectx.h', 'vstsdk2.3/source/common/aeffectx.h', [ - Delete ('${TARGET.dir}'), - Copy ('${TARGET.dir}', '${SOURCE.dir}'), - "sed -i '/struct VstFileType\|struct VstFileSelect/,/};/d' $TARGET" -]) - -a = fst.Object ('fst', 'fst.c') -b = fst.Object ('fstinfofile', 'fstinfofile.c') -c = fst.Object ('vstwin', 'vstwin.c') -d = fst.Object ('vsti', 'vsti.c') - if fst['VST']: - if os.access ('vst/aeffectx.h', os.F_OK): - Default([hackSDK,a,b,c,d]) + vst_dir = Dir ('libs/vst') + vst_sdk_dir = Dir ('vstsdk2.3') + # + # if it exists, try to use the Steinberg zip package + # + vst_sdk_zip = File ('vstsdk2.3.zip') + + if os.access (vst_sdk_zip.abspath, os.F_OK): + print 'VST package discovered.' + elif os.access ('vst_sdk2_3.zip', os.F_OK): + # + # add a build target that unpacks the zip package the Steinberg "meta" zip package + # + vst_meta_zip = fst.Command (vst_sdk_zip, 'vst_sdk2_3.zip', "unzip -o -d ${TARGET.dir} $SOURCES vstsdk2.3.zip" ) + print 'VST meta-package discovered.' else: - print 'You have not installed the VST SDK in the correct location.' - print 'Please see http://ardour.org/building_vst_support for more information' - sys.exit (1) + if os.access ('vstsdk2.3.zip', os.F_OK) != 1: + print 'Did not find vst_sdk2_3.zip or vstsdk2.3.zip in libs/fst.' + print 'Make sure the correct file is in the correct location and correctly named.' + print 'Please see http://ardour.org/building_vst_support for more information.' + sys.exit (1) + + vst_headers = fst.Command ([ 'vst/aeffectx.h', 'vst/AEffect.h' ], vst_sdk_zip, [ + "unzip -qq -d ${SOURCE.dir} -o $SOURCE", + Delete ('$TARGET.dir'), + Copy ('${TARGET.dir}', 'libs/fst/vstsdk2.3/source/common'), + "sed -i '/struct VstFileType\|struct VstFileSelect/,/};/d' $TARGET" + ]) + + a = fst.Object ('fst', 'fst.c') + b = fst.Object ('fstinfofile', 'fstinfofile.c') + c = fst.Object ('vstwin', 'vstwin.c') + d = fst.Object ('vsti', 'vsti.c') + + Default([vst_headers,a,b,c,d]) env.Alias('tarball', env.Distribute (env['DISTTREE'], fst_src + ['SConscript', diff --git a/libs/fst/fstinfofile.c b/libs/fst/fstinfofile.c index 7cc98d2233..7b0c69d015 100644 --- a/libs/fst/fstinfofile.c +++ b/libs/fst/fstinfofile.c @@ -227,7 +227,9 @@ FSTInfo *fst_get_info( char *dllpath ) { FSTInfo *info; char *fstpath; - if( !(h = fst_load( dllpath )) ) return NULL; + if( !(h = fst_load( dllpath )) ) { + return NULL; + } if( !(fst = fst_instantiate( h, simple_master_callback, NULL )) ) { fst_unload( h ); fst_error( "instantiate failed\n" ); diff --git a/libs/glibmm2/SConscript b/libs/glibmm2/SConscript index dbc58e6499..267f846203 100644 --- a/libs/glibmm2/SConscript +++ b/libs/glibmm2/SConscript @@ -28,7 +28,7 @@ else : Default([glibmm2_config_h, libglibmm2]) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libglibmm2)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libglibmm2)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', diff --git a/libs/gtkmm2/atk/SConscript b/libs/gtkmm2/atk/SConscript index 884ac3cbb0..84a5e8251a 100644 --- a/libs/gtkmm2/atk/SConscript +++ b/libs/gtkmm2/atk/SConscript @@ -13,7 +13,7 @@ atkmm.Merge([libraries['glibmm2'], libraries['gtk2'], libraries['sigc2'] ]) libatkmm = atkmm.SharedLibrary('atkmm', atkmm_files) Default(libatkmm) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libatkmm)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libatkmm)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'atkmm.h'] + diff --git a/libs/gtkmm2/gdk/SConscript b/libs/gtkmm2/gdk/SConscript index 58f0ebb2e5..6ea5c7cfef 100644 --- a/libs/gtkmm2/gdk/SConscript +++ b/libs/gtkmm2/gdk/SConscript @@ -14,7 +14,7 @@ gdkmm2.Append(CXXFLAGS="-Ilibs/gtkmm2/gtk") libgdkmm2 = gdkmm2.SharedLibrary('gdkmm2', gdkmm2_files) Default(libgdkmm2) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libgdkmm2)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libgdkmm2)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'gdkmmconfig.h', 'gdkmm.h'] + diff --git a/libs/gtkmm2/gtk/SConscript b/libs/gtkmm2/gtk/SConscript index 90e832b010..b4cd99f023 100644 --- a/libs/gtkmm2/gtk/SConscript +++ b/libs/gtkmm2/gtk/SConscript @@ -13,7 +13,7 @@ gtkmm2.Merge([libraries['glibmm2'], libraries['gtk2'], libraries['sigc2'], libra libgtkmm2 = gtkmm2.SharedLibrary('gtkmm2', gtkmm2_files) Default(libgtkmm2) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libgtkmm2)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libgtkmm2)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'gtkmmconfig.h', 'gtkmm.h'] + diff --git a/libs/gtkmm2/pango/SConscript b/libs/gtkmm2/pango/SConscript index 0ec62a0f02..d045cf2043 100644 --- a/libs/gtkmm2/pango/SConscript +++ b/libs/gtkmm2/pango/SConscript @@ -13,7 +13,7 @@ pangomm.Merge([libraries['glibmm2'], libraries['pango'], libraries['sigc2']]) libpangomm = pangomm.SharedLibrary('pangomm', pangomm_files) Default(libpangomm) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libpangomm)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libpangomm)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'pangomm.h'] + diff --git a/libs/gtkmm2ext/SConscript b/libs/gtkmm2ext/SConscript index e654b6cb52..9c7511c85d 100644 --- a/libs/gtkmm2ext/SConscript +++ b/libs/gtkmm2ext/SConscript @@ -39,10 +39,12 @@ choice.cc click_box.cc dndtreeview.cc fastmeter.cc +focus_entry.cc gtk_ui.cc hexentry.cc idle_adjustment.cc pathlist.cc +pixfader.cc pixscroller.cc popup.cc prompter.cc @@ -67,7 +69,7 @@ Default(libgtkmm2ext) if env['NLS']: i18n (gtkmm2ext, gtkmm2ext_files, env) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libgtkmm2ext)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libgtkmm2ext)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h'] + diff --git a/libs/gtkmm2ext/barcontroller.cc b/libs/gtkmm2ext/barcontroller.cc index 734c4b77e2..f59d192ff1 100644 --- a/libs/gtkmm2ext/barcontroller.cc +++ b/libs/gtkmm2ext/barcontroller.cc @@ -70,8 +70,8 @@ BarController::BarController (Gtk::Adjustment& adj, darea.signal_expose_event().connect (mem_fun (*this, &BarController::expose)); darea.signal_motion_notify_event().connect (mem_fun (*this, &BarController::motion)); - darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press)); - darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release)); + darea.signal_button_press_event().connect (mem_fun (*this, &BarController::button_press), false); + darea.signal_button_release_event().connect (mem_fun (*this, &BarController::button_release), false); darea.signal_scroll_event().connect (mem_fun (*this, &BarController::scroll)); spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated)); @@ -82,9 +82,21 @@ BarController::BarController (Gtk::Adjustment& adj, show_all (); } +void +BarController::drop_grab () +{ + if (grabbed) { + grabbed = false; + darea.remove_modal_grab(); + StopGesture (); + } +} + bool BarController::button_press (GdkEventButton* ev) { + double fract; + if (binding_proxy.button_press_handler (ev)) { return true; } @@ -93,8 +105,7 @@ BarController::button_press (GdkEventButton* ev) case 1: if (ev->type == GDK_2BUTTON_PRESS) { switch_on_release = true; - grabbed = false; - darea.remove_modal_grab(); + drop_grab (); } else { switch_on_release = false; darea.add_modal_grab(); @@ -107,6 +118,9 @@ BarController::button_press (GdkEventButton* ev) break; case 2: + fract = ev->x / (darea.get_width() - 2.0); + adjustment.set_value (adjustment.get_lower() + fract * (adjustment.get_upper() - adjustment.get_lower())); + case 3: break; @@ -121,6 +135,8 @@ BarController::button_press (GdkEventButton* ev) bool BarController::button_release (GdkEventButton* ev) { + drop_grab (); + switch (ev->button) { case 1: if (switch_on_release) { @@ -143,23 +159,11 @@ BarController::button_release (GdkEventButton* ev) mouse_control (ev->x, ev->window, scale); } - darea.remove_modal_grab(); - grabbed = false; - StopGesture (); - grabbed = false; break; case 2: - if (true) { // XXX FIX ME - /* relax */ - } else { - double fract; - fract = ev->x / (darea.get_width() - 2.0); - adjustment.set_value (adjustment.get_lower() + - fract * (adjustment.get_upper() - adjustment.get_lower())); - } - return true; - + break; + case 3: return false; @@ -204,7 +208,7 @@ BarController::motion (GdkEventMotion* ev) double scale; if (!grabbed) { - return TRUE; + return true; } if ((ev->state & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == GDK_SHIFT_MASK) { @@ -225,7 +229,7 @@ BarController::motion (GdkEventMotion* ev) gint BarController::mouse_control (double x, GdkWindow* window, double scaling) { - double fract; + double fract = 0.0; double delta; if (window != grab_window) { @@ -259,7 +263,7 @@ BarController::expose (GdkEventExpose* event) { Glib::RefPtr<Gdk::Window> win (darea.get_window()); Widget* parent; - gint x1, x2, y1, y2; + gint x1=0, x2=0, y1=0, y2=0; gint w, h; double fract; @@ -271,6 +275,7 @@ BarController::expose (GdkEventExpose* event) switch (_style) { case Line: + h = darea.get_height(); x1 = (gint) floor (w * fract); x2 = x1; y1 = 0; @@ -281,22 +286,18 @@ BarController::expose (GdkEventExpose* event) if (parent) { win->draw_rectangle (parent->get_style()->get_fg_gc (parent->get_state()), - true, - 0, 0, darea.get_width(), darea.get_height()); + true, + 0, 0, darea.get_width(), darea.get_height()); } - } else { - win->draw_rectangle (get_style()->get_bg_gc (get_state()), - true, - 0, 0, darea.get_width(), darea.get_height()); - } - if (fract == 0.0) { - win->draw_rectangle (get_style()->get_fg_gc (get_state()), - true, x1, 1, 2, darea.get_height() - 2); } else { - win->draw_rectangle (get_style()->get_fg_gc (get_state()), - true, x1 - 1, 1, 3, darea.get_height() - 2); + + win->draw_rectangle (get_style()->get_bg_gc (get_state()), + true, + 0, 0, darea.get_width() - ((darea.get_width()+1) % 2), darea.get_height()); } + + win->draw_line (get_style()->get_fg_gc (get_state()), x1, 0, x1, h); break; case CenterOut: @@ -456,3 +457,10 @@ BarController::set_use_parent (bool yn) use_parent = yn; queue_draw (); } + +void +BarController::set_sensitive (bool yn) +{ + Frame::set_sensitive (yn); + darea.set_sensitive (yn); +} diff --git a/libs/gtkmm2ext/focus_entry.cc b/libs/gtkmm2ext/focus_entry.cc new file mode 100644 index 0000000000..dbe833d06b --- /dev/null +++ b/libs/gtkmm2ext/focus_entry.cc @@ -0,0 +1,31 @@ +#include <gtkmm2ext/focus_entry.h> + +using namespace Gtkmm2ext; + +FocusEntry::FocusEntry () +{ + next_release_selects = false; +} + +bool +FocusEntry::on_button_press_event (GdkEventButton* ev) +{ + if (!has_focus()) { + next_release_selects = true; + } + return Entry::on_button_press_event (ev); +} + +bool +FocusEntry::on_button_release_event (GdkEventButton* ev) +{ + if (next_release_selects) { + bool ret = Entry::on_button_release_event (ev); + select_region (0, -1); + next_release_selects = false; + return ret; + } + + return Entry::on_button_release_event (ev); +} + diff --git a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h index ebce4e2de9..e5b8e31b58 100644 --- a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h +++ b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h @@ -20,7 +20,8 @@ #ifndef __gtkmm2ext_bar_controller_h__ #define __gtkmm2ext_bar_controller_h__ -#include <gtkmm.h> +#include <gtkmm/frame.h> +#include <gtkmm/drawingarea.h> #include <gtkmm2ext/binding_proxy.h> namespace ARDOUR { @@ -35,10 +36,6 @@ class BarController : public Gtk::Frame BarController (Gtk::Adjustment& adj, PBD::Controllable&, sigc::slot<void,char*,unsigned int>); virtual ~BarController () {} - void set_sensitive (bool yn) { - darea.set_sensitive (yn); - } - enum Style { LeftToRight, RightToLeft, @@ -53,6 +50,8 @@ class BarController : public Gtk::Frame void set_with_text (bool yn); void set_use_parent (bool yn); + void set_sensitive (bool yn); + Gtk::SpinButton& get_spin_button() { return spinner; } sigc::signal<void> StartGesture; @@ -79,12 +78,12 @@ class BarController : public Gtk::Frame Gtk::SpinButton spinner; bool use_parent; - bool button_press (GdkEventButton *); - bool button_release (GdkEventButton *); - bool motion (GdkEventMotion *); - bool expose (GdkEventExpose *); - bool scroll (GdkEventScroll *); - bool entry_focus_out (GdkEventFocus*); + virtual bool button_press (GdkEventButton *); + virtual bool button_release (GdkEventButton *); + virtual bool motion (GdkEventMotion *); + virtual bool expose (GdkEventExpose *); + virtual bool scroll (GdkEventScroll *); + virtual bool entry_focus_out (GdkEventFocus*); gint mouse_control (double x, GdkWindow* w, double scaling); @@ -92,6 +91,7 @@ class BarController : public Gtk::Frame gint switch_to_spinner (); void entry_activated (); + void drop_grab (); }; diff --git a/libs/gtkmm2ext/gtkmm2ext/focus_entry.h b/libs/gtkmm2ext/gtkmm2ext/focus_entry.h new file mode 100644 index 0000000000..5d9d7fdac7 --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/focus_entry.h @@ -0,0 +1,22 @@ +#ifndef __gtkmm2ext_focus_entry_h__ +#define __gtkmm2ext_focus_entry_h__ + +#include <gtkmm/entry.h> + +namespace Gtkmm2ext { + +class FocusEntry : public Gtk::Entry +{ + public: + FocusEntry (); + + protected: + bool on_button_press_event (GdkEventButton*); + bool on_button_release_event (GdkEventButton*); + private: + bool next_release_selects; +}; + +} + +#endif /* __gtkmm2ext_focus_entry_h__ */ diff --git a/libs/gtkmm2ext/gtkmm2ext/pixfader.h b/libs/gtkmm2ext/gtkmm2ext/pixfader.h new file mode 100644 index 0000000000..bb4176240a --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/pixfader.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2006 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. + + $Id: fastmeter.h 570 2006-06-07 21:21:21Z sampo $ +*/ + +#ifndef __gtkmm2ext_pixfader_h__ +#define __gtkmm2ext_pixfader_h__ + +#include <cmath> + +#include <gtkmm/drawingarea.h> +#include <gtkmm/adjustment.h> +#include <gdkmm/pixbuf.h> + +namespace Gtkmm2ext { + +class PixFader : public Gtk::DrawingArea { + public: + PixFader (Glib::RefPtr<Gdk::Pixbuf> belt_image, Gtk::Adjustment& adjustment); + virtual ~PixFader (); + + protected: + Gtk::Adjustment& adjustment; + + void on_size_request (GtkRequisition*); + + bool on_expose_event (GdkEventExpose*); + bool on_button_press_event (GdkEventButton*); + bool on_button_release_event (GdkEventButton*); + bool on_motion_notify_event (GdkEventMotion*); + bool on_scroll_event (GdkEventScroll* ev); + + private: + Glib::RefPtr<Gdk::Pixbuf> pixbuf; + gint pixheight; + + GdkRectangle view; + + GdkWindow* grab_window; + double grab_y; + double grab_start; + int last_drawn; + bool dragging; + float default_value; + int unity_y; + + void adjustment_changed (); + + int display_height (); +}; + + +} /* namespace */ + + #endif /* __gtkmm2ext_pixfader_h__ */ diff --git a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h index f0f645eab7..c137dbabf5 100644 --- a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h +++ b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h @@ -22,7 +22,7 @@ #include <gtkmm.h> #include <gtkmm2ext/popup.h> -#include <gtkmm2ext/pixscroller.h> +#include <gtkmm2ext/pixfader.h> #include <gtkmm2ext/binding_proxy.h> namespace Gtkmm2ext { @@ -35,11 +35,10 @@ namespace PBD { namespace Gtkmm2ext { -class SliderController : public Gtkmm2ext::PixScroller +class SliderController : public Gtkmm2ext::PixFader { public: - SliderController (Glib::RefPtr<Gdk::Pixbuf> slider, - Glib::RefPtr<Gdk::Pixbuf> rail, + SliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment* adj, PBD::Controllable&, bool with_numeric = true); @@ -64,8 +63,7 @@ class SliderController : public Gtkmm2ext::PixScroller class VSliderController : public SliderController { public: - VSliderController (Glib::RefPtr<Gdk::Pixbuf> slider, - Glib::RefPtr<Gdk::Pixbuf> rail, + VSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment *adj, PBD::Controllable&, bool with_numeric = true); @@ -74,8 +72,7 @@ class VSliderController : public SliderController class HSliderController : public SliderController { public: - HSliderController (Glib::RefPtr<Gdk::Pixbuf> slider, - Glib::RefPtr<Gdk::Pixbuf> rail, + HSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment *adj, PBD::Controllable&, bool with_numeric = true); diff --git a/libs/gtkmm2ext/pixfader.cc b/libs/gtkmm2ext/pixfader.cc new file mode 100644 index 0000000000..f3a40ffc69 --- /dev/null +++ b/libs/gtkmm2ext/pixfader.cc @@ -0,0 +1,249 @@ +/* + Copyright (C) 2006 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. + + $Id: fastmeter.h 570 2006-06-07 21:21:21Z sampo $ +*/ + + +#include <iostream> +#include <gtkmm2ext/pixfader.h> + +using namespace Gtkmm2ext; +using namespace Gtk; +using namespace Gdk; +using namespace std; + +PixFader::PixFader (Glib::RefPtr<Pixbuf> belt, Gtk::Adjustment& adj) + : adjustment (adj), + pixbuf (belt) +{ + dragging = false; + default_value = adjustment.get_value(); + last_drawn = -1; + pixheight = pixbuf->get_height(); + + view.x = 0; + view.y = 0; + view.width = pixbuf->get_width(); + view.height = pixheight / 2; + + unity_y = (int) rint (view.height - (default_value * view.height)) - 1; + + add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); + + adjustment.signal_value_changed().connect (mem_fun (*this, &PixFader::adjustment_changed)); + adjustment.signal_changed().connect (mem_fun (*this, &PixFader::adjustment_changed)); +} + +PixFader::~PixFader () +{ +} + +bool +PixFader::on_expose_event (GdkEventExpose* ev) +{ + GdkRectangle intersection; + int dh = display_height (); + int offset_into_pixbuf = (int) floor (view.height / ((float) view.height / dh)); + Glib::RefPtr<Gdk::GC> fg_gc (get_style()->get_fg_gc(get_state())); + + if (gdk_rectangle_intersect (&view, &ev->area, &intersection)) { + get_window()->draw_pixbuf (fg_gc, pixbuf, + intersection.x, offset_into_pixbuf + intersection.y, + intersection.x, intersection.y, + intersection.width, intersection.height, + Gdk::RGB_DITHER_NONE, 0, 0); + + get_window()->draw_line (get_style()->get_bg_gc(STATE_ACTIVE), 0, 0, view.width - 1, 0); /* top */ + get_window()->draw_line (get_style()->get_bg_gc(STATE_ACTIVE), 0, 0, 0, view.height - 1); /* left */ + get_window()->draw_line (get_style()->get_bg_gc(STATE_NORMAL), view.width - 1, 0, view.width - 1, view.height - 1); /* right */ + get_window()->draw_line (get_style()->get_bg_gc(STATE_NORMAL), 0, view.height - 1, view.width - 1, view.height - 1); /* bottom */ + } + + /* always draw the line */ + + get_window()->draw_line (fg_gc, 1, unity_y, view.width - 2, unity_y); + + last_drawn = dh; + return true; +} + +void +PixFader::on_size_request (GtkRequisition* req) +{ + req->width = view.width; + req->height = view.height; +} + +bool +PixFader::on_button_press_event (GdkEventButton* ev) +{ + switch (ev->button) { + case 1: + case 2: + add_modal_grab(); + grab_y = ev->y; + grab_start = ev->y; + grab_window = ev->window; + dragging = true; + break; + default: + break; + } + + + return false; +} + +bool +PixFader::on_button_release_event (GdkEventButton* ev) +{ + double fract; + + switch (ev->button) { + case 1: + if (dragging) { + remove_modal_grab(); + dragging = false; + + if (ev->y == grab_start) { + + /* no motion - just a click */ + + if (ev->state & Gdk::SHIFT_MASK) { + adjustment.set_value (default_value); + } else if (ev->state & GDK_CONTROL_MASK) { + adjustment.set_value (adjustment.get_lower()); + } else if (ev->y < view.height - display_height()) { + /* above the current display height, remember X Window coords */ + adjustment.set_value (adjustment.get_value() + adjustment.get_step_increment()); + } else { + adjustment.set_value (adjustment.get_value() - adjustment.get_step_increment()); + } + } + + } + break; + + case 2: + if (dragging) { + remove_modal_grab(); + dragging = false; + + fract = 1.0 - (ev->y / view.height); // inverted X Window coordinates, grrr + + fract = min (1.0, fract); + fract = max (0.0, fract); + + adjustment.set_value (fract * (adjustment.get_upper() - adjustment.get_lower())); + } + break; + + default: + break; + } + + return false; +} + +bool +PixFader::on_scroll_event (GdkEventScroll* ev) +{ + double scale; + + if (ev->state & GDK_CONTROL_MASK) { + if (ev->state & GDK_MOD1_MASK) { + scale = 0.05; + } else { + scale = 0.1; + } + } else { + scale = 0.5; + } + + switch (ev->direction) { + + case GDK_SCROLL_UP: + /* wheel up */ + adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale)); + break; + case GDK_SCROLL_DOWN: + /* wheel down */ + adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale)); + break; + default: + break; + } + return false; +} + +bool +PixFader::on_motion_notify_event (GdkEventMotion* ev) +{ + if (dragging) { + double fract; + double delta; + double scale; + + if (ev->window != grab_window) { + grab_y = ev->y; + grab_window = ev->window; + return true; + } + + if (ev->state & GDK_CONTROL_MASK) { + if (ev->state & GDK_MOD1_MASK) { + scale = 0.05; + } else { + scale = 0.1; + } + } else { + scale = 1.0; + } + + delta = ev->y - grab_y; + grab_y = ev->y; + + fract = (delta / view.height); + + fract = min (1.0, fract); + fract = max (-1.0, fract); + + // X Window is top->bottom for 0..Y + + fract = -fract; + + adjustment.set_value (adjustment.get_value() + scale * fract * (adjustment.get_upper() - adjustment.get_lower())); + } + + return true; +} + +void +PixFader::adjustment_changed () +{ + if (display_height() != last_drawn) { + queue_draw (); + } +} + +int +PixFader::display_height () +{ + float fract = (adjustment.get_upper() - adjustment.get_value ()) / ((adjustment.get_upper() - adjustment.get_lower())); + return (int) floor (view.height * (1.0 - fract)); +} diff --git a/libs/gtkmm2ext/prompter.cc b/libs/gtkmm2ext/prompter.cc index c3a04f19a8..d06204d356 100644 --- a/libs/gtkmm2ext/prompter.cc +++ b/libs/gtkmm2ext/prompter.cc @@ -86,7 +86,7 @@ Prompter::get_result (string &str, bool strip) { str = entry.get_text (); if (strip) { - strip_whitespace_edges (str); + PBD::strip_whitespace_edges (str); } } diff --git a/libs/gtkmm2ext/slider_controller.cc b/libs/gtkmm2ext/slider_controller.cc index e524eba1cb..3e2b42f409 100644 --- a/libs/gtkmm2ext/slider_controller.cc +++ b/libs/gtkmm2ext/slider_controller.cc @@ -20,7 +20,7 @@ #include <string> #include <gtkmm2ext/gtk_ui.h> -#include <gtkmm2ext/pixscroller.h> +#include <gtkmm2ext/pixfader.h> #include <gtkmm2ext/slider_controller.h> #include "i18n.h" @@ -28,13 +28,12 @@ using namespace Gtkmm2ext; using namespace PBD; -SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> slide, - Glib::RefPtr<Gdk::Pixbuf> rail, +SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment *adj, Controllable& c, bool with_numeric) - : PixScroller (*adj, slide, rail), + : PixFader (image, *adj), binding_proxy (c), spin (*adj, 0, 2) { @@ -47,7 +46,7 @@ SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> slide, void SliderController::set_value (float v) { - adj.set_value (v); + adjustment.set_value (v); } bool @@ -56,16 +55,15 @@ SliderController::on_button_press_event (GdkEventButton *ev) if (binding_proxy.button_press_handler (ev)) { return true; } - return PixScroller::on_button_press_event (ev); + return PixFader::on_button_press_event (ev); } -VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> slide, - Glib::RefPtr<Gdk::Pixbuf> rail, +VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment *adj, Controllable& control, bool with_numeric) - : SliderController (slide, rail, adj, control, with_numeric) + : SliderController (image, adj, control, with_numeric) { if (with_numeric) { spin_frame.add (spin); @@ -76,13 +74,12 @@ VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> slide, } } -HSliderController::HSliderController (Glib::RefPtr<Gdk::Pixbuf> slide, - Glib::RefPtr<Gdk::Pixbuf> rail, +HSliderController::HSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Gtk::Adjustment *adj, Controllable& control, bool with_numeric) - : SliderController (slide, rail, adj, control, with_numeric) + : SliderController (image, adj, control, with_numeric) { if (with_numeric) { spin_frame.add (spin); diff --git a/libs/gtkmm2ext/tearoff.cc b/libs/gtkmm2ext/tearoff.cc index e4a9207195..92bde5541e 100644 --- a/libs/gtkmm2ext/tearoff.cc +++ b/libs/gtkmm2ext/tearoff.cc @@ -127,7 +127,7 @@ TearOff::close_click (GdkEventButton* ev) gint TearOff::window_button_press (GdkEventButton* ev) { - if (dragging) { + if (dragging || ev->button != 1) { dragging = false; own_window.remove_modal_grab(); return true; @@ -172,6 +172,12 @@ TearOff::window_motion (GdkEventMotion* ev) return true; } + if (!(ev->state & GDK_BUTTON1_MASK)) { + dragging = false; + own_window.remove_modal_grab(); + return true; + } + x_delta = ev->x_root - drag_x; y_delta = ev->y_root - drag_y; diff --git a/libs/libglademm/SConscript b/libs/libglademm/SConscript index 1ee6926580..0d22ba243f 100644 --- a/libs/libglademm/SConscript +++ b/libs/libglademm/SConscript @@ -15,7 +15,7 @@ libglade = libglademm.SharedLibrary('libglademm', libglademm_files) Default(libglade) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libglade)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libglade)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'libglademmconfig.h', 'libglademm.h'] + diff --git a/libs/libgnomecanvasmm/SConscript b/libs/libgnomecanvasmm/SConscript index d9620dc378..7cab8b707a 100644 --- a/libs/libgnomecanvasmm/SConscript +++ b/libs/libgnomecanvasmm/SConscript @@ -20,7 +20,7 @@ gnomecanvasmm.Merge([libraries['glibmm2'], libgnomecanvasmm = gnomecanvasmm.SharedLibrary('libgnomecanvasmm', gnomecanvasmm_files) Default(libgnomecanvasmm) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libgnomecanvasmm)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libgnomecanvasmm)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'libgnomecanvasmmconfig.h', 'libgnomecanvasmm.h'] + diff --git a/libs/libsndfile/SConscript b/libs/libsndfile/SConscript index f8e9fc5ecb..9e8ccc93f7 100644 --- a/libs/libsndfile/SConscript +++ b/libs/libsndfile/SConscript @@ -6,8 +6,9 @@ import glob sndfile_files = glob.glob('src/*.c') + glob.glob('src/GSM610/*.c') + glob.glob('src/G72x/*.c') -Import('env install_prefix') +Import('env install_prefix libraries') sndfile = env.Copy() +sndfile.Merge([libraries['flac'] ]) domain = 'libsndfile' @@ -31,7 +32,7 @@ sndfile_h = sndfile.Command('src/sndfile.h', ['src/sndfile.h.in'], 'cd libs/libs Default([sndfile_h,libsndfile]) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libsndfile)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libsndfile)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'NEWS', 'README', 'AUTHORS', 'ChangeLog', diff --git a/libs/libsndfile/configure b/libs/libsndfile/configure index 8aaca4e783..bfa95c3aac 100755 --- a/libs/libsndfile/configure +++ b/libs/libsndfile/configure @@ -12235,9 +12235,17 @@ rm -f conftest.$ac_objext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>conftest.er1 ac_status=$? + + cat conftest.$ac_ext > blah1.c + + echo $CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext > blah1 + grep -v '^ *+' conftest.er1 >conftest.err rm -f conftest.er1 cat conftest.err >&5 + + cat conftest.err > blah2 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } && { ac_try='test -z "$ac_c_werror_flag" diff --git a/libs/midi++2/SConscript b/libs/midi++2/SConscript index 477d49c6ca..e9fbc0abf5 100644 --- a/libs/midi++2/SConscript +++ b/libs/midi++2/SConscript @@ -57,7 +57,7 @@ libmidi2 = midi2.SharedLibrary('midi++', [ sources, sysdep_src ]) Default(libmidi2) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libmidi2)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libmidi2)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript' ] + sources + sysdep_sources + diff --git a/libs/midi++2/jack_midiport.cc b/libs/midi++2/jack_midiport.cc index 4fc24e8711..2aad0e072d 100644 --- a/libs/midi++2/jack_midiport.cc +++ b/libs/midi++2/jack_midiport.cc @@ -109,7 +109,7 @@ JACK_MidiPort::create_ports(PortRequest & req) _jack_output_port = jack_port_register(_jack_client, string(req.tagname).append("_out").c_str(), JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); - jack_midi_reset_new_port( + jack_midi_clear_buffer( jack_port_get_buffer(_jack_output_port, nframes), nframes); ret = ret && (_jack_output_port != NULL); } @@ -118,7 +118,7 @@ JACK_MidiPort::create_ports(PortRequest & req) _jack_input_port = jack_port_register(_jack_client, string(req.tagname).append("_in").c_str(), JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - jack_midi_reset_new_port( + jack_midi_clear_buffer( jack_port_get_buffer(_jack_input_port, nframes), nframes); ret = ret && (_jack_input_port != NULL); } diff --git a/libs/pbd/SConscript b/libs/pbd/SConscript index f474834fd8..afb24a4311 100644 --- a/libs/pbd/SConscript +++ b/libs/pbd/SConscript @@ -20,9 +20,11 @@ pbd.Append(POTFILE=domain + '.pot') pbd_files = Split(""" basename.cc base_ui.cc -convert.cc command.cc +convert.cc +copyfile.cc controllable.cc +enumwriter.cc dmalloc.cc error.cc id.cc @@ -66,7 +68,7 @@ mount_env.Program('mountpoint', 'mountpoint.cc') if env['NLS']: i18n (pbd, pbd_files, env) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libpbd)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libpbd)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h', 'pbd/abstract_ui.cc' ] + diff --git a/libs/pbd/basename.cc b/libs/pbd/basename.cc index a51e393b78..9beed93625 100644 --- a/libs/pbd/basename.cc +++ b/libs/pbd/basename.cc @@ -1,20 +1,13 @@ -#include <iostream> -#include <string.h> #include <pbd/basename.h> +#include <glibmm/miscutils.h> +using Glib::ustring; -// implement this using Glib::path_get_basename -std::string -PBD::basename_nosuffix (const std::string& str) +ustring +PBD::basename_nosuffix (ustring str) { - std::string::size_type slash = str.find_last_of ('/'); - std::string noslash; + ustring base = Glib::path_get_basename (str); - if (slash == std::string::npos) { - noslash = str; - } else { - noslash = str.substr (slash+1); - } + return base.substr (0, base.find_last_of ('.')); - return noslash.substr (0, noslash.find_last_of ('.')); } diff --git a/libs/pbd/controllable.cc b/libs/pbd/controllable.cc index 2264a955ae..049ad0aa21 100644 --- a/libs/pbd/controllable.cc +++ b/libs/pbd/controllable.cc @@ -18,8 +18,10 @@ Controllable::Controllable (std::string name) XMLNode& Controllable::get_state () { - XMLNode* node = new XMLNode (_name); + XMLNode* node = new XMLNode (X_("controllable")); char buf[64]; + + node->add_property (X_("name"), _name); // not reloaded from XML state, just there to look at _id.print (buf, sizeof (buf)); node->add_property (X_("id"), buf); return *node; diff --git a/libs/pbd/copyfile.cc b/libs/pbd/copyfile.cc new file mode 100644 index 0000000000..d36ecef58a --- /dev/null +++ b/libs/pbd/copyfile.cc @@ -0,0 +1,38 @@ +#include <fstream> +#include <unistd.h> + +#include <pbd/copyfile.h> +#include <pbd/error.h> +#include <pbd/compose.h> + +#include "i18n.h" + +using namespace PBD; +using namespace std; + +int +PBD::copy_file (Glib::ustring from, Glib::ustring to) +{ + ifstream in (from.c_str()); + ofstream out (to.c_str()); + + if (!in) { + error << string_compose (_("Could not open %1 for copy"), from) << endmsg; + return -1; + } + + if (!out) { + error << string_compose (_("Could not open %1 as copy"), to) << endmsg; + return -1; + } + + out << in.rdbuf(); + + if (!in || !out) { + error << string_compose (_("Could not copy existing file %1 to %2"), from, to) << endmsg; + unlink (to.c_str()); + return -1; + } + + return 0; +} diff --git a/libs/pbd/enumwriter.cc b/libs/pbd/enumwriter.cc new file mode 100644 index 0000000000..7674410ec9 --- /dev/null +++ b/libs/pbd/enumwriter.cc @@ -0,0 +1,273 @@ +/* + Copyright (C) 2006 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. + + $Id$ +*/ + +#include <ctype.h> + +#include <string.h> +#include <stdlib.h> + +#include <pbd/enumwriter.h> +#include <pbd/error.h> +#include <pbd/compose.h> + +using namespace std; +using namespace PBD; + +#include "i18n.h" + +EnumWriter* EnumWriter::_instance = 0; +map<string,string> EnumWriter::hack_table; + +static int +nocase_cmp(const string & s1, const string& s2) +{ + string::const_iterator it1 = s1.begin(); + string::const_iterator it2 = s2.begin(); + + while ((it1 != s1.end()) && (it2 != s2.end())) { + if(::toupper(*it1) != ::toupper(*it2)) {//letters differ? + // return -1 to indicate 'smaller than', 1 otherwise + return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1; + } + + ++it1; + ++it2; + } + + string::size_type size1 = s1.size(); + string::size_type size2 = s2.size(); + + //return -1,0 or 1 according to strings' lengths + + if (size1 == size2) { + return 0; + } + + return (size1 < size2) ? -1 : 1; +} + +EnumWriter::EnumWriter () +{ + if (_instance == 0) { + _instance = this; + } +} + +EnumWriter::~EnumWriter () +{ +} + +void +EnumWriter::register_distinct (string type, vector<int> v, vector<string> s) +{ + pair<string,EnumRegistration> newpair; + pair<Registry::iterator,bool> result; + + newpair.first = type; + newpair.second = EnumRegistration (v, s, false); + + result = registry.insert (newpair); + + if (!result.second) { + warning << string_compose (_("enum type \"%1\" already registered with the enum writer"), type) << endmsg; + } +} + +void +EnumWriter::register_bits (string type, vector<int> v, vector<string> s) +{ + pair<string,EnumRegistration> newpair; + pair<Registry::iterator,bool> result; + + newpair.first = type; + newpair.second = EnumRegistration (v, s, true); + + result = registry.insert (newpair); + + if (!result.second) { + warning << _("enum type \"%1\" already registered with the enum writer") << endmsg; + } +} + +string +EnumWriter::write (string type, int value) +{ + Registry::iterator x = registry.find (type); + + if (x == registry.end()) { + error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg; + throw unknown_enumeration(); + } + + if (x->second.bitwise) { + return write_bits (x->second, value); + } else { + return write_distinct (x->second, value); + } +} + +int +EnumWriter::read (string type, string value) +{ + Registry::iterator x = registry.find (type); + + if (x == registry.end()) { + error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg; + throw unknown_enumeration(); + } + + if (x->second.bitwise) { + return read_bits (x->second, value); + } else { + return read_distinct (x->second, value); + } +} + +string +EnumWriter::write_bits (EnumRegistration& er, int value) +{ + vector<int>::iterator i; + vector<string>::iterator s; + string result; + + for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) { + if (value & (*i)) { + if (!result.empty()) { + result += ','; + } + result += (*s); + } + } + + return result; +} + +string +EnumWriter::write_distinct (EnumRegistration& er, int value) +{ + vector<int>::iterator i; + vector<string>::iterator s; + + for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) { + if (value == (*i)) { + return (*s); + } + } + + return string(); +} + +int +EnumWriter::read_bits (EnumRegistration& er, string str) +{ + vector<int>::iterator i; + vector<string>::iterator s; + int result = 0; + bool found = false; + string::size_type comma; + + /* catch old-style hex numerics */ + + if (str.length() > 2 && str[0] == '0' && str[1] == 'x') { + return strtol (str.c_str(), (char **) 0, 16); + } + + /* catch old style dec numerics */ + + if (strspn (str.c_str(), "0123456789") == str.length()) { + return strtol (str.c_str(), (char **) 0, 10); + } + + do { + + comma = str.find_first_of (','); + string segment = str.substr (0, comma); + + for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) { + if (segment == *s || nocase_cmp (segment, *s) == 0) { + result |= (*i); + found = true; + } + } + + if (comma == string::npos) { + break; + } + + str = str.substr (comma+1); + + } while (true); + + if (!found) { + throw unknown_enumeration(); + } + + return result; +} + +int +EnumWriter::read_distinct (EnumRegistration& er, string str) +{ + vector<int>::iterator i; + vector<string>::iterator s; + + /* catch old-style hex numerics */ + + if (str.length() > 2 && str[0] == '0' && str[1] == 'x') { + return strtol (str.c_str(), (char **) 0, 16); + } + + /* catch old style dec numerics */ + + if (strspn (str.c_str(), "0123456789") == str.length()) { + return strtol (str.c_str(), (char **) 0, 10); + } + + for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) { + if (str == (*s) || nocase_cmp (str, *s) == 0) { + return (*i); + } + } + + /* failed to find it as-is. check to see if there a hack for the name we're looking up */ + + map<string,string>::iterator x; + + if ((x = hack_table.find (str)) != hack_table.end()) { + + cerr << "found hack for " << str << " = " << x->second << endl; + + str = x->second; + + for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) { + if (str == (*s) || nocase_cmp (str, *s) == 0) { + return (*i); + } + } + } + + throw unknown_enumeration(); +} + +void +EnumWriter::add_to_hack_table (string str, string hacked) +{ + hack_table[str] = hacked; +} diff --git a/libs/pbd/pbd/basename.h b/libs/pbd/pbd/basename.h index a7e36acff0..a622643541 100644 --- a/libs/pbd/pbd/basename.h +++ b/libs/pbd/pbd/basename.h @@ -1,13 +1,13 @@ #ifndef __stupid_basename_h__ #define __stupid_basename_h__ -#include <string> +#include <glibmm/ustring.h> namespace PBD { -extern std::string basename_nosuffix (const std::string&); +Glib::ustring basename_nosuffix (Glib::ustring); -} // namespace PBD +} #endif // __stupid_basename_h__ diff --git a/libs/pbd/pbd/copyfile.h b/libs/pbd/pbd/copyfile.h new file mode 100644 index 0000000000..8a1bf242bb --- /dev/null +++ b/libs/pbd/pbd/copyfile.h @@ -0,0 +1,6 @@ +#include <glibmm/ustring.h> + +namespace PBD { + + int copy_file (Glib::ustring from, Glib::ustring to); +} diff --git a/libs/pbd/pbd/enumwriter.h b/libs/pbd/pbd/enumwriter.h new file mode 100644 index 0000000000..f53388f3cc --- /dev/null +++ b/libs/pbd/pbd/enumwriter.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2006 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. + + $Id$ +*/ + +#include <map> +#include <string> +#include <vector> +#include <exception> + + +namespace PBD { + +class unknown_enumeration : public std::exception { + public: + virtual const char *what() const throw() { return "unknown enumerator in PBD::EnumWriter"; } +}; + +class EnumWriter { + public: + EnumWriter (); + ~EnumWriter (); + + static EnumWriter& instance() { return *_instance; } + + void register_distinct (std::string type, std::vector<int>, std::vector<std::string>); + void register_bits (std::string type, std::vector<int>, std::vector<std::string>); + + std::string write (std::string type, int value); + int read (std::string type, std::string value); + + void add_to_hack_table (std::string str, std::string hacked_str); + + private: + struct EnumRegistration { + std::vector<int> values; + std::vector<std::string> names; + bool bitwise; + + EnumRegistration() {} + EnumRegistration (std::vector<int>& v, std::vector<std::string>& s, bool b) + : values (v), names (s), bitwise (b) {} + }; + + typedef std::map<std::string, EnumRegistration> Registry; + Registry registry; + + std::string write_bits (EnumRegistration&, int value); + std::string write_distinct (EnumRegistration&, int value); + + int read_bits (EnumRegistration&, std::string value); + int read_distinct (EnumRegistration&, std::string value); + + static EnumWriter* _instance; + static std::map<std::string,std::string> hack_table; +}; + +} + +#define enum_2_string(e) (PBD::EnumWriter::instance().write (typeid(e).name(), e)) +#define string_2_enum(str,e) (PBD::EnumWriter::instance().read (typeid(e).name(), (str))) + diff --git a/libs/pbd/pbd/tokenizer.h b/libs/pbd/pbd/tokenizer.h index a976b79341..b80e3eac4a 100644 --- a/libs/pbd/pbd/tokenizer.h +++ b/libs/pbd/pbd/tokenizer.h @@ -4,18 +4,24 @@ #include <iterator> #include <string> +#include <pbd/whitespace.h> + namespace PBD { /** Tokenize string, this should work for standard - strings aswell as Glib::ustring. This is a bit of a hack, + strings as well as Glib::ustring. This is a bit of a hack, there are much better string tokenizing patterns out there. + If strip_whitespace is set to true, tokens will be checked to see + that they still have a length after stripping. If no length, they + are discarded. */ template<typename StringType, typename Iter> unsigned int tokenize(const StringType& str, const StringType& delims, - Iter it) + Iter it, + bool strip_whitespace=false) { typename StringType::size_type start_pos = 0; typename StringType::size_type end_pos = 0; @@ -28,14 +34,30 @@ tokenize(const StringType& str, if (end_pos == str.npos) { end_pos = str.length(); } - *it++ = str.substr(start_pos, end_pos - start_pos); + if (strip_whitespace) { + StringType stripped = str.substr(start_pos, end_pos - start_pos); + strip_whitespace_edges (stripped); + if (stripped.length()) { + *it++ = stripped; + } + } else { + *it++ = str.substr(start_pos, end_pos - start_pos); + } ++token_count; start_pos = str.find_first_not_of(delims, end_pos + 1); } } while (start_pos != str.npos); if (start_pos != str.npos) { - *it++ = str.substr(start_pos, str.length() - start_pos); + if (strip_whitespace) { + StringType stripped = str.substr(start_pos, str.length() - start_pos); + strip_whitespace_edges (stripped); + if (stripped.length()) { + *it++ = stripped; + } + } else { + *it++ = str.substr(start_pos, str.length() - start_pos); + } ++token_count; } diff --git a/libs/pbd/pbd/undo.h b/libs/pbd/pbd/undo.h index eb46750e4f..4dfab5178f 100644 --- a/libs/pbd/pbd/undo.h +++ b/libs/pbd/pbd/undo.h @@ -94,7 +94,7 @@ class UndoHistory : public sigc::trackable void clear_undo (); void clear_redo (); - XMLNode &get_state(); + XMLNode &get_state(uint32_t depth = 0); void save_state(); sigc::signal<void> Changed; diff --git a/libs/pbd/pbd/whitespace.h b/libs/pbd/pbd/whitespace.h index 6620a8fb50..6adb41641c 100644 --- a/libs/pbd/pbd/whitespace.h +++ b/libs/pbd/pbd/whitespace.h @@ -3,6 +3,12 @@ #include <string> +namespace PBD { + +// returns the empty string if the entire string is whitespace +// so check length after calling. extern void strip_whitespace_edges (std::string& str); +} // namespace PBD + #endif // __pbd_whitespace_h__ diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index 277b83bfce..4719d0968e 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -236,13 +236,31 @@ UndoHistory::clear () Changed (); /* EMIT SIGNAL */ } -XMLNode & UndoHistory::get_state() +XMLNode& +UndoHistory::get_state (uint32_t depth) { XMLNode *node = new XMLNode ("UndoHistory"); - list<UndoTransaction*>::iterator it; - for (it = UndoList.begin(); it != UndoList.end(); it++) { - node->add_child_nocopy((*it)->get_state()); + 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 */ + + list<UndoTransaction*> in_order; + + for (list<UndoTransaction*>::reverse_iterator it = UndoList.rbegin(); it != UndoList.rend() && depth; ++it, depth--) { + in_order.push_front (*it); + } + + for (list<UndoTransaction*>::iterator it = in_order.begin(); it != in_order.end(); it++) { + node->add_child_nocopy((*it)->get_state()); + } } return *node; diff --git a/libs/pbd/whitespace.cc b/libs/pbd/whitespace.cc index 53616133ad..a719fb169f 100644 --- a/libs/pbd/whitespace.cc +++ b/libs/pbd/whitespace.cc @@ -2,6 +2,8 @@ using namespace std; +namespace PBD { + void strip_whitespace_edges (string& str) { @@ -24,7 +26,8 @@ strip_whitespace_edges (string& str) } if (i == len) { - /* its all whitespace, not much we can do */ + /* it's all whitespace, not much we can do */ + str = ""; return; } @@ -55,3 +58,4 @@ strip_whitespace_edges (string& str) } } +} // namespace PBD diff --git a/libs/sigc++2/SConscript b/libs/sigc++2/SConscript index 65833dfb53..b29aefb0cd 100644 --- a/libs/sigc++2/SConscript +++ b/libs/sigc++2/SConscript @@ -20,7 +20,7 @@ else : Default([sigc2_config_h,libsigc2]) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libsigc2)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libsigc2)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'NEWS', 'README', 'AUTHORS', 'ChangeLog', diff --git a/libs/soundtouch/SConscript b/libs/soundtouch/SConscript index d37fd1f99b..9ddee87b0c 100644 --- a/libs/soundtouch/SConscript +++ b/libs/soundtouch/SConscript @@ -23,7 +23,7 @@ libst = st.SharedLibrary('soundtouch', soundtouch_files) Default(libst) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libst)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libst)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript'] + soundtouch_files + glob.glob('*.h'))) diff --git a/libs/soundtouch/STTypes.h b/libs/soundtouch/STTypes.h index c404675ecd..e1ea90d428 100644 --- a/libs/soundtouch/STTypes.h +++ b/libs/soundtouch/STTypes.h @@ -47,8 +47,12 @@ typedef unsigned long ulong; typedef unsigned int BOOL; +#ifndef FALSE #define FALSE 0 +#endif +#ifndef TRUE #define TRUE 1 +#endif #endif // _WINDEF_ diff --git a/libs/surfaces/control_protocol/SConscript b/libs/surfaces/control_protocol/SConscript index 88aeeda376..026698500a 100644 --- a/libs/surfaces/control_protocol/SConscript +++ b/libs/surfaces/control_protocol/SConscript @@ -50,7 +50,7 @@ Default(libardour_cp) if env['NLS']: i18n (cp, cp_files, env) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2'), libardour_cp)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libardour_cp)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript' ] + diff --git a/libs/surfaces/control_protocol/control_protocol/smpte.h b/libs/surfaces/control_protocol/control_protocol/smpte.h index 09c1c9616a..b25a268aac 100644 --- a/libs/surfaces/control_protocol/control_protocol/smpte.h +++ b/libs/surfaces/control_protocol/control_protocol/smpte.h @@ -31,28 +31,18 @@ enum Wrap { HOURS }; -/** SMPTE frame rate (in frames per second). - * - * This should be eliminated in favour of a float to support arbitrary rates. - */ -enum FPS { - MTC_24_FPS = 0, - MTC_25_FPS = 1, - MTC_30_FPS_DROP = 2, - MTC_30_FPS = 3 -}; - struct Time { bool negative; uint32_t hours; uint32_t minutes; uint32_t seconds; - uint32_t frames; ///< SMPTE frames (not audio samples) - uint32_t subframes; ///< Typically unused - FPS rate; ///< Frame rate of this Time - static FPS default_rate; ///< Rate to use for default constructor + uint32_t frames; ///< SMPTE frames (not audio samples) + uint32_t subframes; ///< Typically unused + float rate; ///< Frame rate of this Time + static float default_rate;///< Rate to use for default constructor + bool drop; ///< Whether this Time uses dropframe SMPTE - Time(FPS a_rate = default_rate) { + Time(float a_rate = default_rate) { negative = false; hours = 0; minutes = 0; diff --git a/libs/surfaces/control_protocol/smpte.cc b/libs/surfaces/control_protocol/smpte.cc index 55d0660c59..5df159a52b 100644 --- a/libs/surfaces/control_protocol/smpte.cc +++ b/libs/surfaces/control_protocol/smpte.cc @@ -20,10 +20,11 @@ #define SMPTE_IS_ZERO( sm ) (!(sm).frames && !(sm).seconds && !(sm).minutes && !(sm).hours && !(sm.subframes)) #include <control_protocol/smpte.h> +#include <ardour/configuration.h> namespace SMPTE { -FPS Time::default_rate = MTC_30_FPS; +float Time::default_rate = 30.0; /** Increment @a smpte by exactly one frame (keep subframes value). @@ -38,7 +39,7 @@ increment( Time& smpte ) if (smpte.negative) { if (SMPTE_IS_AROUND_ZERO(smpte) && smpte.subframes) { // We have a zero transition involving only subframes - smpte.subframes = 80 - smpte.subframes; + smpte.subframes = ARDOUR::Config->get_subframes_per_frame() - smpte.subframes; smpte.negative = false; return SECONDS; } @@ -50,34 +51,42 @@ increment( Time& smpte ) } return wrap; } - - switch (smpte.rate) { - case MTC_24_FPS: + + switch ((int)ceil(smpte.rate)) { + case 24: if (smpte.frames == 23) { smpte.frames = 0; wrap = SECONDS; } break; - case MTC_25_FPS: + case 25: if (smpte.frames == 24) { smpte.frames = 0; wrap = SECONDS; } break; - case MTC_30_FPS_DROP: - if (smpte.frames == 29) { - if ( ((smpte.minutes + 1) % 10) && (smpte.seconds == 59) ) { - smpte.frames = 2; - } - else { - smpte.frames = 0; - } - wrap = SECONDS; + case 30: + if (smpte.drop) { + if (smpte.frames == 29) { + if ( ((smpte.minutes + 1) % 10) && (smpte.seconds == 59) ) { + smpte.frames = 2; + } + else { + smpte.frames = 0; + } + wrap = SECONDS; + } + } else { + + if (smpte.frames == 29) { + smpte.frames = 0; + wrap = SECONDS; + } } break; - case MTC_30_FPS: - if (smpte.frames == 29) { - smpte.frames = 0; + case 60: + if (smpte.frames == 59) { + smpte.frames = 0; wrap = SECONDS; } break; @@ -121,38 +130,46 @@ decrement( Time& smpte ) return wrap; } else if (SMPTE_IS_AROUND_ZERO(smpte) && smpte.subframes) { // We have a zero transition involving only subframes - smpte.subframes = 80 - smpte.subframes; + smpte.subframes = ARDOUR::Config->get_subframes_per_frame() - smpte.subframes; smpte.negative = true; return SECONDS; } - switch (smpte.rate) { - case MTC_24_FPS: + switch ((int)ceil(smpte.rate)) { + case 24: if (smpte.frames == 0) { smpte.frames = 23; wrap = SECONDS; } break; - case MTC_25_FPS: + case 25: if (smpte.frames == 0) { smpte.frames = 24; wrap = SECONDS; } break; - case MTC_30_FPS_DROP: - if ((smpte.minutes % 10) && (smpte.seconds == 0)) { - if (smpte.frames <= 2) { - smpte.frames = 29; + case 30: + if (smpte.drop) { + if ((smpte.minutes % 10) && (smpte.seconds == 0)) { + if (smpte.frames <= 2) { + smpte.frames = 29; + wrap = SECONDS; + } + } else if (smpte.frames == 0) { + smpte.frames = 29; + wrap = SECONDS; + } + + } else { + if (smpte.frames == 0) { + smpte.frames = 29; wrap = SECONDS; } - } else if (smpte.frames == 0) { - smpte.frames = 29; - wrap = SECONDS; } break; - case MTC_30_FPS: - if (smpte.frames == 0) { - smpte.frames = 29; + case 60: + if (smpte.frames == 0) { + smpte.frames = 59; wrap = SECONDS; } break; @@ -212,7 +229,7 @@ increment_subframes( Time& smpte ) } smpte.subframes++; - if (smpte.subframes >= 80) { + if (smpte.subframes >= ARDOUR::Config->get_subframes_per_frame()) { smpte.subframes = 0; increment( smpte ); return FRAMES; @@ -274,17 +291,19 @@ increment_seconds( Time& smpte ) } } else { // Go to highest possible frame in this second - switch (smpte.rate) { - case MTC_24_FPS: + switch ((int)ceil(smpte.rate)) { + case 24: smpte.frames = 23; break; - case MTC_25_FPS: + case 25: smpte.frames = 24; break; - case MTC_30_FPS_DROP: - case MTC_30_FPS: + case 30: smpte.frames = 29; break; + case 60: + smpte.frames = 59; + break; } // Increment by one frame @@ -304,17 +323,20 @@ seconds_floor( Time& smpte ) frames_floor( smpte ); // Go to lowest possible frame in this second - switch (smpte.rate) { - case MTC_24_FPS: - case MTC_25_FPS: - case MTC_30_FPS: - smpte.frames = 0; - break; - case MTC_30_FPS_DROP: - if ((smpte.minutes % 10) && (smpte.seconds == 0)) { - smpte.frames = 2; + switch ((int)ceil(smpte.rate)) { + case 24: + case 25: + case 30: + case 60: + if (!(smpte.drop)) { + smpte.frames = 0; } else { - smpte.frames = 0; + + if ((smpte.minutes % 10) && (smpte.seconds == 0)) { + smpte.frames = 2; + } else { + smpte.frames = 0; + } } break; } diff --git a/libs/surfaces/generic_midi/SConscript b/libs/surfaces/generic_midi/SConscript index f9c2de08f8..6c76e05464 100644 --- a/libs/surfaces/generic_midi/SConscript +++ b/libs/surfaces/generic_midi/SConscript @@ -34,6 +34,7 @@ genericmidi.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") genericmidi.Merge ([ libraries['ardour'], libraries['ardour_cp'], + libraries['sndfile-ardour'], libraries['midi++2'], libraries['pbd'], libraries['sigc2'], @@ -50,7 +51,7 @@ Default(libardour_genericmidi) if env['NLS']: i18n (genericmidi, genericmidi_files, env) -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_genericmidi)) +env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'surfaces'), libardour_genericmidi)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript' ] + diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 03dbfb353c..0256d5c359 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -234,7 +234,7 @@ GenericMidiControlProtocol::set_state (const XMLNode& node) controllables.clear (); - nlist = node.children(); + nlist = node.children(); // "controls" if (nlist.empty()) { return 0; diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index 6dc9bde8ad..75b5699f18 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -32,8 +32,6 @@ using namespace MIDI; using namespace PBD; using namespace ARDOUR; -bool MIDIControllable::_send_feedback = false; - MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate) : controllable (c), _port (p), bistate (is_bistate) { @@ -288,7 +286,7 @@ MIDIControllable::send_feedback () { byte msg[3]; - if (setting || !_send_feedback || control_type == none) { + if (setting || !feedback || control_type == none) { return; } @@ -302,7 +300,7 @@ MIDIControllable::send_feedback () MIDI::byte* MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool force) { - if (control_type != none &&_send_feedback && bufsize > 2) { + if (control_type != none && feedback && bufsize > 2) { MIDI::byte gm = (MIDI::byte) (controllable.get_value() * 127.0); @@ -345,6 +343,12 @@ MIDIControllable::set_state (const XMLNode& node) return -1; } + if ((prop = node.property ("feedback")) != 0) { + feedback = (prop->value() == "yes"); + } else { + feedback = true; // default + } + bind_midi (control_channel, control_type, control_additional); return 0; @@ -362,6 +366,7 @@ MIDIControllable::get_state () node.add_property ("channel", buf); snprintf (buf, sizeof(buf), "0x%x", (int) control_additional); node.add_property ("additional", buf); + node.add_property ("feedback", (feedback ? "yes" : "no")); return node; } diff --git a/libs/surfaces/generic_midi/midicontrollable.h b/libs/surfaces/generic_midi/midicontrollable.h index ab15f9f4ab..8b63172916 100644 --- a/libs/surfaces/generic_midi/midicontrollable.h +++ b/libs/surfaces/generic_midi/midicontrollable.h @@ -80,8 +80,6 @@ class MIDIControllable : public Stateful std::string _control_description; bool feedback; - static bool _send_feedback; - void midi_receiver (MIDI::Parser &p, MIDI::byte *, size_t); void midi_sense_note (MIDI::Parser &, MIDI::EventTwoBytes *, bool is_on); void midi_sense_note_on (MIDI::Parser &p, MIDI::EventTwoBytes *tb); diff --git a/libs/surfaces/tranzport/SConscript b/libs/surfaces/tranzport/SConscript index 3f9cc5cc15..5d390f3e2f 100644 --- a/libs/surfaces/tranzport/SConscript +++ b/libs/surfaces/tranzport/SConscript @@ -44,12 +44,11 @@ tranzport.Merge ([ libardour_tranzport = tranzport.SharedLibrary('ardour_tranzport', tranzport_files) -Default(libardour_tranzport) - -if env['NLS']: - i18n (tranzport, tranzport_files, env) - -env.Alias('install', env.Install(os.path.join(install_prefix, 'lib/ardour2/surfaces'), libardour_tranzport)) +if tranzport['TRANZPORT']: + Default(libardour_tranzport) + if env['NLS']: + i18n (tranzport, tranzport_files, env) + env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'surfaces'), libardour_tranzport)) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript' ] + diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.cc b/libs/surfaces/tranzport/tranzport_control_protocol.cc index ea85a32a77..bbb78d31d1 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.cc +++ b/libs/surfaces/tranzport/tranzport_control_protocol.cc @@ -18,6 +18,31 @@ $Id$ */ +/* Design notes: The tranzport is a unique device, basically a + 20 lcd gui with 22 shift keys and 8 blinking lights. + + As such it has several unique constraints. The device exerts flow control + by having a usb write fail. It is pointless to retry madly at that point, + the device is busy, and it's not going to become unbusy very quickly. + + So writes need to be either "mandatory" or "unreliable", and therein + lies the rub, as the kernel can also drop writes, and missing an + interrupt in userspace is also generally bad. + + It will be good one day, to break the gui, keyboard, and blinking light + components into separate parts, but for now, this remains monolithic. + + A more complex surface might have hundreds of lights and several displays. + + mike.taht@gmail.com + */ + +#define DEFAULT_USB_TIMEOUT 10 +#define MAX_RETRY 1 +#define MAX_TRANZPORT_INFLIGHT 4 +#define DEBUG_TRANZPORT 0 +#define HAVE_TRANZPORT_KERNEL_DRIVER 0 + #include <iostream> #include <algorithm> #include <cmath> @@ -33,6 +58,7 @@ #include <ardour/route.h> #include <ardour/audio_track.h> #include <ardour/session.h> +#include <ardour/tempo.h> #include <ardour/location.h> #include <ardour/dB.h> @@ -51,6 +77,12 @@ BaseUI::RequestType LEDChange = BaseUI::new_request_type (); BaseUI::RequestType Print = BaseUI::new_request_type (); BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type (); +/* Base Tranzport cmd strings */ + +static const uint8_t cmd_light_on[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 }; +static const uint8_t cmd_light_off[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; +static const uint8_t cmd_write_screen[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 }; + static inline double gain_to_slider_position (ARDOUR::gain_t g) { @@ -74,8 +106,7 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s) /* tranzport controls one track at a time */ set_route_table_size (1); - - timeout = 60000; + timeout = 6000; // what is this for? buttonmask = 0; _datawheel = 0; _device_status = STATUS_OFFLINE; @@ -84,51 +115,212 @@ TranzportControlProtocol::TranzportControlProtocol (Session& s) last_where = max_frames; wheel_mode = WheelTimeline; wheel_shift_mode = WheelShiftGain; + wheel_increment = WheelIncrScreen; + bling_mode = BlingOff; timerclear (&last_wheel_motion); last_wheel_dir = 1; last_track_gain = FLT_MAX; display_mode = DisplayNormal; gain_fraction = 0.0; + invalidate(); + screen_init(); + lights_init(); + print(0,0,"!!Welcome to Ardour!!"); + print(1,0,"!Peace through Music!"); +} - memset (current_screen, 0, sizeof (current_screen)); - memset (pending_screen, 0, sizeof (pending_screen)); +void TranzportControlProtocol::light_validate (LightID light) +{ + lights_invalid[light] = 0; +} + +void TranzportControlProtocol::light_invalidate (LightID light) +{ + lights_invalid[light] = 1; +} + +void TranzportControlProtocol::lights_validate () +{ + memset (lights_invalid, 0, sizeof (lights_invalid)); +} + +void TranzportControlProtocol::lights_invalidate () +{ + memset (lights_invalid, 1, sizeof (lights_invalid)); +} + +void TranzportControlProtocol::lights_init() +{ + for (uint32_t i = 0; i < sizeof(lights_current)/sizeof(lights_current[0]); i++) { + lights_invalid[i] = lights_current[i] = + lights_pending[i] = lights_flash[i] = false; + } +} + + + +int +TranzportControlProtocol::lights_flush () +{ + if ( _device_status == STATUS_OFFLINE) { return (0); } + + // Figure out iterators one day soon + // for (LightID i = i.start(), i = i.end(); i++) { + // if (lights_pending[i] != lights_current[i] || lights_invalid[i]) { + // if (light_set(i, lights_pending[i])) { + // return i-1; + // } + // } + //} + if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) { + if (light_set(LightRecord,lights_pending[LightRecord])) { + return 1; + } + } + if ((lights_pending[LightTrackrec] != lights_current[LightTrackrec]) || lights_invalid[LightTrackrec]) { + if (light_set(LightTrackrec,lights_pending[LightTrackrec])) { + return 1; + } + } + + if ((lights_pending[LightTrackmute] != lights_current[LightTrackmute]) || lights_invalid[LightTrackmute]) { + if (light_set(LightTrackmute,lights_pending[LightTrackmute])) { + return 1; + } + } - for (uint32_t i = 0; i < sizeof(lights)/sizeof(lights[0]); ++i) { - lights[i] = false; + if ((lights_pending[LightTracksolo] != lights_current[LightTracksolo]) || lights_invalid[LightTracksolo]) { + if (light_set(LightTracksolo,lights_pending[LightTracksolo])) { + return 1; + } + } + if ((lights_pending[LightAnysolo] != lights_current[LightAnysolo]) || lights_invalid[LightAnysolo]) { + if (light_set(LightAnysolo,lights_pending[LightAnysolo])) { + return 1; + } + } + if ((lights_pending[LightLoop] != lights_current[LightLoop]) || lights_invalid[LightLoop]) { + if (light_set(LightLoop,lights_pending[LightLoop])) { + return 1; + } } + if ((lights_pending[LightPunch] != lights_current[LightPunch]) || lights_invalid[LightPunch]) { + if (light_set(LightPunch,lights_pending[LightPunch])) { + return 1; + } + } + + return 0; +} - for (uint32_t i = 0; i < sizeof(pending_lights)/sizeof(pending_lights[0]); ++i) { - pending_lights[i] = false; +// Screen specific commands + +void +TranzportControlProtocol::screen_clear () +{ + const char *blank = " "; + print(0,0,blank); + print(1,0,blank); +} + +void TranzportControlProtocol::screen_invalidate () +{ + for(int row = 0; row < 2; row++) { + for(int col = 0; col < 20; col++) { + screen_invalid[row][col] = true; + screen_current[row][col] = 0x7f; + screen_pending[row][col] = ' '; + // screen_flash[row][col] = ' '; + } } + // memset (&screen_invalid, 1, sizeof(screen_invalid)); + // memset (&screen_current, 0x7F, sizeof (screen_current)); // fill cache with a character we otherwise never use } -TranzportControlProtocol::~TranzportControlProtocol () +void TranzportControlProtocol::screen_validate () { - set_active (false); } -bool -TranzportControlProtocol::probe () +void TranzportControlProtocol::screen_init () { - struct usb_bus *bus; - struct usb_device *dev; + screen_invalidate(); +} - usb_init(); - usb_find_busses(); - usb_find_devices(); +int +TranzportControlProtocol::screen_flush () +{ + int cell = 0, row, col_base, col, pending = 0; + if ( _device_status == STATUS_OFFLINE) { return (-1); } + + for (row = 0; row < 2 && pending == 0; row++) { + for (col_base = 0, col = 0; col < 20 && pending == 0; ) { + if ((screen_pending[row][col] != screen_current[row][col]) + || screen_invalid[row][col]) { + + /* something in this cell is different, so dump the cell to the device. */ + + uint8_t cmd[8]; + cmd[0] = 0x00; + cmd[1] = 0x01; + cmd[2] = cell; + cmd[3] = screen_pending[row][col_base]; + cmd[4] = screen_pending[row][col_base+1]; + cmd[5] = screen_pending[row][col_base+2]; + cmd[6] = screen_pending[row][col_base+3]; + cmd[7] = 0x00; - for (bus = usb_busses; bus; bus = bus->next) { + if(write(cmd) != 0) { + /* try to update this cell on the next go-round */ +#if DEBUG_TRANZPORT > 4 + printf("usb screen update failed for some reason... why? \ncmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n", + cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]); +#endif + pending += 1; + // Shouldn't need to do this + // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] = + // screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = true; - for(dev = bus->devices; dev; dev = dev->next) { - if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) { - return true; + } else { + /* successful write: copy to current cached display */ + screen_invalid[row][col_base] = screen_invalid[row][col_base+1] = + screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = false; + memcpy (&screen_current[row][col_base], &screen_pending[row][col_base], 4); + } + + /* skip the rest of the 4 character cell since we wrote+copied it already */ + + col_base += 4; + col = col_base; + cell++; + + } else { + + col++; + + if (col && col % 4 == 0) { + cell++; + col_base += 4; + } } } } + return pending; +} - return false; + +// Tranzport specific + +void TranzportControlProtocol::invalidate() +{ + lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not } +TranzportControlProtocol::~TranzportControlProtocol () +{ + set_active (false); +} + + int TranzportControlProtocol::set_active (bool yn) { @@ -139,7 +331,7 @@ TranzportControlProtocol::set_active (bool yn) if (open ()) { return -1; } - + if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) { _active = true; } else { @@ -148,11 +340,12 @@ TranzportControlProtocol::set_active (bool yn) } else { cerr << "Begin tranzport shutdown\n"; + screen_clear (); + lcd_damage(); + lights_off (); + for(int x = 0; x < 10 && flush(); x++) { usleep(1000); } pthread_cancel_one (thread); - cerr << "Thread dead\n"; - // lcd_clear (); - // lights_off (); - // cerr << "dev reset\n"; + cerr << "Tranzport Thread dead\n"; close (); _active = false; cerr << "End tranzport shutdown\n"; @@ -167,8 +360,8 @@ TranzportControlProtocol::show_track_gain () { if (route_table[0]) { gain_t g = route_get_gain (0); - if (g != last_track_gain) { - char buf[16]; + if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) { + char buf[16]; snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0))); print (0, 9, buf); last_track_gain = g; @@ -191,20 +384,66 @@ void TranzportControlProtocol::next_display_mode () { switch (display_mode) { - case DisplayNormal: - display_mode = DisplayBigMeter; - break; - case DisplayBigMeter: - display_mode = DisplayNormal; - break; + case DisplayNormal: + enter_big_meter_mode(); + break; + + case DisplayBigMeter: + enter_normal_display_mode(); + break; + + case DisplayRecording: + enter_normal_display_mode(); + break; + + case DisplayRecordingMeter: + enter_big_meter_mode(); + break; + + case DisplayConfig: + case DisplayBling: + case DisplayBlingMeter: + enter_normal_display_mode(); + break; } } +// FIXME, these 3 aren't done yet + +void +TranzportControlProtocol::enter_recording_mode () +{ + lcd_damage(); // excessive + screen_clear (); + lights_off (); + display_mode = DisplayRecording; +} + +void +TranzportControlProtocol::enter_bling_mode () +{ + lcd_damage(); + screen_clear (); + lights_off (); + display_mode = DisplayBling; +} + +void +TranzportControlProtocol::enter_config_mode () +{ + lcd_damage(); + screen_clear (); + lights_off (); + display_mode = DisplayConfig; +} + + void TranzportControlProtocol::enter_big_meter_mode () { - lcd_clear (); + screen_clear (); + lcd_damage(); lights_off (); last_meter_fill = 0; display_mode = DisplayBigMeter; @@ -213,16 +452,11 @@ TranzportControlProtocol::enter_big_meter_mode () void TranzportControlProtocol::enter_normal_display_mode () { - last_where += 1; /* force time redisplay */ - last_track_gain = FLT_MAX; /* force gain redisplay */ - - lcd_clear (); + screen_clear (); + lcd_damage(); lights_off (); - show_current_track (); - show_wheel_mode (); - show_wheel_mode (); - show_transport_time (); display_mode = DisplayNormal; + // normal_update(); } @@ -230,10 +464,11 @@ float log_meter (float db) { float def = 0.0f; /* Meter deflection %age */ - - if (db < -70.0f) { - def = 0.0f; - } else if (db < -60.0f) { + + if (db < -70.0f) return 0.0f; + if (db > 6.0f) return 1.0f; + + if (db < -60.0f) { def = (db + 70.0f) * 0.25f; } else if (db < -50.0f) { def = (db + 60.0f) * 0.5f + 2.5f; @@ -245,32 +480,36 @@ log_meter (float db) def = (db + 30.0f) * 2.0f + 30.0f; } else if (db < 6.0f) { def = (db + 20.0f) * 2.5f + 50.0f; - } else { - def = 115.0f; } - + /* 115 is the deflection %age that would be when db=6.0. this is an arbitrary endpoint for our scaling. - */ - + */ + return def/115.0f; } void TranzportControlProtocol::show_meter () { + // you only seem to get a route_table[0] on moving forward - bug elsewhere if (route_table[0] == 0) { + // Principle of least surprise + print (0, 0, "No audio to meter!!!"); + print (1, 0, "Select another track"); return; } float level = route_get_peak_input_power (0, 0); float fraction = log_meter (level); + /* Someday add a peak bar*/ + /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":". the screen is 20 chars wide, so we can display 40 different levels. compute the level, then figure out how many "::" to fill. if the answer is odd, make the last one a ":" - */ + */ uint32_t fill = (uint32_t) floor (fraction * 40); char buf[21]; @@ -285,7 +524,7 @@ TranzportControlProtocol::show_meter () bool add_single_level = (fill % 2 != 0); fill /= 2; - + if (fraction > 0.98) { light_on (LightAnysolo); } @@ -310,7 +549,7 @@ TranzportControlProtocol::show_meter () } /* print() requires this */ - + buf[21] = '\0'; print (0, 0, buf); @@ -318,17 +557,56 @@ TranzportControlProtocol::show_meter () } void +TranzportControlProtocol::show_bbt (nframes_t where) +{ + if ((where != last_where) || lcd_isdamaged(1,9,8)) { + char buf[16]; + BBT_Time bbt; + session->tempo_map().bbt_time (where, bbt); + sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks); + last_bars = bbt.bars; + last_beats = bbt.beats; + last_ticks = bbt.ticks; + last_where = where; + + if(last_ticks < 1960) { print (1, 9, buf); } // save a write so we can do leds + + // if displaymode is recordmode show beats but not yet + lights_pending[LightRecord] = false; + lights_pending[LightAnysolo] = false; + switch(last_beats) { + case 1: if(last_ticks < 500 || last_ticks > 1960) lights_pending[LightRecord] = true; break; + default: if(last_ticks < 250) lights_pending[LightAnysolo] = true; + } + + // update lights for tempo one day + // if (bbt_upper_info_label) { + // TempoMap::Metric m (session->tempo_map().metric_at (when)); + // sprintf (buf, "%-5.2f", m.tempo().beats_per_minute()); + // bbt_lower_info_label->set_text (buf); + // sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor()); + // bbt_upper_info_label->set_text (buf); + } + } + + +void TranzportControlProtocol::show_transport_time () { nframes_t where = session->transport_frame(); - - if (where != last_where) { + show_bbt(where); +} + +void +TranzportControlProtocol::show_smpte (nframes_t where) +{ + if ((where != last_where) || lcd_isdamaged(1,9,10)) { char buf[5]; SMPTE::Time smpte; session->smpte_time (where, smpte); - + if (smpte.negative) { sprintf (buf, "-%02" PRIu32 ":", smpte.hours); } else { @@ -343,7 +621,7 @@ TranzportControlProtocol::show_transport_time () print (1, 15, buf); sprintf (buf, "%02" PRIu32, smpte.frames); - print (1, 18, buf); + print_noretry (1, 18, buf); last_where = where; } @@ -355,6 +633,33 @@ TranzportControlProtocol::_monitor_work (void* arg) return static_cast<TranzportControlProtocol*>(arg)->monitor_work (); } +// I note that these usb specific open, close, probe, read routines are basically +// pure boilerplate and could easily be abstracted elsewhere + +#if !HAVE_TRANZPORT_KERNEL_DRIVER + +bool +TranzportControlProtocol::probe () +{ + struct usb_bus *bus; + struct usb_device *dev; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + for (bus = usb_busses; bus; bus = bus->next) { + + for(dev = bus->devices; dev; dev = dev->next) { + if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) { + return true; + } + } + } + + return false; +} + int TranzportControlProtocol::open () { @@ -424,393 +729,469 @@ TranzportControlProtocol::close () return ret; } + +int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override) +{ + int val; + // Get smarter about handling usb errors soon. Like disconnect + // pthread_testcancel(); + val = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, 10); + // pthread_testcancel(); + return val; +} + int -TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override) +TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override) { int val; - + if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); } val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout); - if (val < 0) + if (val < 0) { +#if DEBUG_TRANZPORT + printf("usb_interrupt_write failed: %d\n", val); +#endif return val; - if (val != 8) + } + + if (val != 8) { +#if DEBUG_TRANZPORT + printf("usb_interrupt_write failed: %d\n", val); +#endif return -1; + } + ++inflight; + return 0; } -void -TranzportControlProtocol::lcd_clear () +int +TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override) { - /* special case this for speed and atomicity */ - - uint8_t cmd[8]; +#if MAX_RETRY > 1 + int val; + int retry = 0; + if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); } - cmd[0] = 0x00; - cmd[1] = 0x01; - cmd[3] = ' '; - cmd[4] = ' '; - cmd[5] = ' '; - cmd[6] = ' '; - cmd[7] = 0x00; - - for (uint8_t i = 0; i < 10; ++i) { - cmd[2] = i; - usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, 1000); + while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) { + printf("usb_interrupt_write failed, retrying: %d\n", val); } - - memset (current_screen, ' ', sizeof (current_screen)); - memset (pending_screen, ' ', sizeof (pending_screen)); -} -void -TranzportControlProtocol::lights_off () + if (retry == MAX_RETRY) { + printf("Too many retries on a tranzport write, aborting\n"); + } + + if (val < 0) { + printf("usb_interrupt_write failed: %d\n", val); + return val; + } + if (val != 8) { + printf("usb_interrupt_write failed: %d\n", val); + return -1; + } + ++inflight; + return 0; +#else + return (write_noretry(cmd,timeout_override)); +#endif + +} + +#else +#error Kernel API not defined yet for Tranzport +// Something like open(/dev/surface/tranzport/event) for reading and raw for writing) +#endif + +// We have a state "Unknown" - STOP USING SPACES FOR IT - switching to arrow character +// We have another state - no_retry. Misleading, as we still retry on the next pass +// I think it's pointless to keep no_retry and instead we should throttle writes +// We have an "displayed" screen +// We always draw into the pending screen, which could be any of several screens +// We have an active screen +// Print arg - we have +// setactive +// so someday I think we need a screen object. + +/* +screen_flash.clear(); +screen_flash.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere +screen_flash.print(1,0,"Nextup:"); + +if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000); + +screen::setactive(screen_name,duration); // duration in ms +screen::getactive(); +*/ + + +int +TranzportControlProtocol::flush () { - uint8_t cmd[8]; + int pending = 0; + if(!(pending = lights_flush())) { + pending = screen_flush(); + } + return pending; +} - cmd[0] = 0x00; - cmd[1] = 0x00; - cmd[3] = 0x00; - cmd[4] = 0x00; - cmd[5] = 0x00; - cmd[6] = 0x00; - cmd[7] = 0x00; +// doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon - cmd[2] = LightRecord; - if (write (cmd, 1000) == 0) { - lights[LightRecord] = false; - } - cmd[2] = LightTrackrec; - if (write (cmd, 1000) == 0) { - lights[LightTrackrec] = false; - } - cmd[2] = LightTrackmute; - if (write (cmd, 1000) == 0) { - lights[LightTrackmute] = false; - } - cmd[2] = LightTracksolo; - if (write (cmd, 1000) == 0) { - lights[LightTracksolo] = false; - } - cmd[2] = LightAnysolo; - if (write (cmd, 1000) == 0) { - lights[LightAnysolo] = false; +bool TranzportControlProtocol::lcd_damage() +{ + screen_invalidate(); + return true; +} + +bool TranzportControlProtocol::lcd_damage (int row, int col, int length) +{ + bool result = false; + int endcol = col+length-1; + if((endcol > 19)) { endcol = 19; } + if((row >= 0 && row < 2) && (col >=0 && col < 20)) { + for(int c = col; c < endcol; c++) { + screen_invalid[row][c] = true; + } + result = true; } - cmd[2] = LightLoop; - if (write (cmd, 1000) == 0) { - lights[LightLoop] = false; + return result; +} + +// Gotta switch to bitfields, this is collossally dumb +// Still working on the layering, arguably screen_invalid should be lcd_invalid + +bool TranzportControlProtocol::lcd_isdamaged () +{ + for(int r = 0; r < 2; r++) { + for(int c = 0; c < 20; c++) { + if(screen_invalid[r][c]) { +#if DEBUG_TRANZPORT > 5 + printf("row: %d,col: %d is damaged, should redraw it\n", r,c); +#endif + return true; + } + } } - cmd[2] = LightPunch; - if (write (cmd, 1000) == 0) { - lights[LightPunch] = false; + return false; +} + +bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length) +{ + bool result = 0; + int endcol = col+length; + if((endcol > 19)) { endcol = 19; } + if((row >= 0 && row < 2) && (col >=0 && col < 20)) { + for(int c = col; c < endcol; c++) { + if(screen_invalid[row][c]) { +#if DEBUG_TRANZPORT > 5 + printf("row: %d,col: %d is damaged, should redraw it\n", row,c); +#endif + return true; + } + } } + return result; +} + +// lcd_clear would be a separate function for a smart display +// here it does nothing, but for the sake of completeness it should +// probably write the lcd, and while I'm on the topic it should probably +// take a row, col, length argument.... + +void +TranzportControlProtocol::lcd_clear () +{ + } +// These lcd commands are not universally used yet and may drop out of the api + int -TranzportControlProtocol::light_on (LightID light) +TranzportControlProtocol::lcd_flush () { - uint8_t cmd[8]; + return 0; +} - if (!lights[light]) { +int +TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override) +{ + return write(cmd,timeout_override); +} - cmd[0] = 0x00; - cmd[1] = 0x00; - cmd[2] = light; - cmd[3] = 0x01; - cmd[4] = 0x00; - cmd[5] = 0x00; - cmd[6] = 0x00; - cmd[7] = 0x00; +void +TranzportControlProtocol::lcd_fill (uint8_t fill_char) +{ +} - if (write (cmd, 1000) == 0) { - lights[light] = true; - return 0; - } else { - return -1; - } +void +TranzportControlProtocol::lcd_print (int row, int col, const char* text) +{ + print(row,col,text); +} - } else { - return 0; - } +void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text) +{ + print(row,col,text); } -int -TranzportControlProtocol::light_off (LightID light) +// Lights are buffered + +void +TranzportControlProtocol::lights_on () { - uint8_t cmd[8]; + lights_pending[LightRecord] = lights_pending[LightTrackrec] = + lights_pending[LightTrackmute] = lights_pending[LightTracksolo] = + lights_pending[LightAnysolo] = lights_pending[LightLoop] = + lights_pending[LightPunch] = true; +} - if (lights[light]) { +void +TranzportControlProtocol::lights_off () +{ + lights_pending[LightRecord] = lights_pending[LightTrackrec] = + lights_pending[LightTrackmute] = lights_pending[LightTracksolo] = + lights_pending[LightAnysolo] = lights_pending[LightLoop] = + lights_pending[LightPunch] = false; +} - cmd[0] = 0x00; - cmd[1] = 0x00; - cmd[2] = light; - cmd[3] = 0x00; - cmd[4] = 0x00; - cmd[5] = 0x00; - cmd[6] = 0x00; - cmd[7] = 0x00; +int +TranzportControlProtocol::light_on (LightID light) +{ + lights_pending[light] = true; + return 0; +} - if (write (cmd, 1000) == 0) { - lights[light] = false; - return 0; - } else { - return -1; - } +int +TranzportControlProtocol::light_off (LightID light) +{ + lights_pending[light] = false; + return 0; +} - } else { +int +TranzportControlProtocol::light_set (LightID light, bool offon) +{ + uint8_t cmd[8]; + cmd[0] = 0x00; cmd[1] = 0x00; cmd[2] = light; cmd[3] = offon; + cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = 0x00; + + if (write (cmd) == 0) { + lights_current[light] = offon; + lights_invalid[light] = false; return 0; + } else { + return -1; } } -void* -TranzportControlProtocol::monitor_work () +int TranzportControlProtocol::rtpriority_set(int priority) { struct sched_param rtparam; int err; - uint8_t buf[8]; - int val; - bool first_time = true; + // preallocate and memlock some stack with memlock? + char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b'; + memset (&rtparam, 0, sizeof (rtparam)); + rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */ + // Note - try SCHED_RR with a low limit + // - we don't care if we can't write everything this ms + // and it will help if we lose the device + if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) { + PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg; + return 1; + } + return 0; +} - PBD::ThreadCreated (pthread_self(), X_("Tranzport")); +// Running with realtime privs is bad when you have problems +int TranzportControlProtocol::rtpriority_unset(int priority) +{ + struct sched_param rtparam; + int err; memset (&rtparam, 0, sizeof (rtparam)); - rtparam.sched_priority = 3; /* XXX should be relative to audio (JACK) thread */ - + rtparam.sched_priority = priority; if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) { - // do we care? not particularly. - PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg; + PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg; + return 1; } + PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg; + return 0; +} + +// Slowly breaking this into where I can make usb processing it's own thread. + +void* +TranzportControlProtocol::monitor_work () +{ + uint8_t buf[8]; + int val = 0, pending = 0; + bool first_time = true; + uint8_t offline = 0; + + PBD::ThreadCreated (pthread_self(), X_("Tranzport")); pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0); pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); - next_track (); + rtpriority_set(); + inflight=0; + flush(); while (true) { /* bInterval for this beastie is 10ms */ - /* anything to read ? */ - if (_device_status == STATUS_OFFLINE) { - light_off (LightRecord); first_time = true; + if(offline++ == 1) { + cerr << "Transport has gone offline\n"; + } + } else { + offline = 0; // hate writing this } - pthread_testcancel(); - val = usb_interrupt_read (udev, READ_ENDPOINT, (char*) buf, 8, 10); - pthread_testcancel(); + val = read(buf); if (val == 8) { process (buf); } +#if DEBUG_TRANZPORT > 2 + if(inflight > 1) printf("Inflight: %d\n", inflight); +#endif + + if (_device_status != STATUS_OFFLINE) { if (first_time) { + invalidate(); lcd_clear (); lights_off (); first_time = false; + offline = 0; + pending = 3; // Give some time for the device to recover } /* update whatever needs updating */ update_state (); - } - } - - return (void*) 0; -} - -int -TranzportControlProtocol::update_state () -{ - int row; - int col_base; - int col; - int cell; - /* do the text updates */ - - switch (display_mode) { - case DisplayBigMeter: - show_meter (); - break; - - case DisplayNormal: - normal_update (); - break; - } - - /* next: flush LCD */ - - cell = 0; - - for (row = 0; row < 2; ++row) { - - for (col_base = 0, col = 0; col < 20; ) { - - if (pending_screen[row][col] != current_screen[row][col]) { - - /* something in this cell is different, so dump the cell - to the device. - */ - - uint8_t cmd[8]; - - cmd[0] = 0x00; - cmd[1] = 0x01; - cmd[2] = cell; - cmd[3] = pending_screen[row][col_base]; - cmd[4] = pending_screen[row][col_base+1]; - cmd[5] = pending_screen[row][col_base+2]; - cmd[6] = pending_screen[row][col_base+3]; - cmd[7] = 0x00; - - if (usb_interrupt_write (udev, WRITE_ENDPOINT, (char *) cmd, 8, 1000) == 8) { - /* successful write: copy to current */ - memcpy (¤t_screen[row][col_base], &pending_screen[row][col_base], 4); - } - - /* skip the rest of the 4 character cell since we wrote+copied it already */ - - col_base += 4; - col = col_base; - cell++; + /* still struggling with a good means of exerting flow control */ + // pending = flush(); + if(pending == 0) { + pending = flush(); } else { - - col++; - - if (col && col % 4 == 0) { - cell++; - col_base += 4; + if(inflight > 0) { + pending = --inflight; // we just did a whole bunch of writes so wait + } else { + pending = 0; } } - } + // pending = 0; + } } - /* now update LED's */ + return (void*) 0; +} - /* per track */ +int TranzportControlProtocol::lights_show_recording() +{ + // FIXME, flash recording light when recording and transport is moving + return lights_show_normal(); +} - if (route_table[0]) { - boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]); - if (at && at->record_enabled()) { - pending_lights[LightTrackrec] = true; - } else { - pending_lights[LightTrackrec] = false; - } - if (route_get_muted (0)) { - pending_lights[LightTrackmute] = true; - } else { - pending_lights[LightTrackmute] = false; - } - if (route_get_soloed (0)) { - pending_lights[LightTracksolo] = true; - } else { - pending_lights[LightTracksolo] = false; - } +// gotta do bling next! - } else { - pending_lights[LightTrackrec] = false; - pending_lights[LightTracksolo] = false; - pending_lights[LightTrackmute] = false; +int TranzportControlProtocol::lights_show_bling() +{ + switch (bling_mode) { + case BlingOff: break; + case BlingKit: break; // rotate rec/mute/solo/any solo back and forth + case BlingRotating: break; // switch between lights + case BlingPairs: break; // Show pairs of lights + case BlingRows: break; // light each row in sequence + case BlingFlashAll: break; // Flash everything randomly } + return 0; +} - /* global */ +int TranzportControlProtocol::lights_show_normal() +{ + /* Track only */ - if (session->get_play_loop()) { - pending_lights[LightLoop] = true; + if (route_table[0]) { + boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]); + lights_pending[LightTrackrec] = at && at->record_enabled(); + lights_pending[LightTrackmute] = route_get_muted(0); + lights_pending[LightTracksolo] = route_get_soloed(0); } else { - pending_lights[LightLoop] = false; + lights_pending[LightTrackrec] = false; + lights_pending[LightTracksolo] = false; + lights_pending[LightTrackmute] = false; } - if (Config->get_punch_in() || Config->get_punch_out()) { - pending_lights[LightPunch] = true; - } else { - pending_lights[LightPunch] = false; - } + /* Global settings */ - if (session->get_record_enabled()) { - pending_lights[LightRecord] = true; - } else { - pending_lights[LightRecord] = false; - } + lights_pending[LightLoop] = session->get_play_loop(); + lights_pending[LightPunch] = Config->get_punch_in() || Config->get_punch_out(); + lights_pending[LightRecord] = session->get_record_enabled(); + lights_pending[LightAnysolo] = session->soloing(); - if (session->soloing ()) { - pending_lights[LightAnysolo] = true; - } else { - pending_lights[LightAnysolo] = false; - } + return 0; +} - /* flush changed light change */ +int TranzportControlProtocol::lights_show_tempo() +{ + // someday soon fiddle with the lights based on the tempo + return lights_show_normal(); +} - if (pending_lights[LightRecord] != lights[LightRecord]) { - if (pending_lights[LightRecord]) { - light_on (LightRecord); - } else { - light_off (LightRecord); - } - } +int +TranzportControlProtocol::update_state () +{ + /* do the text and light updates */ - if (pending_lights[LightTracksolo] != lights[LightTracksolo]) { - if (pending_lights[LightTracksolo]) { - light_on (LightTracksolo); - } else { - light_off (LightTracksolo); - } - } + switch (display_mode) { + case DisplayBigMeter: + lights_show_tempo(); + show_meter (); + break; - if (pending_lights[LightTrackrec] != lights[LightTrackrec]) { - if (pending_lights[LightTrackrec]) { - light_on (LightTrackrec); - } else { - light_off (LightTrackrec); - } - } + case DisplayNormal: + lights_show_normal(); + normal_update (); + break; - if (pending_lights[LightTrackmute] != lights[LightTrackmute]) { - if (pending_lights[LightTrackmute]) { - light_on (LightTrackmute); - } else { - light_off (LightTrackmute); - } - } + case DisplayConfig: + break; - if (pending_lights[LightTracksolo] != lights[LightTracksolo]) { - if (pending_lights[LightTracksolo]) { - light_on (LightTracksolo); - } else { - light_off (LightTracksolo); - } - } + case DisplayRecording: + lights_show_recording(); + normal_update(); + break; - if (pending_lights[LightAnysolo] != lights[LightAnysolo]) { - if (pending_lights[LightAnysolo]) { - light_on (LightAnysolo); - } else { - light_off (LightAnysolo); - } - } + case DisplayRecordingMeter: + lights_show_recording(); + show_meter(); + break; - if (pending_lights[LightLoop] != lights[LightLoop]) { - if (pending_lights[LightLoop]) { - light_on (LightLoop); - } else { - light_off (LightLoop); - } - } + case DisplayBling: + lights_show_bling(); + normal_update(); + break; - if (pending_lights[LightPunch] != lights[LightPunch]) { - if (pending_lights[LightPunch]) { - light_on (LightPunch); - } else { - light_off (LightPunch); - } + case DisplayBlingMeter: + lights_show_bling(); + show_meter(); + break; } - return 0; + } +#define TRANZPORT_BUTTON_HANDLER(callback, button_arg) if (button_changes & button_arg) { \ + if (buttonmask & button_arg) { \ + callback##_press (buttonmask&ButtonShift); } else { callback##_release (buttonmask&ButtonShift); } } + int TranzportControlProtocol::process (uint8_t* buf) { @@ -820,6 +1201,7 @@ TranzportControlProtocol::process (uint8_t* buf) uint32_t button_changes; _device_status = buf[1]; + this_button_mask = 0; this_button_mask |= buf[2] << 24; this_button_mask |= buf[3] << 16; @@ -834,157 +1216,50 @@ TranzportControlProtocol::process (uint8_t* buf) datawheel (); } - if (button_changes & ButtonBattery) { - if (buttonmask & ButtonBattery) { - button_event_battery_press (buttonmask&ButtonShift); - } else { - button_event_battery_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonBacklight) { - if (buttonmask & ButtonBacklight) { - button_event_backlight_press (buttonmask&ButtonShift); - } else { - button_event_backlight_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonTrackLeft) { - if (buttonmask & ButtonTrackLeft) { - button_event_trackleft_press (buttonmask&ButtonShift); - } else { - button_event_trackleft_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonTrackRight) { - if (buttonmask & ButtonTrackRight) { - button_event_trackright_press (buttonmask&ButtonShift); - } else { - button_event_trackright_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonTrackRec) { - if (buttonmask & ButtonTrackRec) { - button_event_trackrec_press (buttonmask&ButtonShift); - } else { - button_event_trackrec_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonTrackMute) { - if (buttonmask & ButtonTrackMute) { - button_event_trackmute_press (buttonmask&ButtonShift); - } else { - button_event_trackmute_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonTrackSolo) { - if (buttonmask & ButtonTrackSolo) { - button_event_tracksolo_press (buttonmask&ButtonShift); - } else { - button_event_tracksolo_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonUndo) { - if (buttonmask & ButtonUndo) { - button_event_undo_press (buttonmask&ButtonShift); - } else { - button_event_undo_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonIn) { - if (buttonmask & ButtonIn) { - button_event_in_press (buttonmask&ButtonShift); - } else { - button_event_in_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonOut) { - if (buttonmask & ButtonOut) { - button_event_out_press (buttonmask&ButtonShift); - } else { - button_event_out_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonPunch) { - if (buttonmask & ButtonPunch) { - button_event_punch_press (buttonmask&ButtonShift); - } else { - button_event_punch_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonLoop) { - if (buttonmask & ButtonLoop) { - button_event_loop_press (buttonmask&ButtonShift); - } else { - button_event_loop_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonPrev) { - if (buttonmask & ButtonPrev) { - button_event_prev_press (buttonmask&ButtonShift); - } else { - button_event_prev_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonAdd) { - if (buttonmask & ButtonAdd) { - button_event_add_press (buttonmask&ButtonShift); - } else { - button_event_add_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonNext) { - if (buttonmask & ButtonNext) { - button_event_next_press (buttonmask&ButtonShift); - } else { - button_event_next_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonRewind) { - if (buttonmask & ButtonRewind) { - button_event_rewind_press (buttonmask&ButtonShift); - } else { - button_event_rewind_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonFastForward) { - if (buttonmask & ButtonFastForward) { - button_event_fastforward_press (buttonmask&ButtonShift); - } else { - button_event_fastforward_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonStop) { - if (buttonmask & ButtonStop) { - button_event_stop_press (buttonmask&ButtonShift); - } else { - button_event_stop_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonPlay) { - if (buttonmask & ButtonPlay) { - button_event_play_press (buttonmask&ButtonShift); - } else { - button_event_play_release (buttonmask&ButtonShift); - } - } - if (button_changes & ButtonRecord) { - if (buttonmask & ButtonRecord) { - button_event_record_press (buttonmask&ButtonShift); - } else { - button_event_record_release (buttonmask&ButtonShift); - } - } - + // SHIFT + STOP + PLAY for bling mode? + // if (button_changes & ButtonPlay & ButtonStop) { + // bling_mode_toggle(); + // } or something like that + + TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery); + TRANZPORT_BUTTON_HANDLER(button_event_backlight,ButtonBacklight); + TRANZPORT_BUTTON_HANDLER(button_event_trackleft,ButtonTrackLeft); + TRANZPORT_BUTTON_HANDLER(button_event_trackright,ButtonTrackRight); + TRANZPORT_BUTTON_HANDLER(button_event_trackrec,ButtonTrackRec); + TRANZPORT_BUTTON_HANDLER(button_event_trackmute,ButtonTrackMute); + TRANZPORT_BUTTON_HANDLER(button_event_tracksolo,ButtonTrackSolo); + TRANZPORT_BUTTON_HANDLER(button_event_undo,ButtonUndo); + TRANZPORT_BUTTON_HANDLER(button_event_in,ButtonIn); + TRANZPORT_BUTTON_HANDLER(button_event_out,ButtonOut); + TRANZPORT_BUTTON_HANDLER(button_event_punch,ButtonPunch); + TRANZPORT_BUTTON_HANDLER(button_event_loop,ButtonLoop); + TRANZPORT_BUTTON_HANDLER(button_event_prev,ButtonPrev); + TRANZPORT_BUTTON_HANDLER(button_event_add,ButtonAdd); + TRANZPORT_BUTTON_HANDLER(button_event_next,ButtonNext); + TRANZPORT_BUTTON_HANDLER(button_event_rewind,ButtonRewind); + TRANZPORT_BUTTON_HANDLER(button_event_fastforward,ButtonFastForward); + TRANZPORT_BUTTON_HANDLER(button_event_stop,ButtonStop); + TRANZPORT_BUTTON_HANDLER(button_event_play,ButtonPlay); + TRANZPORT_BUTTON_HANDLER(button_event_record,ButtonRecord); return 0; } void TranzportControlProtocol::show_current_track () { + char pad[11]; + char *v; + int len; if (route_table[0] == 0) { - print (0, 0, "--------"); + print (0, 0, "----------"); + last_track_gain = FLT_MAX; } else { - print (0, 0, route_get_name (0).substr (0, 8).c_str()); + strcpy(pad," "); + v = (char *)route_get_name (0).substr (0, 10).c_str(); + if((len = strlen(v)) > 0) { + strncpy(pad,(char *)v,len); + } + print (0, 0, pad); } } @@ -1001,11 +1276,24 @@ TranzportControlProtocol::button_event_battery_release (bool shifted) void TranzportControlProtocol::button_event_backlight_press (bool shifted) { +#if DEBUG_TRANZPORT + printf("backlight pressed\n"); +#endif } void TranzportControlProtocol::button_event_backlight_release (bool shifted) { +#if DEBUG_TRANZPORT + printf("backlight released\n\n"); +#endif + if (shifted) { + lcd_damage(); + lcd_clear(); + last_where += 1; /* force time redisplay */ + last_track_gain = FLT_MAX; + normal_update(); // redraw_screen(); + } } void @@ -1048,7 +1336,11 @@ TranzportControlProtocol::button_event_trackrec_release (bool shifted) void TranzportControlProtocol::button_event_trackmute_press (bool shifted) { - route_set_muted (0, !route_get_muted (0)); + if (shifted) { + // Mute ALL? Something useful when a phone call comes in. Mute master? + } else { + route_set_muted (0, !route_get_muted (0)); + } } void @@ -1059,6 +1351,9 @@ TranzportControlProtocol::button_event_trackmute_release (bool shifted) void TranzportControlProtocol::button_event_tracksolo_press (bool shifted) { +#if DEBUG_TRANZPORT + printf("solo pressed\n"); +#endif if (display_mode == DisplayBigMeter) { light_off (LightAnysolo); return; @@ -1074,15 +1369,18 @@ TranzportControlProtocol::button_event_tracksolo_press (bool shifted) void TranzportControlProtocol::button_event_tracksolo_release (bool shifted) { +#if DEBUG_TRANZPORT + printf("solo released\n"); +#endif } void TranzportControlProtocol::button_event_undo_press (bool shifted) { if (shifted) { - redo (); + redo (); // someday flash the screen with what was redone } else { - undo (); + undo (); // someday flash the screen with what was undone } } @@ -1235,7 +1533,11 @@ TranzportControlProtocol::button_event_stop_release (bool shifted) void TranzportControlProtocol::button_event_play_press (bool shifted) { - transport_play (); + if (shifted) { + set_transport_speed (1.0f); + } else { + transport_play (); + } } void @@ -1258,11 +1560,17 @@ TranzportControlProtocol::button_event_record_release (bool shifted) { } +void button_event_mute (bool pressed, bool shifted) +{ + //static int was_pressed = 0; + // if(pressed) { } +} + void TranzportControlProtocol::datawheel () { if ((buttonmask & ButtonTrackRight) || (buttonmask & ButtonTrackLeft)) { - + /* track scrolling */ if (_datawheel < WheelDirectionThreshold) { @@ -1274,7 +1582,7 @@ TranzportControlProtocol::datawheel () timerclear (&last_wheel_motion); } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) { - + if (_datawheel < WheelDirectionThreshold) { next_marker (); } else { @@ -1289,23 +1597,27 @@ TranzportControlProtocol::datawheel () if (route_table[0]) { switch (wheel_shift_mode) { - case WheelShiftGain: - if (_datawheel < WheelDirectionThreshold) { - step_gain_up (); - } else { - step_gain_down (); - } - break; - case WheelShiftPan: - if (_datawheel < WheelDirectionThreshold) { - step_pan_right (); - } else { - step_pan_left (); - } - break; + case WheelShiftGain: + if (_datawheel < WheelDirectionThreshold) { + step_gain_up (); + } else { + step_gain_down (); + } + break; + case WheelShiftPan: + if (_datawheel < WheelDirectionThreshold) { + step_pan_right (); + } else { + step_pan_left (); + } + break; + + case WheelShiftMarker: + break; + + case WheelShiftMaster: + break; - case WheelShiftMaster: - break; } } @@ -1314,17 +1626,17 @@ TranzportControlProtocol::datawheel () } else { switch (wheel_mode) { - case WheelTimeline: - scroll (); - break; - - case WheelScrub: - scrub (); - break; + case WheelTimeline: + scroll (); + break; - case WheelShuttle: - shuttle (); - break; + case WheelScrub: + scrub (); + break; + + case WheelShuttle: + shuttle (); + break; } } } @@ -1332,10 +1644,15 @@ TranzportControlProtocol::datawheel () void TranzportControlProtocol::scroll () { + float m = 1.0; if (_datawheel < WheelDirectionThreshold) { - ScrollTimeline (0.2); + m = 1.0; } else { - ScrollTimeline (-0.2); + m = -1.0; + } + switch(wheel_increment) { + case WheelIncrScreen: ScrollTimeline (0.2*m); break; + default: break; // other modes unimplemented as yet } } @@ -1346,42 +1663,48 @@ TranzportControlProtocol::scrub () struct timeval now; struct timeval delta; int dir; - + gettimeofday (&now, 0); - + if (_datawheel < WheelDirectionThreshold) { dir = 1; } else { dir = -1; } - + if (dir != last_wheel_dir) { /* changed direction, start over */ speed = 0.1f; } else { if (timerisset (&last_wheel_motion)) { - + timersub (&now, &last_wheel_motion, &delta); - + /* 10 clicks per second => speed == 1.0 */ - + speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec); - + } else { - + /* start at half-speed and see where we go from there */ - + speed = 0.5f; } } - + last_wheel_motion = now; last_wheel_dir = dir; - + set_transport_speed (speed * dir); } void +TranzportControlProtocol::config () +{ + // FIXME +} + +void TranzportControlProtocol::shuttle () { if (_datawheel < WheelDirectionThreshold) { @@ -1453,6 +1776,10 @@ TranzportControlProtocol::next_wheel_shift_mode () break; case WheelShiftMaster: wheel_shift_mode = WheelShiftGain; + break; + case WheelShiftMarker: // Not done yet, disabled + wheel_shift_mode = WheelShiftGain; + break; } show_wheel_mode (); @@ -1495,36 +1822,48 @@ TranzportControlProtocol::show_wheel_mode () string text; switch (wheel_mode) { - case WheelTimeline: - text = "Time"; - break; - case WheelScrub: - text = "Scrb"; - break; - case WheelShuttle: - text = "Shtl"; - break; + case WheelTimeline: + text = "Time"; + break; + case WheelScrub: + text = "Scrb"; + break; + case WheelShuttle: + text = "Shtl"; + break; } switch (wheel_shift_mode) { - case WheelShiftGain: - text += ":Gain"; - break; + case WheelShiftGain: + text += ":Gain"; + break; - case WheelShiftPan: - text += ":Pan"; - break; + case WheelShiftPan: + text += ":Pan "; + break; - case WheelShiftMaster: - text += ":Mstr"; - break; + case WheelShiftMaster: + text += ":Mstr"; + break; + + case WheelShiftMarker: + text += ":Mrkr"; + break; } - + print (1, 0, text.c_str()); } +// Was going to keep state around saying to retry or not +// haven't got to it yet, still not sure it's a good idea + void -TranzportControlProtocol::print (int row, int col, const char *text) +TranzportControlProtocol::print (int row, int col, const char *text) { + print_noretry(row,col,text); +} + +void +TranzportControlProtocol::print_noretry (int row, int col, const char *text) { int cell; uint32_t left = strlen (text); @@ -1564,7 +1903,7 @@ TranzportControlProtocol::print (int row, int col, const char *text) /* copy current cell contents into tmp */ - memcpy (tmp, &pending_screen[row][base_col], 4); + memcpy (tmp, &screen_pending[row][base_col], 4); /* overwrite with new text */ @@ -1574,7 +1913,7 @@ TranzportControlProtocol::print (int row, int col, const char *text) /* copy it back to pending */ - memcpy (&pending_screen[row][base_col], tmp, 4); + memcpy (&screen_pending[row][base_col], tmp, 4); text += tocopy; left -= tocopy; @@ -1595,3 +1934,17 @@ TranzportControlProtocol::set_state (const XMLNode& node) { return 0; } + +int +TranzportControlProtocol::save (char *name) +{ + // Presently unimplemented + return 0; +} + +int +TranzportControlProtocol::load (char *name) +{ + // Presently unimplemented + return 0; +} diff --git a/libs/surfaces/tranzport/tranzport_control_protocol.h b/libs/surfaces/tranzport/tranzport_control_protocol.h index e5193a761c..f13e4a3a44 100644 --- a/libs/surfaces/tranzport/tranzport_control_protocol.h +++ b/libs/surfaces/tranzport/tranzport_control_protocol.h @@ -1,3 +1,4 @@ + #ifndef ardour_tranzport_control_protocol_h #define ardour_tranzport_control_protocol_h @@ -72,7 +73,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol enum WheelShiftMode { WheelShiftGain, WheelShiftPan, - WheelShiftMaster + WheelShiftMaster, + WheelShiftMarker }; enum WheelMode { @@ -81,29 +83,68 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol WheelShuttle }; + // FIXME - look at gtk2_ardour for snap settings + + enum WheelIncrement { + WheelIncrSlave, + WheelIncrScreen, + WheelIncrSample, + WheelIncrBeat, + WheelIncrBar, + WheelIncrSecond, + WheelIncrMinute + }; + enum DisplayMode { DisplayNormal, - DisplayBigMeter + DisplayRecording, + DisplayRecordingMeter, + DisplayBigMeter, + DisplayConfig, + DisplayBling, + DisplayBlingMeter + }; + + enum BlingMode { + BlingOff, + BlingKit, + BlingRotating, + BlingPairs, + BlingRows, + BlingFlashAll }; pthread_t thread; uint32_t buttonmask; uint32_t timeout; + uint32_t inflight; uint8_t _datawheel; uint8_t _device_status; - usb_dev_handle* udev; - uint32_t current_track_id; WheelMode wheel_mode; WheelShiftMode wheel_shift_mode; DisplayMode display_mode; + BlingMode bling_mode; + WheelIncrement wheel_increment; + usb_dev_handle* udev; + ARDOUR::gain_t gain_fraction; Glib::Mutex update_lock; - char current_screen[2][20]; - char pending_screen[2][20]; - bool lights[7]; - bool pending_lights[7]; + + bool screen_invalid[2][20]; + char screen_current[2][20]; + char screen_pending[2][20]; + char screen_flash[2][20]; + + bool lights_invalid[7]; + bool lights_current[7]; + bool lights_pending[7]; + bool lights_flash[7]; + + uint32_t last_bars; + uint32_t last_beats; + uint32_t last_ticks; bool last_negative; uint32_t last_hrs; @@ -119,28 +160,94 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol Glib::Mutex io_lock; int open (); - int read (uint32_t timeout_override = 0); + int read (uint8_t *buf,uint32_t timeout_override = 0); int write (uint8_t* cmd, uint32_t timeout_override = 0); + int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0); int close (); + int save(char *name = "default"); + int load(char *name = "default"); + void print (int row, int col, const char* text); + void print_noretry (int row, int col, const char* text); + + int rtpriority_set(int priority = 52); + int rtpriority_unset(int priority = 0); int open_core (struct usb_device*); + static void* _monitor_work (void* arg); + void* monitor_work (); + + int process (uint8_t *); + int update_state(); + void invalidate(); + int flush(); + // bool isuptodate(); // think on this. It seems futile to update more than 30/sec + + // A screen is a cache of what should be on the lcd + + void screen_init(); + void screen_validate(); + void screen_invalidate(); + int screen_flush(); + void screen_clear(); + // bool screen_isuptodate(); // think on this - + + // Commands to write to the lcd + + int lcd_init(); + bool lcd_damage(); + bool lcd_isdamaged(); + + bool lcd_damage(int row, int col = 0, int length = 20); + bool lcd_isdamaged(int row, int col = 0, int length = 20); + + int lcd_flush(); + int lcd_write(uint8_t* cmd, uint32_t timeout_override = 0); // pedantic alias for write + void lcd_fill (uint8_t fill_char); void lcd_clear (); - void print (int row, int col, const char* text); + void lcd_print (int row, int col, const char* text); + void lcd_print_noretry (int row, int col, const char* text); + + // Commands to write to the lights + // FIXME - on some devices lights can have intensity and colors + + void lights_init(); + void lights_validate(); + void lights_invalidate(); + void light_validate(LightID light); + void light_invalidate(LightID light); + int lights_flush(); + int lights_write(uint8_t* cmd,uint32_t timeout_override = 0); // pedantic alias to write + + // a cache of what should be lit + + void lights_off (); + void lights_on (); + int light_set(LightID, bool offon = true); int light_on (LightID); int light_off (LightID); - void lights_off (); + + // some modes for the lights, should probably be renamed + + int lights_show_normal(); + int lights_show_recording(); + int lights_show_tempo(); + int lights_show_bling(); void enter_big_meter_mode (); void enter_normal_display_mode (); + void enter_config_mode(); + void enter_recording_mode(); + void enter_bling_mode(); void next_display_mode (); - void normal_update (); void show_current_track (); void show_track_gain (); void show_transport_time (); + void show_bbt (nframes_t where); + void show_smpte (nframes_t where); void show_wheel_mode (); void show_gain (); void show_pan (); @@ -150,6 +257,7 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol void scrub (); void scroll (); void shuttle (); + void config (); void next_wheel_mode (); void next_wheel_shift_mode (); @@ -162,8 +270,6 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol void step_pan_right (); void step_pan_left (); - static void* _monitor_work (void* arg); - void* monitor_work (); void button_event_battery_press (bool shifted); void button_event_battery_release (bool shifted); @@ -206,8 +312,8 @@ class TranzportControlProtocol : public ARDOUR::ControlProtocol void button_event_record_press (bool shifted); void button_event_record_release (bool shifted); - int process (uint8_t *); - int update_state(); + // new api + void button_event_mute (bool pressed, bool shifted); }; |