initial volley of work for AudioPlaylistSource, the basic prototype for sources-that-are-nested

git-svn-id: svn://localhost/ardour2/branches/3.0@9507 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2011-05-13 19:55:39 +00:00
parent d0bb7df866
commit 41b23ca647
11 changed files with 392 additions and 20 deletions

View File

@ -0,0 +1,72 @@
/*
Copyright (C) 2011 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_audio_playlist_source_h__
#define __ardour_audio_playlist_source_h__
#include <string>
#include <boost/shared_ptr.hpp>
#include "ardour/ardour.h"
#include "ardour/audiosource.h"
namespace ARDOUR {
class AudioPlaylist;
class AudioPlaylistSource : public AudioSource {
public:
virtual ~AudioPlaylistSource ();
bool empty() const;
std::string peak_path (std::string audio_path);
uint32_t n_channels() const;
bool clamped_at_unity () const { return false; }
framecnt_t read_unlocked (Sample *dst, framepos_t start, framecnt_t cnt) const;
framecnt_t write_unlocked (Sample *src, framecnt_t cnt);
float sample_rate () const;
int setup_peakfile ();
XMLNode& get_state ();
int set_state (const XMLNode&, int version);
bool can_truncate_peaks() const { return false; }
bool can_be_analysed() const { return _length > 0; }
protected:
friend class SourceFactory;
AudioPlaylistSource (Session&, const std::string& name, boost::shared_ptr<AudioPlaylist>, uint32_t chn,
frameoffset_t begin, framecnt_t len, bool copy, Source::Flag flags);
AudioPlaylistSource (Session&, const XMLNode&);
private:
boost::shared_ptr<AudioPlaylist> _playlist;
frameoffset_t _playlist_offset;
framecnt_t _playlist_length;
uint32_t _playlist_channel;
std::string _peak_path;
};
} /* namespace */
#endif /* __ardour_audio_playlist_source_h__ */

View File

