Fix the horrible mess that was anything related to sources and paths.
Most significant changes: - Factor out FileSource from AudioFileSource, use for SMFSource too - Explicitly pass embedded rather than mysterious name mangling or whatever - Destroy a ton of duplicated or very-nearly-duplicated code - Clean up and document all that weird source stuff in session.cc git-svn-id: svn://localhost/ardour2/branches/3.0@4609 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
4565b73a39
commit
022818b4a7
|
@ -672,9 +672,10 @@ Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile,
|
|||
if ((s = session->source_by_path_and_channel (path, n)) == 0) {
|
||||
|
||||
source = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createReadable (DataType::AUDIO, *session, path, n,
|
||||
SourceFactory::createReadable (DataType::AUDIO, *session,
|
||||
path, false, n,
|
||||
(mode == ImportAsTapeTrack
|
||||
? Source::Destructive
|
||||
? Source::Destructive
|
||||
: Source::Flag (0)),
|
||||
true, true));
|
||||
} else {
|
||||
|
|
|
@ -204,7 +204,10 @@ Editor::write_region (string path, boost::shared_ptr<AudioRegion> region)
|
|||
|
||||
|
||||
try {
|
||||
fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *session, path, false, session->frame_rate()));
|
||||
fs = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createWritable (DataType::AUDIO, *session,
|
||||
path, true,
|
||||
false, session->frame_rate()));
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
|
@ -340,7 +343,10 @@ Editor::write_audio_range (AudioPlaylist& playlist, const ChanCount& count, list
|
|||
path = s;
|
||||
|
||||
try {
|
||||
fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *session, path, false, session->frame_rate()));
|
||||
fs = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createWritable (DataType::AUDIO, *session,
|
||||
path, true,
|
||||
false, session->frame_rate()));
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
|
|
|
@ -332,8 +332,8 @@ SoundFileBox::audition ()
|
|||
for (int n = 0; n < sf_info.channels; ++n) {
|
||||
try {
|
||||
afs = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createReadable (DataType::AUDIO, *_session, path,
|
||||
n, Source::Flag (0), false));
|
||||
SourceFactory::createReadable (DataType::AUDIO, *_session,
|
||||
path, false, n, Source::Flag (0), false));
|
||||
|
||||
srclist.push_back(afs);
|
||||
|
||||
|
@ -639,13 +639,13 @@ SoundFileBrowser::meter ()
|
|||
bool
|
||||
SoundFileBrowser::on_audio_filter (const FileFilter::Info& filter_info)
|
||||
{
|
||||
return AudioFileSource::safe_file_extension (filter_info.filename);
|
||||
return AudioFileSource::safe_audio_file_extension (filter_info.filename);
|
||||
}
|
||||
|
||||
bool
|
||||
SoundFileBrowser::on_midi_filter (const FileFilter::Info& filter_info)
|
||||
{
|
||||
return SMFSource::safe_file_extension (filter_info.filename);
|
||||
return SMFSource::safe_midi_file_extension (filter_info.filename);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1096,7 +1096,7 @@ SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool&
|
|||
src_needed = true;
|
||||
}
|
||||
|
||||
} else if (SMFSource::safe_file_extension (*i)) {
|
||||
} else if (SMFSource::safe_midi_file_extension (*i)) {
|
||||
|
||||
Evoral::SMFReader reader(*i);
|
||||
if (reader.num_tracks() > 1) {
|
||||
|
|
|
@ -86,6 +86,7 @@ export_status.cc
|
|||
export_timespan.cc
|
||||
export_utilities.cc
|
||||
filename_extensions.cc
|
||||
file_source.cc
|
||||
filesystem_paths.cc
|
||||
filter.cc
|
||||
find_session.cc
|
||||
|
@ -152,7 +153,6 @@ session_state_utils.cc
|
|||
session_time.cc
|
||||
session_transport.cc
|
||||
session_utils.cc
|
||||
silentfilesource.cc
|
||||
smf_source.cc
|
||||
sndfile_helpers.cc
|
||||
sndfileimportable.cc
|
||||
|
|
|
@ -21,18 +21,12 @@
|
|||
#define __ardour_audiofilesource_h__
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <ardour/audiosource.h>
|
||||
#include <ardour/file_source.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class non_existent_source : public std::exception {
|
||||
public:
|
||||
virtual const char *what() const throw() { return "audio file does not exist"; }
|
||||
};
|
||||
|
||||
struct SoundFileInfo {
|
||||
float samplerate;
|
||||
uint16_t channels;
|
||||
|
@ -41,107 +35,78 @@ struct SoundFileInfo {
|
|||
int64_t timecode;
|
||||
};
|
||||
|
||||
class AudioFileSource : public AudioSource {
|
||||
public:
|
||||
class AudioFileSource : public AudioSource, public FileSource {
|
||||
public:
|
||||
virtual ~AudioFileSource ();
|
||||
|
||||
bool set_name (const std::string& newname) {
|
||||
return (set_source_name(newname, destructive()) == 0);
|
||||
}
|
||||
int set_source_name (Glib::ustring newname, bool destructive);
|
||||
|
||||
Glib::ustring path() const { return _path; }
|
||||
Glib::ustring peak_path (Glib::ustring audio_path);
|
||||
Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path,
|
||||
Glib::ustring audio_path);
|
||||
|
||||
uint16_t channel() const { return _channel; }
|
||||
|
||||
static void set_peak_dir (Glib::ustring dir) { peak_dir = dir; }
|
||||
|
||||
static bool get_soundfile_info (Glib::ustring path, SoundFileInfo& _info, std::string& error);
|
||||
|
||||
static bool safe_file_extension (Glib::ustring path);
|
||||
|
||||
void set_allow_remove_if_empty (bool yn);
|
||||
void mark_for_remove();
|
||||
bool safe_file_extension (const Glib::ustring& path) const {
|
||||
return safe_audio_file_extension(path);
|
||||
}
|
||||
|
||||
/* this block of methods do nothing for regular file sources, but are significant
|
||||
for files used in destructive recording.
|
||||
*/
|
||||
virtual nframes_t last_capture_start_frame() const { return 0; }
|
||||
virtual void mark_capture_start (nframes_t) {}
|
||||
virtual void mark_capture_end () {}
|
||||
virtual void clear_capture_marks() {}
|
||||
virtual bool one_of_several_channels () const { return false; }
|
||||
virtual void mark_capture_start (nframes_t) {}
|
||||
virtual void mark_capture_end () {}
|
||||
virtual void clear_capture_marks() {}
|
||||
virtual bool one_of_several_channels () const { return false; }
|
||||
|
||||
virtual int update_header (nframes_t when, struct tm&, time_t) = 0;
|
||||
virtual int flush_header () = 0;
|
||||
|
||||
int move_to_trash (const Glib::ustring& trash_dir_name);
|
||||
|
||||
static bool is_empty (Session&, Glib::ustring path);
|
||||
void mark_streaming_write_completed ();
|
||||
|
||||
void mark_take (Glib::ustring);
|
||||
Glib::ustring take_id() const { return _take_id; }
|
||||
|
||||
bool is_embedded() const { return _is_embedded; }
|
||||
|
||||
static void set_bwf_serial_number (int);
|
||||
|
||||
static void set_search_path (Glib::ustring string);
|
||||
static void set_header_position_offset (nframes_t offset );
|
||||
|
||||
int setup_peakfile ();
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
bool destructive() const { return (_flags & Destructive); }
|
||||
virtual bool set_destructive (bool yn) { return false; }
|
||||
bool can_truncate_peaks() const { return !destructive(); }
|
||||
bool can_be_analysed() const { return _length > 0; }
|
||||
|
||||
void mark_immutable ();
|
||||
bool can_truncate_peaks() const { return !destructive(); }
|
||||
bool can_be_analysed() const { return _length > 0; }
|
||||
|
||||
static bool safe_audio_file_extension (const Glib::ustring& path);
|
||||
|
||||
static bool is_empty (Session&, Glib::ustring path);
|
||||
|
||||
static void set_bwf_serial_number (int);
|
||||
static void set_header_position_offset (nframes_t offset );
|
||||
|
||||
static sigc::signal<void> HeaderPositionOffsetChanged;
|
||||
|
||||
protected:
|
||||
|
||||
protected:
|
||||
/** Constructor to be called for existing external-to-session files */
|
||||
AudioFileSource (Session&, Glib::ustring path, Source::Flag flags);
|
||||
AudioFileSource (Session&, const Glib::ustring& path, bool embedded, Source::Flag flags);
|
||||
|
||||
/** Constructor to be called for new in-session files */
|
||||
AudioFileSource (Session&, Glib::ustring path, Source::Flag flags,
|
||||
AudioFileSource (Session&, const Glib::ustring& path, bool embedded, Source::Flag flags,
|
||||
SampleFormat samp_format, HeaderFormat hdr_format);
|
||||
|
||||
/** Constructor to be called for existing in-session files */
|
||||
AudioFileSource (Session&, const XMLNode&, bool must_exit = true);
|
||||
AudioFileSource (Session&, const XMLNode&, bool must_exist = true);
|
||||
|
||||
int init (Glib::ustring idstr, bool must_exist);
|
||||
int init (const Glib::ustring& idstr, bool must_exist);
|
||||
|
||||
static bool determine_embeddedness (Glib::ustring path);
|
||||
|
||||
virtual void set_timeline_position (int64_t pos);
|
||||
virtual void set_header_timeline_position () = 0;
|
||||
virtual void handle_header_position_change () {}
|
||||
|
||||
bool find (Glib::ustring& path, bool must_exist, bool& is_new, uint16_t& chan);
|
||||
bool removable() const;
|
||||
bool writable() const { return _flags & Writable; }
|
||||
|
||||
int move_dependents_to_trash();
|
||||
|
||||
static Sample* get_interleave_buffer (nframes_t size);
|
||||
|
||||
Glib::ustring _path;
|
||||
Glib::ustring _take_id;
|
||||
int64_t _timeline_position;
|
||||
bool _file_is_new;
|
||||
uint16_t _channel;
|
||||
bool _is_embedded;
|
||||
|
||||
static Glib::ustring peak_dir;
|
||||
static Glib::ustring search_path;
|
||||
|
||||
static char bwf_country_code[3];
|
||||
static char bwf_organization_code[4];
|
||||
|
|
|
@ -43,7 +43,7 @@ using std::vector;
|
|||
|
||||
namespace ARDOUR {
|
||||
|
||||
class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR::AudioSource>
|
||||
class AudioSource : virtual public Source, public boost::enable_shared_from_this<ARDOUR::AudioSource>
|
||||
{
|
||||
public:
|
||||
AudioSource (Session&, Glib::ustring name);
|
||||
|
@ -68,7 +68,6 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
|
|||
|
||||
virtual float sample_rate () const = 0;
|
||||
|
||||
virtual void mark_for_remove() = 0;
|
||||
virtual void mark_streaming_write_completed () {}
|
||||
|
||||
virtual bool can_truncate_peaks() const { return true; }
|
||||
|
@ -116,7 +115,6 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
|
|||
static bool _build_peakfiles;
|
||||
|
||||
bool _peaks_built;
|
||||
mutable Glib::Mutex _lock;
|
||||
mutable Glib::Mutex _peaks_ready_lock;
|
||||
Glib::ustring peakpath;
|
||||
Glib::ustring _captured_for;
|
||||
|
@ -138,8 +136,6 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
|
|||
virtual Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path,
|
||||
Glib::ustring audio_path) = 0;
|
||||
|
||||
void update_length (nframes_t pos, nframes_t cnt);
|
||||
|
||||
virtual int read_peaks_with_fpp (PeakData *peaks,
|
||||
nframes_t npeaks, nframes_t start, nframes_t cnt,
|
||||
double samples_per_visual_peak, nframes_t fpp) const;
|
||||
|
|
83
libs/ardour/ardour/file_source.h
Normal file
83
libs/ardour/ardour/file_source.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright (C) 2006-2009 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_filesource_h__
|
||||
#define __ardour_filesource_h__
|
||||
|
||||
#include <exception>
|
||||
#include <time.h>
|
||||
#include <ardour/source.h>
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class MissingSource : public std::exception {
|
||||
public:
|
||||
virtual const char *what() const throw() { return "source file does not exist"; }
|
||||
};
|
||||
|
||||
/** A source associated with a file on disk somewhere */
|
||||
class FileSource : virtual public Source {
|
||||
public:
|
||||
const Glib::ustring& path() const { return _path; }
|
||||
|
||||
virtual bool safe_file_extension (const Glib::ustring& path) const = 0;
|
||||
|
||||
int move_to_trash (const Glib::ustring& trash_dir_name);
|
||||
void mark_take (const Glib::ustring& id);
|
||||
void mark_immutable ();
|
||||
|
||||
const Glib::ustring& take_id () const { return _take_id; }
|
||||
bool is_embedded () const { return _is_embedded; }
|
||||
uint16_t channel() const { return _channel; }
|
||||
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
int set_source_name (const Glib::ustring& newname, bool destructive);
|
||||
|
||||
static void set_search_path (DataType type, const Glib::ustring& path);
|
||||
|
||||
protected:
|
||||
FileSource (Session& session, DataType type,
|
||||
const Glib::ustring& path, bool embedded,
|
||||
Source::Flag flags = Source::Flag(0));
|
||||
|
||||
FileSource (Session& session, const XMLNode& node, bool must_exist);
|
||||
|
||||
virtual int init (const Glib::ustring& idstr, bool must_exist);
|
||||
|
||||
virtual int move_dependents_to_trash() { return 0; }
|
||||
|
||||
bool find (DataType type, const Glib::ustring& path,
|
||||
bool must_exist, bool& is_new, uint16_t& chan);
|
||||
|
||||
bool removable () const;
|
||||
|
||||
Glib::ustring _path;
|
||||
Glib::ustring _take_id;
|
||||
bool _file_is_new;
|
||||
uint16_t _channel;
|
||||
bool _is_embedded;
|
||||
|
||||
static map<DataType, Glib::ustring> search_paths;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_filesource_h__ */
|
||||
|
|
@ -38,7 +38,7 @@ class MidiModel;
|
|||
template<typename T> class MidiRingBuffer;
|
||||
|
||||
/** Source for MIDI data */
|
||||
class MidiSource : public Source
|
||||
class MidiSource : virtual public Source
|
||||
{
|
||||
public:
|
||||
typedef double TimeType;
|
||||
|
@ -60,14 +60,10 @@ class MidiSource : public Source
|
|||
virtual void append_event_unlocked_beats(const Evoral::Event<double>& ev) = 0;
|
||||
virtual void append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev) = 0;
|
||||
|
||||
virtual void mark_for_remove() = 0;
|
||||
virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
|
||||
virtual void mark_streaming_write_started ();
|
||||
virtual void mark_streaming_write_completed ();
|
||||
|
||||
uint64_t timeline_position () { return _timeline_position; }
|
||||
void set_timeline_position (nframes_t when);
|
||||
|
||||
virtual void session_saved();
|
||||
|
||||
std::string captured_for() const { return _captured_for; }
|
||||
|
@ -90,6 +86,8 @@ class MidiSource : public Source
|
|||
virtual void destroy_model() = 0;
|
||||
|
||||
void set_note_mode(NoteMode mode);
|
||||
|
||||
void set_timeline_position (int64_t pos);
|
||||
|
||||
boost::shared_ptr<MidiModel> model() { return _model; }
|
||||
void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
|
||||
|
@ -106,9 +104,7 @@ class MidiSource : public Source
|
|||
nframes_t stamp_offset, nframes_t negative_stamp_offset) const = 0;
|
||||
virtual nframes_t write_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t cnt) = 0;
|
||||
|
||||
mutable Glib::Mutex _lock;
|
||||
std::string _captured_for;
|
||||
uint64_t _timeline_position;
|
||||
mutable uint32_t _read_data_count; ///< modified in read()
|
||||
mutable uint32_t _write_data_count; ///< modified in write()
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@ class Route : public IO
|
|||
ControlOut = 0x4
|
||||
};
|
||||
|
||||
|
||||
Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max,
|
||||
Flag flags = Flag(0), DataType default_type = DataType::AUDIO);
|
||||
Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO);
|
||||
|
@ -91,14 +90,15 @@ class Route : public IO
|
|||
/* these are the core of the API of a Route. see the protected sections as well */
|
||||
|
||||
|
||||
virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
|
||||
virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
|
||||
|
||||
virtual int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
|
||||
virtual int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
|
||||
|
||||
virtual int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, bool can_record, bool rec_monitors_input);
|
||||
|
||||
virtual int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t offset, bool can_record, bool rec_monitors_input);
|
||||
virtual void toggle_monitor_input ();
|
||||
virtual bool can_record() { return false; }
|
||||
virtual void set_record_enable (bool yn, void *src) {}
|
||||
|
@ -136,8 +136,8 @@ class Route : public IO
|
|||
void drop_mix_group (void *);
|
||||
RouteGroup *mix_group () { return _mix_group; }
|
||||
|
||||
virtual void set_meter_point (MeterPoint, void *src);
|
||||
MeterPoint meter_point() const { return _meter_point; }
|
||||
virtual void set_meter_point (MeterPoint, void *src);
|
||||
MeterPoint meter_point() const { return _meter_point; }
|
||||
|
||||
/* Processors */
|
||||
|
||||
|
@ -271,56 +271,53 @@ class Route : public IO
|
|||
void curve_reallocate ();
|
||||
|
||||
protected:
|
||||
Flag _flags;
|
||||
|
||||
/* tight cache-line access here is more important than sheer speed of
|
||||
access.
|
||||
*/
|
||||
|
||||
bool _muted : 1;
|
||||
bool _soloed : 1;
|
||||
bool _solo_safe : 1;
|
||||
bool _recordable : 1;
|
||||
bool _mute_affects_pre_fader : 1;
|
||||
bool _mute_affects_post_fader : 1;
|
||||
bool _mute_affects_control_outs : 1;
|
||||
bool _mute_affects_main_outs : 1;
|
||||
bool _silent : 1;
|
||||
bool _declickable : 1;
|
||||
int _pending_declick;
|
||||
|
||||
MeterPoint _meter_point;
|
||||
|
||||
gain_t solo_gain;
|
||||
gain_t mute_gain;
|
||||
gain_t desired_solo_gain;
|
||||
gain_t desired_mute_gain;
|
||||
|
||||
|
||||
|
||||
nframes_t _initial_delay;
|
||||
nframes_t _roll_delay;
|
||||
ProcessorList _processors;
|
||||
Glib::RWLock _processor_lock;
|
||||
IO *_control_outs;
|
||||
Glib::Mutex _control_outs_lock;
|
||||
RouteGroup *_edit_group;
|
||||
RouteGroup *_mix_group;
|
||||
std::string _comment;
|
||||
bool _have_internal_generator;
|
||||
|
||||
boost::shared_ptr<ToggleControllable> _solo_control;
|
||||
boost::shared_ptr<ToggleControllable> _mute_control;
|
||||
|
||||
nframes_t check_initial_delay (nframes_t, nframes_t&, nframes_t&);
|
||||
|
||||
void passthru (nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t nframes, nframes_t offset, int declick, bool meter_inputs);
|
||||
void passthru (nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t nframes, nframes_t offset, int declick, bool meter_inputs);
|
||||
|
||||
virtual void process_output_buffers (BufferSet& bufs,
|
||||
nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t nframes, nframes_t offset, bool with_processors, int declick,
|
||||
bool meter);
|
||||
nframes_t start_frame, nframes_t end_frame,
|
||||
nframes_t nframes, nframes_t offset, bool with_processors, int declick,
|
||||
bool meter);
|
||||
|
||||
Flag _flags;
|
||||
int _pending_declick;
|
||||
MeterPoint _meter_point;
|
||||
|
||||
gain_t solo_gain;
|
||||
gain_t mute_gain;
|
||||
gain_t desired_solo_gain;
|
||||
gain_t desired_mute_gain;
|
||||
|
||||
nframes_t _initial_delay;
|
||||
nframes_t _roll_delay;
|
||||
ProcessorList _processors;
|
||||
Glib::RWLock _processor_lock;
|
||||
IO *_control_outs;
|
||||
Glib::Mutex _control_outs_lock;
|
||||
RouteGroup *_edit_group;
|
||||
RouteGroup *_mix_group;
|
||||
std::string _comment;
|
||||
bool _have_internal_generator;
|
||||
|
||||
boost::shared_ptr<ToggleControllable> _solo_control;
|
||||
boost::shared_ptr<ToggleControllable> _mute_control;
|
||||
|
||||
/* tight cache-line access here is more important than sheer speed of access.
|
||||
keep these after things that should be aligned
|
||||
*/
|
||||
|
||||
bool _muted : 1;
|
||||
bool _soloed : 1;
|
||||
bool _solo_safe : 1;
|
||||
bool _recordable : 1;
|
||||
bool _mute_affects_pre_fader : 1;
|
||||
bool _mute_affects_post_fader : 1;
|
||||
bool _mute_affects_control_outs : 1;
|
||||
bool _mute_affects_main_outs : 1;
|
||||
bool _silent : 1;
|
||||
bool _declickable : 1;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -348,10 +345,8 @@ class Route : public IO
|
|||
|
||||
static uint32_t order_key_cnt;
|
||||
|
||||
struct ltstr
|
||||
{
|
||||
bool operator()(const char* s1, const char* s2) const
|
||||
{
|
||||
struct ltstr {
|
||||
bool operator()(const char* s1, const char* s2) const {
|
||||
return strcmp(s1, s2) < 0;
|
||||
}
|
||||
};
|
||||
|
@ -365,8 +360,7 @@ class Route : public IO
|
|||
int reset_processor_counts (ProcessorStreams*); /* locked */
|
||||
int _reset_processor_counts (ProcessorStreams*); /* unlocked */
|
||||
|
||||
/* processor I/O channels and plugin count handling */
|
||||
|
||||
/** processor I/O channels and plugin count handling */
|
||||
struct ProcessorCount {
|
||||
boost::shared_ptr<ARDOUR::Processor> processor;
|
||||
ChanCount in;
|
||||
|
@ -376,7 +370,8 @@ class Route : public IO
|
|||
};
|
||||
|
||||
int32_t apply_some_processor_counts (std::list<ProcessorCount>& iclist);
|
||||
bool check_some_processor_counts (std::list<ProcessorCount>& iclist, ChanCount required_inputs, ProcessorStreams* err_streams);
|
||||
bool check_some_processor_counts (std::list<ProcessorCount>& iclist,
|
||||
ChanCount required_inputs, ProcessorStreams* err_streams);
|
||||
|
||||
void set_deferred_state ();
|
||||
void add_processor_from_xml (const XMLNode&);
|
||||
|
|
|
@ -150,9 +150,7 @@ class Session : public PBD::StatefulDestructible
|
|||
SetAudioRange,
|
||||
SetPlayRange,
|
||||
|
||||
/* only one of each of these events
|
||||
can be queued at any one time
|
||||
*/
|
||||
/* only one of each of these events can be queued at any one time */
|
||||
|
||||
StopOnce,
|
||||
AutoLoop
|
||||
|
@ -165,32 +163,33 @@ class Session : public PBD::StatefulDestructible
|
|||
Clear
|
||||
};
|
||||
|
||||
Type type;
|
||||
Action action;
|
||||
nframes_t action_frame;
|
||||
nframes_t target_frame;
|
||||
double speed;
|
||||
Type type;
|
||||
Action action;
|
||||
nframes_t action_frame;
|
||||
nframes_t target_frame;
|
||||
double speed;
|
||||
|
||||
union {
|
||||
void* ptr;
|
||||
bool yes_or_no;
|
||||
nframes_t target2_frame;
|
||||
SlaveSource slave;
|
||||
Route* route;
|
||||
void* ptr;
|
||||
bool yes_or_no;
|
||||
nframes_t target2_frame;
|
||||
SlaveSource slave;
|
||||
Route* route;
|
||||
};
|
||||
|
||||
boost::shared_ptr<Region> region;
|
||||
|
||||
list<AudioRange> audio_range;
|
||||
list<MusicRange> music_range;
|
||||
list<AudioRange> audio_range;
|
||||
list<MusicRange> music_range;
|
||||
|
||||
boost::shared_ptr<Region> region;
|
||||
|
||||
Event(Type t, Action a, nframes_t when, nframes_t where, double spd, bool yn = false)
|
||||
: type (t),
|
||||
action (a),
|
||||
action_frame (when),
|
||||
target_frame (where),
|
||||
speed (spd),
|
||||
yes_or_no (yn) {}
|
||||
: type (t)
|
||||
, action (a)
|
||||
, action_frame (when)
|
||||
, target_frame (where)
|
||||
, speed (spd)
|
||||
, yes_or_no (yn)
|
||||
{}
|
||||
|
||||
void set_ptr (void* p) {
|
||||
ptr = p;
|
||||
|
@ -275,12 +274,12 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
Glib::ustring peak_path (Glib::ustring) const;
|
||||
|
||||
static string change_audio_path_by_name (string oldpath, string oldname, string newname, bool destructive);
|
||||
static string change_midi_path_by_name (string oldpath, string oldname, string newname, bool destructive);
|
||||
static string change_source_path_by_name (string oldpath, string oldname, string newname, bool destructive);
|
||||
|
||||
string peak_path_from_audio_path (string) const;
|
||||
string audio_path_from_name (string, uint32_t nchans, uint32_t chan, bool destructive);
|
||||
string midi_path_from_name (string);
|
||||
string new_audio_source_name (const string&, uint32_t nchans, uint32_t chan, bool destructive);
|
||||
string new_midi_source_name (const string&);
|
||||
string new_source_path_from_name (DataType type, const string&);
|
||||
|
||||
void process (nframes_t nframes);
|
||||
|
||||
|
@ -400,16 +399,15 @@ class Session : public PBD::StatefulDestructible
|
|||
bool transport_locked () const;
|
||||
|
||||
int wipe ();
|
||||
//int wipe_diskstream (AudioDiskstream *);
|
||||
|
||||
int remove_region_from_region_list (boost::shared_ptr<Region>);
|
||||
|
||||
nframes_t get_maximum_extent () const;
|
||||
nframes_t current_end_frame() const { return end_location->start(); }
|
||||
nframes_t current_start_frame() const { return start_location->start(); }
|
||||
// "actual" sample rate of session, set by current audioengine rate, pullup/down etc.
|
||||
/// "actual" sample rate of session, set by current audioengine rate, pullup/down etc.
|
||||
nframes_t frame_rate() const { return _current_frame_rate; }
|
||||
// "native" sample rate of session, regardless of current audioengine rate, pullup/down etc
|
||||
/// "native" sample rate of session, regardless of current audioengine rate, pullup/down etc
|
||||
nframes_t nominal_frame_rate() const { return _nominal_frame_rate; }
|
||||
nframes_t frames_per_hour() const { return _frames_per_hour; }
|
||||
|
||||
|
@ -441,10 +439,10 @@ class Session : public PBD::StatefulDestructible
|
|||
void remove_event (nframes_t frame, Event::Type type);
|
||||
void clear_events (Event::Type type);
|
||||
|
||||
nframes_t get_block_size() const { return current_block_size; }
|
||||
nframes_t get_block_size() const { return current_block_size; }
|
||||
nframes_t worst_output_latency () const { return _worst_output_latency; }
|
||||
nframes_t worst_input_latency () const { return _worst_input_latency; }
|
||||
nframes_t worst_track_latency () const { return _worst_track_latency; }
|
||||
nframes_t worst_input_latency () const { return _worst_input_latency; }
|
||||
nframes_t worst_track_latency () const { return _worst_track_latency; }
|
||||
|
||||
int save_state (std::string snapshot_name, bool pending = false);
|
||||
int restore_state (std::string snapshot_name);
|
||||
|
@ -470,7 +468,7 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
/// The instant xml file is written to the session directory
|
||||
void add_instant_xml (XMLNode&, bool write_to_config = true);
|
||||
XMLNode * instant_xml (const std::string& str);
|
||||
XMLNode* instant_xml (const std::string& str);
|
||||
|
||||
enum StateOfTheState {
|
||||
Clean = 0x0,
|
||||
|
@ -495,8 +493,8 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
sigc::signal<void,RouteGroup*> edit_group_added;
|
||||
sigc::signal<void,RouteGroup*> mix_group_added;
|
||||
sigc::signal<void> edit_group_removed;
|
||||
sigc::signal<void> mix_group_removed;
|
||||
sigc::signal<void> edit_group_removed;
|
||||
sigc::signal<void> mix_group_removed;
|
||||
|
||||
void foreach_edit_group (sigc::slot<void,RouteGroup*> sl) {
|
||||
for (list<RouteGroup *>::iterator i = edit_groups.begin(); i != edit_groups.end(); i++) {
|
||||
|
@ -512,11 +510,13 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
/* fundamental operations. duh. */
|
||||
|
||||
std::list<boost::shared_ptr<AudioTrack> > new_audio_track (int input_channels, int output_channels, TrackMode mode = Normal, uint32_t how_many = 1);
|
||||
std::list<boost::shared_ptr<AudioTrack> > new_audio_track (
|
||||
int input_channels, int output_channels, TrackMode mode = Normal, uint32_t how_many = 1);
|
||||
|
||||
RouteList new_audio_route (int input_channels, int output_channels, uint32_t how_many);
|
||||
|
||||
std::list<boost::shared_ptr<MidiTrack> > new_midi_track (TrackMode mode = Normal, uint32_t how_many = 1);
|
||||
//boost::shared_ptr<Route> new_midi_route (uint32_t how_many = 1);
|
||||
std::list<boost::shared_ptr<MidiTrack> > new_midi_track (
|
||||
TrackMode mode = Normal, uint32_t how_many = 1);
|
||||
|
||||
void remove_route (boost::shared_ptr<Route>);
|
||||
void resort_routes ();
|
||||
|
@ -552,8 +552,8 @@ class Session : public PBD::StatefulDestructible
|
|||
void sync_time_vars();
|
||||
|
||||
void bbt_time (nframes_t when, BBT_Time&);
|
||||
void smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset, bool use_subframes ) const;
|
||||
void sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, bool use_subframes ) const;
|
||||
void smpte_to_sample(SMPTE::Time& smpte, nframes_t& sample, bool use_offset, bool use_subframes) const;
|
||||
void sample_to_smpte(nframes_t sample, SMPTE::Time& smpte, bool use_offset, bool use_subframes) const;
|
||||
void smpte_time (SMPTE::Time &);
|
||||
void smpte_time (nframes_t when, SMPTE::Time&);
|
||||
void smpte_time_subframes (nframes_t when, SMPTE::Time&);
|
||||
|
@ -656,23 +656,19 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
int remove_last_capture ();
|
||||
|
||||
/* handlers should return -1 for "stop cleanup", 0 for
|
||||
"yes, delete this playlist" and 1 for "no, don't delete
|
||||
this playlist.
|
||||
/** handlers should return -1 for "stop cleanup",
|
||||
0 for "yes, delete this playlist",
|
||||
1 for "no, don't delete this playlist".
|
||||
*/
|
||||
|
||||
sigc::signal<int,boost::shared_ptr<ARDOUR::Playlist> > AskAboutPlaylistDeletion;
|
||||
|
||||
/* handlers should return 0 for "ignore the rate mismatch"
|
||||
and !0 for "do not use this session"
|
||||
/** handlers should return 0 for "ignore the rate mismatch",
|
||||
!0 for "do not use this session"
|
||||
*/
|
||||
|
||||
static sigc::signal<int,nframes_t, nframes_t> AskAboutSampleRateMismatch;
|
||||
|
||||
/* handlers should return !0 for use pending state, 0 for
|
||||
ignore it.
|
||||
/** handlers should return !0 for use pending state, 0 for ignore it.
|
||||
*/
|
||||
|
||||
static sigc::signal<int> AskAboutPendingState;
|
||||
|
||||
boost::shared_ptr<AudioFileSource> create_audio_source_for_session (ARDOUR::AudioDiskstream&, uint32_t which_channel, bool destructive);
|
||||
|
@ -780,7 +776,7 @@ class Session : public PBD::StatefulDestructible
|
|||
sigc::signal<void,boost::shared_ptr<Bundle> > BundleAdded;
|
||||
sigc::signal<void,boost::shared_ptr<Bundle> > BundleRemoved;
|
||||
|
||||
/* MIDI */
|
||||
/* MIDI control */
|
||||
|
||||
void midi_panic(void);
|
||||
int set_mtc_port (string port_tag);
|
||||
|
@ -917,7 +913,7 @@ class Session : public PBD::StatefulDestructible
|
|||
|
||||
/* clicking */
|
||||
|
||||
boost::shared_ptr<IO> click_io() { return _click_io; }
|
||||
boost::shared_ptr<IO> click_io() { return _click_io; }
|
||||
|
||||
/* disk, buffer loads */
|
||||
|
||||
|
@ -1078,10 +1074,10 @@ class Session : public PBD::StatefulDestructible
|
|||
bool follow_slave (nframes_t, nframes_t);
|
||||
void calculate_moving_average_of_slave_delta(int dir, nframes_t this_delta);
|
||||
void track_slave_state(
|
||||
float slave_speed,
|
||||
float slave_speed,
|
||||
nframes_t slave_transport_frame,
|
||||
nframes_t this_delta,
|
||||
bool starting);
|
||||
bool starting);
|
||||
void follow_slave_silently(nframes_t nframes, nframes_t offset, float slave_speed);
|
||||
|
||||
void set_slave_source (SlaveSource);
|
||||
|
@ -1119,7 +1115,6 @@ class Session : public PBD::StatefulDestructible
|
|||
}
|
||||
|
||||
int get_transport_declick_required () {
|
||||
|
||||
if (transport_sub_state & PendingDeclickIn) {
|
||||
transport_sub_state &= ~PendingDeclickIn;
|
||||
return 1;
|
||||
|
@ -1131,7 +1126,8 @@ class Session : public PBD::StatefulDestructible
|
|||
}
|
||||
|
||||
bool maybe_stop (nframes_t limit) {
|
||||
if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
|
||||
if ( (_transport_speed > 0.0f && _transport_frame >= limit)
|
||||
|| (_transport_speed < 0.0f && _transport_frame == 0) ) {
|
||||
stop_transport ();
|
||||
return true;
|
||||
}
|
||||
|
@ -1194,7 +1190,9 @@ class Session : public PBD::StatefulDestructible
|
|||
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); }
|
||||
inline bool transport_work_requested() const {
|
||||
return g_atomic_int_get(&butler_should_do_transport_work);
|
||||
}
|
||||
|
||||
struct ButlerRequest {
|
||||
enum Type {
|
||||
|
@ -1498,8 +1496,6 @@ class Session : public PBD::StatefulDestructible
|
|||
SourceMap get_sources() { return sources; }
|
||||
|
||||
private:
|
||||
|
||||
|
||||
int load_sources (const XMLNode& node);
|
||||
XMLNode& get_sources_as_xml ();
|
||||
|
||||
|
|
|
@ -26,26 +26,27 @@
|
|||
namespace ARDOUR {
|
||||
|
||||
class SilentFileSource : public AudioFileSource {
|
||||
public:
|
||||
virtual ~SilentFileSource ();
|
||||
|
||||
public:
|
||||
int update_header (nframes_t when, struct tm&, time_t) { return 0; }
|
||||
int flush_header () { return 0; }
|
||||
float sample_rate () const { return _sample_rate; }
|
||||
|
||||
void set_length (nframes_t len);
|
||||
void set_length (nframes_t len) { _length = len; }
|
||||
|
||||
bool destructive() const { return false; }
|
||||
bool can_be_analysed() const { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
float _sample_rate;
|
||||
|
||||
SilentFileSource (Session&, const XMLNode&, nframes_t nframes, float sample_rate);
|
||||
|
||||
protected:
|
||||
friend class SourceFactory;
|
||||
|
||||
SilentFileSource (Session& s, const XMLNode& x, nframes_t len, float srate)
|
||||
: Source (s, x)
|
||||
, AudioFileSource (s, x, false)
|
||||
, _sample_rate(srate)
|
||||
{
|
||||
_length = len;
|
||||
}
|
||||
|
||||
nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const {
|
||||
memset (dst, 0, sizeof (Sample) * cnt);
|
||||
return cnt;
|
||||
|
@ -55,11 +56,13 @@ class SilentFileSource : public AudioFileSource {
|
|||
|
||||
void set_header_timeline_position () {}
|
||||
|
||||
int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit, nframes_t fpp) const {
|
||||
int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt,
|
||||
double samples_per_unit, nframes_t fpp) const {
|
||||
memset (peaks, 0, sizeof (PeakData) * npeaks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
float _sample_rate;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
|
|
@ -18,14 +18,14 @@
|
|||
|
||||
*/
|
||||
|
||||
#ifndef __ardour_smf_filesource_h__
|
||||
#define __ardour_smf_filesource_h__
|
||||
#ifndef __ardour_smf_source_h__
|
||||
#define __ardour_smf_source_h__
|
||||
|
||||
#include <cstdio>
|
||||
#include <time.h>
|
||||
|
||||
#include <ardour/midi_source.h>
|
||||
#include <evoral/SMF.hpp>
|
||||
#include <ardour/midi_source.h>
|
||||
#include <ardour/file_source.h>
|
||||
|
||||
namespace Evoral { template<typename T> class Event; }
|
||||
|
||||
|
@ -34,51 +34,42 @@ namespace ARDOUR {
|
|||
template<typename T> class MidiRingBuffer;
|
||||
|
||||
/** Standard Midi File (Type 0) Source */
|
||||
class SMFSource : public MidiSource, public Evoral::SMF {
|
||||
public:
|
||||
class SMFSource : public MidiSource, public FileSource, public Evoral::SMF {
|
||||
public:
|
||||
/** Constructor for existing external-to-session files */
|
||||
SMFSource (Session& session, std::string path, Source::Flag flags = Source::Flag(0));
|
||||
SMFSource (Session& session, const Glib::ustring& path, bool embedded,
|
||||
Source::Flag flags = Source::Flag(0));
|
||||
|
||||
/* Constructor for existing in-session files */
|
||||
SMFSource (Session& session, const XMLNode&);
|
||||
/** Constructor for existing in-session files */
|
||||
SMFSource (Session& session, const XMLNode&, bool must_exist = false);
|
||||
|
||||
virtual ~SMFSource ();
|
||||
|
||||
bool safe_file_extension (const Glib::ustring& path) const {
|
||||
return safe_midi_file_extension(path);
|
||||
}
|
||||
|
||||
bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
|
||||
int set_source_name (string newname, bool destructive);
|
||||
|
||||
static bool safe_file_extension (const Glib::ustring& path);
|
||||
|
||||
Glib::ustring path() const { return _path; }
|
||||
|
||||
void set_allow_remove_if_empty (bool yn);
|
||||
void mark_for_remove();
|
||||
|
||||
void append_event_unlocked_beats(const Evoral::Event<double>& ev);
|
||||
void append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev);
|
||||
|
||||
int move_to_trash (const string trash_dir_name);
|
||||
void append_event_unlocked_beats (const Evoral::Event<double>& ev);
|
||||
void append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev);
|
||||
|
||||
void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
|
||||
void mark_streaming_write_completed ();
|
||||
|
||||
void mark_take (string);
|
||||
string take_id() const { return _take_id; }
|
||||
|
||||
static void set_search_path (string);
|
||||
static void set_header_position_offset (nframes_t offset, bool negative);
|
||||
|
||||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
void load_model(bool lock=true, bool force_reload=false);
|
||||
void destroy_model();
|
||||
void load_model (bool lock=true, bool force_reload=false);
|
||||
void destroy_model ();
|
||||
|
||||
void flush_midi();
|
||||
void flush_midi ();
|
||||
|
||||
static void set_header_position_offset (nframes_t offset, bool negative);
|
||||
|
||||
private:
|
||||
int init (string idstr, bool must_exist);
|
||||
static bool safe_midi_file_extension (const Glib::ustring& path);
|
||||
|
||||
private:
|
||||
nframes_t read_unlocked (
|
||||
MidiRingBuffer<nframes_t>& dst,
|
||||
nframes_t start,
|
||||
|
@ -90,22 +81,13 @@ class SMFSource : public MidiSource, public Evoral::SMF {
|
|||
MidiRingBuffer<nframes_t>& src,
|
||||
nframes_t cnt);
|
||||
|
||||
bool find (std::string path, bool must_exist, bool& is_new);
|
||||
bool removable() const;
|
||||
bool writable() const { return _flags & Writable; }
|
||||
|
||||
void set_default_controls_interpolation();
|
||||
void set_default_controls_interpolation ();
|
||||
|
||||
Glib::ustring _path;
|
||||
string _take_id;
|
||||
bool _allow_remove_if_empty;
|
||||
double _last_ev_time_beats;
|
||||
nframes_t _last_ev_time_frames;
|
||||
|
||||
static string _search_path;
|
||||
double _last_ev_time_beats;
|
||||
nframes_t _last_ev_time_frames;
|
||||
};
|
||||
|
||||
}; /* namespace ARDOUR */
|
||||
|
||||
#endif /* __ardour_smf_filesource_h__ */
|
||||
#endif /* __ardour_smf_source_h__ */
|
||||
|
||||
|
|
|
@ -29,17 +29,15 @@ namespace ARDOUR {
|
|||
|
||||
class SndFileSource : public AudioFileSource {
|
||||
public:
|
||||
/* constructor to be called for existing external-to-session files */
|
||||
/** Constructor to be called for existing external-to-session files */
|
||||
SndFileSource (Session&, const Glib::ustring& path, bool embedded, int chn, Flag flags);
|
||||
|
||||
SndFileSource (Session&, Glib::ustring path, int chn, Flag flags);
|
||||
|
||||
/* constructor to be called for new in-session files */
|
||||
|
||||
SndFileSource (Session&, Glib::ustring path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate,
|
||||
Flag flags = SndFileSource::default_writable_flags);
|
||||
/* Constructor to be called for new in-session files */
|
||||
SndFileSource (Session&, const Glib::ustring& path, bool embedded,
|
||||
SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate,
|
||||
Flag flags = SndFileSource::default_writable_flags);
|
||||
|
||||
/* constructor to be called for existing in-session files */
|
||||
|
||||
/** Constructor to be called for existing in-session files */
|
||||
SndFileSource (Session&, const XMLNode&);
|
||||
|
||||
~SndFileSource ();
|
||||
|
@ -77,7 +75,7 @@ class SndFileSource : public AudioFileSource {
|
|||
SF_INFO _info;
|
||||
BroadcastInfo *_broadcast_info;
|
||||
|
||||
void init ();
|
||||
void init_sndfile ();
|
||||
int open();
|
||||
int setup_broadcast_info (nframes_t when, struct tm&, time_t);
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
Destructive = 0x80
|
||||
};
|
||||
|
||||
Source (Session&, const std::string& name, DataType type, Flag flags=Flag(0));
|
||||
Source (Session&, DataType type, const std::string& name, Flag flags=Flag(0));
|
||||
Source (Session&, const XMLNode&);
|
||||
|
||||
virtual ~Source ();
|
||||
|
@ -63,11 +63,12 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
|
||||
nframes_t length() const { return _length; }
|
||||
|
||||
virtual Glib::ustring path() const = 0;
|
||||
virtual const Glib::ustring& path() const = 0;
|
||||
|
||||
virtual nframes_t natural_position() const { return 0; }
|
||||
|
||||
virtual void mark_for_remove() = 0;
|
||||
void mark_for_remove();
|
||||
|
||||
virtual void mark_streaming_write_started () {}
|
||||
virtual void mark_streaming_write_completed () = 0;
|
||||
|
||||
|
@ -76,8 +77,10 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
XMLNode& get_state ();
|
||||
int set_state (const XMLNode&);
|
||||
|
||||
virtual bool destructive() const { return false; }
|
||||
virtual bool length_mutable() const { return false; }
|
||||
bool destructive() const { return (_flags & Destructive); }
|
||||
bool writable () const { return _flags & Writable; }
|
||||
virtual bool set_destructive (bool yn) { return false; }
|
||||
virtual bool length_mutable() const { return false; }
|
||||
|
||||
void use () { _in_use++; }
|
||||
void disuse () { if (_in_use) { _in_use--; } }
|
||||
|
@ -103,6 +106,11 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
|
||||
void update_length (nframes_t pos, nframes_t cnt);
|
||||
|
||||
int64_t timeline_position() const { return _timeline_position; }
|
||||
virtual void set_timeline_position (int64_t pos);
|
||||
|
||||
void set_allow_remove_if_empty (bool yn);
|
||||
|
||||
virtual const Evoral::TimeConverter<double, nframes_t>& time_converter() const {
|
||||
return Evoral::IdentityConverter<double, nframes_t>();
|
||||
}
|
||||
|
@ -114,7 +122,9 @@ class Source : public SessionObject, public ARDOUR::Readable
|
|||
Flag _flags;
|
||||
time_t _timestamp;
|
||||
nframes_t _length;
|
||||
int64_t _timeline_position;
|
||||
bool _analysed;
|
||||
mutable Glib::Mutex _lock;
|
||||
mutable Glib::Mutex _analysis_lock;
|
||||
Glib::Mutex _playlist_lock;
|
||||
|
||||
|
|
|
@ -44,10 +44,12 @@ class SourceFactory {
|
|||
static boost::shared_ptr<Source> createSilent (Session&, const XMLNode& node,
|
||||
nframes_t nframes, float sample_rate);
|
||||
|
||||
static boost::shared_ptr<Source> createReadable (DataType type, Session&, std::string path,
|
||||
static boost::shared_ptr<Source> createReadable (DataType type, Session&,
|
||||
const std::string& path, bool embedded,
|
||||
int chn, Source::Flag flags, bool announce = true, bool async = false);
|
||||
|
||||
static boost::shared_ptr<Source> createWritable (DataType type, Session&, std::string name,
|
||||
static boost::shared_ptr<Source> createWritable (DataType type, Session&,
|
||||
const std::string& path, bool embedded,
|
||||
bool destructive, nframes_t rate, bool announce = true, bool async = false);
|
||||
|
||||
static Glib::Cond* PeaksToBuild;
|
||||
|
|
|
@ -2336,7 +2336,9 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
|
|||
|
||||
try {
|
||||
fs = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate()));
|
||||
SourceFactory::createWritable (DataType::AUDIO, _session,
|
||||
prop->value(), true,
|
||||
false, _session.frame_rate()));
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
|
@ -2370,22 +2372,25 @@ AudioDiskstream::use_pending_capture_data (XMLNode& node)
|
|||
boost::shared_ptr<AudioRegion> region;
|
||||
|
||||
try {
|
||||
region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(),
|
||||
region_name_from_path (first_fs->name(), true),
|
||||
0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
|
||||
region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (
|
||||
pending_sources, 0, first_fs->length(),
|
||||
region_name_from_path (first_fs->name(), true), 0,
|
||||
Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile)));
|
||||
region->special_set_position (0);
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
error << string_compose (_("%1: cannot create whole-file region from pending capture sources"),
|
||||
_name)
|
||||
<< endmsg;
|
||||
error << string_compose (
|
||||
_("%1: cannot create whole-file region from pending capture sources"),
|
||||
_name) << endmsg;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true)));
|
||||
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) {
|
||||
|
|
|
@ -136,7 +136,6 @@ AudioLibrary::search_members_and (vector<string>& members, const vector<string>
|
|||
if (*head != 0) {
|
||||
lrdf_uris* ulist = lrdf_match_multi(*head);
|
||||
for (uint32_t j = 0; ulist && j < ulist->count; ++j) {
|
||||
// cerr << "AND: " << Glib::filename_from_uri(ulist->items[j]) << endl;
|
||||
members.push_back(Glib::filename_from_uri(ulist->items[j]));
|
||||
}
|
||||
lrdf_free_uris(ulist);
|
||||
|
|
|
@ -63,13 +63,12 @@ using namespace PBD;
|
|||
using namespace Glib;
|
||||
|
||||
ustring AudioFileSource::peak_dir = "";
|
||||
ustring AudioFileSource::search_path;
|
||||
|
||||
sigc::signal<void> AudioFileSource::HeaderPositionOffsetChanged;
|
||||
uint64_t AudioFileSource::header_position_offset = 0;
|
||||
|
||||
/* XXX maybe this too */
|
||||
char AudioFileSource::bwf_serial_number[13] = "000000000000";
|
||||
char AudioFileSource::bwf_serial_number[13] = "000000000000";
|
||||
|
||||
struct SizedSampleBuffer {
|
||||
nframes_t size;
|
||||
|
@ -86,24 +85,23 @@ struct SizedSampleBuffer {
|
|||
|
||||
Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT;
|
||||
|
||||
/** Constructor used for existing internal-to-session files. File must exist. */
|
||||
AudioFileSource::AudioFileSource (Session& s, ustring path, Source::Flag flags)
|
||||
: AudioSource (s, path)
|
||||
, _channel (0)
|
||||
/** Constructor used for existing internal-to-session files. */
|
||||
AudioFileSource::AudioFileSource (Session& s, const ustring& path, bool embedded, Source::Flag flags)
|
||||
: Source (s, DataType::AUDIO, path, flags)
|
||||
, AudioSource (s, path)
|
||||
, FileSource (s, DataType::AUDIO, path, embedded, flags)
|
||||
{
|
||||
_is_embedded = AudioFileSource::determine_embeddedness (path);
|
||||
|
||||
if (init (path, true)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Constructor used for new internal-to-session files. File cannot exist. */
|
||||
AudioFileSource::AudioFileSource (Session& s, ustring path, Source::Flag flags,
|
||||
/** Constructor used for new internal-to-session files. */
|
||||
AudioFileSource::AudioFileSource (Session& s, const ustring& path, bool embedded, Source::Flag flags,
|
||||
SampleFormat samp_format, HeaderFormat hdr_format)
|
||||
: AudioSource (s, path)
|
||||
, _channel (0)
|
||||
: Source (s, DataType::AUDIO, path, flags)
|
||||
, AudioSource (s, path)
|
||||
, FileSource (s, DataType::AUDIO, path, embedded, flags)
|
||||
{
|
||||
_is_embedded = false;
|
||||
|
||||
|
@ -114,16 +112,15 @@ AudioFileSource::AudioFileSource (Session& s, ustring path, Source::Flag flags,
|
|||
|
||||
/** Constructor used for existing internal-to-session files. File must exist. */
|
||||
AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
|
||||
: AudioSource (s, node)
|
||||
/* _channel is set in set_state() or init() */
|
||||
: Source (s, node)
|
||||
, AudioSource (s, node)
|
||||
, FileSource (s, node, must_exist)
|
||||
{
|
||||
if (set_state (node)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
string foo = _name;
|
||||
|
||||
if (init (foo, must_exist)) {
|
||||
if (init (_name, must_exist)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
@ -136,37 +133,13 @@ AudioFileSource::~AudioFileSource ()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
AudioFileSource::determine_embeddedness (ustring path)
|
||||
{
|
||||
return (path.find("/") == 0);
|
||||
}
|
||||
|
||||
bool
|
||||
AudioFileSource::removable () const
|
||||
{
|
||||
return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0));
|
||||
}
|
||||
|
||||
int
|
||||
AudioFileSource::init (ustring pathstr, bool must_exist)
|
||||
AudioFileSource::init (const ustring& pathstr, bool must_exist)
|
||||
{
|
||||
_length = 0;
|
||||
_timeline_position = 0;
|
||||
_peaks_built = false;
|
||||
|
||||
if (!find (pathstr, must_exist, _file_is_new, _channel)) {
|
||||
throw non_existent_source ();
|
||||
}
|
||||
|
||||
if (_file_is_new && must_exist) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return FileSource::init (pathstr, must_exist);
|
||||
}
|
||||
|
||||
|
||||
ustring
|
||||
AudioFileSource::peak_path (ustring audio_path)
|
||||
{
|
||||
|
@ -282,45 +255,21 @@ AudioFileSource::get_state ()
|
|||
int
|
||||
AudioFileSource::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
if (Source::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (AudioSource::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("channel"))) != 0) {
|
||||
_channel = atoi (prop->value());
|
||||
} else {
|
||||
_channel = 0;
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("name"))) != 0) {
|
||||
_is_embedded = AudioFileSource::determine_embeddedness (prop->value());
|
||||
} else {
|
||||
_is_embedded = false;
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("destructive"))) != 0) {
|
||||
/* old style, from the period when we had DestructiveFileSource */
|
||||
_flags = Flag (_flags | Destructive);
|
||||
|
||||
if (FileSource::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::mark_for_remove ()
|
||||
{
|
||||
// This operation is not allowed for sources for destructive tracks or embedded files.
|
||||
// Fortunately mark_for_remove() is never called for embedded files. This function
|
||||
// must be fixed if that ever happens.
|
||||
if (_flags & Destructive) {
|
||||
return;
|
||||
}
|
||||
|
||||
_flags = Flag (_flags | Removable | RemoveAtDestroy);
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::mark_streaming_write_completed ()
|
||||
{
|
||||
|
@ -339,262 +288,10 @@ AudioFileSource::mark_streaming_write_completed ()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::mark_take (ustring id)
|
||||
{
|
||||
if (writable()) {
|
||||
_take_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
AudioFileSource::move_to_trash (const ustring& trash_dir_name)
|
||||
AudioFileSource::move_dependents_to_trash()
|
||||
{
|
||||
if (is_embedded()) {
|
||||
cerr << "tried to move an embedded region to trash" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!writable()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* don't move the file across filesystems, just stick it in the
|
||||
trash_dir_name directory on whichever filesystem it was already on
|
||||
*/
|
||||
|
||||
ustring newpath;
|
||||
newpath = Glib::path_get_dirname (_path);
|
||||
newpath = Glib::path_get_dirname (newpath);
|
||||
|
||||
newpath += string("/") + trash_dir_name + "/";
|
||||
newpath += Glib::path_get_basename (_path);
|
||||
|
||||
/* the new path already exists, try versioning */
|
||||
if (access (newpath.c_str(), F_OK) == 0) {
|
||||
char buf[PATH_MAX+1];
|
||||
int version = 1;
|
||||
ustring newpath_v;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
|
||||
newpath_v = buf;
|
||||
|
||||
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
|
||||
newpath_v = buf;
|
||||
}
|
||||
|
||||
if (version == 999) {
|
||||
PBD::error << string_compose (
|
||||
_("there are already 1000 files with names like %1; versioning discontinued"),
|
||||
newpath)
|
||||
<< endmsg;
|
||||
} else {
|
||||
newpath = newpath_v;
|
||||
}
|
||||
}
|
||||
|
||||
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (
|
||||
_("cannot rename audio file source from %1 to %2 (%3)"),
|
||||
_path, newpath, strerror (errno)) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (::unlink (peakpath.c_str()) != 0) {
|
||||
error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
|
||||
peakpath, _path, strerror (errno))
|
||||
<< endmsg;
|
||||
/* try to back out */
|
||||
rename (newpath.c_str(), _path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
_path = newpath;
|
||||
peakpath = "";
|
||||
|
||||
/* file can not be removed twice, since the operation is not idempotent */
|
||||
_flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& chan)
|
||||
{
|
||||
ustring::size_type pos;
|
||||
bool ret = false;
|
||||
|
||||
isnew = false;
|
||||
|
||||
if (pathstr[0] != '/') {
|
||||
|
||||
/* non-absolute pathname: find pathstr in search path */
|
||||
|
||||
vector<ustring> dirs;
|
||||
int cnt;
|
||||
ustring fullpath;
|
||||
ustring keeppath;
|
||||
|
||||
if (search_path.length() == 0) {
|
||||
error << _("FileSource: search path not set") << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
split (search_path, dirs, ':');
|
||||
|
||||
cnt = 0;
|
||||
|
||||
for (vector<ustring>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
|
||||
fullpath = *i;
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
|
||||
fullpath += pathstr;
|
||||
|
||||
/* i (paul) made a nasty design error by using ':' as a special character in
|
||||
Ardour 0.99 .. this hack tries to make things sort of work.
|
||||
*/
|
||||
|
||||
if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
|
||||
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
|
||||
/* its a real file, no problem */
|
||||
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
|
||||
} else {
|
||||
|
||||
if (must_exist) {
|
||||
|
||||
/* might be an older session using file:channel syntax. see if the version
|
||||
without the :suffix exists
|
||||
*/
|
||||
|
||||
ustring shorter = pathstr.substr (0, pos);
|
||||
fullpath = *i;
|
||||
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
|
||||
fullpath += shorter;
|
||||
|
||||
if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
chan = atoi (pathstr.substr (pos+1));
|
||||
pathstr = shorter;
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* new derived file (e.g. for timefx) being created in a newer session */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 1) {
|
||||
|
||||
error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path) << endmsg;
|
||||
goto out;
|
||||
|
||||
} else if (cnt == 0) {
|
||||
|
||||
if (must_exist) {
|
||||
error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, search_path) << endmsg;
|
||||
goto out;
|
||||
} else {
|
||||
isnew = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Current find() is unable to parse relative path names to yet non-existant
|
||||
sources. QuickFix(tm) */
|
||||
if (keeppath == "") {
|
||||
if (must_exist) {
|
||||
error << "AudioFileSource::find(), keeppath = \"\", but the file must exist" << endl;
|
||||
} else {
|
||||
keeppath = pathstr;
|
||||
}
|
||||
}
|
||||
|
||||
_name = pathstr;
|
||||
_path = keeppath;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* external files and/or very very old style sessions include full paths */
|
||||
|
||||
/* ugh, handle ':' situation */
|
||||
|
||||
if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
|
||||
|
||||
ustring shorter = pathstr.substr (0, pos);
|
||||
|
||||
if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
chan = atoi (pathstr.substr (pos+1));
|
||||
pathstr = shorter;
|
||||
}
|
||||
}
|
||||
|
||||
_path = pathstr;
|
||||
|
||||
if (is_embedded()) {
|
||||
_name = pathstr;
|
||||
} else {
|
||||
_name = pathstr.substr (pathstr.find_last_of ('/') + 1);
|
||||
}
|
||||
|
||||
if (!Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
|
||||
/* file does not exist or we cannot read it */
|
||||
|
||||
if (must_exist) {
|
||||
error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (errno != ENOENT) {
|
||||
error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* a new file */
|
||||
|
||||
isnew = true;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* already exists */
|
||||
|
||||
ret = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::set_search_path (ustring p)
|
||||
{
|
||||
search_path = p;
|
||||
return ::unlink (peakpath.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -604,55 +301,6 @@ AudioFileSource::set_header_position_offset (nframes_t offset)
|
|||
HeaderPositionOffsetChanged ();
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::set_timeline_position (int64_t pos)
|
||||
{
|
||||
_timeline_position = pos;
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::set_allow_remove_if_empty (bool yn)
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (yn) {
|
||||
_flags = Flag (_flags | RemovableIfEmpty);
|
||||
} else {
|
||||
_flags = Flag (_flags & ~RemovableIfEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
AudioFileSource::set_source_name (ustring newname, bool destructive)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
ustring oldpath = _path;
|
||||
ustring newpath = Session::change_audio_path_by_name (oldpath, _name, newname, destructive);
|
||||
|
||||
if (newpath.empty()) {
|
||||
error << string_compose (_("programming error: %1"), "cannot generate a changed audio path") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Test whether newpath exists, if yes notify the user but continue.
|
||||
if (access(newpath.c_str(),F_OK) == 0) {
|
||||
error << _("Programming error! Ardour tried to rename a file over another file! It's safe to continue working, but please report this to the developers.") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
|
||||
error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_name = Glib::path_get_basename (newpath);
|
||||
_path = newpath;
|
||||
|
||||
return rename_peakfile (peak_path (_path));
|
||||
}
|
||||
|
||||
bool
|
||||
AudioFileSource::is_empty (Session& s, ustring path)
|
||||
{
|
||||
|
@ -678,7 +326,7 @@ AudioFileSource::setup_peakfile ()
|
|||
}
|
||||
|
||||
bool
|
||||
AudioFileSource::safe_file_extension(ustring file)
|
||||
AudioFileSource::safe_audio_file_extension(const ustring& file)
|
||||
{
|
||||
const char* suffixes[] = {
|
||||
".wav", ".WAV",
|
||||
|
@ -715,19 +363,6 @@ AudioFileSource::safe_file_extension(ustring file)
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
AudioFileSource::mark_immutable ()
|
||||
{
|
||||
/* destructive sources stay writable, and their other flags don't
|
||||
change.
|
||||
*/
|
||||
|
||||
if (!(_flags & Destructive)) {
|
||||
_flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Sample*
|
||||
AudioFileSource::get_interleave_buffer (nframes_t size)
|
||||
{
|
||||
|
|
|
@ -56,7 +56,7 @@ bool AudioSource::_build_peakfiles = false;
|
|||
#define _FPP 256
|
||||
|
||||
AudioSource::AudioSource (Session& s, ustring name)
|
||||
: Source (s, name, DataType::AUDIO)
|
||||
: Source (s, DataType::AUDIO, name)
|
||||
{
|
||||
_peaks_built = false;
|
||||
_peak_byte_max = 0;
|
||||
|
@ -118,8 +118,6 @@ AudioSource::set_state (const XMLNode& node)
|
|||
{
|
||||
const XMLProperty* prop;
|
||||
|
||||
Source::set_state (node);
|
||||
|
||||
if ((prop = node.property ("captured-for")) != 0) {
|
||||
_captured_for = prop->value();
|
||||
}
|
||||
|
@ -913,11 +911,3 @@ AudioSource::available_peaks (double zoom_factor) const
|
|||
return (end/sizeof(PeakData)) * _FPP;
|
||||
}
|
||||
|
||||
void
|
||||
AudioSource::update_length (nframes_t pos, nframes_t cnt)
|
||||
{
|
||||
if (pos + cnt > _length) {
|
||||
_length = pos+cnt;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ Configuration::load_state ()
|
|||
|
||||
/* load system configuration first */
|
||||
|
||||
if ( find_file_in_search_path (ardour_search_path() + system_config_search_path(),
|
||||
if (find_file_in_search_path (ardour_search_path() + system_config_search_path(),
|
||||
"ardour_system.rc", system_rc_file) )
|
||||
{
|
||||
XMLTree tree;
|
||||
|
|
419
libs/ardour/file_source.cc
Normal file
419
libs/ardour/file_source.cc
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
Copyright (C) 2006-2009 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h> // for rename(), sigh
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <pbd/convert.h>
|
||||
#include <pbd/basename.h>
|
||||
#include <pbd/mountpoint.h>
|
||||
#include <pbd/stl_delete.h>
|
||||
#include <pbd/strsplit.h>
|
||||
#include <pbd/shortpath.h>
|
||||
#include <pbd/enumwriter.h>
|
||||
|
||||
#include <glibmm/miscutils.h>
|
||||
#include <glibmm/fileutils.h>
|
||||
#include <glibmm/thread.h>
|
||||
|
||||
#include <ardour/file_source.h>
|
||||
#include <ardour/session.h>
|
||||
#include <ardour/session_directory.h>
|
||||
#include <ardour/source_factory.h>
|
||||
#include <ardour/filename_extensions.h>
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace PBD;
|
||||
using namespace Glib;
|
||||
|
||||
static const std::string PATH_SEP = "/"; // I don't do windows
|
||||
|
||||
map<DataType, ustring> FileSource::search_paths;
|
||||
|
||||
FileSource::FileSource (Session& session, DataType type,
|
||||
const ustring& path, bool embedded, Source::Flag flag)
|
||||
: Source(session, type, path, flag)
|
||||
, _path(path)
|
||||
, _file_is_new(true)
|
||||
, _channel (0)
|
||||
, _is_embedded(embedded)
|
||||
{
|
||||
}
|
||||
|
||||
FileSource::FileSource (Session& session, const XMLNode& node, bool must_exist)
|
||||
: Source(session, node)
|
||||
, _file_is_new(false)
|
||||
{
|
||||
_path = _name;
|
||||
_is_embedded = (_path.find(PATH_SEP) != string::npos);
|
||||
}
|
||||
|
||||
bool
|
||||
FileSource::removable () const
|
||||
{
|
||||
return (_flags & Removable)
|
||||
&& ( (_flags & RemoveAtDestroy)
|
||||
|| ((_flags & RemovableIfEmpty) && length() == 0));
|
||||
}
|
||||
|
||||
int
|
||||
FileSource::init (const ustring& pathstr, bool must_exist)
|
||||
{
|
||||
_length = 0;
|
||||
_timeline_position = 0;
|
||||
|
||||
if (!find (_type, pathstr, must_exist, _file_is_new, _channel)) {
|
||||
throw MissingSource ();
|
||||
}
|
||||
|
||||
if (_file_is_new && must_exist) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
FileSource::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
|
||||
if ((prop = node.property (X_("channel"))) != 0) {
|
||||
_channel = atoi (prop->value());
|
||||
} else {
|
||||
_channel = 0;
|
||||
}
|
||||
|
||||
_is_embedded = (_name.find(PATH_SEP) == string::npos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FileSource::mark_take (const ustring& id)
|
||||
{
|
||||
if (writable ()) {
|
||||
_take_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
FileSource::move_to_trash (const ustring& trash_dir_name)
|
||||
{
|
||||
if (is_embedded()) {
|
||||
cerr << "tried to move an embedded region to trash" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!writable()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* don't move the file across filesystems, just stick it in the
|
||||
trash_dir_name directory on whichever filesystem it was already on
|
||||
*/
|
||||
|
||||
ustring newpath;
|
||||
newpath = Glib::path_get_dirname (_path);
|
||||
newpath = Glib::path_get_dirname (newpath);
|
||||
|
||||
newpath += string(PATH_SEP) + trash_dir_name + PATH_SEP;
|
||||
newpath += Glib::path_get_basename (_path);
|
||||
|
||||
/* the new path already exists, try versioning */
|
||||
if (access (newpath.c_str(), F_OK) == 0) {
|
||||
char buf[PATH_MAX+1];
|
||||
int version = 1;
|
||||
ustring newpath_v;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
|
||||
newpath_v = buf;
|
||||
|
||||
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
|
||||
newpath_v = buf;
|
||||
}
|
||||
|
||||
if (version == 999) {
|
||||
PBD::error << string_compose (
|
||||
_("there are already 1000 files with names like %1; versioning discontinued"),
|
||||
newpath) << endmsg;
|
||||
} else {
|
||||
newpath = newpath_v;
|
||||
}
|
||||
}
|
||||
|
||||
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (
|
||||
_("cannot rename file source from %1 to %2 (%3)"),
|
||||
_path, newpath, strerror (errno)) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (move_dependents_to_trash() != 0) {
|
||||
/* try to back out */
|
||||
rename (newpath.c_str(), _path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
_path = newpath;
|
||||
|
||||
/* file can not be removed twice, since the operation is not idempotent */
|
||||
_flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Find the actual source file based on \a path.
|
||||
*
|
||||
* If the source is embedded, \a path should be a filename (no slashes).
|
||||
* If the source is external, \a path should be a full path.
|
||||
* In either case, _path is set to the complete absolute path of the source file.
|
||||
* \return true iff the file was found.
|
||||
*/
|
||||
bool
|
||||
FileSource::find (DataType type, const ustring& path, bool must_exist, bool& isnew, uint16_t& chan)
|
||||
{
|
||||
Glib::ustring search_path = search_paths[type];
|
||||
|
||||
ustring pathstr = path;
|
||||
ustring::size_type pos;
|
||||
bool ret = false;
|
||||
|
||||
isnew = false;
|
||||
|
||||
if (pathstr[0] != '/') {
|
||||
|
||||
/* non-absolute pathname: find pathstr in search path */
|
||||
|
||||
vector<ustring> dirs;
|
||||
int cnt;
|
||||
ustring fullpath;
|
||||
ustring keeppath;
|
||||
|
||||
if (search_path.length() == 0) {
|
||||
error << _("FileSource: search path not set") << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
split (search_path, dirs, ':');
|
||||
|
||||
cnt = 0;
|
||||
|
||||
for (vector<ustring>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
|
||||
fullpath = *i;
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
|
||||
fullpath += pathstr;
|
||||
|
||||
/* i (paul) made a nasty design error by using ':' as a special character in
|
||||
Ardour 0.99 .. this hack tries to make things sort of work.
|
||||
*/
|
||||
|
||||
if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
|
||||
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
|
||||
/* its a real file, no problem */
|
||||
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
|
||||
} else {
|
||||
|
||||
if (must_exist) {
|
||||
|
||||
/* might be an older session using file:channel syntax. see if the version
|
||||
without the :suffix exists
|
||||
*/
|
||||
|
||||
ustring shorter = pathstr.substr (0, pos);
|
||||
fullpath = *i;
|
||||
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
|
||||
fullpath += shorter;
|
||||
|
||||
if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
chan = atoi (pathstr.substr (pos+1));
|
||||
pathstr = shorter;
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* new derived file (e.g. for timefx) being created in a newer session */
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 1) {
|
||||
|
||||
error << string_compose (
|
||||
_("FileSource: \"%1\" is ambigous when searching %2\n\t"),
|
||||
pathstr, search_path) << endmsg;
|
||||
goto out;
|
||||
|
||||
} else if (cnt == 0) {
|
||||
|
||||
if (must_exist) {
|
||||
error << string_compose(
|
||||
_("Filesource: cannot find required file (%1): while searching %2"),
|
||||
pathstr, search_path) << endmsg;
|
||||
goto out;
|
||||
} else {
|
||||
isnew = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Current find() is unable to parse relative path names to yet non-existant
|
||||
sources. QuickFix(tm) */
|
||||
if (keeppath == "") {
|
||||
if (must_exist) {
|
||||
error << "FileSource::find(), keeppath = \"\", but the file must exist" << endl;
|
||||
} else {
|
||||
keeppath = pathstr;
|
||||
}
|
||||
}
|
||||
|
||||
_path = keeppath;
|
||||
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* external files and/or very very old style sessions include full paths */
|
||||
|
||||
/* ugh, handle ':' situation */
|
||||
|
||||
if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
|
||||
|
||||
ustring shorter = pathstr.substr (0, pos);
|
||||
|
||||
if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
chan = atoi (pathstr.substr (pos+1));
|
||||
pathstr = shorter;
|
||||
}
|
||||
}
|
||||
|
||||
_path = pathstr;
|
||||
|
||||
if (!Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
|
||||
|
||||
/* file does not exist or we cannot read it */
|
||||
|
||||
if (must_exist) {
|
||||
error << string_compose(
|
||||
_("Filesource: cannot find required file (%1): %2"),
|
||||
_path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (errno != ENOENT) {
|
||||
error << string_compose(
|
||||
_("Filesource: cannot check for existing file (%1): %2"),
|
||||
_path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* a new file */
|
||||
isnew = true;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* already exists */
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_is_embedded) {
|
||||
_name = Glib::path_get_basename (_name);
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
FileSource::set_source_name (const ustring& newname, bool destructive)
|
||||
{
|
||||
Glib::Mutex::Lock lm (_lock);
|
||||
ustring oldpath = _path;
|
||||
ustring newpath = Session::change_source_path_by_name (oldpath, _name, newname, destructive);
|
||||
|
||||
if (newpath.empty()) {
|
||||
error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Test whether newpath exists, if yes notify the user but continue.
|
||||
if (access(newpath.c_str(),F_OK) == 0) {
|
||||
error << _("Programming error! Ardour tried to rename a file over another file! It's safe to continue working, but please report this to the developers.") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
|
||||
error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_name = Glib::path_get_basename (newpath);
|
||||
_path = newpath;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FileSource::set_search_path (DataType type, const ustring& p)
|
||||
{
|
||||
search_paths[type] = p;
|
||||
}
|
||||
|
||||
void
|
||||
FileSource::mark_immutable ()
|
||||
{
|
||||
/* destructive sources stay writable, and their other flags don't change. */
|
||||
if (!(_flags & Destructive)) {
|
||||
_flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,9 @@ Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs, s
|
|||
|
||||
try {
|
||||
nsrcs.push_back (boost::dynamic_pointer_cast<Source> (
|
||||
SourceFactory::createWritable (region->data_type(), session, path, false, session.frame_rate())));
|
||||
SourceFactory::createWritable (region->data_type(), session,
|
||||
path, true,
|
||||
false, session.frame_rate())));
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
|
|
|
@ -217,13 +217,10 @@ create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
|
|||
const DataType type = ((*i).rfind(".mid") != string::npos)
|
||||
? DataType::MIDI : DataType::AUDIO;
|
||||
|
||||
source = SourceFactory::createWritable (
|
||||
type,
|
||||
sess,
|
||||
i->c_str(),
|
||||
source = SourceFactory::createWritable (type, sess,
|
||||
i->c_str(), true,
|
||||
false, // destructive
|
||||
samplerate
|
||||
);
|
||||
samplerate);
|
||||
}
|
||||
catch (const failed_constructor& err)
|
||||
{
|
||||
|
@ -365,7 +362,7 @@ write_midi_data_to_new_files (Evoral::SMF* source, Session::ImportStatus& status
|
|||
}
|
||||
|
||||
} catch (...) {
|
||||
error << "Corrupt MIDI file " << source->path() << endl;
|
||||
error << "Corrupt MIDI file " << source->file_path() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,8 +50,7 @@ using namespace PBD;
|
|||
sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
|
||||
|
||||
MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
|
||||
: Source (s, name, DataType::MIDI, flags)
|
||||
, _timeline_position(0)
|
||||
: Source (s, DataType::MIDI, name, flags)
|
||||
, _read_data_count(0)
|
||||
, _write_data_count(0)
|
||||
, _converter(s, _timeline_position)
|
||||
|
@ -62,7 +61,6 @@ MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
|
|||
|
||||
MidiSource::MidiSource (Session& s, const XMLNode& node)
|
||||
: Source (s, node)
|
||||
, _timeline_position(0)
|
||||
, _read_data_count(0)
|
||||
, _write_data_count(0)
|
||||
, _converter(s, _timeline_position)
|
||||
|
@ -98,8 +96,6 @@ MidiSource::set_state (const XMLNode& node)
|
|||
{
|
||||
const XMLProperty* prop;
|
||||
|
||||
Source::set_state (node);
|
||||
|
||||
if ((prop = node.property ("captured-for")) != 0) {
|
||||
_captured_for = prop->value();
|
||||
}
|
||||
|
@ -161,10 +157,10 @@ MidiSource::file_changed (string path)
|
|||
}
|
||||
|
||||
void
|
||||
MidiSource::set_timeline_position (nframes_t when)
|
||||
MidiSource::set_timeline_position (int64_t pos)
|
||||
{
|
||||
_timeline_position = when;
|
||||
_converter.set_origin(when);
|
||||
Source::set_timeline_position(pos);
|
||||
_converter.set_origin(pos);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -225,7 +221,8 @@ MidiSource::session_saved()
|
|||
string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
|
||||
|
||||
boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
|
||||
SourceFactory::createWritable(DataType::MIDI, _session, newpath, 1, 0, true));
|
||||
SourceFactory::createWritable(DataType::MIDI, _session,
|
||||
newpath, true, false, _session.frame_rate()));
|
||||
|
||||
newsrc->set_timeline_position(_timeline_position);
|
||||
_model->write_to(newsrc);
|
||||
|
|
|
@ -2534,7 +2534,9 @@ Session::region_name (string& result, string base, bool newlevel)
|
|||
char buf[16];
|
||||
string subbase;
|
||||
|
||||
assert(base.find("/") == string::npos);
|
||||
if (base.find("/") != string::npos) {
|
||||
base = base.substr(base.find_last_of("/") + 1);
|
||||
}
|
||||
|
||||
if (base == "") {
|
||||
|
||||
|
@ -2842,6 +2844,7 @@ Session::remove_region_from_region_list (boost::shared_ptr<Region> r)
|
|||
}
|
||||
|
||||
/* Source Management */
|
||||
|
||||
void
|
||||
Session::add_source (boost::shared_ptr<Source> source)
|
||||
{
|
||||
|
@ -2912,7 +2915,6 @@ Session::source_by_id (const PBD::ID& id)
|
|||
return source;
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<Source>
|
||||
Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
|
||||
{
|
||||
|
@ -2920,26 +2922,19 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
|
|||
|
||||
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||
cerr << "comparing " << path << " with " << i->second->name() << endl;
|
||||
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
|
||||
boost::shared_ptr<AudioFileSource> afs
|
||||
= boost::dynamic_pointer_cast<AudioFileSource>(i->second);
|
||||
|
||||
if (afs && afs->path() == path && chn == afs->channel()) {
|
||||
return afs;
|
||||
}
|
||||
|
||||
}
|
||||
return boost::shared_ptr<Source>();
|
||||
}
|
||||
|
||||
Glib::ustring
|
||||
Session::peak_path (Glib::ustring base) const
|
||||
{
|
||||
sys::path peakfile_path(_session_dir->peak_path());
|
||||
peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
|
||||
return peakfile_path.to_string();
|
||||
}
|
||||
|
||||
string
|
||||
Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive)
|
||||
Session::change_source_path_by_name (string path, string oldname, string newname, bool destructive)
|
||||
{
|
||||
string look_for;
|
||||
string old_basename = PBD::basename_nosuffix (oldname);
|
||||
|
@ -2987,7 +2982,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname,
|
|||
|
||||
/* non-destructive file sources have a name of the form:
|
||||
|
||||
/path/to/NAME-nnnnn(%[LR])?.wav
|
||||
/path/to/NAME-nnnnn(%[LR])?.ext
|
||||
|
||||
the task here is to replace NAME with the new name.
|
||||
*/
|
||||
|
@ -3025,7 +3020,7 @@ Session::change_audio_path_by_name (string path, string oldname, string newname,
|
|||
if (postfix != string::npos) {
|
||||
suffix = suffix.substr (postfix);
|
||||
} else {
|
||||
error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl;
|
||||
error << "Logic error in Session::change_source_path_by_name(), please report" << endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
|
@ -3052,8 +3047,42 @@ Session::change_audio_path_by_name (string path, string oldname, string newname,
|
|||
return path;
|
||||
}
|
||||
|
||||
/** Return the full path (in some session directory) for a new embedded source.
|
||||
* \a name must be a session-unique name that does not contain slashes
|
||||
* (e.g. as returned by new_*_source_name)
|
||||
*/
|
||||
string
|
||||
Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive)
|
||||
Session::new_source_path_from_name (DataType type, const string& name)
|
||||
{
|
||||
assert(name.find("/") == string::npos);
|
||||
|
||||
SessionDirectory sdir(get_best_session_directory_for_new_source());
|
||||
|
||||
sys::path p;
|
||||
if (type == DataType::AUDIO) {
|
||||
p = sdir.sound_path();
|
||||
} else if (type == DataType::MIDI) {
|
||||
p = sdir.midi_path();
|
||||
} else {
|
||||
error << "Unknown source type, unable to create file path" << endmsg;
|
||||
return "";
|
||||
}
|
||||
|
||||
p /= name;
|
||||
return p.to_string();
|
||||
}
|
||||
|
||||
Glib::ustring
|
||||
Session::peak_path (Glib::ustring base) const
|
||||
{
|
||||
sys::path peakfile_path(_session_dir->peak_path());
|
||||
peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
|
||||
return peakfile_path.to_string();
|
||||
}
|
||||
|
||||
/** Return a unique name based on \a base for a new internal audio source */
|
||||
string
|
||||
Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
|
||||
{
|
||||
string spath;
|
||||
uint32_t cnt;
|
||||
|
@ -3062,12 +3091,9 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool
|
|||
string legalized;
|
||||
|
||||
buf[0] = '\0';
|
||||
legalized = legalize_for_path (name);
|
||||
|
||||
/* find a "version" of the file name that doesn't exist in
|
||||
any of the possible directories.
|
||||
*/
|
||||
legalized = legalize_for_path (base);
|
||||
|
||||
// Find a "version" of the base name that doesn't exist in any of the possible directories.
|
||||
for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
|
||||
|
||||
vector<space_and_path>::iterator i;
|
||||
|
@ -3080,18 +3106,24 @@ Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool
|
|||
spath = sdir.sound_path().to_string();
|
||||
|
||||
if (destructive) {
|
||||
|
||||
if (nchan < 2) {
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
|
||||
spath.c_str(), cnt, legalized.c_str());
|
||||
} else if (nchan == 2) {
|
||||
if (chan == 0) {
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", spath.c_str(), cnt, legalized.c_str());
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav",
|
||||
spath.c_str(), cnt, legalized.c_str());
|
||||
} else {
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.c_str());
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav",
|
||||
spath.c_str(), cnt, legalized.c_str());
|
||||
}
|
||||
} else if (nchan < 26) {
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav",
|
||||
spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
|
||||
} else {
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
|
||||
snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav",
|
||||
spath.c_str(), cnt, legalized.c_str());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -3125,173 +3157,42 @@ 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;
|
||||
error << string_compose(
|
||||
_("There are already %1 recordings for %2, which I consider too many."),
|
||||
limit, base) << endmsg;
|
||||
destroy ();
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
/* we now have a unique name for the file, but figure out where to
|
||||
actually put it.
|
||||
*/
|
||||
|
||||
string foo = buf;
|
||||
|
||||
SessionDirectory sdir(get_best_session_directory_for_new_source ());
|
||||
|
||||
spath = sdir.sound_path().to_string();
|
||||
spath += '/';
|
||||
|
||||
string::size_type pos = foo.find_last_of ('/');
|
||||
|
||||
if (pos == string::npos) {
|
||||
spath += foo;
|
||||
} else {
|
||||
spath += foo.substr (pos + 1);
|
||||
}
|
||||
|
||||
return spath;
|
||||
return Glib::path_get_basename(buf);
|
||||
}
|
||||
|
||||
/** Create a new embedded audio source */
|
||||
boost::shared_ptr<AudioFileSource>
|
||||
Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
|
||||
{
|
||||
string spath = audio_path_from_name (ds.name(), ds.n_channels().n_audio(), chan, destructive);
|
||||
const size_t n_chans = ds.n_channels().n_audio();
|
||||
const string name = new_audio_source_name (ds.name(), n_chans, chan, destructive);
|
||||
const string path = new_source_path_from_name(DataType::AUDIO, name);
|
||||
return boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate()));
|
||||
SourceFactory::createWritable (
|
||||
DataType::AUDIO, *this, path, true, destructive, frame_rate()));
|
||||
}
|
||||
|
||||
// FIXME: _terrible_ code duplication
|
||||
/** Return a unique name based on \a base for a new internal MIDI source */
|
||||
string
|
||||
Session::change_midi_path_by_name (string path, string oldname, string newname, bool destructive)
|
||||
Session::new_midi_source_name (const string& base)
|
||||
{
|
||||
string look_for;
|
||||
string old_basename = PBD::basename_nosuffix (oldname);
|
||||
string new_legalized = legalize_for_path (newname);
|
||||
|
||||
/* note: we know (or assume) the old path is already valid */
|
||||
|
||||
if (destructive) {
|
||||
|
||||
/* destructive file sources have a name of the form:
|
||||
|
||||
/path/to/Tnnnn-NAME(%[LR])?.wav
|
||||
|
||||
the task here is to replace NAME with the new name.
|
||||
*/
|
||||
|
||||
/* find last slash */
|
||||
|
||||
string dir;
|
||||
string prefix;
|
||||
string::size_type slash;
|
||||
string::size_type dash;
|
||||
|
||||
if ((slash = path.find_last_of ('/')) == string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
dir = path.substr (0, slash+1);
|
||||
|
||||
/* '-' is not a legal character for the NAME part of the path */
|
||||
|
||||
if ((dash = path.find_last_of ('-')) == string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
prefix = path.substr (slash+1, dash-(slash+1));
|
||||
|
||||
path = dir;
|
||||
path += prefix;
|
||||
path += '-';
|
||||
path += new_legalized;
|
||||
path += ".mid"; /* XXX gag me with a spoon */
|
||||
|
||||
} else {
|
||||
|
||||
/* non-destructive file sources have a name of the form:
|
||||
|
||||
/path/to/NAME-nnnnn(%[LR])?.wav
|
||||
|
||||
the task here is to replace NAME with the new name.
|
||||
*/
|
||||
|
||||
string dir;
|
||||
string suffix;
|
||||
string::size_type slash;
|
||||
string::size_type dash;
|
||||
string::size_type postfix;
|
||||
|
||||
/* find last slash */
|
||||
|
||||
if ((slash = path.find_last_of ('/')) == string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
dir = path.substr (0, slash+1);
|
||||
|
||||
/* '-' is not a legal character for the NAME part of the path */
|
||||
|
||||
if ((dash = path.find_last_of ('-')) == string::npos) {
|
||||
return "";
|
||||
}
|
||||
|
||||
suffix = path.substr (dash+1);
|
||||
|
||||
// Suffix is now everything after the dash. Now we need to eliminate
|
||||
// the nnnnn part, which is done by either finding a '%' or a '.'
|
||||
|
||||
postfix = suffix.find_last_of ("%");
|
||||
if (postfix == string::npos) {
|
||||
postfix = suffix.find_last_of ('.');
|
||||
}
|
||||
|
||||
if (postfix != string::npos) {
|
||||
suffix = suffix.substr (postfix);
|
||||
} else {
|
||||
error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
const uint32_t limit = 10000;
|
||||
char buf[PATH_MAX+1];
|
||||
|
||||
for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
|
||||
|
||||
snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
|
||||
|
||||
if (access (buf, F_OK) != 0) {
|
||||
path = buf;
|
||||
break;
|
||||
}
|
||||
path = "";
|
||||
}
|
||||
|
||||
if (path == "") {
|
||||
error << "FATAL ERROR! Could not find a " << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
string
|
||||
Session::midi_path_from_name (string name)
|
||||
{
|
||||
string spath;
|
||||
uint32_t cnt;
|
||||
char buf[PATH_MAX+1];
|
||||
const uint32_t limit = 10000;
|
||||
string legalized;
|
||||
|
||||
buf[0] = '\0';
|
||||
legalized = legalize_for_path (name);
|
||||
|
||||
/* find a "version" of the file name that doesn't exist in
|
||||
any of the possible directories.
|
||||
*/
|
||||
legalized = legalize_for_path (base);
|
||||
|
||||
// Find a "version" of the file name that doesn't exist in any of the possible directories.
|
||||
for (cnt = 1; cnt <= limit; ++cnt) {
|
||||
|
||||
vector<space_and_path>::iterator i;
|
||||
|
@ -3302,12 +3203,9 @@ Session::midi_path_from_name (string name)
|
|||
SessionDirectory sdir((*i).path);
|
||||
|
||||
sys::path p = sdir.midi_path();
|
||||
|
||||
p /= legalized;
|
||||
|
||||
spath = p.to_string();
|
||||
|
||||
snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt);
|
||||
snprintf (buf, sizeof(buf), "%s-%u.mid", p.to_string().c_str(), cnt);
|
||||
|
||||
if (sys::exists (buf)) {
|
||||
existing++;
|
||||
|
@ -3319,39 +3217,28 @@ Session::midi_path_from_name (string name)
|
|||
}
|
||||
|
||||
if (cnt > limit) {
|
||||
error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
|
||||
error << string_compose(
|
||||
_("There are already %1 recordings for %2, which I consider too many."),
|
||||
limit, base) << endmsg;
|
||||
destroy ();
|
||||
throw failed_constructor();
|
||||
}
|
||||
}
|
||||
|
||||
/* we now have a unique name for the file, but figure out where to
|
||||
actually put it.
|
||||
*/
|
||||
|
||||
string foo = buf;
|
||||
|
||||
SessionDirectory sdir(get_best_session_directory_for_new_source ());
|
||||
|
||||
spath = sdir.midi_path().to_string();
|
||||
spath += '/';
|
||||
|
||||
string::size_type pos = foo.find_last_of ('/');
|
||||
|
||||
if (pos == string::npos) {
|
||||
spath += foo;
|
||||
} else {
|
||||
spath += foo.substr (pos + 1);
|
||||
}
|
||||
|
||||
return spath;
|
||||
return Glib::path_get_basename(buf);
|
||||
}
|
||||
|
||||
|
||||
/** Create a new embedded MIDI source */
|
||||
boost::shared_ptr<MidiSource>
|
||||
Session::create_midi_source_for_session (MidiDiskstream& ds)
|
||||
{
|
||||
string mpath = midi_path_from_name (ds.name());
|
||||
const string name = new_midi_source_name (ds.name());
|
||||
const string path = new_source_path_from_name (DataType::MIDI, name);
|
||||
|
||||
return boost::dynamic_pointer_cast<SMFSource> (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate()));
|
||||
return boost::dynamic_pointer_cast<SMFSource> (
|
||||
SourceFactory::createWritable (
|
||||
DataType::MIDI, *this, path, true, false, frame_rate()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -4130,7 +4017,7 @@ Session::write_one_track (AudioTrack& track, nframes_t start, nframes_t end,
|
|||
|
||||
try {
|
||||
fsource = boost::dynamic_pointer_cast<AudioFileSource> (
|
||||
SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
|
||||
SourceFactory::createWritable (DataType::AUDIO, *this, buf, true, false, frame_rate()));
|
||||
}
|
||||
|
||||
catch (failed_constructor& err) {
|
||||
|
|
|
@ -414,12 +414,7 @@ Session::setup_raid_path (string path)
|
|||
SearchPath sound_search_path;
|
||||
SearchPath midi_search_path;
|
||||
|
||||
for (
|
||||
SearchPath::const_iterator i = search_path.begin();
|
||||
i != search_path.end();
|
||||
++i
|
||||
)
|
||||
{
|
||||
for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
|
||||
sp.path = (*i).to_string ();
|
||||
sp.blocks = 0; // not needed
|
||||
session_dirs.push_back (sp);
|
||||
|
@ -430,14 +425,11 @@ Session::setup_raid_path (string path)
|
|||
midi_search_path += sdir.midi_path ();
|
||||
}
|
||||
|
||||
// set the AudioFileSource and SMFSource search path
|
||||
|
||||
AudioFileSource::set_search_path (sound_search_path.to_string ());
|
||||
SMFSource::set_search_path (midi_search_path.to_string ());
|
||||
|
||||
// set the search path for each data type
|
||||
FileSource::set_search_path (DataType::AUDIO, sound_search_path.to_string ());
|
||||
SMFSource::set_search_path (DataType::MIDI, midi_search_path.to_string ());
|
||||
|
||||
// reset the round-robin soundfile path thingie
|
||||
|
||||
last_rr_session_dir = session_dirs.begin();
|
||||
}
|
||||
|
||||
|
@ -997,16 +989,11 @@ Session::state(bool full_state)
|
|||
|
||||
for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
|
||||
|
||||
/* Don't save information about AudioFileSources that are empty */
|
||||
/* Don't save information about non-destructive file sources that are empty */
|
||||
/* FIXME: MIDI breaks if this is made FileSource like it should be... */
|
||||
|
||||
boost::shared_ptr<AudioFileSource> fs;
|
||||
|
||||
if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) {
|
||||
|
||||
/* Don't save sources that are empty, unless they're destructive (which are OK
|
||||
if they are empty, because we will re-use them every time.)
|
||||
*/
|
||||
|
||||
if (!fs->destructive()) {
|
||||
if (fs->length() == 0) {
|
||||
continue;
|
||||
|
@ -1739,14 +1726,11 @@ Session::load_sources (const XMLNode& node)
|
|||
set_dirty();
|
||||
|
||||
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
||||
|
||||
try {
|
||||
if ((source = XMLSourceFactory (**niter)) == 0) {
|
||||
error << _("Session: cannot create Source from XML description.") << endmsg;
|
||||
}
|
||||
}
|
||||
|
||||
catch (non_existent_source& err) {
|
||||
} catch (MissingSource& err) {
|
||||
warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
|
||||
source = SourceFactory::createSilent (*this, **niter, max_frames, _current_frame_rate);
|
||||
}
|
||||
|
@ -2622,9 +2606,9 @@ Session::cleanup_sources (Session::cleanup_report& rep)
|
|||
*/
|
||||
|
||||
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||
boost::shared_ptr<AudioFileSource> fs;
|
||||
boost::shared_ptr<FileSource> fs;
|
||||
|
||||
if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (i->second)) != 0) {
|
||||
if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
|
||||
all_sources.insert (fs->path());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2007 Paul Davis
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include <ardour/silentfilesource.h>
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
SilentFileSource::SilentFileSource (Session& s, const XMLNode& node, nframes_t len, float sr)
|
||||
: AudioFileSource (s, node, false)
|
||||
{
|
||||
_length = len;
|
||||
_sample_rate = sr;
|
||||
}
|
||||
|
||||
SilentFileSource::~SilentFileSource ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SilentFileSource::set_length (nframes_t len)
|
||||
{
|
||||
_length = len;
|
||||
}
|
|
@ -45,32 +45,31 @@
|
|||
#include "i18n.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
|
||||
string SMFSource::_search_path;
|
||||
using namespace Glib;
|
||||
|
||||
/** Constructor used for new internal-to-session files. File cannot exist. */
|
||||
SMFSource::SMFSource(Session& s, std::string path, Source::Flag flags)
|
||||
: MidiSource(s, region_name_from_path(path, false))
|
||||
SMFSource::SMFSource (Session& s, const ustring& path, bool embedded, Source::Flag flags)
|
||||
: Source(s, DataType::MIDI, path, flags)
|
||||
, MidiSource(s, path)
|
||||
, FileSource(s, DataType::MIDI, path, embedded, flags)
|
||||
, Evoral::SMF()
|
||||
, _allow_remove_if_empty(true)
|
||||
, _last_ev_time_beats(0.0)
|
||||
, _last_ev_time_frames(0)
|
||||
{
|
||||
if (init(path, false)) {
|
||||
if (init(_name, false)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
if (create(path)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
}
|
||||
|
||||
/** Constructor used for existing internal-to-session files. File must exist. */
|
||||
SMFSource::SMFSource(Session& s, const XMLNode& node)
|
||||
: MidiSource(s, node)
|
||||
, _allow_remove_if_empty(true)
|
||||
/** Constructor used for existing internal-to-session files. */
|
||||
SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
|
||||
: Source(s, node)
|
||||
, MidiSource(s, node)
|
||||
, FileSource(s, node, must_exist)
|
||||
, _last_ev_time_beats(0.0)
|
||||
, _last_ev_time_frames(0)
|
||||
{
|
||||
|
@ -85,8 +84,6 @@ SMFSource::SMFSource(Session& s, const XMLNode& node)
|
|||
if (open(_path)) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
}
|
||||
|
||||
SMFSource::~SMFSource ()
|
||||
|
@ -96,40 +93,12 @@ SMFSource::~SMFSource ()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SMFSource::removable () const
|
||||
{
|
||||
return (_flags & Removable) && ((_flags & RemoveAtDestroy) ||
|
||||
((_flags & RemovableIfEmpty) && is_empty()));
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::init (string pathstr, bool must_exist)
|
||||
{
|
||||
bool is_new = false;
|
||||
|
||||
if (!find (pathstr, must_exist, is_new)) {
|
||||
cerr << "cannot find " << pathstr << " with me = " << must_exist << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_new && must_exist) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** All stamps in audio frames */
|
||||
nframes_t
|
||||
SMFSource::read_unlocked (MidiRingBuffer<nframes_t>& dst, nframes_t start, nframes_t dur,
|
||||
nframes_t stamp_offset, nframes_t negative_stamp_offset) const
|
||||
{
|
||||
//cerr << "SMF read_unlocked " << name() << " read "
|
||||
//<< start << ", count=" << dur << ", offset=" << stamp_offset << endl;
|
||||
|
||||
int ret;
|
||||
int ret = 0;
|
||||
uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
|
||||
|
||||
_read_data_count = 0;
|
||||
|
@ -266,7 +235,7 @@ SMFSource::write_unlocked (MidiRingBuffer<nframes_t>& src, nframes_t dur)
|
|||
|
||||
/** Append an event with a timestamp in beats (double) */
|
||||
void
|
||||
SMFSource::append_event_unlocked_beats(const Evoral::Event<double>& ev)
|
||||
SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
|
||||
{
|
||||
if (ev.size() == 0) {
|
||||
return;
|
||||
|
@ -297,7 +266,7 @@ SMFSource::append_event_unlocked_beats(const Evoral::Event<double>& ev)
|
|||
|
||||
/** Append an event with a timestamp in frames (nframes_t) */
|
||||
void
|
||||
SMFSource::append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev)
|
||||
SMFSource::append_event_unlocked_frames (const Evoral::Event<nframes_t>& ev)
|
||||
{
|
||||
if (ev.size() == 0) {
|
||||
return;
|
||||
|
@ -330,48 +299,30 @@ SMFSource::append_event_unlocked_frames(const Evoral::Event<nframes_t>& ev)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
XMLNode&
|
||||
SMFSource::get_state ()
|
||||
{
|
||||
XMLNode& root (MidiSource::get_state());
|
||||
char buf[16];
|
||||
snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
|
||||
root.add_property ("flags", buf);
|
||||
return root;
|
||||
return MidiSource::get_state();
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::set_state (const XMLNode& node)
|
||||
{
|
||||
const XMLProperty* prop;
|
||||
if (Source::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (MidiSource::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((prop = node.property (X_("flags"))) != 0) {
|
||||
int ival;
|
||||
sscanf (prop->value().c_str(), "0x%x", &ival);
|
||||
_flags = Flag (ival);
|
||||
} else {
|
||||
_flags = Flag (0);
|
||||
|
||||
if (FileSource::set_state (node)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(_name.find("/") == string::npos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_for_remove ()
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
_flags = Flag (_flags | RemoveAtDestroy);
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
|
||||
{
|
||||
|
@ -394,212 +345,14 @@ SMFSource::mark_streaming_write_completed ()
|
|||
Evoral::SMF::end_write ();
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::mark_take (string id)
|
||||
{
|
||||
if (writable()) {
|
||||
_take_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::move_to_trash (const string trash_dir_name)
|
||||
{
|
||||
if (!writable()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* don't move the file across filesystems, just stick it in the
|
||||
trash_dir_name directory on whichever filesystem it was already on
|
||||
*/
|
||||
|
||||
Glib::ustring newpath;
|
||||
newpath = Glib::path_get_dirname (_path);
|
||||
newpath = Glib::path_get_dirname (newpath);
|
||||
|
||||
newpath += string("/") + trash_dir_name + "/";
|
||||
newpath += Glib::path_get_basename (_path);
|
||||
|
||||
/* the new path already exists, try versioning */
|
||||
if (access (newpath.c_str(), F_OK) == 0) {
|
||||
char buf[PATH_MAX+1];
|
||||
int version = 1;
|
||||
string newpath_v;
|
||||
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
|
||||
newpath_v = buf;
|
||||
|
||||
while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
|
||||
snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
|
||||
newpath_v = buf;
|
||||
}
|
||||
|
||||
if (version == 999) {
|
||||
PBD::error << string_compose (
|
||||
_("there are already 1000 files with names like %1; versioning discontinued"),
|
||||
newpath) << endmsg;
|
||||
} else {
|
||||
newpath = newpath_v;
|
||||
}
|
||||
}
|
||||
|
||||
if (::rename (_path.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (
|
||||
_("cannot rename midi file source from %1 to %2 (%3)"),
|
||||
_path, newpath, strerror (errno)) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_path = newpath;
|
||||
|
||||
/* file can not be removed twice, since the operation is not idempotent */
|
||||
_flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
SMFSource::safe_file_extension(const Glib::ustring& file)
|
||||
SMFSource::safe_midi_file_extension (const Glib::ustring& file)
|
||||
{
|
||||
return (file.rfind(".mid") != Glib::ustring::npos);
|
||||
}
|
||||
|
||||
// FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
|
||||
bool
|
||||
SMFSource::find (string pathstr, bool must_exist, bool& isnew)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
isnew = false;
|
||||
|
||||
if (pathstr[0] != '/') {
|
||||
|
||||
/* non-absolute pathname: find pathstr in search path */
|
||||
|
||||
vector<string> dirs;
|
||||
int cnt;
|
||||
string fullpath;
|
||||
string keeppath;
|
||||
|
||||
if (_search_path.length() == 0) {
|
||||
PBD::error << _("FileSource: search path not set") << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
split (_search_path, dirs, ':');
|
||||
|
||||
cnt = 0;
|
||||
|
||||
for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
|
||||
fullpath = *i;
|
||||
if (fullpath[fullpath.length()-1] != '/') {
|
||||
fullpath += '/';
|
||||
}
|
||||
fullpath += pathstr;
|
||||
|
||||
if (access (fullpath.c_str(), R_OK) == 0) {
|
||||
keeppath = fullpath;
|
||||
++cnt;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt > 1) {
|
||||
|
||||
PBD::error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, _search_path) << endmsg;
|
||||
goto out;
|
||||
|
||||
} else if (cnt == 0) {
|
||||
|
||||
if (must_exist) {
|
||||
PBD::error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, _search_path) << endmsg;
|
||||
goto out;
|
||||
} else {
|
||||
isnew = true;
|
||||
}
|
||||
}
|
||||
|
||||
_name = pathstr;
|
||||
_path = keeppath;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* external files and/or very very old style sessions include full paths */
|
||||
|
||||
_path = pathstr;
|
||||
_name = pathstr.substr (pathstr.find_last_of ('/') + 1);
|
||||
|
||||
if (access (_path.c_str(), R_OK) != 0) {
|
||||
|
||||
/* file does not exist or we cannot read it */
|
||||
|
||||
if (must_exist) {
|
||||
PBD::error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (errno != ENOENT) {
|
||||
PBD::error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* a new file */
|
||||
|
||||
isnew = true;
|
||||
ret = true;
|
||||
|
||||
} else {
|
||||
|
||||
/* already exists */
|
||||
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::set_search_path (string p)
|
||||
{
|
||||
_search_path = p;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SMFSource::set_allow_remove_if_empty (bool yn)
|
||||
{
|
||||
if (writable()) {
|
||||
_allow_remove_if_empty = yn;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SMFSource::set_source_name (string newname, bool destructive)
|
||||
{
|
||||
//Glib::Mutex::Lock lm (_lock); FIXME
|
||||
string oldpath = _path;
|
||||
string newpath = Session::change_midi_path_by_name (oldpath, _name, newname, destructive);
|
||||
|
||||
if (newpath.empty()) {
|
||||
PBD::error << string_compose (_("programming error: %1"), "cannot generate a changed midi path") << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
|
||||
PBD::error << string_compose (_("cannot rename midi file for %1 to %2"), _name, newpath) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_name = Glib::path_get_basename (newpath);
|
||||
_path = newpath;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::load_model(bool lock, bool force_reload)
|
||||
SMFSource::load_model (bool lock, bool force_reload)
|
||||
{
|
||||
if (_writing) {
|
||||
return;
|
||||
|
@ -662,7 +415,7 @@ SMFSource::load_model(bool lock, bool force_reload)
|
|||
#define LINEAR_INTERPOLATION_MODE_WORKS_PROPERLY 0
|
||||
|
||||
void
|
||||
SMFSource::set_default_controls_interpolation()
|
||||
SMFSource::set_default_controls_interpolation ()
|
||||
{
|
||||
// set interpolation style to defaults, can be changed by the GUI later
|
||||
Evoral::ControlSet::Controls controls = _model->controls();
|
||||
|
@ -679,14 +432,14 @@ SMFSource::set_default_controls_interpolation()
|
|||
|
||||
|
||||
void
|
||||
SMFSource::destroy_model()
|
||||
SMFSource::destroy_model ()
|
||||
{
|
||||
//cerr << _name << " destroying model " << _model.get() << endl;
|
||||
_model.reset();
|
||||
}
|
||||
|
||||
void
|
||||
SMFSource::flush_midi()
|
||||
SMFSource::flush_midi ()
|
||||
{
|
||||
Evoral::SMF::end_write();
|
||||
}
|
||||
|
|
|
@ -50,38 +50,40 @@ const Source::Flag SndFileSource::default_writable_flags = Source::Flag (
|
|||
Source::CanRename );
|
||||
|
||||
SndFileSource::SndFileSource (Session& s, const XMLNode& node)
|
||||
: AudioFileSource (s, node)
|
||||
: Source(s, node)
|
||||
, AudioFileSource (s, node)
|
||||
{
|
||||
init ();
|
||||
init_sndfile ();
|
||||
|
||||
if (open()) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
||||
SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags)
|
||||
/* files created this way are never writable or removable */
|
||||
: AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
|
||||
/** Files created this way are never writable or removable */
|
||||
SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded, int chn, Flag flags)
|
||||
: Source(s, DataType::AUDIO, path, flags)
|
||||
, AudioFileSource (s, path, embedded,
|
||||
Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
|
||||
{
|
||||
_channel = chn;
|
||||
|
||||
init ();
|
||||
init_sndfile ();
|
||||
|
||||
if (open()) {
|
||||
throw failed_constructor ();
|
||||
}
|
||||
}
|
||||
|
||||
SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
|
||||
: AudioFileSource (s, path, flags, sfmt, hf)
|
||||
/** This constructor is used to construct new files, not open existing ones. */
|
||||
SndFileSource::SndFileSource (Session& s, const ustring& path, bool embedded,
|
||||
SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
|
||||
: Source(s, DataType::AUDIO, path, flags)
|
||||
, AudioFileSource (s, path, embedded, flags, sfmt, hf)
|
||||
{
|
||||
int fmt = 0;
|
||||
|
||||
init ();
|
||||
|
||||
/* this constructor is used to construct new files, not open
|
||||
existing ones.
|
||||
*/
|
||||
init_sndfile ();
|
||||
|
||||
_file_is_new = true;
|
||||
|
||||
|
@ -161,7 +163,7 @@ SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, Heade
|
|||
}
|
||||
|
||||
void
|
||||
SndFileSource::init ()
|
||||
SndFileSource::init_sndfile ()
|
||||
{
|
||||
ustring file;
|
||||
|
||||
|
@ -191,7 +193,8 @@ SndFileSource::init ()
|
|||
_timeline_position = header_position_offset;
|
||||
}
|
||||
|
||||
AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
|
||||
AudioFileSource::HeaderPositionOffsetChanged.connect (
|
||||
mem_fun (*this, &SndFileSource::handle_header_position_change));
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -46,10 +46,11 @@
|
|||
using namespace std;
|
||||
using namespace ARDOUR;
|
||||
|
||||
Source::Source (Session& s, const string& name, DataType type, Flag flags)
|
||||
Source::Source (Session& s, DataType type, const string& name, Flag flags)
|
||||
: SessionObject(s, name)
|
||||
, _type(type)
|
||||
, _flags(flags)
|
||||
, _timeline_position(0)
|
||||
{
|
||||
_analysed = false;
|
||||
_timestamp = 0;
|
||||
|
@ -61,6 +62,7 @@ Source::Source (Session& s, const XMLNode& node)
|
|||
: SessionObject(s, "unnamed source")
|
||||
, _type(DataType::AUDIO)
|
||||
, _flags (Flag (Writable|CanRename))
|
||||
, _timeline_position(0)
|
||||
{
|
||||
_timestamp = 0;
|
||||
_length = 0;
|
||||
|
@ -83,7 +85,7 @@ Source::get_state ()
|
|||
XMLNode *node = new XMLNode ("Source");
|
||||
char buf[64];
|
||||
|
||||
node->add_property ("name", _name);
|
||||
node->add_property ("name", name());
|
||||
node->add_property ("type", _type.to_string());
|
||||
node->add_property (X_("flags"), enum_2_string (_flags));
|
||||
_id.print (buf, sizeof (buf));
|
||||
|
@ -128,6 +130,11 @@ Source::set_state (const XMLNode& node)
|
|||
_flags = Flag (0);
|
||||
|
||||
}
|
||||
|
||||
/* old style, from the period when we had DestructiveFileSource */
|
||||
if ((prop = node.property (X_("destructive"))) != 0) {
|
||||
_flags = Flag (_flags | Destructive);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -136,7 +143,7 @@ void
|
|||
Source::update_length (nframes_t pos, nframes_t cnt)
|
||||
{
|
||||
if (pos + cnt > _length) {
|
||||
_length = pos+cnt;
|
||||
_length = pos + cnt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,3 +279,36 @@ Source::check_for_analysis_data_on_disk ()
|
|||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
Source::mark_for_remove ()
|
||||
{
|
||||
// This operation is not allowed for sources for destructive tracks or embedded files.
|
||||
// Fortunately mark_for_remove() is never called for embedded files. This function
|
||||
// must be fixed if that ever happens.
|
||||
if (_flags & Destructive) {
|
||||
return;
|
||||
}
|
||||
|
||||
_flags = Flag (_flags | Removable | RemoveAtDestroy);
|
||||
}
|
||||
|
||||
void
|
||||
Source::set_timeline_position (int64_t pos)
|
||||
{
|
||||
_timeline_position = pos;
|
||||
}
|
||||
|
||||
void
|
||||
Source::set_allow_remove_if_empty (bool yn)
|
||||
{
|
||||
if (!writable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (yn) {
|
||||
_flags = Flag (_flags | RemovableIfEmpty);
|
||||
} else {
|
||||
_flags = Flag (_flags & ~RemovableIfEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,7 +179,8 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
|
|||
}
|
||||
|
||||
boost::shared_ptr<Source>
|
||||
SourceFactory::createReadable (DataType type, Session& s, string path, int chn, Source::Flag flags, bool announce, bool defer_peaks)
|
||||
SourceFactory::createReadable (DataType type, Session& s, const string& path, bool embedded,
|
||||
int chn, Source::Flag flags, bool announce, bool defer_peaks)
|
||||
{
|
||||
if (type == DataType::AUDIO) {
|
||||
|
||||
|
@ -187,7 +188,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
|
|||
|
||||
try {
|
||||
|
||||
boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags));
|
||||
boost::shared_ptr<Source> ret (new SndFileSource (s, path, embedded, chn, flags));
|
||||
|
||||
if (setup_peakfile (ret, defer_peaks)) {
|
||||
return boost::shared_ptr<Source>();
|
||||
|
@ -203,7 +204,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
|
|||
catch (failed_constructor& err) {
|
||||
#ifdef USE_COREAUDIO_FOR_FILES
|
||||
|
||||
boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, chn, flags));
|
||||
boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, embedded, chn, flags));
|
||||
if (setup_peakfile (ret, defer_peaks)) {
|
||||
return boost::shared_ptr<Source>();
|
||||
}
|
||||
|
@ -224,8 +225,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
|
|||
|
||||
} else if (type == DataType::MIDI) {
|
||||
|
||||
// FIXME: flags?
|
||||
boost::shared_ptr<Source> ret (new SMFSource (s, path, SMFSource::Flag(0)));
|
||||
boost::shared_ptr<Source> ret (new SMFSource (s, path, embedded, SMFSource::Flag(0)));
|
||||
|
||||
if (announce) {
|
||||
SourceCreated (ret);
|
||||
|
@ -239,12 +239,13 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
|
|||
}
|
||||
|
||||
boost::shared_ptr<Source>
|
||||
SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce, bool defer_peaks)
|
||||
SourceFactory::createWritable (DataType type, Session& s, const std::string& path, bool embedded,
|
||||
bool destructive, nframes_t rate, bool announce, bool defer_peaks)
|
||||
{
|
||||
/* this might throw failed_constructor(), which is OK */
|
||||
|
||||
if (type == DataType::AUDIO) {
|
||||
boost::shared_ptr<Source> ret (new SndFileSource (s, path,
|
||||
boost::shared_ptr<Source> ret (new SndFileSource (s, path, embedded,
|
||||
Config->get_native_file_data_format(),
|
||||
Config->get_native_file_header_format(),
|
||||
rate,
|
||||
|
@ -265,7 +266,7 @@ SourceFactory::createWritable (DataType type, Session& s, std::string path, bool
|
|||
|
||||
} else if (type == DataType::MIDI) {
|
||||
|
||||
boost::shared_ptr<Source> ret (new SMFSource (s, path));
|
||||
boost::shared_ptr<Source> ret (new SMFSource (s, path, embedded, Source::Flag(0)));
|
||||
|
||||
// no analysis data - this is a new file
|
||||
|
||||
|
@ -278,3 +279,4 @@ SourceFactory::createWritable (DataType type, Session& s, std::string path, bool
|
|||
|
||||
return boost::shared_ptr<Source> ();
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
int create(const std::string& path, int track=1, uint16_t ppqn=19200) THROW_FILE_ERROR;
|
||||
void close() THROW_FILE_ERROR;
|
||||
|
||||
const std::string& path() const { return _path; };
|
||||
const std::string& file_path() const { return _file_path; };
|
||||
|
||||
void seek_to_start() const;
|
||||
int seek_to_track(int track);
|
||||
|
@ -65,7 +65,7 @@ public:
|
|||
void flush() {};
|
||||
|
||||
private:
|
||||
std::string _path;
|
||||
std::string _file_path;
|
||||
smf_t* _smf;
|
||||
smf_track_t* _smf_track;
|
||||
bool _empty; ///< true iff file contains(non-empty) events
|
||||
|
|
|
@ -80,8 +80,8 @@ SMF::open(const std::string& path, int track) THROW_FILE_ERROR
|
|||
smf_delete(_smf);
|
||||
}
|
||||
|
||||
_path = path;
|
||||
_smf = smf_load(_path.c_str());
|
||||
_file_path = path;
|
||||
_smf = smf_load(_file_path.c_str());
|
||||
if (_smf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ SMF::create(const std::string& path, int track, uint16_t ppqn) THROW_FILE_ERROR
|
|||
smf_delete(_smf);
|
||||
}
|
||||
|
||||
_path = path;
|
||||
_file_path = path;
|
||||
|
||||
_smf = smf_new();
|
||||
if (smf_set_ppqn(_smf, ppqn) != 0) {
|
||||
|
@ -148,7 +148,7 @@ void
|
|||
SMF::close() THROW_FILE_ERROR
|
||||
{
|
||||
if (_smf) {
|
||||
if (smf_save(_smf, _path.c_str()) != 0) {
|
||||
if (smf_save(_smf, _file_path.c_str()) != 0) {
|
||||
throw FileError();
|
||||
}
|
||||
smf_delete(_smf);
|
||||
|
@ -258,7 +258,7 @@ SMF::begin_write()
|
|||
void
|
||||
SMF::end_write() THROW_FILE_ERROR
|
||||
{
|
||||
if (smf_save(_smf, _path.c_str()) != 0)
|
||||
if (smf_save(_smf, _file_path.c_str()) != 0)
|
||||
throw FileError();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user