@ -47,11 +47,11 @@ class AudioSource : virtual public Source,
virtual ~AudioSource (); virtual ~AudioSource ();
framecnt_t readable_length() const { return _length; } framecnt_t readable_length() const { return _length; }
uint32_t n_channels() const { return 1; } virtual uint32_t n_channels() const { return 1; }
bool empty() const; virtual bool empty() const;
framecnt_t length (framepos_t pos) const; framecnt_t length (framepos_t pos) const;
void update_length (framepos_t pos, framecnt_t cnt); void update_length (framepos_t pos, framecnt_t cnt);
virtual framecnt_t available_peaks (double zoom) const; virtual framecnt_t available_peaks (double zoom) const;
@ -129,7 +129,7 @@ class AudioSource : virtual public Source,
virtual framecnt_t write_unlocked (Sample *dst, framecnt_t cnt) = 0; virtual framecnt_t write_unlocked (Sample *dst, framecnt_t cnt) = 0;
virtual std::string peak_path(std::string audio_path) = 0; virtual std::string peak_path(std::string audio_path) = 0;
virtual std::string find_broken_peakfile (std::string missing_peak_path, virtual std::string find_broken_peakfile (std::string missing_peak_path,
std::string audio_path) = 0; std::string audio_path) { return std::string(); }
virtual int read_peaks_with_fpp (PeakData *peaks, virtual int read_peaks_with_fpp (PeakData *peaks,
framecnt_t npeaks, framepos_t start, framecnt_t cnt, framecnt_t npeaks, framepos_t start, framecnt_t cnt,

View File

@ -46,7 +46,7 @@ class FileSource : virtual public Source {
public: public:
virtual ~FileSource () {} virtual ~FileSource () {}
const std::string& path() const { return _path; } virtual const std::string& path() const { return _path; }
virtual bool safe_file_extension (const std::string& path) const = 0; virtual bool safe_file_extension (const std::string& path) const = 0;

View File

@ -173,15 +173,12 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
static PBD::Signal1<void,std::string> Dialog; static PBD::Signal1<void,std::string> Dialog;
std::string sound_dir (bool with_path = true) const; int ensure_subdirs ();
std::string peak_dir () const;
std::string dead_dir () const;
std::string automation_dir () const; std::string automation_dir () const;
std::string analysis_dir() const; std::string analysis_dir() const;
std::string plugins_dir() const; std::string plugins_dir() const;
int ensure_subdirs ();
std::string peak_path (std::string) const; std::string peak_path (std::string) const;
std::string change_source_path_by_name (std::string oldpath, std::string oldname, std::string newname, bool destructive); std::string change_source_path_by_name (std::string oldpath, std::string oldname, std::string newname, bool destructive);

View File

@ -64,8 +64,6 @@ class Source : public SessionObject
virtual framecnt_t length (framepos_t pos) const = 0; virtual framecnt_t length (framepos_t pos) const = 0;
virtual void update_length (framepos_t pos, framecnt_t cnt) = 0; virtual void update_length (framepos_t pos, framecnt_t cnt) = 0;
virtual const std::string& path() const = 0;
virtual framepos_t natural_position() const { return 0; } virtual framepos_t natural_position() const { return 0; }
void mark_for_remove(); void mark_for_remove();

View File

@ -32,6 +32,7 @@ namespace ARDOUR {
class Session; class Session;
class AudioSource; class AudioSource;
class Playlist;
class SourceFactory { class SourceFactory {
public: public:
@ -43,13 +44,21 @@ class SourceFactory {
static boost::shared_ptr<Source> createSilent (Session&, const XMLNode& node, static boost::shared_ptr<Source> createSilent (Session&, const XMLNode& node,
framecnt_t nframes, float sample_rate); framecnt_t nframes, float sample_rate);
static boost::shared_ptr<Source> createReadable (DataType type, Session&, static boost::shared_ptr<Source> createReadable
const std::string& path, (DataType type, Session&,
int chn, Source::Flag flags, bool announce = true, bool async = false); const std::string& path,
int chn, Source::Flag flags, bool announce = true, bool async = false);
static boost::shared_ptr<Source> createWritable (DataType type, Session&, static boost::shared_ptr<Source> createWritable
const std::string& path, const std::string& origin, (DataType type, Session&,
bool destructive, framecnt_t rate, bool announce = true, bool async = false); const std::string& path, const std::string& origin,
bool destructive, framecnt_t rate, bool announce = true, bool async = false);
static boost::shared_ptr<Source> createFromPlaylist
(DataType type, Session& s, boost::shared_ptr<Playlist> p, const std::string& name,
uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, Source::Flag flags,
bool announce, bool defer_peaks);
static Glib::Cond* PeaksToBuild; static Glib::Cond* PeaksToBuild;
static Glib::StaticMutex peak_building_lock; static Glib::StaticMutex peak_building_lock;

View File

@ -0,0 +1,250 @@
/*
Copyright (C) 2011 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.
*/
#ifdef WAF_BUILD
#include "libardour-config.h"
#endif
#include <vector>
#include <cstdio>
#include <glibmm/fileutils.h>
#include "pbd/error.h"
#include "pbd/convert.h"
#include "pbd/enumwriter.h"
#include "ardour/audioplaylist.h"
#include "ardour/audio_playlist_source.h"
#include "ardour/audioregion.h"
#include "ardour/debug.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
#include "ardour/session_playlists.h"
#include "ardour/source_factory.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
AudioPlaylistSource::AudioPlaylistSource (Session& s, const std::string& name, boost::shared_ptr<AudioPlaylist> p,
uint32_t chn, frameoffset_t begin, framecnt_t len, bool copy, Source::Flag flags)
: Source (s, DataType::AUDIO, name)
, AudioSource (s, name)
, _playlist_channel (chn)
{
/* PlaylistSources are never writable, renameable, removable or destructive */
_flags = Flag (_flags & ~(Writable|CanRename|Removable|RemovableIfEmpty|RemoveAtDestroy|Destructive));
if (!copy) {
_playlist = p;
_playlist_offset = begin;
_playlist_length = len;
} else {
_playlist.reset (new AudioPlaylist (p, begin, len, "XXXNAMEXXX", true));
_playlist_offset = 0;
_playlist_length = len;
}
_peak_path = tempnam (_session.session_directory().peak_path().to_string().c_str(), "apspk");
}
AudioPlaylistSource::AudioPlaylistSource (Session& s, const XMLNode& node)
: Source (s, DataType::AUDIO, "toBeRenamed")
, AudioSource (s, node)
{
/* PlaylistSources are never writable, renameable, removable or destructive */
_flags = Flag (_flags & ~(Writable|CanRename|Removable|RemovableIfEmpty|RemoveAtDestroy|Destructive));
set_state (node, 3000);
}
AudioPlaylistSource::~AudioPlaylistSource ()
{
}
XMLNode&
AudioPlaylistSource::get_state ()
{
XMLNode& node (AudioSource::get_state ());
char buf[64];
snprintf (buf, sizeof (buf), "%" PRIi64, _playlist_offset);
node.add_property ("offset", buf);
snprintf (buf, sizeof (buf), "%" PRIu64, _playlist_length);
node.add_property ("length", buf);
snprintf (buf, sizeof (buf), "%" PRIu32, _playlist_channel);
node.add_property ("channel", buf);
node.add_property ("name", name());
node.add_property ("peak-path", _peak_path);
return node;
}
int
AudioPlaylistSource::set_state (const XMLNode& node, int /* version */)
{
/* get playlist ID */
const XMLProperty *prop = node.property (X_("playlist"));
if (!prop) {
throw failed_constructor ();
}
PBD::ID id (prop->value());
/* get playlist */
boost::shared_ptr<Playlist> p = _session.playlists->by_id (id);
if (!p) {
throw failed_constructor ();
}
/* other properties */
if ((prop = node.property (X_("name"))) == 0) {
throw failed_constructor ();
}
set_name (prop->value());
if ((prop = node.property (X_("offset"))) == 0) {
throw failed_constructor ();
}
sscanf (prop->value().c_str(), "%" PRIi64, &_playlist_offset);
if ((prop = node.property (X_("length"))) == 0) {
throw failed_constructor ();
}
sscanf (prop->value().c_str(), "%" PRIu64, &_playlist_length);
if ((prop = node.property (X_("channel"))) == 0) {
throw failed_constructor ();
}
sscanf (prop->value().c_str(), "%" PRIu32, &_playlist_channel);
if ((prop = node.property (X_("peak-path"))) == 0) {
throw failed_constructor ();
}
_peak_path = prop->value ();
return 0;
}
framecnt_t
AudioPlaylistSource::read_unlocked (Sample* dst, framepos_t start, framecnt_t cnt) const
{
framecnt_t to_read;
framecnt_t to_zero;
pair<framepos_t,framepos_t> extent = _playlist->get_extent();
/* we must be careful not to read beyond the end of our "section" of
* the playlist, because otherwise we may read data that exists, but
* is not supposed be part of our data.
*/
if (cnt > _playlist_length - start) {
to_read = _playlist_length - start;
to_zero = cnt - to_read;
} else {
to_read = cnt;
to_zero = 0;
}
_playlist->read (dst, 0, 0, start+_playlist_offset, to_read, _playlist_channel);
if (to_zero) {
memset (dst+to_read, 0, sizeof (Sample) * to_zero);
}
return cnt;
}
framecnt_t
AudioPlaylistSource::write_unlocked (Sample *src, framecnt_t cnt)
{
fatal << string_compose (_("programming error: %1"), "AudioPlaylistSource::write() called - should be impossible") << endmsg;
/*NOTREACHED*/
return 0;
}
bool
AudioPlaylistSource::empty () const
{
return !_playlist || _playlist->empty();
}
uint32_t
AudioPlaylistSource::n_channels () const
{
/* use just the first region to decide */
if (empty()) {
return 1;
}
boost::shared_ptr<Region> r = _playlist->region_list().front ();
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
return ar->audio_source()->n_channels ();
}
float
AudioPlaylistSource::sample_rate () const
{
/* use just the first region to decide */
if (empty()) {
_session.frame_rate ();
}
boost::shared_ptr<Region> r = _playlist->region_list().front ();
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
return ar->audio_source()->sample_rate ();
}
int
AudioPlaylistSource::setup_peakfile ()
{
/* the peak data is setup once and once only
*/
if (!Glib::file_test (_peak_path, Glib::FILE_TEST_EXISTS)) {
/* the 2nd argument here will be passed
in to ::peak_path, and is irrelevant
since our peak file path is fixed and
not dependent on anything.
*/
return initialize_peakfile (true, string());
}
return 0;
}
string
AudioPlaylistSource::peak_path (string /*audio_path*/)
{
return _peak_path;
}

View File

@ -461,7 +461,11 @@ write_midi_data_to_new_files (Evoral::SMF* source, ImportStatus& status,
static void static void
remove_file_source (boost::shared_ptr<Source> source) remove_file_source (boost::shared_ptr<Source> source)
{ {
::unlink (source->path().c_str()); boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (source);
if (fs) {
::unlink (fs->path().c_str());
}
} }
// This function is still unable to cleanly update an existing source, even though // This function is still unable to cleanly update an existing source, even though

View File

@ -300,7 +300,7 @@ Session::destroy ()
DEBUG_TRACE (DEBUG::Destruction, "delete sources\n"); DEBUG_TRACE (DEBUG::Destruction, "delete sources\n");
for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) { for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->path(), i->second.use_count())); DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for source %1 ; pre-ref = %2\n", i->second->name(), i->second.use_count()));
i->second->drop_references (); i->second->drop_references ();
} }

View File

@ -28,6 +28,8 @@
#include "pbd/pthread_utils.h" #include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h" #include "pbd/stacktrace.h"
#include "ardour/audioplaylist.h"
#include "ardour/audio_playlist_source.h"
#include "ardour/source_factory.h" #include "ardour/source_factory.h"
#include "ardour/sndfilesource.h" #include "ardour/sndfilesource.h"
#include "ardour/silentfilesource.h" #include "ardour/silentfilesource.h"
@ -322,3 +324,42 @@ SourceFactory::createWritable (DataType type, Session& s, const std::string& pat
return boost::shared_ptr<Source> (); return boost::shared_ptr<Source> ();
} }
boost::shared_ptr<Source>
SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> p, const std::string& name,
uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, Source::Flag flags,
bool announce, bool defer_peaks)
{
if (type == DataType::AUDIO) {
try {
boost::shared_ptr<AudioPlaylist> ap = boost::dynamic_pointer_cast<AudioPlaylist>(p);
if (ap) {
Source* src = new AudioPlaylistSource (s, name, ap, chn, start, len, copy, flags);
boost::shared_ptr<Source> ret (src);
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
ret->check_for_analysis_data_on_disk ();
if (announce) {
SourceCreated (ret);
}
return ret;
}
}
catch (failed_constructor& err) {
/* relax - return at function scope */
}
} else if (type == DataType::MIDI) {
}
return boost::shared_ptr<Source>();
}

View File

@ -39,6 +39,7 @@ libardour_sources = [
'audio_library.cc', 'audio_library.cc',
'audio_playlist.cc', 'audio_playlist.cc',
'audio_playlist_importer.cc', 'audio_playlist_importer.cc',
'audio_playlist_source.cc',
'audio_port.cc', 'audio_port.cc',
'audio_region_importer.cc', 'audio_region_importer.cc',
'audio_track.cc', 'audio_track.cc',